Hi Patrick, On 10 Apr 2024, at 2:53, Patrick Steinhardt wrote: > On Mon, Apr 08, 2024 at 05:38:13PM +0000, John Cai via GitGitGadget wrote: >> From: John Cai <johncai86@xxxxxxxxx> >> >> 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 teach show-ref a >> --symbolic-name option that will cause git-show-ref(1) to print out the >> value symbolic references points to. > > I think it was Peff who shared a way to achieve this without actually > introducing a new option via `git for-each-ref --format=`. Can we maybe > provide some benchmarks to demonstrate that this variant is preferable > over what's already possible? Yes, it would be better to not introduce a new option. I've done a quick benchmark including my changes to add the unresolved symref to the iterator, and some changes to integrate this into ref-filter.c. Here are the results: $ hyperfine --warmup 5 "git for-each-ref \ --format='%(refname) %(objectname) %(symref)'" "./git for-each-ref \ --format='%(refname) %(objectname) %(symref)'" Benchmark 1: git for-each-ref --format='%(refname) %(objectname) %(symref)' Time (mean ± σ): 213.5 ms ± 9.9 ms [User: 7.5 ms, System: 14.3 ms] Range (min … max): 202.7 ms … 236.3 ms 14 runs Benchmark 2: ./git for-each-ref --format='%(refname) %(objectname) %(symref)' Time (mean ± σ): 10.8 ms ± 1.3 ms [User: 4.4 ms, System: 6.2 ms] Range (min … max): 9.5 ms … 17.5 ms 189 runs Summary ./git for-each-ref --format='%(refname) %(objectname) %(symref)' ran 19.72 ± 2.62 times faster than git for-each-ref --format='%(refname) %(objectname) %(symref)' It looks like this gives us a nice speedup. I will send up a new version that improves git-for-each-ref instead. thanks John > > Patrick > >> Signed-off-by: John Cai <johncai86@xxxxxxxxx> >> --- >> Documentation/git-show-ref.txt | 21 ++++++++++++++++++- >> builtin/show-ref.c | 38 ++++++++++++++++++++++++---------- >> refs.c | 6 ++++++ >> refs.h | 2 +- >> t/t1403-show-ref.sh | 20 ++++++++++++++++++ >> 5 files changed, 74 insertions(+), 13 deletions(-) >> >> diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt >> index ba757470059..9627b34b37f 100644 >> --- a/Documentation/git-show-ref.txt >> +++ b/Documentation/git-show-ref.txt >> @@ -10,7 +10,7 @@ SYNOPSIS >> [verse] >> 'git show-ref' [--head] [-d | --dereference] >> [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags] >> - [--heads] [--] [<pattern>...] >> + [--heads] [--symbolic-name] [--] [<pattern>...] >> 'git show-ref' --verify [-q | --quiet] [-d | --dereference] >> [-s | --hash[=<n>]] [--abbrev[=<n>]] >> [--] [<ref>...] >> @@ -58,6 +58,11 @@ OPTIONS >> Dereference tags into object IDs as well. They will be shown with `^{}` >> appended. >> >> +--symbolic-name:: >> + >> + Print out the value the reference points to without dereferencing. This >> + is useful to know the reference that a symbolic ref is pointing to. >> + >> -s:: >> --hash[=<n>]:: >> >> @@ -146,6 +151,20 @@ $ git show-ref --heads --hash >> ... >> ----------------------------------------------------------------------------- >> >> +When using `--symbolic-name`, the output is in the format: >> + >> +----------- >> +<oid> SP <ref> SP <symbolic-name> >> +----------- >> + >> +For example, >> + >> +----------------------------------------------------------------------------- >> +$ git show-ref --symbolic-name >> +b75428bae1d090f60bdd4b67185f814bc8f0819d refs/heads/SYMBOLIC_REF ref:refs/heads/main >> +... >> +----------------------------------------------------------------------------- >> + >> EXAMPLES >> -------- >> >> diff --git a/builtin/show-ref.c b/builtin/show-ref.c >> index 1c15421e600..1d681505eac 100644 >> --- a/builtin/show-ref.c >> +++ b/builtin/show-ref.c >> @@ -12,7 +12,7 @@ >> static const char * const show_ref_usage[] = { >> N_("git show-ref [--head] [-d | --dereference]\n" >> " [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n" >> - " [--heads] [--] [<pattern>...]"), >> + " [--heads] [--symbolic-name] [--] [<pattern>...]"), >> N_("git show-ref --verify [-q | --quiet] [-d | --dereference]\n" >> " [-s | --hash[=<n>]] [--abbrev[=<n>]]\n" >> " [--] [<ref>...]"), >> @@ -26,10 +26,13 @@ struct show_one_options { >> int hash_only; >> int abbrev; >> int deref_tags; >> + int symbolic_name; >> }; >> >> static void show_one(const struct show_one_options *opts, >> - const char *refname, const struct object_id *oid) >> + const char *refname, >> + const char *referent, >> + const struct object_id *oid, const int is_symref) >> { >> const char *hex; >> struct object_id peeled; >> @@ -44,7 +47,9 @@ static void show_one(const struct show_one_options *opts, >> hex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev); >> if (opts->hash_only) >> printf("%s\n", hex); >> - else >> + else if (opts->symbolic_name & is_symref) { >> + printf("%s %s ref:%s\n", hex, refname, referent); >> + } else >> printf("%s %s\n", hex, refname); >> >> if (!opts->deref_tags) >> @@ -63,8 +68,11 @@ struct show_ref_data { >> int show_head; >> }; >> >> -static int show_ref(const char *refname, const struct object_id *oid, >> - int flag UNUSED, void *cbdata) >> +static int show_ref_referent(struct repository *repo UNUSED, >> + const char *refname, >> + const char *referent, >> + const struct object_id *oid, >> + int flag, void *cbdata) >> { >> struct show_ref_data *data = cbdata; >> >> @@ -91,11 +99,17 @@ static int show_ref(const char *refname, const struct object_id *oid, >> match: >> data->found_match++; >> >> - show_one(data->show_one_opts, refname, oid); >> + show_one(data->show_one_opts, refname, referent, oid, flag & REF_ISSYMREF); >> >> return 0; >> } >> >> +static int show_ref(const char *refname, const struct object_id *oid, >> + int flag, void *cbdata) >> +{ >> + return show_ref_referent(NULL, refname, NULL, oid, flag, cbdata); >> +} >> + >> static int add_existing(const char *refname, >> const struct object_id *oid UNUSED, >> int flag UNUSED, void *cbdata) >> @@ -171,10 +185,11 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts, >> >> while (*refs) { >> struct object_id oid; >> + int flags = 0; >> >> if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) && >> - !read_ref(*refs, &oid)) { >> - show_one(show_one_opts, *refs, &oid); >> + !read_ref_full(*refs, 0, &oid, &flags)) { >> + show_one(show_one_opts, *refs, NULL, &oid, flags & REF_ISSYMREF); >> } >> else if (!show_one_opts->quiet) >> die("'%s' - not a valid ref", *refs); >> @@ -208,11 +223,11 @@ static int cmd_show_ref__patterns(const struct patterns_options *opts, >> 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); >> + for_each_ref_all("refs/heads/", show_ref_referent, &show_ref_data); >> if (opts->tags_only) >> - for_each_fullref_in("refs/tags/", show_ref, &show_ref_data); >> + for_each_ref_all("refs/tags/", show_ref_referent, &show_ref_data); >> } else { >> - for_each_ref(show_ref, &show_ref_data); >> + for_each_ref_all("", show_ref_referent, &show_ref_data); >> } >> if (!show_ref_data.found_match) >> return 1; >> @@ -289,6 +304,7 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix) >> 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, "symbolic-name", &show_one_opts.symbolic_name, N_("print out symbolic reference values")), >> OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, " >> "requires exact ref path")), >> OPT_HIDDEN_BOOL('h', NULL, &patterns_opts.show_head, >> diff --git a/refs.c b/refs.c >> index 77ae38ea214..9488ad9594d 100644 >> --- a/refs.c >> +++ b/refs.c >> @@ -1734,6 +1734,12 @@ int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_dat >> DO_FOR_EACH_INCLUDE_BROKEN, cb_data); >> } >> >> +int for_each_ref_all(const char *prefix, each_repo_ref_fn fn, void *cb_data) >> +{ >> + return do_for_each_repo_ref(the_repository, prefix, fn, 0, >> + 0, cb_data); >> +} >> + >> int for_each_namespaced_ref(const char **exclude_patterns, >> each_ref_fn fn, void *cb_data) >> { >> diff --git a/refs.h b/refs.h >> index 23e5aaba2e9..54b459375be 100644 >> --- a/refs.h >> +++ b/refs.h >> @@ -337,7 +337,6 @@ int refs_for_each_branch_ref(struct ref_store *refs, >> each_ref_fn fn, void *cb_data); >> int refs_for_each_remote_ref(struct ref_store *refs, >> each_ref_fn fn, void *cb_data); >> - >> /* just iterates the head ref. */ >> int head_ref(each_ref_fn fn, void *cb_data); >> >> @@ -381,6 +380,7 @@ int for_each_tag_ref(each_ref_fn fn, void *cb_data); >> int for_each_branch_ref(each_ref_fn fn, void *cb_data); >> int for_each_remote_ref(each_ref_fn fn, void *cb_data); >> int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data); >> +int for_each_ref_all(const char *prefix, each_repo_ref_fn fn, void *cb_data); >> >> /* iterates all refs that match the specified glob pattern. */ >> int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data); >> diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh >> index 33fb7a38fff..0aebe709dca 100755 >> --- a/t/t1403-show-ref.sh >> +++ b/t/t1403-show-ref.sh >> @@ -286,4 +286,24 @@ test_expect_success '--exists with existing special ref' ' >> git show-ref --exists FETCH_HEAD >> ' >> >> +test_expect_success '--symbolic-name with a non symbolic ref' ' >> + commit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) && >> + cat >expect <<-EOF && >> + $commit_oid refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME >> + EOF >> + git show-ref --symbolic-name refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME >actual && >> + test_cmp expect actual >> +' >> + >> +test_expect_success '--symbolic-name with symbolic ref' ' >> + test_when_finished "git symbolic-ref -d refs/heads/SYMBOLIC_REF_A" && >> + commit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) && >> + cat >expect <<-EOF && >> + $commit_oid refs/heads/SYMBOLIC_REF_A ref:refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME >> + EOF >> + git symbolic-ref refs/heads/SYMBOLIC_REF_A refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME && >> + git show-ref --symbolic-name SYMBOLIC_REF_A >actual && >> + test_cmp expect actual >> +' >> + >> test_done >> -- >> gitgitgadget