Skip to content

Commit

Permalink
show-ref: add --unresolved option
Browse files Browse the repository at this point in the history
For reftable development, it would be handy to have a tool to provide
the direct value of any ref whether it be a symbolic ref or not.
Currently there is git-symbolic-ref, which only works for symbolic refs,
and git-rev-parse, which will resolve the ref. Let's add a --unresolved
option that will only take one ref and return whatever it points to
without dereferencing it.
  • Loading branch information
john-cai committed Mar 4, 2024
1 parent b387623 commit e9145b6
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 12 deletions.
8 changes: 8 additions & 0 deletions Documentation/git-show-ref.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ SYNOPSIS
[--] [<ref>...]
'git show-ref' --exclude-existing[=<pattern>]
'git show-ref' --exists <ref>
'git show-ref' --unresolved <ref>

DESCRIPTION
-----------
Expand Down Expand Up @@ -76,6 +77,13 @@ OPTIONS
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.

--unresolved::

Prints out what the reference points to without resolving it. 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
Expand Down
33 changes: 22 additions & 11 deletions builtin/show-ref.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ static const char * const show_ref_usage[] = {
" [--] [<ref>...]"),
N_("git show-ref --exclude-existing[=<pattern>]"),
N_("git show-ref --exists <ref>"),
N_("git show-ref --unresolved <ref>"),
NULL
};

Expand Down Expand Up @@ -220,11 +221,11 @@ static int cmd_show_ref__patterns(const struct patterns_options *opts,
return 0;
}

static int cmd_show_ref__exists(const char **refs)
static int cmd_show_ref__raw(const char **refs, int show)
{
struct strbuf unused_referent = STRBUF_INIT;
struct object_id unused_oid;
unsigned int unused_type;
struct strbuf referent = STRBUF_INIT;
struct object_id oid;
unsigned int type;
int failure_errno = 0;
const char *ref;
int ret = 0;
Expand All @@ -236,7 +237,7 @@ static int cmd_show_ref__exists(const char **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,
&oid, &referent, &type,
&failure_errno)) {
if (failure_errno == ENOENT || failure_errno == EISDIR) {
error(_("reference does not exist"));
Expand All @@ -250,8 +251,16 @@ static int cmd_show_ref__exists(const char **refs)
goto out;
}

if (!show)
goto out;

if (type & REF_ISSYMREF)
printf("ref: %s\n", referent.buf);
else
printf("ref: %s\n", oid_to_hex(&oid));

out:
strbuf_release(&unused_referent);
strbuf_release(&referent);
return ret;
}

Expand Down Expand Up @@ -284,11 +293,12 @@ 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;
int verify = 0, exists = 0, unresolved = 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, "unresolved", &unresolved, N_("print out unresolved value of reference")),
OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, "
"requires exact ref path")),
OPT_HIDDEN_BOOL('h', NULL, &patterns_opts.show_head,
Expand All @@ -314,16 +324,17 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, show_ref_options,
show_ref_usage, 0);

die_for_incompatible_opt3(exclude_existing_opts.enabled, "--exclude-existing",
die_for_incompatible_opt4(exclude_existing_opts.enabled, "--exclude-existing",
verify, "--verify",
exists, "--exists");
exists, "--exists",
unresolved, "--unresolved");

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 if (exists || unresolved)
return cmd_show_ref__raw(argv, unresolved);
else
return cmd_show_ref__patterns(&patterns_opts, &show_one_opts, argv);
}
48 changes: 47 additions & 1 deletion t/t1403-show-ref.sh
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,6 @@ test_expect_success 'show-ref --verify with dangling ref' '
test_must_fail git show-ref --verify refs/tags/dangling
)
'

test_expect_success 'show-ref sub-modes are mutually exclusive' '
test_must_fail git show-ref --verify --exclude-existing 2>err &&
grep "verify" err &&
Expand All @@ -218,6 +217,16 @@ test_expect_success 'show-ref sub-modes are mutually exclusive' '
test_must_fail git show-ref --exclude-existing --exists 2>err &&
grep "exclude-existing" err &&
grep "exists" err &&
grep "cannot be used together" err &&
test_must_fail git show-ref --exclude-existing --unresolved 2>err &&
grep "exclude-existing" err &&
grep "unresolved" err &&
grep "cannot be used together" err &&
test_must_fail git show-ref --verify --unresolved 2>err &&
grep "verify" err &&
grep "unresolved" err &&
grep "cannot be used together" err
'

Expand Down Expand Up @@ -286,4 +295,41 @@ test_expect_success '--exists with existing special ref' '
git show-ref --exists FETCH_HEAD
'

test_expect_success '--unresolved with existing reference' '
commit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) &&
cat >expect <<-EOF &&
ref: $commit_oid
EOF
git show-ref --unresolved refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME >actual &&
test_cmp expect actual
'

test_expect_success '--unresolved with symbolic ref' '
test_when_finished "git symbolic-ref -d SYMBOLIC_REF_A" &&
cat >expect <<-EOF &&
ref: refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
EOF
git symbolic-ref SYMBOLIC_REF_A refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&
git show-ref --unresolved SYMBOLIC_REF_A >actual &&
test_cmp expect actual
'

test_expect_success '--unresolved with nonexistent object ID' '
oid=$(test_oid 002) &&
test-tool ref-store main update-ref msg refs/heads/missing-oid-2 $oid $ZERO_OID REF_SKIP_OID_VERIFICATION &&
cat >expect <<-EOF &&
ref: $oid
EOF
git show-ref --unresolved refs/heads/missing-oid-2 >actual &&
test_cmp expect actual
'

test_expect_success '--unresolved with nonexistent reference' '
cat >expect <<-EOF &&
error: reference does not exist
EOF
test_expect_code 2 git show-ref --unresolved refs/heads/not-exist 2>err &&
test_cmp expect err
'

test_done

0 comments on commit e9145b6

Please sign in to comment.