Merge branch 'ps/show-ref'
Teach "git show-ref" a mode to check the existence of a ref. * ps/show-ref: t: use git-show-ref(1) to check for ref existence builtin/show-ref: add new mode to check for reference existence builtin/show-ref: explicitly spell out different modes in synopsis builtin/show-ref: ensure mutual exclusiveness of subcommands builtin/show-ref: refactor options for patterns subcommand builtin/show-ref: stop using global vars for `show_one()` builtin/show-ref: stop using global variable to count matches builtin/show-ref: refactor `--exclude-existing` options builtin/show-ref: fix dead code when passing patterns builtin/show-ref: fix leaking string buffer builtin/show-ref: split up different subcommands builtin/show-ref: convert pattern to a local variablemaint
commit
d8972a5abd
|
@ -8,10 +8,14 @@ git-show-ref - List references in a local repository
|
|||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git show-ref' [-q | --quiet] [--verify] [--head] [-d | --dereference]
|
||||
'git show-ref' [--head] [-d | --dereference]
|
||||
[-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]
|
||||
[--heads] [--] [<pattern>...]
|
||||
'git show-ref' --verify [-q | --quiet] [-d | --dereference]
|
||||
[-s | --hash[=<n>]] [--abbrev[=<n>]]
|
||||
[--] [<ref>...]
|
||||
'git show-ref' --exclude-existing[=<pattern>]
|
||||
'git show-ref' --exists <ref>
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
@ -27,6 +31,10 @@ The `--exclude-existing` form is a filter that does the inverse. It reads
|
|||
refs from stdin, one ref per line, and shows those that don't exist in
|
||||
the local repository.
|
||||
|
||||
The `--exists` form can be used to check for the existence of a single
|
||||
references. This form does not verify whether the reference resolves to an
|
||||
actual object.
|
||||
|
||||
Use of this utility is encouraged in favor of directly accessing files under
|
||||
the `.git` directory.
|
||||
|
||||
|
@ -62,6 +70,12 @@ OPTIONS
|
|||
Aside from returning an error code of 1, it will also print an error
|
||||
message if `--quiet` was not specified.
|
||||
|
||||
--exists::
|
||||
|
||||
Check whether the given reference exists. Returns an exit code of 0 if
|
||||
it does, 2 if it is missing, and 1 in case looking up the reference
|
||||
failed with an error other than the reference being missing.
|
||||
|
||||
--abbrev[=<n>]::
|
||||
|
||||
Abbreviate the object name. When using `--hash`, you do
|
||||
|
@ -70,8 +84,8 @@ OPTIONS
|
|||
-q::
|
||||
--quiet::
|
||||
|
||||
Do not print any results to stdout. When combined with `--verify`, this
|
||||
can be used to silently check if a reference exists.
|
||||
Do not print any results to stdout. Can be used with `--verify` to
|
||||
silently check if a reference exists.
|
||||
|
||||
--exclude-existing[=<pattern>]::
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "config.h"
|
||||
#include "gettext.h"
|
||||
#include "hex.h"
|
||||
#include "refs.h"
|
||||
#include "refs/refs-internal.h"
|
||||
#include "object-name.h"
|
||||
#include "object-store-ll.h"
|
||||
#include "object.h"
|
||||
|
@ -11,19 +11,26 @@
|
|||
#include "parse-options.h"
|
||||
|
||||
static const char * const show_ref_usage[] = {
|
||||
N_("git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
|
||||
N_("git show-ref [--head] [-d | --dereference]\n"
|
||||
" [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
|
||||
" [--heads] [--] [<pattern>...]"),
|
||||
N_("git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
|
||||
" [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
|
||||
" [--] [<ref>...]"),
|
||||
N_("git show-ref --exclude-existing[=<pattern>]"),
|
||||
N_("git show-ref --exists <ref>"),
|
||||
NULL
|
||||
};
|
||||
|
||||
static int deref_tags, show_head, tags_only, heads_only, found_match, verify,
|
||||
quiet, hash_only, abbrev, exclude_arg;
|
||||
static const char **pattern;
|
||||
static const char *exclude_existing_arg;
|
||||
struct show_one_options {
|
||||
int quiet;
|
||||
int hash_only;
|
||||
int abbrev;
|
||||
int deref_tags;
|
||||
};
|
||||
|
||||
static void show_one(const char *refname, const struct object_id *oid)
|
||||
static void show_one(const struct show_one_options *opts,
|
||||
const char *refname, const struct object_id *oid)
|
||||
{
|
||||
const char *hex;
|
||||
struct object_id peeled;
|
||||
|
@ -32,33 +39,42 @@ static void show_one(const char *refname, const struct object_id *oid)
|
|||
die("git show-ref: bad ref %s (%s)", refname,
|
||||
oid_to_hex(oid));
|
||||
|
||||
if (quiet)
|
||||
if (opts->quiet)
|
||||
return;
|
||||
|
||||
hex = repo_find_unique_abbrev(the_repository, oid, abbrev);
|
||||
if (hash_only)
|
||||
hex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev);
|
||||
if (opts->hash_only)
|
||||
printf("%s\n", hex);
|
||||
else
|
||||
printf("%s %s\n", hex, refname);
|
||||
|
||||
if (!deref_tags)
|
||||
if (!opts->deref_tags)
|
||||
return;
|
||||
|
||||
if (!peel_iterated_oid(oid, &peeled)) {
|
||||
hex = repo_find_unique_abbrev(the_repository, &peeled, abbrev);
|
||||
hex = repo_find_unique_abbrev(the_repository, &peeled, opts->abbrev);
|
||||
printf("%s %s^{}\n", hex, refname);
|
||||
}
|
||||
}
|
||||
|
||||
struct show_ref_data {
|
||||
const struct show_one_options *show_one_opts;
|
||||
const char **patterns;
|
||||
int found_match;
|
||||
int show_head;
|
||||
};
|
||||
|
||||
static int show_ref(const char *refname, const struct object_id *oid,
|
||||
int flag UNUSED, void *cbdata UNUSED)
|
||||
int flag UNUSED, void *cbdata)
|
||||
{
|
||||
if (show_head && !strcmp(refname, "HEAD"))
|
||||
struct show_ref_data *data = cbdata;
|
||||
|
||||
if (data->show_head && !strcmp(refname, "HEAD"))
|
||||
goto match;
|
||||
|
||||
if (pattern) {
|
||||
if (data->patterns) {
|
||||
int reflen = strlen(refname);
|
||||
const char **p = pattern, *m;
|
||||
const char **p = data->patterns, *m;
|
||||
while ((m = *p++) != NULL) {
|
||||
int len = strlen(m);
|
||||
if (len > reflen)
|
||||
|
@ -74,9 +90,9 @@ static int show_ref(const char *refname, const struct object_id *oid,
|
|||
}
|
||||
|
||||
match:
|
||||
found_match++;
|
||||
data->found_match++;
|
||||
|
||||
show_one(refname, oid);
|
||||
show_one(data->show_one_opts, refname, oid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -90,6 +106,15 @@ static int add_existing(const char *refname,
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct exclude_existing_options {
|
||||
/*
|
||||
* We need an explicit `enabled` field because it is perfectly valid
|
||||
* for `pattern` to be `NULL` even if `--exclude-existing` was given.
|
||||
*/
|
||||
int enabled;
|
||||
const char *pattern;
|
||||
};
|
||||
|
||||
/*
|
||||
* read "^(?:<anything>\s)?<refname>(?:\^\{\})?$" from the standard input,
|
||||
* and
|
||||
|
@ -99,11 +124,11 @@ static int add_existing(const char *refname,
|
|||
* (4) ignore if refname is a ref that exists in the local repository;
|
||||
* (5) otherwise output the line.
|
||||
*/
|
||||
static int exclude_existing(const char *match)
|
||||
static int cmd_show_ref__exclude_existing(const struct exclude_existing_options *opts)
|
||||
{
|
||||
static struct string_list existing_refs = STRING_LIST_INIT_DUP;
|
||||
struct string_list existing_refs = STRING_LIST_INIT_DUP;
|
||||
char buf[1024];
|
||||
int matchlen = match ? strlen(match) : 0;
|
||||
int patternlen = opts->pattern ? strlen(opts->pattern) : 0;
|
||||
|
||||
for_each_ref(add_existing, &existing_refs);
|
||||
while (fgets(buf, sizeof(buf), stdin)) {
|
||||
|
@ -119,11 +144,11 @@ static int exclude_existing(const char *match)
|
|||
for (ref = buf + len; buf < ref; ref--)
|
||||
if (isspace(ref[-1]))
|
||||
break;
|
||||
if (match) {
|
||||
if (opts->pattern) {
|
||||
int reflen = buf + len - ref;
|
||||
if (reflen < matchlen)
|
||||
if (reflen < patternlen)
|
||||
continue;
|
||||
if (strncmp(ref, match, matchlen))
|
||||
if (strncmp(ref, opts->pattern, patternlen))
|
||||
continue;
|
||||
}
|
||||
if (check_refname_format(ref, 0)) {
|
||||
|
@ -134,97 +159,172 @@ static int exclude_existing(const char *match)
|
|||
printf("%s\n", buf);
|
||||
}
|
||||
}
|
||||
|
||||
string_list_clear(&existing_refs, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
|
||||
const char **refs)
|
||||
{
|
||||
if (!refs || !*refs)
|
||||
die("--verify requires a reference");
|
||||
|
||||
while (*refs) {
|
||||
struct object_id oid;
|
||||
|
||||
if ((starts_with(*refs, "refs/") || !strcmp(*refs, "HEAD")) &&
|
||||
!read_ref(*refs, &oid)) {
|
||||
show_one(show_one_opts, *refs, &oid);
|
||||
}
|
||||
else if (!show_one_opts->quiet)
|
||||
die("'%s' - not a valid ref", *refs);
|
||||
else
|
||||
return 1;
|
||||
refs++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct patterns_options {
|
||||
int show_head;
|
||||
int heads_only;
|
||||
int tags_only;
|
||||
};
|
||||
|
||||
static int cmd_show_ref__patterns(const struct patterns_options *opts,
|
||||
const struct show_one_options *show_one_opts,
|
||||
const char **patterns)
|
||||
{
|
||||
struct show_ref_data show_ref_data = {
|
||||
.show_one_opts = show_one_opts,
|
||||
.show_head = opts->show_head,
|
||||
};
|
||||
|
||||
if (patterns && *patterns)
|
||||
show_ref_data.patterns = patterns;
|
||||
|
||||
if (opts->show_head)
|
||||
head_ref(show_ref, &show_ref_data);
|
||||
if (opts->heads_only || opts->tags_only) {
|
||||
if (opts->heads_only)
|
||||
for_each_fullref_in("refs/heads/", show_ref, &show_ref_data);
|
||||
if (opts->tags_only)
|
||||
for_each_fullref_in("refs/tags/", show_ref, &show_ref_data);
|
||||
} else {
|
||||
for_each_ref(show_ref, &show_ref_data);
|
||||
}
|
||||
if (!show_ref_data.found_match)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_show_ref__exists(const char **refs)
|
||||
{
|
||||
struct strbuf unused_referent = STRBUF_INIT;
|
||||
struct object_id unused_oid;
|
||||
unsigned int unused_type;
|
||||
int failure_errno = 0;
|
||||
const char *ref;
|
||||
int ret = 0;
|
||||
|
||||
if (!refs || !*refs)
|
||||
die("--exists requires a reference");
|
||||
ref = *refs++;
|
||||
if (*refs)
|
||||
die("--exists requires exactly one reference");
|
||||
|
||||
if (refs_read_raw_ref(get_main_ref_store(the_repository), ref,
|
||||
&unused_oid, &unused_referent, &unused_type,
|
||||
&failure_errno)) {
|
||||
if (failure_errno == ENOENT) {
|
||||
error(_("reference does not exist"));
|
||||
ret = 2;
|
||||
} else {
|
||||
errno = failure_errno;
|
||||
error_errno(_("failed to look up reference"));
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
strbuf_release(&unused_referent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hash_callback(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
hash_only = 1;
|
||||
struct show_one_options *opts = opt->value;
|
||||
struct option abbrev_opt = *opt;
|
||||
|
||||
opts->hash_only = 1;
|
||||
/* Use full length SHA1 if no argument */
|
||||
if (!arg)
|
||||
return 0;
|
||||
return parse_opt_abbrev_cb(opt, arg, unset);
|
||||
|
||||
abbrev_opt.value = &opts->abbrev;
|
||||
return parse_opt_abbrev_cb(&abbrev_opt, arg, unset);
|
||||
}
|
||||
|
||||
static int exclude_existing_callback(const struct option *opt, const char *arg,
|
||||
int unset)
|
||||
{
|
||||
struct exclude_existing_options *opts = opt->value;
|
||||
BUG_ON_OPT_NEG(unset);
|
||||
exclude_arg = 1;
|
||||
*(const char **)opt->value = arg;
|
||||
opts->enabled = 1;
|
||||
opts->pattern = arg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct option show_ref_options[] = {
|
||||
OPT_BOOL(0, "tags", &tags_only, N_("only show tags (can be combined with heads)")),
|
||||
OPT_BOOL(0, "heads", &heads_only, N_("only show heads (can be combined with tags)")),
|
||||
OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, "
|
||||
"requires exact ref path")),
|
||||
OPT_HIDDEN_BOOL('h', NULL, &show_head,
|
||||
N_("show the HEAD reference, even if it would be filtered out")),
|
||||
OPT_BOOL(0, "head", &show_head,
|
||||
N_("show the HEAD reference, even if it would be filtered out")),
|
||||
OPT_BOOL('d', "dereference", &deref_tags,
|
||||
N_("dereference tags into object IDs")),
|
||||
OPT_CALLBACK_F('s', "hash", &abbrev, N_("n"),
|
||||
N_("only show SHA1 hash using <n> digits"),
|
||||
PARSE_OPT_OPTARG, &hash_callback),
|
||||
OPT__ABBREV(&abbrev),
|
||||
OPT__QUIET(&quiet,
|
||||
N_("do not print results to stdout (useful with --verify)")),
|
||||
OPT_CALLBACK_F(0, "exclude-existing", &exclude_existing_arg,
|
||||
N_("pattern"), N_("show refs from stdin that aren't in local repository"),
|
||||
PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
int cmd_show_ref(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct exclude_existing_options exclude_existing_opts = {0};
|
||||
struct patterns_options patterns_opts = {0};
|
||||
struct show_one_options show_one_opts = {0};
|
||||
int verify = 0, exists = 0;
|
||||
const struct option show_ref_options[] = {
|
||||
OPT_BOOL(0, "tags", &patterns_opts.tags_only, N_("only show tags (can be combined with heads)")),
|
||||
OPT_BOOL(0, "heads", &patterns_opts.heads_only, N_("only show heads (can be combined with tags)")),
|
||||
OPT_BOOL(0, "exists", &exists, N_("check for reference existence without resolving")),
|
||||
OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, "
|
||||
"requires exact ref path")),
|
||||
OPT_HIDDEN_BOOL('h', NULL, &patterns_opts.show_head,
|
||||
N_("show the HEAD reference, even if it would be filtered out")),
|
||||
OPT_BOOL(0, "head", &patterns_opts.show_head,
|
||||
N_("show the HEAD reference, even if it would be filtered out")),
|
||||
OPT_BOOL('d', "dereference", &show_one_opts.deref_tags,
|
||||
N_("dereference tags into object IDs")),
|
||||
OPT_CALLBACK_F('s', "hash", &show_one_opts, N_("n"),
|
||||
N_("only show SHA1 hash using <n> digits"),
|
||||
PARSE_OPT_OPTARG, &hash_callback),
|
||||
OPT__ABBREV(&show_one_opts.abbrev),
|
||||
OPT__QUIET(&show_one_opts.quiet,
|
||||
N_("do not print results to stdout (useful with --verify)")),
|
||||
OPT_CALLBACK_F(0, "exclude-existing", &exclude_existing_opts,
|
||||
N_("pattern"), N_("show refs from stdin that aren't in local repository"),
|
||||
PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
git_config(git_default_config, NULL);
|
||||
|
||||
argc = parse_options(argc, argv, prefix, show_ref_options,
|
||||
show_ref_usage, 0);
|
||||
|
||||
if (exclude_arg)
|
||||
return exclude_existing(exclude_existing_arg);
|
||||
if ((!!exclude_existing_opts.enabled + !!verify + !!exists) > 1)
|
||||
die(_("only one of '%s', '%s' or '%s' can be given"),
|
||||
"--exclude-existing", "--verify", "--exists");
|
||||
|
||||
pattern = argv;
|
||||
if (!*pattern)
|
||||
pattern = NULL;
|
||||
|
||||
if (verify) {
|
||||
if (!pattern)
|
||||
die("--verify requires a reference");
|
||||
while (*pattern) {
|
||||
struct object_id oid;
|
||||
|
||||
if ((starts_with(*pattern, "refs/") || !strcmp(*pattern, "HEAD")) &&
|
||||
!read_ref(*pattern, &oid)) {
|
||||
show_one(*pattern, &oid);
|
||||
}
|
||||
else if (!quiet)
|
||||
die("'%s' - not a valid ref", *pattern);
|
||||
else
|
||||
return 1;
|
||||
pattern++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (show_head)
|
||||
head_ref(show_ref, NULL);
|
||||
if (heads_only || tags_only) {
|
||||
if (heads_only)
|
||||
for_each_fullref_in("refs/heads/", show_ref, NULL);
|
||||
if (tags_only)
|
||||
for_each_fullref_in("refs/tags/", show_ref, NULL);
|
||||
} else {
|
||||
for_each_ref(show_ref, NULL);
|
||||
}
|
||||
if (!found_match) {
|
||||
if (verify && !quiet)
|
||||
die("No match");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
if (exclude_existing_opts.enabled)
|
||||
return cmd_show_ref__exclude_existing(&exclude_existing_opts);
|
||||
else if (verify)
|
||||
return cmd_show_ref__verify(&show_one_opts, argv);
|
||||
else if (exists)
|
||||
return cmd_show_ref__exists(argv);
|
||||
else
|
||||
return cmd_show_ref__patterns(&patterns_opts, &show_one_opts, argv);
|
||||
}
|
||||
|
|
|
@ -196,4 +196,74 @@ test_expect_success 'show-ref --verify with dangling ref' '
|
|||
)
|
||||
'
|
||||
|
||||
test_expect_success 'show-ref sub-modes are mutually exclusive' '
|
||||
cat >expect <<-EOF &&
|
||||
fatal: only one of ${SQ}--exclude-existing${SQ}, ${SQ}--verify${SQ} or ${SQ}--exists${SQ} can be given
|
||||
EOF
|
||||
|
||||
test_must_fail git show-ref --verify --exclude-existing 2>err &&
|
||||
test_cmp expect err &&
|
||||
|
||||
test_must_fail git show-ref --verify --exists 2>err &&
|
||||
test_cmp expect err &&
|
||||
|
||||
test_must_fail git show-ref --exclude-existing --exists 2>err &&
|
||||
test_cmp expect err
|
||||
'
|
||||
|
||||
test_expect_success '--exists with existing reference' '
|
||||
git show-ref --exists refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
||||
'
|
||||
|
||||
test_expect_success '--exists with missing reference' '
|
||||
test_expect_code 2 git show-ref --exists refs/heads/does-not-exist
|
||||
'
|
||||
|
||||
test_expect_success '--exists does not use DWIM' '
|
||||
test_expect_code 2 git show-ref --exists $GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 2>err &&
|
||||
grep "reference does not exist" err
|
||||
'
|
||||
|
||||
test_expect_success '--exists with HEAD' '
|
||||
git show-ref --exists HEAD
|
||||
'
|
||||
|
||||
test_expect_success '--exists with bad reference name' '
|
||||
test_when_finished "git update-ref -d refs/heads/bad...name" &&
|
||||
new_oid=$(git rev-parse HEAD) &&
|
||||
test-tool ref-store main update-ref msg refs/heads/bad...name $new_oid $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
|
||||
git show-ref --exists refs/heads/bad...name
|
||||
'
|
||||
|
||||
test_expect_success '--exists with arbitrary symref' '
|
||||
test_when_finished "git symbolic-ref -d refs/symref" &&
|
||||
git symbolic-ref refs/symref refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&
|
||||
git show-ref --exists refs/symref
|
||||
'
|
||||
|
||||
test_expect_success '--exists with dangling symref' '
|
||||
test_when_finished "git symbolic-ref -d refs/heads/dangling" &&
|
||||
git symbolic-ref refs/heads/dangling refs/heads/does-not-exist &&
|
||||
git show-ref --exists refs/heads/dangling
|
||||
'
|
||||
|
||||
test_expect_success '--exists with nonexistent object ID' '
|
||||
test-tool ref-store main update-ref msg refs/heads/missing-oid $(test_oid 001) $ZERO_OID REF_SKIP_OID_VERIFICATION &&
|
||||
git show-ref --exists refs/heads/missing-oid
|
||||
'
|
||||
|
||||
test_expect_success '--exists with non-commit object' '
|
||||
tree_oid=$(git rev-parse HEAD^{tree}) &&
|
||||
test-tool ref-store main update-ref msg refs/heads/tree ${tree_oid} $ZERO_OID REF_SKIP_OID_VERIFICATION &&
|
||||
git show-ref --exists refs/heads/tree
|
||||
'
|
||||
|
||||
test_expect_success '--exists with directory fails with generic error' '
|
||||
cat >expect <<-EOF &&
|
||||
error: failed to look up reference: Is a directory
|
||||
EOF
|
||||
test_expect_code 1 git show-ref --exists refs/heads 2>err &&
|
||||
test_cmp expect err
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -205,8 +205,9 @@ test_expect_success 'update-ref --no-deref -d can delete symref to broken name'
|
|||
test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
|
||||
test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
|
||||
test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
|
||||
test_ref_exists refs/heads/badname &&
|
||||
git update-ref --no-deref -d refs/heads/badname >output 2>error &&
|
||||
test_path_is_missing .git/refs/heads/badname &&
|
||||
test_ref_missing refs/heads/badname &&
|
||||
test_must_be_empty output &&
|
||||
test_must_be_empty error
|
||||
'
|
||||
|
@ -216,8 +217,9 @@ test_expect_success 'branch -d can delete symref to broken name' '
|
|||
test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
|
||||
test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
|
||||
test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
|
||||
test_ref_exists refs/heads/badname &&
|
||||
git branch -d badname >output 2>error &&
|
||||
test_path_is_missing .git/refs/heads/badname &&
|
||||
test_ref_missing refs/heads/badname &&
|
||||
test_i18ngrep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
|
||||
test_must_be_empty error
|
||||
'
|
||||
|
@ -225,8 +227,9 @@ test_expect_success 'branch -d can delete symref to broken name' '
|
|||
test_expect_success 'update-ref --no-deref -d can delete dangling symref to broken name' '
|
||||
test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
|
||||
test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
|
||||
test_ref_exists refs/heads/badname &&
|
||||
git update-ref --no-deref -d refs/heads/badname >output 2>error &&
|
||||
test_path_is_missing .git/refs/heads/badname &&
|
||||
test_ref_missing refs/heads/badname &&
|
||||
test_must_be_empty output &&
|
||||
test_must_be_empty error
|
||||
'
|
||||
|
@ -234,8 +237,9 @@ test_expect_success 'update-ref --no-deref -d can delete dangling symref to brok
|
|||
test_expect_success 'branch -d can delete dangling symref to broken name' '
|
||||
test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
|
||||
test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
|
||||
test_ref_exists refs/heads/badname &&
|
||||
git branch -d badname >output 2>error &&
|
||||
test_path_is_missing .git/refs/heads/badname &&
|
||||
test_ref_missing refs/heads/badname &&
|
||||
test_i18ngrep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
|
||||
test_must_be_empty error
|
||||
'
|
||||
|
@ -245,8 +249,9 @@ test_expect_success 'update-ref -d can delete broken name through symref' '
|
|||
test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
|
||||
test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
|
||||
test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
|
||||
test_ref_exists refs/heads/broken...ref &&
|
||||
git update-ref -d refs/heads/badname >output 2>error &&
|
||||
test_path_is_missing .git/refs/heads/broken...ref &&
|
||||
test_ref_missing refs/heads/broken...ref &&
|
||||
test_must_be_empty output &&
|
||||
test_must_be_empty error
|
||||
'
|
||||
|
@ -254,8 +259,9 @@ test_expect_success 'update-ref -d can delete broken name through symref' '
|
|||
test_expect_success 'update-ref --no-deref -d can delete symref with broken name' '
|
||||
printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
|
||||
test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
|
||||
test_ref_exists refs/heads/broken...symref &&
|
||||
git update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&
|
||||
test_path_is_missing .git/refs/heads/broken...symref &&
|
||||
test_ref_missing refs/heads/broken...symref &&
|
||||
test_must_be_empty output &&
|
||||
test_must_be_empty error
|
||||
'
|
||||
|
@ -263,8 +269,9 @@ test_expect_success 'update-ref --no-deref -d can delete symref with broken name
|
|||
test_expect_success 'branch -d can delete symref with broken name' '
|
||||
printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
|
||||
test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
|
||||
test_ref_exists refs/heads/broken...symref &&
|
||||
git branch -d broken...symref >output 2>error &&
|
||||
test_path_is_missing .git/refs/heads/broken...symref &&
|
||||
test_ref_missing refs/heads/broken...symref &&
|
||||
test_i18ngrep "Deleted branch broken...symref (was refs/heads/main)" output &&
|
||||
test_must_be_empty error
|
||||
'
|
||||
|
@ -272,8 +279,9 @@ test_expect_success 'branch -d can delete symref with broken name' '
|
|||
test_expect_success 'update-ref --no-deref -d can delete dangling symref with broken name' '
|
||||
printf "ref: refs/heads/idonotexist\n" >.git/refs/heads/broken...symref &&
|
||||
test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
|
||||
test_ref_exists refs/heads/broken...symref &&
|
||||
git update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&
|
||||
test_path_is_missing .git/refs/heads/broken...symref &&
|
||||
test_ref_missing refs/heads/broken...symref &&
|
||||
test_must_be_empty output &&
|
||||
test_must_be_empty error
|
||||
'
|
||||
|
@ -281,8 +289,9 @@ test_expect_success 'update-ref --no-deref -d can delete dangling symref with br
|
|||
test_expect_success 'branch -d can delete dangling symref with broken name' '
|
||||
printf "ref: refs/heads/idonotexist\n" >.git/refs/heads/broken...symref &&
|
||||
test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
|
||||
test_ref_exists refs/heads/broken...symref &&
|
||||
git branch -d broken...symref >output 2>error &&
|
||||
test_path_is_missing .git/refs/heads/broken...symref &&
|
||||
test_ref_missing refs/heads/broken...symref &&
|
||||
test_i18ngrep "Deleted branch broken...symref (was refs/heads/idonotexist)" output &&
|
||||
test_must_be_empty error
|
||||
'
|
||||
|
|
|
@ -25,7 +25,7 @@ test_expect_success 'prepare a trivial repository' '
|
|||
|
||||
test_expect_success 'git branch --help should not have created a bogus branch' '
|
||||
test_might_fail git branch --man --help </dev/null >/dev/null 2>&1 &&
|
||||
test_path_is_missing .git/refs/heads/--help
|
||||
test_ref_missing refs/heads/--help
|
||||
'
|
||||
|
||||
test_expect_success 'branch -h in broken repository' '
|
||||
|
@ -40,7 +40,8 @@ test_expect_success 'branch -h in broken repository' '
|
|||
'
|
||||
|
||||
test_expect_success 'git branch abc should create a branch' '
|
||||
git branch abc && test_path_is_file .git/refs/heads/abc
|
||||
git branch abc &&
|
||||
test_ref_exists refs/heads/abc
|
||||
'
|
||||
|
||||
test_expect_success 'git branch abc should fail when abc exists' '
|
||||
|
@ -61,11 +62,13 @@ test_expect_success 'git branch --force abc should succeed when abc exists' '
|
|||
'
|
||||
|
||||
test_expect_success 'git branch a/b/c should create a branch' '
|
||||
git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c
|
||||
git branch a/b/c &&
|
||||
test_ref_exists refs/heads/a/b/c
|
||||
'
|
||||
|
||||
test_expect_success 'git branch mb main... should create a branch' '
|
||||
git branch mb main... && test_path_is_file .git/refs/heads/mb
|
||||
git branch mb main... &&
|
||||
test_ref_exists refs/heads/mb
|
||||
'
|
||||
|
||||
test_expect_success 'git branch HEAD should fail' '
|
||||
|
@ -78,14 +81,14 @@ EOF
|
|||
test_expect_success 'git branch --create-reflog d/e/f should create a branch and a log' '
|
||||
GIT_COMMITTER_DATE="2005-05-26 23:30" \
|
||||
git -c core.logallrefupdates=false branch --create-reflog d/e/f &&
|
||||
test_path_is_file .git/refs/heads/d/e/f &&
|
||||
test_ref_exists refs/heads/d/e/f &&
|
||||
test_path_is_file .git/logs/refs/heads/d/e/f &&
|
||||
test_cmp expect .git/logs/refs/heads/d/e/f
|
||||
'
|
||||
|
||||
test_expect_success 'git branch -d d/e/f should delete a branch and a log' '
|
||||
git branch -d d/e/f &&
|
||||
test_path_is_missing .git/refs/heads/d/e/f &&
|
||||
test_ref_missing refs/heads/d/e/f &&
|
||||
test_must_fail git reflog exists refs/heads/d/e/f
|
||||
'
|
||||
|
||||
|
@ -213,7 +216,7 @@ test_expect_success 'git branch -M should leave orphaned HEAD alone' '
|
|||
test_commit initial &&
|
||||
git checkout --orphan lonely &&
|
||||
grep lonely .git/HEAD &&
|
||||
test_path_is_missing .git/refs/head/lonely &&
|
||||
test_ref_missing refs/head/lonely &&
|
||||
git branch -M main mistress &&
|
||||
grep lonely .git/HEAD
|
||||
)
|
||||
|
@ -799,8 +802,8 @@ test_expect_success 'deleting a symref' '
|
|||
git symbolic-ref refs/heads/symref refs/heads/target &&
|
||||
echo "Deleted branch symref (was refs/heads/target)." >expect &&
|
||||
git branch -d symref >actual &&
|
||||
test_path_is_file .git/refs/heads/target &&
|
||||
test_path_is_missing .git/refs/heads/symref &&
|
||||
test_ref_exists refs/heads/target &&
|
||||
test_ref_missing refs/heads/symref &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
|
@ -809,16 +812,16 @@ test_expect_success 'deleting a dangling symref' '
|
|||
test_path_is_file .git/refs/heads/dangling-symref &&
|
||||
echo "Deleted branch dangling-symref (was nowhere)." >expect &&
|
||||
git branch -d dangling-symref >actual &&
|
||||
test_path_is_missing .git/refs/heads/dangling-symref &&
|
||||
test_ref_missing refs/heads/dangling-symref &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'deleting a self-referential symref' '
|
||||
git symbolic-ref refs/heads/self-reference refs/heads/self-reference &&
|
||||
test_path_is_file .git/refs/heads/self-reference &&
|
||||
test_ref_exists refs/heads/self-reference &&
|
||||
echo "Deleted branch self-reference (was refs/heads/self-reference)." >expect &&
|
||||
git branch -d self-reference >actual &&
|
||||
test_path_is_missing .git/refs/heads/self-reference &&
|
||||
test_ref_missing refs/heads/self-reference &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
|
@ -826,8 +829,8 @@ test_expect_success 'renaming a symref is not allowed' '
|
|||
git symbolic-ref refs/heads/topic refs/heads/main &&
|
||||
test_must_fail git branch -m topic new-topic &&
|
||||
git symbolic-ref refs/heads/topic &&
|
||||
test_path_is_file .git/refs/heads/main &&
|
||||
test_path_is_missing .git/refs/heads/new-topic
|
||||
test_ref_exists refs/heads/main &&
|
||||
test_ref_missing refs/heads/new-topic
|
||||
'
|
||||
|
||||
test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '
|
||||
|
@ -1142,7 +1145,7 @@ EOF
|
|||
test_expect_success 'git checkout -b g/h/i -l should create a branch and a log' '
|
||||
GIT_COMMITTER_DATE="2005-05-26 23:30" \
|
||||
git checkout -b g/h/i -l main &&
|
||||
test_path_is_file .git/refs/heads/g/h/i &&
|
||||
test_ref_exists refs/heads/g/h/i &&
|
||||
test_path_is_file .git/logs/refs/heads/g/h/i &&
|
||||
test_cmp expect .git/logs/refs/heads/g/h/i
|
||||
'
|
||||
|
|
|
@ -143,7 +143,7 @@ test_expect_success 'git pull --dry-run' '
|
|||
cd clonedry &&
|
||||
git pull --dry-run ../parent &&
|
||||
test_path_is_missing .git/FETCH_HEAD &&
|
||||
test_path_is_missing .git/refs/heads/main &&
|
||||
test_ref_missing refs/heads/main &&
|
||||
test_path_is_missing .git/index &&
|
||||
test_path_is_missing file
|
||||
)
|
||||
|
@ -157,7 +157,7 @@ test_expect_success 'git pull --all --dry-run' '
|
|||
git remote add origin ../parent &&
|
||||
git pull --all --dry-run &&
|
||||
test_path_is_missing .git/FETCH_HEAD &&
|
||||
test_path_is_missing .git/refs/remotes/origin/main &&
|
||||
test_ref_missing refs/remotes/origin/main &&
|
||||
test_path_is_missing .git/index &&
|
||||
test_path_is_missing file
|
||||
)
|
||||
|
|
|
@ -69,7 +69,7 @@ test_expect_success 'local clone of repo with nonexistent ref in HEAD' '
|
|||
git clone a d &&
|
||||
(cd d &&
|
||||
git fetch &&
|
||||
test ! -e .git/refs/remotes/origin/HEAD)
|
||||
test_ref_missing refs/remotes/origin/HEAD)
|
||||
'
|
||||
|
||||
test_expect_success 'bundle clone without .bundle suffix' '
|
||||
|
|
|
@ -251,6 +251,61 @@ debug () {
|
|||
done
|
||||
}
|
||||
|
||||
# Usage: test_ref_exists [options] <ref>
|
||||
#
|
||||
# -C <dir>:
|
||||
# Run all git commands in directory <dir>
|
||||
#
|
||||
# This helper function checks whether a reference exists. Symrefs or object IDs
|
||||
# will not be resolved. Can be used to check references with bad names.
|
||||
test_ref_exists () {
|
||||
local indir=
|
||||
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-C)
|
||||
indir="$2"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done &&
|
||||
|
||||
indir=${indir:+"$indir"/} &&
|
||||
|
||||
if test "$#" != 1
|
||||
then
|
||||
BUG "expected exactly one reference"
|
||||
fi &&
|
||||
|
||||
git ${indir:+ -C "$indir"} show-ref --exists "$1"
|
||||
}
|
||||
|
||||
# Behaves the same as test_ref_exists, except that it checks for the absence of
|
||||
# a reference. This is preferable to `! test_ref_exists` as this function is
|
||||
# able to distinguish actually-missing references from other, generic errors.
|
||||
test_ref_missing () {
|
||||
test_ref_exists "$@"
|
||||
case "$?" in
|
||||
2)
|
||||
# This is the good case.
|
||||
return 0
|
||||
;;
|
||||
0)
|
||||
echo >&4 "test_ref_missing: reference exists"
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
echo >&4 "test_ref_missing: generic error"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Usage: test_commit [options] <message> [<file> [<contents> [<tag>]]]
|
||||
# -C <dir>:
|
||||
# Run all git commands in directory <dir>
|
||||
|
|
Loading…
Reference in New Issue