Re: [PATCH v2 3/3] show-ref: add --symbolic-name option
- From
John Cai <johncai86@gmail.com>
- Date
- Apr 12, 2024, 15:23 UTC
- Message-ID
- <2E94E03A-CBDE-406D-AB65-DEAC4BB26761@gmail.com>
- In-Reply-To
- <ZhY3UCwJeMcG4fH1@tanuki>
Hi Patrick,
On 10 Apr 2024, at 2:53, Patrick Steinhardt wrote:
Show 14 quoted lines
> On Mon, Apr 08, 2024 at 05:38:13PM +0000, John Cai via GitGitGadget wrote: >> From: John Cai <johncai86@gmail.com> >> >> 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
Show 235 quoted lines
>
> Patrick
>
>> Signed-off-by: John Cai <johncai86@gmail.com>
>> ---
>> 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