{"thread":{"id":"61049","subject":"[PATCH] show-ref: add --unresolved option","startedAt":"2024-03-04T22:52:01Z","lastAt":"2024-04-12T15:23:37Z","messageCount":28,"participants":["John Cai via GitGitGadget","Junio C Hamano","Phillip Wood","Kristoffer Haugsbakk","John Cai","Jeff King","Patrick Steinhardt","Jean-Noël Avila","phillip.wood123@gmail.com"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"489928","messageId":"pull.1684.git.git.1709592718743.gitgitgadget@gmail.com","threadId":"61049","inReplyTo":null,"subject":"[PATCH] show-ref: add --unresolved option","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-03-04T22:51:58Z","receivedAt":"2024-03-04T22:52:01Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"From: John Cai <johncai86@gmail.com>\n\nFor reftable development, it would be handy to have a tool to provide\nthe direct value of any ref whether it be a symbolic ref or not.\nCurrently there is git-symbolic-ref, which only works for symbolic refs,\nand git-rev-parse, which will resolve the ref. Let's add a --unresolved\noption that will only take one ref and return whatever it points to\nwithout dereferencing it.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n    show-ref: add --unresolved option\n    \n    For reftable development, it would be handy to have a tool to provide\n    the direct value of any ref whether it be a symbolic ref or not.\n    Currently there is git-symbolic-ref, which only works for symbolic refs,\n    and git-rev-parse, which will resolve the ref. Let's add a --unresolved\n    option that will only take one ref and return whatever it points to\n    without dereferencing it.\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1684%2Fjohn-cai%2Fjc%2Fshow-ref-direct-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1684/john-cai/jc/show-ref-direct-v1\nPull-Request: https://github.com/git/git/pull/1684\n\n Documentation/git-show-ref.txt |  8 ++++++\n builtin/show-ref.c             | 33 ++++++++++++++++--------\n t/t1403-show-ref.sh            | 47 ++++++++++++++++++++++++++++++++++\n 3 files changed, 77 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt\nindex ba757470059..2f9b4de1346 100644\n--- a/Documentation/git-show-ref.txt\n+++ b/Documentation/git-show-ref.txt\n@@ -16,6 +16,7 @@ SYNOPSIS\n \t     [--] [<ref>...]\n 'git show-ref' --exclude-existing[=<pattern>]\n 'git show-ref' --exists <ref>\n+'git show-ref' --unresolved <ref>\n \n DESCRIPTION\n -----------\n@@ -76,6 +77,13 @@ OPTIONS\n \tit does, 2 if it is missing, and 1 in case looking up the reference\n \tfailed with an error other than the reference being missing.\n \n+--unresolved::\n+\n+\tPrints out what the reference points to without resolving it. Returns\n+\tan exit code of 0 if it does, 2 if it is missing, and 1 in case looking\n+\tup the reference failed with an error other than the reference being\n+\tmissing.\n+\n --abbrev[=<n>]::\n \n \tAbbreviate the object name.  When using `--hash`, you do\ndiff --git a/builtin/show-ref.c b/builtin/show-ref.c\nindex 1c15421e600..58efa078399 100644\n--- a/builtin/show-ref.c\n+++ b/builtin/show-ref.c\n@@ -18,6 +18,7 @@ static const char * const show_ref_usage[] = {\n \t   \"             [--] [<ref>...]\"),\n \tN_(\"git show-ref --exclude-existing[=<pattern>]\"),\n \tN_(\"git show-ref --exists <ref>\"),\n+\tN_(\"git show-ref --unresolved <ref>\"),\n \tNULL\n };\n \n@@ -220,11 +221,11 @@ static int cmd_show_ref__patterns(const struct patterns_options *opts,\n \treturn 0;\n }\n \n-static int cmd_show_ref__exists(const char **refs)\n+static int cmd_show_ref__raw(const char **refs, int show)\n {\n-\tstruct strbuf unused_referent = STRBUF_INIT;\n-\tstruct object_id unused_oid;\n-\tunsigned int unused_type;\n+\tstruct strbuf referent = STRBUF_INIT;\n+\tstruct object_id oid;\n+\tunsigned int type;\n \tint failure_errno = 0;\n \tconst char *ref;\n \tint ret = 0;\n@@ -236,7 +237,7 @@ static int cmd_show_ref__exists(const char **refs)\n \t\tdie(\"--exists requires exactly one reference\");\n \n \tif (refs_read_raw_ref(get_main_ref_store(the_repository), ref,\n-\t\t\t      &unused_oid, &unused_referent, &unused_type,\n+\t\t\t      &oid, &referent, &type,\n \t\t\t      &failure_errno)) {\n \t\tif (failure_errno == ENOENT || failure_errno == EISDIR) {\n \t\t\terror(_(\"reference does not exist\"));\n@@ -250,8 +251,16 @@ static int cmd_show_ref__exists(const char **refs)\n \t\tgoto out;\n \t}\n \n+\t\tif (!show)\n+\t\t\tgoto out;\n+\n+\t\tif (type & REF_ISSYMREF)\n+\t\t\tprintf(\"ref: %s\\n\", referent.buf);\n+\t\telse\n+\t\t\tprintf(\"ref: %s\\n\", oid_to_hex(&oid));\n+\n out:\n-\tstrbuf_release(&unused_referent);\n+\tstrbuf_release(&referent);\n \treturn ret;\n }\n \n@@ -284,11 +293,12 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)\n \tstruct exclude_existing_options exclude_existing_opts = {0};\n \tstruct patterns_options patterns_opts = {0};\n \tstruct show_one_options show_one_opts = {0};\n-\tint verify = 0, exists = 0;\n+\tint verify = 0, exists = 0, unresolved = 0;\n \tconst struct option show_ref_options[] = {\n \t\tOPT_BOOL(0, \"tags\", &patterns_opts.tags_only, N_(\"only show tags (can be combined with heads)\")),\n \t\tOPT_BOOL(0, \"heads\", &patterns_opts.heads_only, N_(\"only show heads (can be combined with tags)\")),\n \t\tOPT_BOOL(0, \"exists\", &exists, N_(\"check for reference existence without resolving\")),\n+\t\tOPT_BOOL(0, \"unresolved\", &unresolved, N_(\"print out unresolved value of reference\")),\n \t\tOPT_BOOL(0, \"verify\", &verify, N_(\"stricter reference checking, \"\n \t\t\t    \"requires exact ref path\")),\n \t\tOPT_HIDDEN_BOOL('h', NULL, &patterns_opts.show_head,\n@@ -314,16 +324,17 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, show_ref_options,\n \t\t\t     show_ref_usage, 0);\n \n-\tdie_for_incompatible_opt3(exclude_existing_opts.enabled, \"--exclude-existing\",\n+\tdie_for_incompatible_opt4(exclude_existing_opts.enabled, \"--exclude-existing\",\n \t\t\t\t  verify, \"--verify\",\n-\t\t\t\t  exists, \"--exists\");\n+\t\t\t\t  exists, \"--exists\",\n+\t\t\t\t  unresolved, \"--unresolved\");\n \n \tif (exclude_existing_opts.enabled)\n \t\treturn cmd_show_ref__exclude_existing(&exclude_existing_opts);\n \telse if (verify)\n \t\treturn cmd_show_ref__verify(&show_one_opts, argv);\n-\telse if (exists)\n-\t\treturn cmd_show_ref__exists(argv);\n+\telse if (exists || unresolved)\n+\t\treturn cmd_show_ref__raw(argv, unresolved);\n \telse\n \t\treturn cmd_show_ref__patterns(&patterns_opts, &show_one_opts, argv);\n }\ndiff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh\nindex 33fb7a38fff..11811201738 100755\n--- a/t/t1403-show-ref.sh\n+++ b/t/t1403-show-ref.sh\n@@ -218,6 +218,16 @@ test_expect_success 'show-ref sub-modes are mutually exclusive' '\n \ttest_must_fail git show-ref --exclude-existing --exists 2>err &&\n \tgrep \"exclude-existing\" err &&\n \tgrep \"exists\" err &&\n+\tgrep \"cannot be used together\" err &&\n+\n+\ttest_must_fail git show-ref --exclude-existing --unresolved 2>err &&\n+\tgrep \"exclude-existing\" err &&\n+\tgrep \"unresolved\" err &&\n+\tgrep \"cannot be used together\" err &&\n+\n+\ttest_must_fail git show-ref --verify --unresolved 2>err &&\n+\tgrep \"verify\" err &&\n+\tgrep \"unresolved\" err &&\n \tgrep \"cannot be used together\" err\n '\n \n@@ -286,4 +296,41 @@ test_expect_success '--exists with existing special ref' '\n \tgit show-ref --exists FETCH_HEAD\n '\n \n+test_expect_success '--unresolved with existing reference' '\n+\tcommit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) &&\n+\tcat >expect <<-EOF &&\n+\tref: $commit_oid\n+\tEOF\n+\tgit show-ref --unresolved refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--unresolved with symbolic ref' '\n+\ttest_when_finished \"git symbolic-ref -d SYMBOLIC_REF_A\" &&\n+\tcat >expect <<-EOF &&\n+\tref: refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n+\tEOF\n+\tgit symbolic-ref SYMBOLIC_REF_A refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&\n+\tgit show-ref --unresolved SYMBOLIC_REF_A >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--unresolved with nonexistent object ID' '\n+\toid=$(test_oid 002) &&\n+\ttest-tool ref-store main update-ref msg refs/heads/missing-oid-2 $oid $ZERO_OID REF_SKIP_OID_VERIFICATION &&\n+\tcat >expect <<-EOF &&\n+\tref: $oid\n+\tEOF\n+\tgit show-ref --unresolved refs/heads/missing-oid-2 >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--unresolved with nonexistent reference' '\n+\tcat >expect <<-EOF &&\n+\terror: reference does not exist\n+\tEOF\n+\ttest_expect_code 2 git show-ref --unresolved refs/heads/not-exist 2>err &&\n+\ttest_cmp expect err\n+'\n+\n test_done\n\nbase-commit: b387623c12f3f4a376e4d35a610fd3e55d7ea907\n-- \ngitgitgadget\n"},{"id":"489929","messageId":"xmqqplw9mviu.fsf@gitster.g","threadId":"61049","inReplyTo":"pull.1684.git.git.1709592718743.gitgitgadget@gmail.com","subject":"Re: [PATCH] show-ref: add --unresolved option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-03-04T23:23:21Z","receivedAt":"2024-03-04T23:23:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"John Cai via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: John Cai <johncai86@gmail.com>\n>\n> For reftable development, it would be handy to have a tool to provide\n> the direct value of any ref whether it be a symbolic ref or not.\n> Currently there is git-symbolic-ref, which only works for symbolic refs,\n> and git-rev-parse, which will resolve the ref. Let's add a --unresolved\n> option that will only take one ref and return whatever it points to\n> without dereferencing it.\n\nThe approach may be reasonble, but the above description can use\nsome improvements.\n\n * Even though the title of the patch says show-ref, the last\n   sentence is a bit too far from there and it was unclear to what\n   you are adding a new feature at least to me during my first read.\n\n      Let's teach show-ref a `--unresolved` optionthat will ...\n\n   may make it easier to follow.\n\n * \"Whatever it points to without dereferencing it\" implied that it\n   assumes what it is asked to show can be dereferenced, which\n   invites a natural question: what happens to a thing that is not\n   dereferenceable in the first place?  The implementation seems to\n   show either symbolic-ref target (for symbolic refs) or the object\n   name (for others), but let's make it easier for readers.\n\n>  Documentation/git-show-ref.txt |  8 ++++++\n>  builtin/show-ref.c             | 33 ++++++++++++++++--------\n>  t/t1403-show-ref.sh            | 47 ++++++++++++++++++++++++++++++++++\n>  3 files changed, 77 insertions(+), 11 deletions(-)\n>\n> diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt\n> index ba757470059..2f9b4de1346 100644\n> --- a/Documentation/git-show-ref.txt\n> +++ b/Documentation/git-show-ref.txt\n> @@ -16,6 +16,7 @@ SYNOPSIS\n>  \t     [--] [<ref>...]\n>  'git show-ref' --exclude-existing[=<pattern>]\n>  'git show-ref' --exists <ref>\n> +'git show-ref' --unresolved <ref>\n>  \n>  DESCRIPTION\n>  -----------\n> @@ -76,6 +77,13 @@ OPTIONS\n>  \tit does, 2 if it is missing, and 1 in case looking up the reference\n>  \tfailed with an error other than the reference being missing.\n>  \n> +--unresolved::\n> +\n> +\tPrints out what the reference points to without resolving it. Returns\n> +\tan exit code of 0 if it does, 2 if it is missing, and 1 in case looking\n> +\tup the reference failed with an error other than the reference being\n> +\tmissing.\n\nExactly the same issue as in the proposed log message, i.e. what is\nprinted for what kind of ref is not really clear.\n\n> -static int cmd_show_ref__exists(const char **refs)\n> +static int cmd_show_ref__raw(const char **refs, int show)\n>  {\n> -\tstruct strbuf unused_referent = STRBUF_INIT;\n> -\tstruct object_id unused_oid;\n> -\tunsigned int unused_type;\n> +\tstruct strbuf referent = STRBUF_INIT;\n> +\tstruct object_id oid;\n> +\tunsigned int type;\n>  \tint failure_errno = 0;\n>  \tconst char *ref;\n>  \tint ret = 0;\n> @@ -236,7 +237,7 @@ static int cmd_show_ref__exists(const char **refs)\n>  \t\tdie(\"--exists requires exactly one reference\");\n>  \n>  \tif (refs_read_raw_ref(get_main_ref_store(the_repository), ref,\n> -\t\t\t      &unused_oid, &unused_referent, &unused_type,\n> +\t\t\t      &oid, &referent, &type,\n>  \t\t\t      &failure_errno)) {\n>  \t\tif (failure_errno == ENOENT || failure_errno == EISDIR) {\n>  \t\t\terror(_(\"reference does not exist\"));\n> @@ -250,8 +251,16 @@ static int cmd_show_ref__exists(const char **refs)\n>  \t\tgoto out;\n>  \t}\n>  \n> +\t\tif (!show)\n> +\t\t\tgoto out;\n> +\n> +\t\tif (type & REF_ISSYMREF)\n> +\t\t\tprintf(\"ref: %s\\n\", referent.buf);\n> +\t\telse\n> +\t\t\tprintf(\"ref: %s\\n\", oid_to_hex(&oid));\n\nIf I create a symbolic ref whose value is deadbeef....deadbeef 40-hex,\nI cannot tell from this output if it is a symbolic ref of a ref that\nstores an object whose name is that hash.  Reserve the use of \"ref: %s\"\nto the symbolic refs (so that it will also match how the files backend\nstores them in modern Git), and use some other prefix (or no\nperfix).\n\nActually, I am not sure if what is proposed is even a good\ninterface.  Given a repository with these few refs:\n\n    $ git show-ref refs/heads/master\n    b387623c12f3f4a376e4d35a610fd3e55d7ea907 refs/heads/master\n    $ git show-ref refs/remotes/repo/HEAD\n    b387623c12f3f4a376e4d35a610fd3e55d7ea907 refs/remotes/repo/HEAD\n    $ git symbolic-ref refs/remotes/repo/HEAD\n    refs/remotes/repo/master\n\nI would think that the second command above shows the gap in feature\nset our current \"show-ref\" has.  If we could do\n\n    $ git show-ref --<option> refs/heads/master refs/remotes/repo/HEAD\n    b387623c12f3f4a376e4d35a610fd3e55d7ea907 refs/heads/master\n    ref:refs/remotes/repo/master refs/remotes/repo/HEAD\n\nor alternatively\n\n    $ git show-ref --<option> refs/heads/master refs/remotes/repo/HEAD\n    b387623c12f3f4a376e4d35a610fd3e55d7ea907 refs/heads/master\n    ref:refs/remotes/repo/master b387623c12f3f4a376e4d35a610fd3e55d7ea907 refs/remotes/repo/HEAD\n\nwouldn't it match the existing feature set better?  You also do not\nhave to limit yourself to single ref query per process invocation.\n\nI am not sure if you need to worry about quoting of the values of\nsymbolic-ref, though.  You _might_ need to move the (optional)\nsymref information to the end, i.e. something like this you might\nprefer.  I dunno.\n\n    $ git show-ref --<option> refs/remotes/repo/HEAD\n    b387623c12f3f4a376e4d35a610fd3e55d7ea907 refs/remotes/repo/HEAD refs/remotes/repo/master \n\nI do not know what the <option> should be called, either.  From an\nend-user's point of view, the option tells the command to also\nreport which ref the ref points at, if it were a symbolic one.\n\"unresolved\" may be technically acceptable name to those who know\nthe underlying implementation (i.e. we tell read_raw_ref not to\nresolve when it does its thing), but I am afraid that is a bit too\nopaque implementation detail for end-users who are expected to learn\nthis option.\n\n"},{"id":"489960","messageId":"a3de2b7b-4603-4604-a4d2-938a598e312e@gmail.com","threadId":"61049","inReplyTo":"pull.1684.git.git.1709592718743.gitgitgadget@gmail.com","subject":"Re: [PATCH] show-ref: add --unresolved option","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-03-05T15:30:35Z","receivedAt":"2024-03-05T15:30:38Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi John\n\nOn 04/03/2024 22:51, John Cai via GitGitGadget wrote:\n> From: John Cai <johncai86@gmail.com>\n> \n> For reftable development, it would be handy to have a tool to provide\n> the direct value of any ref whether it be a symbolic ref or not.\n> Currently there is git-symbolic-ref, which only works for symbolic refs,\n> and git-rev-parse, which will resolve the ref. Let's add a --unresolved\n> option that will only take one ref and return whatever it points to\n> without dereferencing it.\n\n\"--unresolved\" makes me think of merge conflicts. I wonder if \n\"--no-dereference\" would be clearer.\n\nBest Wishes\n\nPhillip\n\n> Signed-off-by: John Cai <johncai86@gmail.com>\n> ---\n>      show-ref: add --unresolved option\n>      \n>      For reftable development, it would be handy to have a tool to provide\n>      the direct value of any ref whether it be a symbolic ref or not.\n>      Currently there is git-symbolic-ref, which only works for symbolic refs,\n>      and git-rev-parse, which will resolve the ref. Let's add a --unresolved\n>      option that will only take one ref and return whatever it points to\n>      without dereferencing it.\n> \n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1684%2Fjohn-cai%2Fjc%2Fshow-ref-direct-v1\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1684/john-cai/jc/show-ref-direct-v1\n> Pull-Request: https://github.com/git/git/pull/1684\n> \n>   Documentation/git-show-ref.txt |  8 ++++++\n>   builtin/show-ref.c             | 33 ++++++++++++++++--------\n>   t/t1403-show-ref.sh            | 47 ++++++++++++++++++++++++++++++++++\n>   3 files changed, 77 insertions(+), 11 deletions(-)\n> \n> diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt\n> index ba757470059..2f9b4de1346 100644\n> --- a/Documentation/git-show-ref.txt\n> +++ b/Documentation/git-show-ref.txt\n> @@ -16,6 +16,7 @@ SYNOPSIS\n>   \t     [--] [<ref>...]\n>   'git show-ref' --exclude-existing[=<pattern>]\n>   'git show-ref' --exists <ref>\n> +'git show-ref' --unresolved <ref>\n>   \n>   DESCRIPTION\n>   -----------\n> @@ -76,6 +77,13 @@ OPTIONS\n>   \tit does, 2 if it is missing, and 1 in case looking up the reference\n>   \tfailed with an error other than the reference being missing.\n>   \n> +--unresolved::\n> +\n> +\tPrints out what the reference points to without resolving it. Returns\n> +\tan exit code of 0 if it does, 2 if it is missing, and 1 in case looking\n> +\tup the reference failed with an error other than the reference being\n> +\tmissing.\n> +\n>   --abbrev[=<n>]::\n>   \n>   \tAbbreviate the object name.  When using `--hash`, you do\n> diff --git a/builtin/show-ref.c b/builtin/show-ref.c\n> index 1c15421e600..58efa078399 100644\n> --- a/builtin/show-ref.c\n> +++ b/builtin/show-ref.c\n> @@ -18,6 +18,7 @@ static const char * const show_ref_usage[] = {\n>   \t   \"             [--] [<ref>...]\"),\n>   \tN_(\"git show-ref --exclude-existing[=<pattern>]\"),\n>   \tN_(\"git show-ref --exists <ref>\"),\n> +\tN_(\"git show-ref --unresolved <ref>\"),\n>   \tNULL\n>   };\n>   \n> @@ -220,11 +221,11 @@ static int cmd_show_ref__patterns(const struct patterns_options *opts,\n>   \treturn 0;\n>   }\n>   \n> -static int cmd_show_ref__exists(const char **refs)\n> +static int cmd_show_ref__raw(const char **refs, int show)\n>   {\n> -\tstruct strbuf unused_referent = STRBUF_INIT;\n> -\tstruct object_id unused_oid;\n> -\tunsigned int unused_type;\n> +\tstruct strbuf referent = STRBUF_INIT;\n> +\tstruct object_id oid;\n> +\tunsigned int type;\n>   \tint failure_errno = 0;\n>   \tconst char *ref;\n>   \tint ret = 0;\n> @@ -236,7 +237,7 @@ static int cmd_show_ref__exists(const char **refs)\n>   \t\tdie(\"--exists requires exactly one reference\");\n>   \n>   \tif (refs_read_raw_ref(get_main_ref_store(the_repository), ref,\n> -\t\t\t      &unused_oid, &unused_referent, &unused_type,\n> +\t\t\t      &oid, &referent, &type,\n>   \t\t\t      &failure_errno)) {\n>   \t\tif (failure_errno == ENOENT || failure_errno == EISDIR) {\n>   \t\t\terror(_(\"reference does not exist\"));\n> @@ -250,8 +251,16 @@ static int cmd_show_ref__exists(const char **refs)\n>   \t\tgoto out;\n>   \t}\n>   \n> +\t\tif (!show)\n> +\t\t\tgoto out;\n> +\n> +\t\tif (type & REF_ISSYMREF)\n> +\t\t\tprintf(\"ref: %s\\n\", referent.buf);\n> +\t\telse\n> +\t\t\tprintf(\"ref: %s\\n\", oid_to_hex(&oid));\n> +\n>   out:\n> -\tstrbuf_release(&unused_referent);\n> +\tstrbuf_release(&referent);\n>   \treturn ret;\n>   }\n>   \n> @@ -284,11 +293,12 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)\n>   \tstruct exclude_existing_options exclude_existing_opts = {0};\n>   \tstruct patterns_options patterns_opts = {0};\n>   \tstruct show_one_options show_one_opts = {0};\n> -\tint verify = 0, exists = 0;\n> +\tint verify = 0, exists = 0, unresolved = 0;\n>   \tconst struct option show_ref_options[] = {\n>   \t\tOPT_BOOL(0, \"tags\", &patterns_opts.tags_only, N_(\"only show tags (can be combined with heads)\")),\n>   \t\tOPT_BOOL(0, \"heads\", &patterns_opts.heads_only, N_(\"only show heads (can be combined with tags)\")),\n>   \t\tOPT_BOOL(0, \"exists\", &exists, N_(\"check for reference existence without resolving\")),\n> +\t\tOPT_BOOL(0, \"unresolved\", &unresolved, N_(\"print out unresolved value of reference\")),\n>   \t\tOPT_BOOL(0, \"verify\", &verify, N_(\"stricter reference checking, \"\n>   \t\t\t    \"requires exact ref path\")),\n>   \t\tOPT_HIDDEN_BOOL('h', NULL, &patterns_opts.show_head,\n> @@ -314,16 +324,17 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)\n>   \targc = parse_options(argc, argv, prefix, show_ref_options,\n>   \t\t\t     show_ref_usage, 0);\n>   \n> -\tdie_for_incompatible_opt3(exclude_existing_opts.enabled, \"--exclude-existing\",\n> +\tdie_for_incompatible_opt4(exclude_existing_opts.enabled, \"--exclude-existing\",\n>   \t\t\t\t  verify, \"--verify\",\n> -\t\t\t\t  exists, \"--exists\");\n> +\t\t\t\t  exists, \"--exists\",\n> +\t\t\t\t  unresolved, \"--unresolved\");\n>   \n>   \tif (exclude_existing_opts.enabled)\n>   \t\treturn cmd_show_ref__exclude_existing(&exclude_existing_opts);\n>   \telse if (verify)\n>   \t\treturn cmd_show_ref__verify(&show_one_opts, argv);\n> -\telse if (exists)\n> -\t\treturn cmd_show_ref__exists(argv);\n> +\telse if (exists || unresolved)\n> +\t\treturn cmd_show_ref__raw(argv, unresolved);\n>   \telse\n>   \t\treturn cmd_show_ref__patterns(&patterns_opts, &show_one_opts, argv);\n>   }\n> diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh\n> index 33fb7a38fff..11811201738 100755\n> --- a/t/t1403-show-ref.sh\n> +++ b/t/t1403-show-ref.sh\n> @@ -218,6 +218,16 @@ test_expect_success 'show-ref sub-modes are mutually exclusive' '\n>   \ttest_must_fail git show-ref --exclude-existing --exists 2>err &&\n>   \tgrep \"exclude-existing\" err &&\n>   \tgrep \"exists\" err &&\n> +\tgrep \"cannot be used together\" err &&\n> +\n> +\ttest_must_fail git show-ref --exclude-existing --unresolved 2>err &&\n> +\tgrep \"exclude-existing\" err &&\n> +\tgrep \"unresolved\" err &&\n> +\tgrep \"cannot be used together\" err &&\n> +\n> +\ttest_must_fail git show-ref --verify --unresolved 2>err &&\n> +\tgrep \"verify\" err &&\n> +\tgrep \"unresolved\" err &&\n>   \tgrep \"cannot be used together\" err\n>   '\n>   \n> @@ -286,4 +296,41 @@ test_expect_success '--exists with existing special ref' '\n>   \tgit show-ref --exists FETCH_HEAD\n>   '\n>   \n> +test_expect_success '--unresolved with existing reference' '\n> +\tcommit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) &&\n> +\tcat >expect <<-EOF &&\n> +\tref: $commit_oid\n> +\tEOF\n> +\tgit show-ref --unresolved refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success '--unresolved with symbolic ref' '\n> +\ttest_when_finished \"git symbolic-ref -d SYMBOLIC_REF_A\" &&\n> +\tcat >expect <<-EOF &&\n> +\tref: refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n> +\tEOF\n> +\tgit symbolic-ref SYMBOLIC_REF_A refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&\n> +\tgit show-ref --unresolved SYMBOLIC_REF_A >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success '--unresolved with nonexistent object ID' '\n> +\toid=$(test_oid 002) &&\n> +\ttest-tool ref-store main update-ref msg refs/heads/missing-oid-2 $oid $ZERO_OID REF_SKIP_OID_VERIFICATION &&\n> +\tcat >expect <<-EOF &&\n> +\tref: $oid\n> +\tEOF\n> +\tgit show-ref --unresolved refs/heads/missing-oid-2 >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success '--unresolved with nonexistent reference' '\n> +\tcat >expect <<-EOF &&\n> +\terror: reference does not exist\n> +\tEOF\n> +\ttest_expect_code 2 git show-ref --unresolved refs/heads/not-exist 2>err &&\n> +\ttest_cmp expect err\n> +'\n> +\n>   test_done\n> \n> base-commit: b387623c12f3f4a376e4d35a610fd3e55d7ea907\n"},{"id":"489968","messageId":"177596a5-dae3-4fb1-902e-7ad74a678ff1@app.fastmail.com","threadId":"61049","inReplyTo":"a3de2b7b-4603-4604-a4d2-938a598e312e@gmail.com","subject":"Re: [PATCH] show-ref: add --unresolved option","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2024-03-05T17:01:01Z","receivedAt":"2024-03-05T17:01:24Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"On Tue, Mar 5, 2024, at 16:30, Phillip Wood wrote:\n> Hi John\n>\n> On 04/03/2024 22:51, John Cai via GitGitGadget wrote:\n>> From: John Cai <johncai86@gmail.com>\n>>\n>> For reftable development, it would be handy to have a tool to provide\n>> the direct value of any ref whether it be a symbolic ref or not.\n>> Currently there is git-symbolic-ref, which only works for symbolic refs,\n>> and git-rev-parse, which will resolve the ref. Let's add a --unresolved\n>> option that will only take one ref and return whatever it points to\n>> without dereferencing it.\n>\n> \"--unresolved\" makes me think of merge conflicts. I wonder if\n> \"--no-dereference\" would be clearer.\n\nYeah, a `--no`-style option looks more consistent.\n"},{"id":"489989","messageId":"44609CA3-1215-4EAD-8456-DDD66A3B93A8@gmail.com","threadId":"61049","inReplyTo":"xmqqplw9mviu.fsf@gitster.g","subject":"Re: [PATCH] show-ref: add --unresolved option","fromName":"John Cai","fromEmail":"johncai86@gmail.com","sentAt":"2024-03-05T20:56:03Z","receivedAt":"2024-03-05T20:56:05Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"Hi Junio,\n\nOn 4 Mar 2024, at 18:23, Junio C Hamano wrote:\n\n> \"John Cai via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>> From: John Cai <johncai86@gmail.com>\n>>\n>> For reftable development, it would be handy to have a tool to provide\n>> the direct value of any ref whether it be a symbolic ref or not.\n>> Currently there is git-symbolic-ref, which only works for symbolic refs,\n>> and git-rev-parse, which will resolve the ref. Let's add a --unresolved\n>> option that will only take one ref and return whatever it points to\n>> without dereferencing it.\n>\n> The approach may be reasonble, but the above description can use\n> some improvements.\n>\n>  * Even though the title of the patch says show-ref, the last\n>    sentence is a bit too far from there and it was unclear to what\n>    you are adding a new feature at least to me during my first read.\n>\n>       Let's teach show-ref a `--unresolved` optionthat will ...\n>\n>    may make it easier to follow.\n>\n>  * \"Whatever it points to without dereferencing it\" implied that it\n>    assumes what it is asked to show can be dereferenced, which\n>    invites a natural question: what happens to a thing that is not\n>    dereferenceable in the first place?  The implementation seems to\n>    show either symbolic-ref target (for symbolic refs) or the object\n>    name (for others), but let's make it easier for readers.\n\nYeah good point. The language could be made more precise.\n\n>\n>>  Documentation/git-show-ref.txt |  8 ++++++\n>>  builtin/show-ref.c             | 33 ++++++++++++++++--------\n>>  t/t1403-show-ref.sh            | 47 ++++++++++++++++++++++++++++++++++\n>>  3 files changed, 77 insertions(+), 11 deletions(-)\n>>\n>> diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt\n>> index ba757470059..2f9b4de1346 100644\n>> --- a/Documentation/git-show-ref.txt\n>> +++ b/Documentation/git-show-ref.txt\n>> @@ -16,6 +16,7 @@ SYNOPSIS\n>>  \t     [--] [<ref>...]\n>>  'git show-ref' --exclude-existing[=<pattern>]\n>>  'git show-ref' --exists <ref>\n>> +'git show-ref' --unresolved <ref>\n>>\n>>  DESCRIPTION\n>>  -----------\n>> @@ -76,6 +77,13 @@ OPTIONS\n>>  \tit does, 2 if it is missing, and 1 in case looking up the reference\n>>  \tfailed with an error other than the reference being missing.\n>>\n>> +--unresolved::\n>> +\n>> +\tPrints out what the reference points to without resolving it. Returns\n>> +\tan exit code of 0 if it does, 2 if it is missing, and 1 in case looking\n>> +\tup the reference failed with an error other than the reference being\n>> +\tmissing.\n>\n> Exactly the same issue as in the proposed log message, i.e. what is\n> printed for what kind of ref is not really clear.\n>\n>> -static int cmd_show_ref__exists(const char **refs)\n>> +static int cmd_show_ref__raw(const char **refs, int show)\n>>  {\n>> -\tstruct strbuf unused_referent = STRBUF_INIT;\n>> -\tstruct object_id unused_oid;\n>> -\tunsigned int unused_type;\n>> +\tstruct strbuf referent = STRBUF_INIT;\n>> +\tstruct object_id oid;\n>> +\tunsigned int type;\n>>  \tint failure_errno = 0;\n>>  \tconst char *ref;\n>>  \tint ret = 0;\n>> @@ -236,7 +237,7 @@ static int cmd_show_ref__exists(const char **refs)\n>>  \t\tdie(\"--exists requires exactly one reference\");\n>>\n>>  \tif (refs_read_raw_ref(get_main_ref_store(the_repository), ref,\n>> -\t\t\t      &unused_oid, &unused_referent, &unused_type,\n>> +\t\t\t      &oid, &referent, &type,\n>>  \t\t\t      &failure_errno)) {\n>>  \t\tif (failure_errno == ENOENT || failure_errno == EISDIR) {\n>>  \t\t\terror(_(\"reference does not exist\"));\n>> @@ -250,8 +251,16 @@ static int cmd_show_ref__exists(const char **refs)\n>>  \t\tgoto out;\n>>  \t}\n>>\n>> +\t\tif (!show)\n>> +\t\t\tgoto out;\n>> +\n>> +\t\tif (type & REF_ISSYMREF)\n>> +\t\t\tprintf(\"ref: %s\\n\", referent.buf);\n>> +\t\telse\n>> +\t\t\tprintf(\"ref: %s\\n\", oid_to_hex(&oid));\n>\n> If I create a symbolic ref whose value is deadbeef....deadbeef 40-hex,\n> I cannot tell from this output if it is a symbolic ref of a ref that\n> stores an object whose name is that hash.  Reserve the use of \"ref: %s\"\n> to the symbolic refs (so that it will also match how the files backend\n> stores them in modern Git), and use some other prefix (or no\n> perfix).\n>\n> Actually, I am not sure if what is proposed is even a good\n> interface.  Given a repository with these few refs:\n>\n>     $ git show-ref refs/heads/master\n>     b387623c12f3f4a376e4d35a610fd3e55d7ea907 refs/heads/master\n>     $ git show-ref refs/remotes/repo/HEAD\n>     b387623c12f3f4a376e4d35a610fd3e55d7ea907 refs/remotes/repo/HEAD\n>     $ git symbolic-ref refs/remotes/repo/HEAD\n>     refs/remotes/repo/master\n>\n> I would think that the second command above shows the gap in feature\n> set our current \"show-ref\" has.  If we could do\n>\n>     $ git show-ref --<option> refs/heads/master refs/remotes/repo/HEAD\n>     b387623c12f3f4a376e4d35a610fd3e55d7ea907 refs/heads/master\n>     ref:refs/remotes/repo/master refs/remotes/repo/HEAD\n\nI like this option. It makes it clear that it's a symbolic ref without adding\nadditional output to the command.\n\ncc'ing Patrick here for his thoughts as well since he has interest in this topic.\n\n>\n> or alternatively\n>\n>     $ git show-ref --<option> refs/heads/master refs/remotes/repo/HEAD\n>     b387623c12f3f4a376e4d35a610fd3e55d7ea907 refs/heads/master\n>     ref:refs/remotes/repo/master b387623c12f3f4a376e4d35a610fd3e55d7ea907 refs/remotes/repo/HEAD\n>\n> wouldn't it match the existing feature set better?  You also do not\n> have to limit yourself to single ref query per process invocation.\n>\n> I am not sure if you need to worry about quoting of the values of\n> symbolic-ref, though.  You _might_ need to move the (optional)\n> symref information to the end, i.e. something like this you might\n> prefer.  I dunno.\n>\n>     $ git show-ref --<option> refs/remotes/repo/HEAD\n>     b387623c12f3f4a376e4d35a610fd3e55d7ea907 refs/remotes/repo/HEAD refs/remotes/repo/master\n>\n> I do not know what the <option> should be called, either.  From an\n> end-user's point of view, the option tells the command to also\n> report which ref the ref points at, if it were a symbolic one.\n> \"unresolved\" may be technically acceptable name to those who know\n> the underlying implementation (i.e. we tell read_raw_ref not to\n> resolve when it does its thing), but I am afraid that is a bit too\n> opaque implementation detail for end-users who are expected to learn\n> this option.\n\nI think something like --no-dereference that was suggested in [1] could work\nsince the concept of dereferencing should be familiar to the user. However, this\nmaybe confusing because of the existing --dereference flag that is specific to\ntags...\n\n1. https://lore.kernel.org/git/a3de2b7b-4603-4604-a4d2-938a598e312e@gmail.com/\n\nthanks\nJohn\n\n"},{"id":"490017","messageId":"xmqqzfvcz7sq.fsf@gitster.g","threadId":"61049","inReplyTo":"44609CA3-1215-4EAD-8456-DDD66A3B93A8@gmail.com","subject":"Re: [PATCH] show-ref: add --unresolved option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-03-05T21:29:41Z","receivedAt":"2024-03-05T21:29:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"John Cai <johncai86@gmail.com> writes:\n\n> I think something like --no-dereference that was suggested in [1] could work\n> since the concept of dereferencing should be familiar to the user. However, this\n> maybe confusing because of the existing --dereference flag that is specific to\n> tags...\n\nTrue.\n\nAs a symbolic reference is like a symbolic link [*], another possibility\nis to use \"follow\", which often is used to refer to the action of\nreading the contents of a symbolic link and using it as the target\npathname (e.g., O_NOFOLLOW flag forbids open(2) from following\nsymbolic links).  Unfortunately the checkbox feature \"--follow\" the\n\"git log\" has refers to an entirely different kind of following, so\nit is just as confusing as --dereference.\n\nPerhaps \"--readlink\", if you are showing only \"ref: refs/heads/main\"\ninstead of the usual object name in the output?  If we are showing\nboth, we may want to use a name that signals the optional nature of\nthe symref part of the output, e.g., \"--with-symref-target\" [*].\n\n\n[Footnote]\n\n * In fact that is how the symbolic reference started out as.  We\n   literally used a symbolic link .git/HEAD that points at the\n   current branch, which can be seen in v0.99~418 (git-init-db: set\n   up the full default environment, 2005-05-30).\n\n * Yes, I know, I am terrible at naming things---my names may be\n   descriptive but end up being unusably long and verbose.\n\n"},{"id":"490041","messageId":"20240306003343.GA3797463@coredump.intra.peff.net","threadId":"61049","inReplyTo":"a3de2b7b-4603-4604-a4d2-938a598e312e@gmail.com","subject":"Re: [PATCH] show-ref: add --unresolved option","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2024-03-06T00:33:43Z","receivedAt":"2024-03-06T00:33:51Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Mar 05, 2024 at 03:30:35PM +0000, Phillip Wood wrote:\n\n> Hi John\n> \n> On 04/03/2024 22:51, John Cai via GitGitGadget wrote:\n> > From: John Cai <johncai86@gmail.com>\n> > \n> > For reftable development, it would be handy to have a tool to provide\n> > the direct value of any ref whether it be a symbolic ref or not.\n> > Currently there is git-symbolic-ref, which only works for symbolic refs,\n> > and git-rev-parse, which will resolve the ref. Let's add a --unresolved\n> > option that will only take one ref and return whatever it points to\n> > without dereferencing it.\n> \n> \"--unresolved\" makes me think of merge conflicts. I wonder if\n> \"--no-dereference\" would be clearer.\n\nWe have \"--no-deref\" in \"git update-ref\" already. It is probably better\nto stay consistent.\n\n-Peff\n"},{"id":"490043","messageId":"20240306004139.GB3797463@coredump.intra.peff.net","threadId":"61049","inReplyTo":"pull.1684.git.git.1709592718743.gitgitgadget@gmail.com","subject":"Re: [PATCH] show-ref: add --unresolved option","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2024-03-06T00:41:39Z","receivedAt":"2024-03-06T00:41:41Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Mar 04, 2024 at 10:51:58PM +0000, John Cai via GitGitGadget wrote:\n\n> From: John Cai <johncai86@gmail.com>\n> \n> For reftable development, it would be handy to have a tool to provide\n> the direct value of any ref whether it be a symbolic ref or not.\n> Currently there is git-symbolic-ref, which only works for symbolic refs,\n> and git-rev-parse, which will resolve the ref. Let's add a --unresolved\n> option that will only take one ref and return whatever it points to\n> without dereferencing it.\n\nWhat about \"git rev-parse --symbolic-full-name\"? I don't think that\nbehaves quite the same as your patch here:\n\n  - it is actually not a true no-deref; it resolves to the final name\n    and then prints it (so the behavior is the same for a single-level\n    symref, but I believe a multi-level symref chain like\n    one->two->three will print \"three\" when resolving \"one\").\n\n  - it always prints the resolved name, whereas your patch prints an oid\n    for non-symrefs\n\nI'm not sure if those are important or not, as I don't quite understand\nwhat you're trying to accomplish. I'd probably have just run:\n\n  git symbolic-ref -q $name || git rev-parse --verify $name\n\nI'm not opposed to making that more ergonomic, but I think we should\navoid redundant plumbing options if we can (I'm not sure yet if this is\nredundant or not, but in general I find \"show-ref\" to be a weird mix of\n\"rev-parse\" and \"for-each-ref\" that I'd be just as happy if it did not\nexist).\n\n-Peff\n"},{"id":"490057","messageId":"xmqqv860umo9.fsf@gitster.g","threadId":"61049","inReplyTo":"20240306003343.GA3797463@coredump.intra.peff.net","subject":"Re: [PATCH] show-ref: add --unresolved option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-03-06T02:19:34Z","receivedAt":"2024-03-06T02:19:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Tue, Mar 05, 2024 at 03:30:35PM +0000, Phillip Wood wrote:\n>\n>> Hi John\n>> \n>> On 04/03/2024 22:51, John Cai via GitGitGadget wrote:\n>> > From: John Cai <johncai86@gmail.com>\n>> > \n>> > For reftable development, it would be handy to have a tool to provide\n>> > the direct value of any ref whether it be a symbolic ref or not.\n>> > Currently there is git-symbolic-ref, which only works for symbolic refs,\n>> > and git-rev-parse, which will resolve the ref. Let's add a --unresolved\n>> > option that will only take one ref and return whatever it points to\n>> > without dereferencing it.\n>> \n>> \"--unresolved\" makes me think of merge conflicts. I wonder if\n>> \"--no-dereference\" would be clearer.\n>\n> We have \"--no-deref\" in \"git update-ref\" already. It is probably better\n> to stay consistent.\n\nThat's an excellent precedent.  Thanks.\n"},{"id":"490060","messageId":"Zegbw2i-PGfvb5q_@tanuki","threadId":"61049","inReplyTo":"20240306004139.GB3797463@coredump.intra.peff.net","subject":"Re: [PATCH] show-ref: add --unresolved option","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-03-06T07:31:15Z","receivedAt":"2024-03-06T07:31:22Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, Mar 05, 2024 at 07:41:39PM -0500, Jeff King wrote:\n> On Mon, Mar 04, 2024 at 10:51:58PM +0000, John Cai via GitGitGadget wrote:\n> \n> > From: John Cai <johncai86@gmail.com>\n> > \n> > For reftable development, it would be handy to have a tool to provide\n> > the direct value of any ref whether it be a symbolic ref or not.\n> > Currently there is git-symbolic-ref, which only works for symbolic refs,\n> > and git-rev-parse, which will resolve the ref. Let's add a --unresolved\n> > option that will only take one ref and return whatever it points to\n> > without dereferencing it.\n> \n> What about \"git rev-parse --symbolic-full-name\"? I don't think that\n> behaves quite the same as your patch here:\n> \n>   - it is actually not a true no-deref; it resolves to the final name\n>     and then prints it (so the behavior is the same for a single-level\n>     symref, but I believe a multi-level symref chain like\n>     one->two->three will print \"three\" when resolving \"one\").\n> \n>   - it always prints the resolved name, whereas your patch prints an oid\n>     for non-symrefs\n> \n> I'm not sure if those are important or not, as I don't quite understand\n> what you're trying to accomplish. I'd probably have just run:\n> \n>   git symbolic-ref -q $name || git rev-parse --verify $name\n> \n> I'm not opposed to making that more ergonomic, but I think we should\n> avoid redundant plumbing options if we can (I'm not sure yet if this is\n> redundant or not, but in general I find \"show-ref\" to be a weird mix of\n> \"rev-parse\" and \"for-each-ref\" that I'd be just as happy if it did not\n> exist).\n\nYeah, the proposed patch basically aims to do the above chained command\nin an easier way. I think that this would be a nice addition to make\nthis easier to use and better discoverable. But in the long run what I\nthink would be really useful is to extend git-for-each-ref(1) and/or\ngit-show-ref(1) so that they can print all existing refs with their\ndirect values. Right now, it's impossible to get a globally consistent\nview of all refs in the refdb with their unresolved values.\n\nThat will end up a bit more involved though. The ref iterators we have\nright now do not provide any way to return symref targets to the caller,\nso we would have to first extend the interfaces and adapt both backends\nto support this. But this is kind of where I want to end up.\n\nPatrick\n"},{"id":"490061","messageId":"20240306075128.GA4099518@coredump.intra.peff.net","threadId":"61049","inReplyTo":"Zegbw2i-PGfvb5q_@tanuki","subject":"Re: [PATCH] show-ref: add --unresolved option","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2024-03-06T07:51:28Z","receivedAt":"2024-03-06T07:51:31Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Mar 06, 2024 at 08:31:15AM +0100, Patrick Steinhardt wrote:\n\n> Yeah, the proposed patch basically aims to do the above chained command\n> in an easier way. I think that this would be a nice addition to make\n> this easier to use and better discoverable. But in the long run what I\n> think would be really useful is to extend git-for-each-ref(1) and/or\n> git-show-ref(1) so that they can print all existing refs with their\n> direct values. Right now, it's impossible to get a globally consistent\n> view of all refs in the refdb with their unresolved values.\n\nYeah, it seems like if this were a format-specifier for for-each-ref it\nwould be a lot more flexible.\n\nYou can do:\n\n  git for-each-ref --format='%(refname) %(objectname) %(symref)'\n\nto get the resolved values next to the symrefs (if any). I think that\ndoes a full resolution, though (so again, if you had one->two->three,\nyou can never learn about the intermediate \"two\").\n\n> That will end up a bit more involved though. The ref iterators we have\n> right now do not provide any way to return symref targets to the caller,\n> so we would have to first extend the interfaces and adapt both backends\n> to support this. But this is kind of where I want to end up.\n\nI think for-each-ref in the above command works by calling\nresolve_refdup() itself, and then recording the result. It would be nice\nto get it from the iterator, though (more efficient, and avoids any\nraces).\n\n-Peff\n"},{"id":"490066","messageId":"ef5958e1-af3f-4869-b0e3-ff1448c0d73c@gmail.com","threadId":"61049","inReplyTo":"pull.1684.git.git.1709592718743.gitgitgadget@gmail.com","subject":"Re: [PATCH] show-ref: add --unresolved option","fromName":"Jean-Noël Avila","fromEmail":"avila.jn@gmail.com","sentAt":"2024-03-06T09:36:17Z","receivedAt":"2024-03-06T09:36:20Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"Le 04/03/2024 à 23:51, John Cai via GitGitGadget a écrit :\n> From: John Cai <johncai86@gmail.com>\n> > @@ -76,6 +77,13 @@ OPTIONS\n>  \tit does, 2 if it is missing, and 1 in case looking up the reference\n>  \tfailed with an error other than the reference being missing.\n>  \n> +--unresolved::\n> +\n> +\tPrints out what the reference points to without resolving it. Returns\n\nStyle nitpicking: we use imperative form in the descriptions of options\n→ Print out what... Return...\n\n> +\tan exit code of 0 if it does, 2 if it is missing, and 1 in case looking\n> +\tup the reference failed with an error other than the reference being\n> +\tmissing.\n> +\n>  --abbrev[=<n>]::\n>  \n>  \tAbbreviate the object name.  When using `--hash`, you do\n\n"},{"id":"490098","messageId":"xmqq34t3tifv.fsf@gitster.g","threadId":"61049","inReplyTo":"20240306075128.GA4099518@coredump.intra.peff.net","subject":"Re: [PATCH] show-ref: add --unresolved option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-03-06T16:48:36Z","receivedAt":"2024-03-06T16:48:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> You can do:\n>\n>   git for-each-ref --format='%(refname) %(objectname) %(symref)'\n\nWe can even do that \"ref target || object name\" thing with\n\n--format='%(refname) %(if)%(symref)%(then)%(symref)%(else)%(objectname)'\n\nif we wanted to.  But if we have both available, I think the output\nthat adds the symref target, if available, after the object name, is\nbetter than the output that switches between the two.\n\n> to get the resolved values next to the symrefs (if any). I think that\n> does a full resolution, though (so again, if you had one->two->three,\n> you can never learn about the intermediate \"two\").\n\nYeah, I know we discussed the usefulness of tag-of-tag-of-something,\nbut this is a similar one.  \n\n> I think for-each-ref in the above command works by calling\n> resolve_refdup() itself, and then recording the result. It would be nice\n> to get it from the iterator, though (more efficient, and avoids any\n> races).\n\nIndeed.  Thanks for an interesting thought.\n"},{"id":"492570","messageId":"pull.1684.v2.git.git.1712597893.gitgitgadget@gmail.com","threadId":"61049","inReplyTo":"pull.1684.git.git.1709592718743.gitgitgadget@gmail.com","subject":"[PATCH v2 0/3] show-ref: add --symbolic-name option","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-04-08T17:38:10Z","receivedAt":"2024-04-08T17:38:16Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"For reftable development, it would be handy to have a tool to provide the\ndirect value of any ref whether it be a symbolic ref or not. Currently there\nis git-symbolic-ref, which only works for symbolic refs, and git-rev-parse,\nwhich will resolve the ref. Let's add a --symbolic-name option that will\nprint out the value the ref directly points to without dereferencing it.\n\nChanges since V1:\n\n * changed output format to print out values as a third column\n * made plumbing changes to enable the value of a symbolic ref to be read\n   from the iterator\n * changed the name of the flag\n\nJohn Cai (3):\n  refs: keep track of unresolved reference value in iterator\n  refs: add referent to each_repo_ref_fn\n  show-ref: add --symbolic-name option\n\n Documentation/git-show-ref.txt | 21 ++++++++++++++++++-\n builtin/replace.c              |  1 +\n builtin/show-ref.c             | 38 ++++++++++++++++++++++++----------\n builtin/submodule--helper.c    |  2 +-\n refs.c                         | 31 ++++++++++++++++++---------\n refs.h                         |  6 ++++--\n refs/files-backend.c           | 20 ++++++++++--------\n refs/iterator.c                |  3 ++-\n refs/ref-cache.c               |  3 +++\n refs/ref-cache.h               |  2 ++\n refs/refs-internal.h           |  1 +\n refs/reftable-backend.c        | 13 ++++++++----\n remote.c                       |  2 +-\n replace-object.c               |  1 +\n sequencer.c                    |  4 ++--\n t/helper/test-ref-store.c      |  2 +-\n t/t1403-show-ref.sh            | 20 ++++++++++++++++++\n worktree.c                     |  4 +++-\n 18 files changed, 130 insertions(+), 44 deletions(-)\n\n\nbase-commit: 7774cfed6261ce2900c84e55906da708c711d601\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1684%2Fjohn-cai%2Fjc%2Fshow-ref-direct-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1684/john-cai/jc/show-ref-direct-v2\nPull-Request: https://github.com/git/git/pull/1684\n\nRange-diff vs v1:\n\n -:  ----------- > 1:  6adc9dd26da refs: keep track of unresolved reference value in iterator\n -:  ----------- > 2:  b60e78560e0 refs: add referent to each_repo_ref_fn\n 1:  c32572a8d0b ! 3:  a9e6644327a show-ref: add --unresolved option\n     @@ Metadata\n      Author: John Cai <johncai86@gmail.com>\n      \n       ## Commit message ##\n     -    show-ref: add --unresolved option\n     +    show-ref: add --symbolic-name option\n      \n          For reftable development, it would be handy to have a tool to provide\n          the direct value of any ref whether it be a symbolic ref or not.\n          Currently there is git-symbolic-ref, which only works for symbolic refs,\n     -    and git-rev-parse, which will resolve the ref. Let's add a --unresolved\n     -    option that will only take one ref and return whatever it points to\n     -    without dereferencing it.\n     +    and git-rev-parse, which will resolve the ref. Let's teach show-ref a\n     +    --symbolic-name option that will cause git-show-ref(1) to print out the\n     +    value symbolic references points to.\n      \n          Signed-off-by: John Cai <johncai86@gmail.com>\n      \n       ## Documentation/git-show-ref.txt ##\n      @@ Documentation/git-show-ref.txt: SYNOPSIS\n     + [verse]\n     + 'git show-ref' [--head] [-d | --dereference]\n     + \t     [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n     +-\t     [--heads] [--] [<pattern>...]\n     ++\t     [--heads] [--symbolic-name] [--] [<pattern>...]\n     + 'git show-ref' --verify [-q | --quiet] [-d | --dereference]\n     + \t     [-s | --hash[=<n>]] [--abbrev[=<n>]]\n       \t     [--] [<ref>...]\n     - 'git show-ref' --exclude-existing[=<pattern>]\n     - 'git show-ref' --exists <ref>\n     -+'git show-ref' --unresolved <ref>\n     - \n     - DESCRIPTION\n     - -----------\n      @@ Documentation/git-show-ref.txt: OPTIONS\n     - \tit does, 2 if it is missing, and 1 in case looking up the reference\n     - \tfailed with an error other than the reference being missing.\n     + \tDereference tags into object IDs as well. They will be shown with `^{}`\n     + \tappended.\n     + \n     ++--symbolic-name::\n     ++\n     ++\tPrint out the value the reference points to without dereferencing. This\n     ++\tis useful to know the reference that a symbolic ref is pointing to.\n     ++\n     + -s::\n     + --hash[=<n>]::\n     + \n     +@@ Documentation/git-show-ref.txt: $ git show-ref --heads --hash\n     + ...\n     + -----------------------------------------------------------------------------\n       \n     -+--unresolved::\n     ++When using `--symbolic-name`, the output is in the format:\n     ++\n     ++-----------\n     ++<oid> SP <ref> SP <symbolic-name>\n     ++-----------\n      +\n     -+\tPrints out what the reference points to without resolving it. Returns\n     -+\tan exit code of 0 if it does, 2 if it is missing, and 1 in case looking\n     -+\tup the reference failed with an error other than the reference being\n     -+\tmissing.\n     ++For example,\n      +\n     - --abbrev[=<n>]::\n     ++-----------------------------------------------------------------------------\n     ++$ git show-ref --symbolic-name\n     ++b75428bae1d090f60bdd4b67185f814bc8f0819d refs/heads/SYMBOLIC_REF ref:refs/heads/main\n     ++...\n     ++-----------------------------------------------------------------------------\n     ++\n     + EXAMPLES\n     + --------\n       \n     - \tAbbreviate the object name.  When using `--hash`, you do\n      \n       ## builtin/show-ref.c ##\n     -@@ builtin/show-ref.c: static const char * const show_ref_usage[] = {\n     +@@\n     + static const char * const show_ref_usage[] = {\n     + \tN_(\"git show-ref [--head] [-d | --dereference]\\n\"\n     + \t   \"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\\n\"\n     +-\t   \"             [--heads] [--] [<pattern>...]\"),\n     ++\t   \"             [--heads] [--symbolic-name] [--] [<pattern>...]\"),\n     + \tN_(\"git show-ref --verify [-q | --quiet] [-d | --dereference]\\n\"\n     + \t   \"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\\n\"\n       \t   \"             [--] [<ref>...]\"),\n     - \tN_(\"git show-ref --exclude-existing[=<pattern>]\"),\n     - \tN_(\"git show-ref --exists <ref>\"),\n     -+\tN_(\"git show-ref --unresolved <ref>\"),\n     - \tNULL\n     +@@ builtin/show-ref.c: struct show_one_options {\n     + \tint hash_only;\n     + \tint abbrev;\n     + \tint deref_tags;\n     ++\tint symbolic_name;\n       };\n       \n     -@@ builtin/show-ref.c: static int cmd_show_ref__patterns(const struct patterns_options *opts,\n     - \treturn 0;\n     - }\n     + static void show_one(const struct show_one_options *opts,\n     +-\t\t     const char *refname, const struct object_id *oid)\n     ++\t\t     const char *refname,\n     ++\t\t     const char *referent,\n     ++\t\t     const struct object_id *oid, const int is_symref)\n     + {\n     + \tconst char *hex;\n     + \tstruct object_id peeled;\n     +@@ builtin/show-ref.c: static void show_one(const struct show_one_options *opts,\n     + \thex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev);\n     + \tif (opts->hash_only)\n     + \t\tprintf(\"%s\\n\", hex);\n     +-\telse\n     ++\telse if (opts->symbolic_name & is_symref) {\n     ++\t\tprintf(\"%s %s ref:%s\\n\", hex, refname, referent);\n     ++\t} else\n     + \t\tprintf(\"%s %s\\n\", hex, refname);\n       \n     --static int cmd_show_ref__exists(const char **refs)\n     -+static int cmd_show_ref__raw(const char **refs, int show)\n     + \tif (!opts->deref_tags)\n     +@@ builtin/show-ref.c: struct show_ref_data {\n     + \tint show_head;\n     + };\n     + \n     +-static int show_ref(const char *refname, const struct object_id *oid,\n     +-\t\t    int flag UNUSED, void *cbdata)\n     ++static int show_ref_referent(struct repository *repo UNUSED,\n     ++\t\t\t     const char *refname,\n     ++\t\t\t     const char *referent,\n     ++\t\t\t     const struct object_id *oid,\n     ++\t\t\t     int flag, void *cbdata)\n       {\n     --\tstruct strbuf unused_referent = STRBUF_INIT;\n     --\tstruct object_id unused_oid;\n     --\tunsigned int unused_type;\n     -+\tstruct strbuf referent = STRBUF_INIT;\n     -+\tstruct object_id oid;\n     -+\tunsigned int type;\n     - \tint failure_errno = 0;\n     - \tconst char *ref;\n     - \tint ret = 0;\n     -@@ builtin/show-ref.c: static int cmd_show_ref__exists(const char **refs)\n     - \t\tdie(\"--exists requires exactly one reference\");\n     - \n     - \tif (refs_read_raw_ref(get_main_ref_store(the_repository), ref,\n     --\t\t\t      &unused_oid, &unused_referent, &unused_type,\n     -+\t\t\t      &oid, &referent, &type,\n     - \t\t\t      &failure_errno)) {\n     - \t\tif (failure_errno == ENOENT || failure_errno == EISDIR) {\n     - \t\t\terror(_(\"reference does not exist\"));\n     -@@ builtin/show-ref.c: static int cmd_show_ref__exists(const char **refs)\n     - \t\tgoto out;\n     - \t}\n     + \tstruct show_ref_data *data = cbdata;\n       \n     -+\t\tif (!show)\n     -+\t\t\tgoto out;\n     -+\n     -+\t\tif (type & REF_ISSYMREF)\n     -+\t\t\tprintf(\"ref: %s\\n\", referent.buf);\n     -+\t\telse\n     -+\t\t\tprintf(\"ref: %s\\n\", oid_to_hex(&oid));\n     -+\n     - out:\n     --\tstrbuf_release(&unused_referent);\n     -+\tstrbuf_release(&referent);\n     - \treturn ret;\n     +@@ builtin/show-ref.c: static int show_ref(const char *refname, const struct object_id *oid,\n     + match:\n     + \tdata->found_match++;\n     + \n     +-\tshow_one(data->show_one_opts, refname, oid);\n     ++\tshow_one(data->show_one_opts, refname, referent, oid, flag & REF_ISSYMREF);\n     + \n     + \treturn 0;\n       }\n       \n     ++static int show_ref(const char *refname, const struct object_id *oid,\n     ++\t\t    int flag, void *cbdata)\n     ++{\n     ++\treturn show_ref_referent(NULL, refname, NULL, oid, flag, cbdata);\n     ++}\n     ++\n     + static int add_existing(const char *refname,\n     + \t\t\tconst struct object_id *oid UNUSED,\n     + \t\t\tint flag UNUSED, void *cbdata)\n     +@@ builtin/show-ref.c: static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,\n     + \n     + \twhile (*refs) {\n     + \t\tstruct object_id oid;\n     ++\t\tint flags = 0;\n     + \n     + \t\tif ((starts_with(*refs, \"refs/\") || refname_is_safe(*refs)) &&\n     +-\t\t    !read_ref(*refs, &oid)) {\n     +-\t\t\tshow_one(show_one_opts, *refs, &oid);\n     ++\t\t    !read_ref_full(*refs, 0, &oid, &flags)) {\n     ++\t\t\tshow_one(show_one_opts, *refs, NULL, &oid, flags & REF_ISSYMREF);\n     + \t\t}\n     + \t\telse if (!show_one_opts->quiet)\n     + \t\t\tdie(\"'%s' - not a valid ref\", *refs);\n     +@@ builtin/show-ref.c: static int cmd_show_ref__patterns(const struct patterns_options *opts,\n     + \t\thead_ref(show_ref, &show_ref_data);\n     + \tif (opts->heads_only || opts->tags_only) {\n     + \t\tif (opts->heads_only)\n     +-\t\t\tfor_each_fullref_in(\"refs/heads/\", show_ref, &show_ref_data);\n     ++\t\t\tfor_each_ref_all(\"refs/heads/\", show_ref_referent, &show_ref_data);\n     + \t\tif (opts->tags_only)\n     +-\t\t\tfor_each_fullref_in(\"refs/tags/\", show_ref, &show_ref_data);\n     ++\t\t\tfor_each_ref_all(\"refs/tags/\", show_ref_referent, &show_ref_data);\n     + \t} else {\n     +-\t\tfor_each_ref(show_ref, &show_ref_data);\n     ++\t\tfor_each_ref_all(\"\", show_ref_referent, &show_ref_data);\n     + \t}\n     + \tif (!show_ref_data.found_match)\n     + \t\treturn 1;\n      @@ builtin/show-ref.c: int cmd_show_ref(int argc, const char **argv, const char *prefix)\n     - \tstruct exclude_existing_options exclude_existing_opts = {0};\n     - \tstruct patterns_options patterns_opts = {0};\n     - \tstruct show_one_options show_one_opts = {0};\n     --\tint verify = 0, exists = 0;\n     -+\tint verify = 0, exists = 0, unresolved = 0;\n     - \tconst struct option show_ref_options[] = {\n       \t\tOPT_BOOL(0, \"tags\", &patterns_opts.tags_only, N_(\"only show tags (can be combined with heads)\")),\n       \t\tOPT_BOOL(0, \"heads\", &patterns_opts.heads_only, N_(\"only show heads (can be combined with tags)\")),\n       \t\tOPT_BOOL(0, \"exists\", &exists, N_(\"check for reference existence without resolving\")),\n     -+\t\tOPT_BOOL(0, \"unresolved\", &unresolved, N_(\"print out unresolved value of reference\")),\n     ++\t\tOPT_BOOL(0, \"symbolic-name\", &show_one_opts.symbolic_name, N_(\"print out symbolic reference values\")),\n       \t\tOPT_BOOL(0, \"verify\", &verify, N_(\"stricter reference checking, \"\n       \t\t\t    \"requires exact ref path\")),\n       \t\tOPT_HIDDEN_BOOL('h', NULL, &patterns_opts.show_head,\n     -@@ builtin/show-ref.c: int cmd_show_ref(int argc, const char **argv, const char *prefix)\n     - \targc = parse_options(argc, argv, prefix, show_ref_options,\n     - \t\t\t     show_ref_usage, 0);\n     - \n     --\tdie_for_incompatible_opt3(exclude_existing_opts.enabled, \"--exclude-existing\",\n     -+\tdie_for_incompatible_opt4(exclude_existing_opts.enabled, \"--exclude-existing\",\n     - \t\t\t\t  verify, \"--verify\",\n     --\t\t\t\t  exists, \"--exists\");\n     -+\t\t\t\t  exists, \"--exists\",\n     -+\t\t\t\t  unresolved, \"--unresolved\");\n     - \n     - \tif (exclude_existing_opts.enabled)\n     - \t\treturn cmd_show_ref__exclude_existing(&exclude_existing_opts);\n     - \telse if (verify)\n     - \t\treturn cmd_show_ref__verify(&show_one_opts, argv);\n     --\telse if (exists)\n     --\t\treturn cmd_show_ref__exists(argv);\n     -+\telse if (exists || unresolved)\n     -+\t\treturn cmd_show_ref__raw(argv, unresolved);\n     - \telse\n     - \t\treturn cmd_show_ref__patterns(&patterns_opts, &show_one_opts, argv);\n     - }\n      \n     - ## t/t1403-show-ref.sh ##\n     -@@ t/t1403-show-ref.sh: test_expect_success 'show-ref sub-modes are mutually exclusive' '\n     - \ttest_must_fail git show-ref --exclude-existing --exists 2>err &&\n     - \tgrep \"exclude-existing\" err &&\n     - \tgrep \"exists\" err &&\n     -+\tgrep \"cannot be used together\" err &&\n     -+\n     -+\ttest_must_fail git show-ref --exclude-existing --unresolved 2>err &&\n     -+\tgrep \"exclude-existing\" err &&\n     -+\tgrep \"unresolved\" err &&\n     -+\tgrep \"cannot be used together\" err &&\n     + ## refs.c ##\n     +@@ refs.c: int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_dat\n     + \t\t\t\t    DO_FOR_EACH_INCLUDE_BROKEN, cb_data);\n     + }\n     + \n     ++int for_each_ref_all(const char *prefix, each_repo_ref_fn fn, void *cb_data)\n     ++{\n     ++\treturn do_for_each_repo_ref(the_repository, prefix, fn, 0,\n     ++\t\t\t\t    0, cb_data);\n     ++}\n      +\n     -+\ttest_must_fail git show-ref --verify --unresolved 2>err &&\n     -+\tgrep \"verify\" err &&\n     -+\tgrep \"unresolved\" err &&\n     - \tgrep \"cannot be used together\" err\n     - '\n     + int for_each_namespaced_ref(const char **exclude_patterns,\n     + \t\t\t    each_ref_fn fn, void *cb_data)\n     + {\n     +\n     + ## refs.h ##\n     +@@ refs.h: int refs_for_each_branch_ref(struct ref_store *refs,\n     + \t\t\t     each_ref_fn fn, void *cb_data);\n     + int refs_for_each_remote_ref(struct ref_store *refs,\n     + \t\t\t     each_ref_fn fn, void *cb_data);\n     +-\n     + /* just iterates the head ref. */\n     + int head_ref(each_ref_fn fn, void *cb_data);\n     + \n     +@@ refs.h: int for_each_tag_ref(each_ref_fn fn, void *cb_data);\n     + int for_each_branch_ref(each_ref_fn fn, void *cb_data);\n     + int for_each_remote_ref(each_ref_fn fn, void *cb_data);\n     + int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data);\n     ++int for_each_ref_all(const char *prefix, each_repo_ref_fn fn, void *cb_data);\n       \n     + /* iterates all refs that match the specified glob pattern. */\n     + int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);\n     +\n     + ## t/t1403-show-ref.sh ##\n      @@ t/t1403-show-ref.sh: test_expect_success '--exists with existing special ref' '\n       \tgit show-ref --exists FETCH_HEAD\n       '\n       \n     -+test_expect_success '--unresolved with existing reference' '\n     ++test_expect_success '--symbolic-name with a non symbolic ref' '\n      +\tcommit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) &&\n      +\tcat >expect <<-EOF &&\n     -+\tref: $commit_oid\n     -+\tEOF\n     -+\tgit show-ref --unresolved refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME >actual &&\n     -+\ttest_cmp expect actual\n     -+'\n     -+\n     -+test_expect_success '--unresolved with symbolic ref' '\n     -+\ttest_when_finished \"git symbolic-ref -d SYMBOLIC_REF_A\" &&\n     -+\tcat >expect <<-EOF &&\n     -+\tref: refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n     ++\t$commit_oid refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n      +\tEOF\n     -+\tgit symbolic-ref SYMBOLIC_REF_A refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&\n     -+\tgit show-ref --unresolved SYMBOLIC_REF_A >actual &&\n     ++\tgit show-ref --symbolic-name refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME >actual &&\n      +\ttest_cmp expect actual\n      +'\n      +\n     -+test_expect_success '--unresolved with nonexistent object ID' '\n     -+\toid=$(test_oid 002) &&\n     -+\ttest-tool ref-store main update-ref msg refs/heads/missing-oid-2 $oid $ZERO_OID REF_SKIP_OID_VERIFICATION &&\n     ++test_expect_success '--symbolic-name with symbolic ref' '\n     ++\ttest_when_finished \"git symbolic-ref -d refs/heads/SYMBOLIC_REF_A\" &&\n     ++\tcommit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) &&\n      +\tcat >expect <<-EOF &&\n     -+\tref: $oid\n     ++\t$commit_oid refs/heads/SYMBOLIC_REF_A ref:refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n      +\tEOF\n     -+\tgit show-ref --unresolved refs/heads/missing-oid-2 >actual &&\n     ++\tgit symbolic-ref refs/heads/SYMBOLIC_REF_A refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&\n     ++\tgit show-ref --symbolic-name SYMBOLIC_REF_A >actual &&\n      +\ttest_cmp expect actual\n      +'\n     -+\n     -+test_expect_success '--unresolved with nonexistent reference' '\n     -+\tcat >expect <<-EOF &&\n     -+\terror: reference does not exist\n     -+\tEOF\n     -+\ttest_expect_code 2 git show-ref --unresolved refs/heads/not-exist 2>err &&\n     -+\ttest_cmp expect err\n     -+'\n      +\n       test_done\n\n-- \ngitgitgadget\n"},{"id":"492571","messageId":"b60e78560e0d8e9b73090fad7b1b91eee19abf75.1712597893.git.gitgitgadget@gmail.com","threadId":"61049","inReplyTo":"pull.1684.v2.git.git.1712597893.gitgitgadget@gmail.com","subject":"[PATCH v2 2/3] refs: add referent to each_repo_ref_fn","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-04-08T17:38:12Z","receivedAt":"2024-04-08T17:38:18Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"From: John Cai <johncai86@gmail.com>\n\nThere is no way for callers of the refs api functions that use iterators\nto get a hold of a direct value of a symbolic ref before its resolved in\nthe callback functions, as either each_ref_fn nor each_repo_ref_fn have\nan argument for this.\n\nThis did not matter since the iterators did not hold onto the direct\nvalue of a reference anyway. But, the previous commit started to save\nthe value of the direct reference in the iterator.\n\nAdd an argument to each_repo_ref_fn that gets passed the direct\nvalue of a reference.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n builtin/replace.c | 1 +\n refs.c            | 2 ++\n refs.h            | 1 +\n refs/iterator.c   | 2 +-\n replace-object.c  | 1 +\n 5 files changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex da59600ad22..36fa58db82c 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -48,6 +48,7 @@ struct show_data {\n };\n \n static int show_reference(struct repository *r, const char *refname,\n+\t\t\t  const char *referent UNUSED,\n \t\t\t  const struct object_id *oid,\n \t\t\t  int flag UNUSED, void *cb_data)\n {\ndiff --git a/refs.c b/refs.c\nindex b87a680249e..77ae38ea214 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1664,6 +1664,7 @@ struct do_for_each_ref_help {\n \n static int do_for_each_ref_helper(struct repository *r UNUSED,\n \t\t\t\t  const char *refname,\n+\t\t\t\t  const char *referent,\n \t\t\t\t  const struct object_id *oid,\n \t\t\t\t  int flags,\n \t\t\t\t  void *cb_data)\n@@ -2565,6 +2566,7 @@ struct do_for_each_reflog_help {\n \n static int do_for_each_reflog_helper(struct repository *r UNUSED,\n \t\t\t\t     const char *refname,\n+\t\t\t\t     const char *referent,\n \t\t\t\t     const struct object_id *oid UNUSED,\n \t\t\t\t     int flags,\n \t\t\t\t     void *cb_data)\ndiff --git a/refs.h b/refs.h\nindex 2e740c692ac..23e5aaba2e9 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -311,6 +311,7 @@ typedef int each_ref_fn(const char *refname,\n  */\n typedef int each_repo_ref_fn(struct repository *r,\n \t\t\t     const char *refname,\n+\t\t\t     const char *referent,\n \t\t\t     const struct object_id *oid,\n \t\t\t     int flags,\n \t\t\t     void *cb_data);\ndiff --git a/refs/iterator.c b/refs/iterator.c\nindex 26ca6f645ee..7e04d8427a9 100644\n--- a/refs/iterator.c\n+++ b/refs/iterator.c\n@@ -449,7 +449,7 @@ int do_for_each_repo_ref_iterator(struct repository *r, struct ref_iterator *ite\n \n \tcurrent_ref_iter = iter;\n \twhile ((ok = ref_iterator_advance(iter)) == ITER_OK) {\n-\t\tretval = fn(r, iter->refname, iter->oid, iter->flags, cb_data);\n+\t\tretval = fn(r, iter->refname, iter->referent, iter->oid, iter->flags, cb_data);\n \t\tif (retval) {\n \t\t\t/*\n \t\t\t * If ref_iterator_abort() returns ITER_ERROR,\ndiff --git a/replace-object.c b/replace-object.c\nindex 523215589de..32d90f35327 100644\n--- a/replace-object.c\n+++ b/replace-object.c\n@@ -10,6 +10,7 @@\n \n static int register_replace_ref(struct repository *r,\n \t\t\t\tconst char *refname,\n+\t\t\t\tconst char *referent UNUSED,\n \t\t\t\tconst struct object_id *oid,\n \t\t\t\tint flag UNUSED,\n \t\t\t\tvoid *cb_data UNUSED)\n-- \ngitgitgadget\n\n"},{"id":"492572","messageId":"6adc9dd26da4459d246591ce148c960b33bde336.1712597893.git.gitgitgadget@gmail.com","threadId":"61049","inReplyTo":"pull.1684.v2.git.git.1712597893.gitgitgadget@gmail.com","subject":"[PATCH v2 1/3] refs: keep track of unresolved reference value in iterator","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-04-08T17:38:11Z","receivedAt":"2024-04-08T17:38:18Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"From: John Cai <johncai86@gmail.com>\n\nSince ref iterators do not hold onto the direct value of a reference\nwithout resolving it, the only way to get ahold of a direct value of a\nsymbolic ref is to make a separate call to refs_read_symbolic_ref.\n\nTo make accessing the direct value of a symbolic ref more efficient,\nlet's save the direct value of the ref in the iterator for both the\nfiles backend and reftable backend.\n\nTo do so, we also need to add an argument to refs_resolve_ref_unsafe to\nsave the direct value of the reference somewhere.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n builtin/submodule--helper.c |  2 +-\n refs.c                      | 23 +++++++++++++----------\n refs.h                      |  3 ++-\n refs/files-backend.c        | 20 +++++++++++---------\n refs/iterator.c             |  1 +\n refs/ref-cache.c            |  3 +++\n refs/ref-cache.h            |  2 ++\n refs/refs-internal.h        |  1 +\n refs/reftable-backend.c     | 13 +++++++++----\n remote.c                    |  2 +-\n sequencer.c                 |  4 ++--\n t/helper/test-ref-store.c   |  2 +-\n worktree.c                  |  4 +++-\n 13 files changed, 50 insertions(+), 30 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex fda50f2af1e..cf9966b7827 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -45,7 +45,7 @@ static int repo_get_default_remote(struct repository *repo, char **default_remot\n \tchar *dest = NULL;\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct ref_store *store = get_main_ref_store(repo);\n-\tconst char *refname = refs_resolve_ref_unsafe(store, \"HEAD\", 0, NULL,\n+\tconst char *refname = refs_resolve_ref_unsafe(store, \"HEAD\", NULL, 0, NULL,\n \t\t\t\t\t\t      NULL);\n \n \tif (!refname)\ndiff --git a/refs.c b/refs.c\nindex 55d2e0b2cb9..b87a680249e 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -379,7 +379,7 @@ char *refs_resolve_refdup(struct ref_store *refs,\n {\n \tconst char *result;\n \n-\tresult = refs_resolve_ref_unsafe(refs, refname, resolve_flags,\n+\tresult = refs_resolve_ref_unsafe(refs, refname, NULL, resolve_flags,\n \t\t\t\t\t oid, flags);\n \treturn xstrdup_or_null(result);\n }\n@@ -404,7 +404,7 @@ int read_ref_full(const char *refname, int resolve_flags, struct object_id *oid,\n {\n \tstruct ref_store *refs = get_main_ref_store(the_repository);\n \n-\tif (refs_resolve_ref_unsafe(refs, refname, resolve_flags,\n+\tif (refs_resolve_ref_unsafe(refs, refname, NULL, resolve_flags,\n \t\t\t\t    oid, flags))\n \t\treturn 0;\n \treturn -1;\n@@ -417,7 +417,7 @@ int read_ref(const char *refname, struct object_id *oid)\n \n int refs_ref_exists(struct ref_store *refs, const char *refname)\n {\n-\treturn !!refs_resolve_ref_unsafe(refs, refname, RESOLVE_REF_READING,\n+\treturn !!refs_resolve_ref_unsafe(refs, refname, NULL, RESOLVE_REF_READING,\n \t\t\t\t\t NULL, NULL);\n }\n \n@@ -773,7 +773,7 @@ int expand_ref(struct repository *repo, const char *str, int len,\n \t\tthis_result = refs_found ? &oid_from_ref : oid;\n \t\tstrbuf_reset(&fullref);\n \t\tstrbuf_addf(&fullref, *p, len, str);\n-\t\tr = refs_resolve_ref_unsafe(refs, fullref.buf,\n+\t\tr = refs_resolve_ref_unsafe(refs, fullref.buf, NULL,\n \t\t\t\t\t    RESOLVE_REF_READING,\n \t\t\t\t\t    this_result, &flag);\n \t\tif (r) {\n@@ -807,7 +807,7 @@ int repo_dwim_log(struct repository *r, const char *str, int len,\n \n \t\tstrbuf_reset(&path);\n \t\tstrbuf_addf(&path, *p, len, str);\n-\t\tref = refs_resolve_ref_unsafe(refs, path.buf,\n+\t\tref = refs_resolve_ref_unsafe(refs, path.buf, NULL,\n \t\t\t\t\t      RESOLVE_REF_READING,\n \t\t\t\t\t      oid ? &hash : NULL, NULL);\n \t\tif (!ref)\n@@ -876,7 +876,7 @@ int is_pseudoref(struct ref_store *refs, const char *refname)\n \t\treturn 0;\n \n \tif (ends_with(refname, \"_HEAD\")) {\n-\t\trefs_resolve_ref_unsafe(refs, refname,\n+\t\trefs_resolve_ref_unsafe(refs, refname, NULL,\n \t\t\t\t\tRESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n \t\t\t\t\t&oid, NULL);\n \t\treturn !is_null_oid(&oid);\n@@ -884,7 +884,7 @@ int is_pseudoref(struct ref_store *refs, const char *refname)\n \n \tfor (i = 0; i < ARRAY_SIZE(irregular_pseudorefs); i++)\n \t\tif (!strcmp(refname, irregular_pseudorefs[i])) {\n-\t\t\trefs_resolve_ref_unsafe(refs, refname,\n+\t\t\trefs_resolve_ref_unsafe(refs, refname, NULL,\n \t\t\t\t\t\tRESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n \t\t\t\t\t\t&oid, NULL);\n \t\t\treturn !is_null_oid(&oid);\n@@ -1590,7 +1590,7 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n \tstruct object_id oid;\n \tint flag;\n \n-\tif (refs_resolve_ref_unsafe(refs, \"HEAD\", RESOLVE_REF_READING,\n+\tif (refs_resolve_ref_unsafe(refs, \"HEAD\", NULL, RESOLVE_REF_READING,\n \t\t\t\t    &oid, &flag))\n \t\treturn fn(\"HEAD\", &oid, flag, cb_data);\n \n@@ -1928,6 +1928,7 @@ int refs_read_symbolic_ref(struct ref_store *ref_store, const char *refname,\n \n const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n \t\t\t\t    const char *refname,\n+\t\t\t\t    char **referent,\n \t\t\t\t    int resolve_flags,\n \t\t\t\t    struct object_id *oid,\n \t\t\t\t    int *flags)\n@@ -1989,6 +1990,8 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n \t\t}\n \n \t\t*flags |= read_flags;\n+\t\tif (referent && read_flags & REF_ISSYMREF && sb_refname.len > 0)\n+\t\t\t*referent = sb_refname.buf;\n \n \t\tif (!(read_flags & REF_ISSYMREF)) {\n \t\t\tif (*flags & REF_BAD_NAME) {\n@@ -2024,7 +2027,7 @@ int refs_init_db(struct ref_store *refs, int flags, struct strbuf *err)\n const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \t\t\t       struct object_id *oid, int *flags)\n {\n-\treturn refs_resolve_ref_unsafe(get_main_ref_store(the_repository), refname,\n+\treturn refs_resolve_ref_unsafe(get_main_ref_store(the_repository), refname, NULL,\n \t\t\t\t       resolve_flags, oid, flags);\n }\n \n@@ -2039,7 +2042,7 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,\n \tif (!refs)\n \t\treturn -1;\n \n-\tif (!refs_resolve_ref_unsafe(refs, refname, 0, oid, &flags) ||\n+\tif (!refs_resolve_ref_unsafe(refs, refname, NULL, 0, oid, &flags) ||\n \t    is_null_oid(oid))\n \t\treturn -1;\n \treturn 0;\ndiff --git a/refs.h b/refs.h\nindex 298caf6c618..2e740c692ac 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -71,9 +71,10 @@ struct pack_refs_opts {\n \tstruct ref_exclusions *exclusions;\n \tstruct string_list *includes;\n };\n-\n const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n+\n \t\t\t\t    const char *refname,\n+\t\t\t\t    char **referent,\n \t\t\t\t    int resolve_flags,\n \t\t\t\t    struct object_id *oid,\n \t\t\t\t    int *flags);\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex a098d14ea00..90342549af9 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -235,8 +235,9 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n {\n \tstruct object_id oid;\n \tint flag;\n+\tchar *referent;\n \n-\tif (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,\n+\tif (!refs_resolve_ref_unsafe(&refs->base, refname, &referent, RESOLVE_REF_READING,\n \t\t\t\t     &oid, &flag)) {\n \t\toidclr(&oid);\n \t\tflag |= REF_ISBROKEN;\n@@ -258,7 +259,7 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n \t\toidclr(&oid);\n \t\tflag |= REF_BAD_NAME | REF_ISBROKEN;\n \t}\n-\tadd_entry_to_dir(dir, create_ref_entry(refname, &oid, flag));\n+\tadd_entry_to_dir(dir, create_ref_entry(refname, referent, &oid, flag));\n }\n \n /*\n@@ -843,6 +844,7 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \t\t\tcontinue;\n \n \t\titer->base.refname = iter->iter0->refname;\n+\t\titer->base.referent = iter->iter0->referent;\n \t\titer->base.oid = iter->iter0->oid;\n \t\titer->base.flags = iter->iter0->flags;\n \t\treturn ITER_OK;\n@@ -1109,7 +1111,7 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,\n \t\tgoto error_return;\n \t}\n \n-\tif (!refs_resolve_ref_unsafe(&refs->base, lock->ref_name, 0,\n+\tif (!refs_resolve_ref_unsafe(&refs->base, lock->ref_name, NULL, 0,\n \t\t\t\t     &lock->old_oid, NULL))\n \t\toidclr(&lock->old_oid);\n \tgoto out;\n@@ -1444,7 +1446,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,\n \t\tgoto out;\n \t}\n \n-\tif (!refs_resolve_ref_unsafe(&refs->base, oldrefname,\n+\tif (!refs_resolve_ref_unsafe(&refs->base, oldrefname, NULL,\n \t\t\t\t     RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n \t\t\t\t     &orig_oid, &flag)) {\n \t\tret = error(\"refname %s not found\", oldrefname);\n@@ -1490,7 +1492,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,\n \t * the safety anyway; we want to delete the reference whatever\n \t * its current value.\n \t */\n-\tif (!copy && refs_resolve_ref_unsafe(&refs->base, newrefname,\n+\tif (!copy && refs_resolve_ref_unsafe(&refs->base, newrefname, NULL,\n \t\t\t\t\t     RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n \t\t\t\t\t     NULL, NULL) &&\n \t    refs_delete_ref(&refs->base, NULL, newrefname,\n@@ -1863,7 +1865,7 @@ static int commit_ref_update(struct files_ref_store *refs,\n \t\tint head_flag;\n \t\tconst char *head_ref;\n \n-\t\thead_ref = refs_resolve_ref_unsafe(&refs->base, \"HEAD\",\n+\t\thead_ref = refs_resolve_ref_unsafe(&refs->base, \"HEAD\", NULL,\n \t\t\t\t\t\t   RESOLVE_REF_READING,\n \t\t\t\t\t\t   NULL, &head_flag);\n \t\tif (head_ref && (head_flag & REF_ISSYMREF) &&\n@@ -1911,7 +1913,7 @@ static void update_symref_reflog(struct files_ref_store *refs,\n \tstruct object_id new_oid;\n \n \tif (logmsg &&\n-\t    refs_resolve_ref_unsafe(&refs->base, target,\n+\t    refs_resolve_ref_unsafe(&refs->base, target, NULL,\n \t\t\t\t    RESOLVE_REF_READING, &new_oid, NULL) &&\n \t    files_log_ref_write(refs, refname, &lock->old_oid,\n \t\t\t\t&new_oid, logmsg, 0, &err)) {\n@@ -2505,7 +2507,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,\n \t\t\t * to record and possibly check old_oid:\n \t\t\t */\n \t\t\tif (!refs_resolve_ref_unsafe(&refs->base,\n-\t\t\t\t\t\t     referent.buf, 0,\n+\t\t\t\t\t\t     referent.buf, NULL, 0,\n \t\t\t\t\t\t     &lock->old_oid, NULL)) {\n \t\t\t\tif (update->flags & REF_HAVE_OLD) {\n \t\t\t\t\tstrbuf_addf(err, \"cannot lock ref '%s': \"\n@@ -3200,7 +3202,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\t\tint type;\n \t\t\tconst char *ref;\n \n-\t\t\tref = refs_resolve_ref_unsafe(&refs->base, refname,\n+\t\t\tref = refs_resolve_ref_unsafe(&refs->base, refname, NULL,\n \t\t\t\t\t\t      RESOLVE_REF_NO_RECURSE,\n \t\t\t\t\t\t      NULL, &type);\n \t\t\tupdate = !!(ref && !(type & REF_ISSYMREF));\ndiff --git a/refs/iterator.c b/refs/iterator.c\nindex 9db8b056d56..26ca6f645ee 100644\n--- a/refs/iterator.c\n+++ b/refs/iterator.c\n@@ -199,6 +199,7 @@ static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \t\t}\n \n \t\tif (selection & ITER_YIELD_CURRENT) {\n+\t\t\titer->base.referent = (*iter->current)->referent;\n \t\t\titer->base.refname = (*iter->current)->refname;\n \t\t\titer->base.oid = (*iter->current)->oid;\n \t\t\titer->base.flags = (*iter->current)->flags;\ndiff --git a/refs/ref-cache.c b/refs/ref-cache.c\nindex 9f9797209a4..4c23b92414a 100644\n--- a/refs/ref-cache.c\n+++ b/refs/ref-cache.c\n@@ -34,12 +34,14 @@ struct ref_dir *get_ref_dir(struct ref_entry *entry)\n }\n \n struct ref_entry *create_ref_entry(const char *refname,\n+\t\t\t\t   const char *referent,\n \t\t\t\t   const struct object_id *oid, int flag)\n {\n \tstruct ref_entry *ref;\n \n \tFLEX_ALLOC_STR(ref, name, refname);\n \toidcpy(&ref->u.value.oid, oid);\n+\tref->u.value.referent = referent;\n \tref->flag = flag;\n \treturn ref;\n }\n@@ -428,6 +430,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \t\t\tlevel->prefix_state = entry_prefix_state;\n \t\t\tlevel->index = -1;\n \t\t} else {\n+\t\t\titer->base.referent = entry->u.value.referent;\n \t\t\titer->base.refname = entry->name;\n \t\t\titer->base.oid = &entry->u.value.oid;\n \t\t\titer->base.flags = entry->flag;\ndiff --git a/refs/ref-cache.h b/refs/ref-cache.h\nindex 95c76e27c83..12ddee4fddc 100644\n--- a/refs/ref-cache.h\n+++ b/refs/ref-cache.h\n@@ -42,6 +42,7 @@ struct ref_value {\n \t * referred to by the last reference in the symlink chain.\n \t */\n \tstruct object_id oid;\n+\tconst char *referent;\n };\n \n /*\n@@ -173,6 +174,7 @@ struct ref_entry *create_dir_entry(struct ref_cache *cache,\n \t\t\t\t   const char *dirname, size_t len);\n \n struct ref_entry *create_ref_entry(const char *refname,\n+\t\t\t\t   const char *referent,\n \t\t\t\t   const struct object_id *oid, int flag);\n \n /*\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 56641aa57a1..a10363eccf4 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -319,6 +319,7 @@ enum do_for_each_ref_flags {\n struct ref_iterator {\n \tstruct ref_iterator_vtable *vtable;\n \tconst char *refname;\n+\tconst char *referent;\n \tconst struct object_id *oid;\n \tunsigned int flags;\n };\ndiff --git a/refs/reftable-backend.c b/refs/reftable-backend.c\nindex e206d5a073c..7e8d15cc6a0 100644\n--- a/refs/reftable-backend.c\n+++ b/refs/reftable-backend.c\n@@ -342,6 +342,7 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \n \twhile (!iter->err) {\n \t\tint flags = 0;\n+\t\tchar **symref = NULL;\n \n \t\titer->err = reftable_iterator_next_ref(&iter->iter, &iter->ref);\n \t\tif (iter->err)\n@@ -377,7 +378,10 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \t\t\toidread(&iter->oid, iter->ref.value.val2.value);\n \t\t\tbreak;\n \t\tcase REFTABLE_REF_SYMREF:\n-\t\t\tif (!refs_resolve_ref_unsafe(&iter->refs->base, iter->ref.refname,\n+\t\t\tif (!iter->ref.value.symref)\n+\t\t\t\tsymref = &iter->ref.value.symref;\n+\n+\t\t\tif (!refs_resolve_ref_unsafe(&iter->refs->base, iter->ref.refname, symref,\n \t\t\t\t\t\t     RESOLVE_REF_READING, &iter->oid, &flags))\n \t\t\t\toidclr(&iter->oid);\n \t\t\tbreak;\n@@ -406,6 +410,7 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \t\t\t\tcontinue;\n \n \t\titer->base.refname = iter->ref.refname;\n+\t\titer->base.referent = iter->ref.value.symref;\n \t\titer->base.oid = &iter->oid;\n \t\titer->base.flags = flags;\n \n@@ -877,7 +882,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,\n \t\t\t * so it is safe to call `refs_resolve_ref_unsafe()`\n \t\t\t * here without causing races.\n \t\t\t */\n-\t\t\tconst char *resolved = refs_resolve_ref_unsafe(&refs->base, u->refname, 0,\n+\t\t\tconst char *resolved = refs_resolve_ref_unsafe(&refs->base, u->refname, NULL, 0,\n \t\t\t\t\t\t\t\t       &current_oid, NULL);\n \n \t\t\tif (u->flags & REF_NO_DEREF) {\n@@ -1252,7 +1257,7 @@ static int write_create_symref_table(struct reftable_writer *writer, void *cb_da\n \t * never happens.\n \t */\n \tif (!create->logmsg ||\n-\t    !refs_resolve_ref_unsafe(&create->refs->base, create->target,\n+\t    !refs_resolve_ref_unsafe(&create->refs->base, create->target, NULL,\n \t\t\t\t     RESOLVE_REF_READING, &new_oid, NULL) ||\n \t    !should_write_log(&create->refs->base, create->refname))\n \t\treturn 0;\n@@ -1263,7 +1268,7 @@ static int write_create_symref_table(struct reftable_writer *writer, void *cb_da\n \tlog.value.update.message = xstrndup(create->logmsg,\n \t\t\t\t\t    create->refs->write_options.block_size / 2);\n \tmemcpy(log.value.update.new_hash, new_oid.hash, GIT_MAX_RAWSZ);\n-\tif (refs_resolve_ref_unsafe(&create->refs->base, create->refname,\n+\tif (refs_resolve_ref_unsafe(&create->refs->base, create->refname, NULL,\n \t\t\t\t    RESOLVE_REF_READING, &old_oid, NULL))\n \t\tmemcpy(log.value.update.old_hash, old_oid.hash, GIT_MAX_RAWSZ);\n \ndiff --git a/remote.c b/remote.c\nindex 2b650b813b7..36525451e9c 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -518,7 +518,7 @@ static void read_config(struct repository *repo, int early)\n \trepo->remote_state->current_branch = NULL;\n \tif (startup_info->have_repository && !early) {\n \t\tconst char *head_ref = refs_resolve_ref_unsafe(\n-\t\t\tget_main_ref_store(repo), \"HEAD\", 0, NULL, &flag);\n+\t\t\tget_main_ref_store(repo), \"HEAD\", NULL, 0, NULL, &flag);\n \t\tif (head_ref && (flag & REF_ISSYMREF) &&\n \t\t    skip_prefix(head_ref, \"refs/heads/\", &head_ref)) {\n \t\t\trepo->remote_state->current_branch = make_branch(\ndiff --git a/sequencer.c b/sequencer.c\nindex fa838f264f5..e9cc2a4fdf4 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1430,7 +1430,7 @@ void print_commit_summary(struct repository *r,\n \tdiff_setup_done(&rev.diffopt);\n \n \trefs = get_main_ref_store(r);\n-\thead = refs_resolve_ref_unsafe(refs, \"HEAD\", 0, NULL, NULL);\n+\thead = refs_resolve_ref_unsafe(refs, \"HEAD\", NULL, 0, NULL, NULL);\n \tif (!head)\n \t\tdie(_(\"unable to resolve HEAD after creating commit\"));\n \tif (!strcmp(head, \"HEAD\"))\n@@ -4651,7 +4651,7 @@ static int apply_save_autostash_ref(struct repository *r, const char *refname,\n \tif (!refs_ref_exists(get_main_ref_store(r), refname))\n \t\treturn 0;\n \n-\tif (!refs_resolve_ref_unsafe(get_main_ref_store(r), refname,\n+\tif (!refs_resolve_ref_unsafe(get_main_ref_store(r), refname, NULL,\n \t\t\t\t     RESOLVE_REF_READING, &stash_oid, &flag))\n \t\treturn -1;\n \tif (flag & REF_ISSYMREF)\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex 7a0f6cac53d..bc501c5253c 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -203,7 +203,7 @@ static int cmd_resolve_ref(struct ref_store *refs, const char **argv)\n \tint flags;\n \tconst char *ref;\n \n-\tref = refs_resolve_ref_unsafe(refs, refname, resolve_flags,\n+\tref = refs_resolve_ref_unsafe(refs, refname, NULL, resolve_flags,\n \t\t\t\t      &oid, &flags);\n \tprintf(\"%s %s 0x%x\\n\", oid_to_hex(&oid), ref ? ref : \"(null)\", flags);\n \treturn ref ? 0 : 1;\ndiff --git a/worktree.c b/worktree.c\nindex b02a05a74a3..1fb865c2ae7 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -42,6 +42,7 @@ static void add_head_info(struct worktree *wt)\n \n \ttarget = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),\n \t\t\t\t\t \"HEAD\",\n+\t\t\t\t\t NULL,\n \t\t\t\t\t 0,\n \t\t\t\t\t &wt->head_oid, &flags);\n \tif (!target)\n@@ -446,7 +447,7 @@ int is_shared_symref(const struct worktree *wt, const char *symref,\n \t}\n \n \trefs = get_worktree_ref_store(wt);\n-\tsymref_target = refs_resolve_ref_unsafe(refs, symref, 0,\n+\tsymref_target = refs_resolve_ref_unsafe(refs, symref, NULL, 0,\n \t\t\t\t\t\tNULL, &flags);\n \tif ((flags & REF_ISSYMREF) &&\n \t    symref_target && !strcmp(symref_target, target))\n@@ -547,6 +548,7 @@ int other_head_refs(each_ref_fn fn, void *cb_data)\n \t\tstrbuf_worktree_ref(wt, &refname, \"HEAD\");\n \t\tif (refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n \t\t\t\t\t    refname.buf,\n+\t\t\t\t\t    NULL,\n \t\t\t\t\t    RESOLVE_REF_READING,\n \t\t\t\t\t    &oid, &flag))\n \t\t\tret = fn(refname.buf, &oid, flag, cb_data);\n-- \ngitgitgadget\n\n"},{"id":"492573","messageId":"a9e6644327a04f1d309eca812ace9c4159781353.1712597893.git.gitgitgadget@gmail.com","threadId":"61049","inReplyTo":"pull.1684.v2.git.git.1712597893.gitgitgadget@gmail.com","subject":"[PATCH v2 3/3] show-ref: add --symbolic-name option","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-04-08T17:38:13Z","receivedAt":"2024-04-08T17:38:20Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"From: John Cai <johncai86@gmail.com>\n\nFor reftable development, it would be handy to have a tool to provide\nthe direct value of any ref whether it be a symbolic ref or not.\nCurrently there is git-symbolic-ref, which only works for symbolic refs,\nand git-rev-parse, which will resolve the ref. Let's teach show-ref a\n--symbolic-name option that will cause git-show-ref(1) to print out the\nvalue symbolic references points to.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n Documentation/git-show-ref.txt | 21 ++++++++++++++++++-\n builtin/show-ref.c             | 38 ++++++++++++++++++++++++----------\n refs.c                         |  6 ++++++\n refs.h                         |  2 +-\n t/t1403-show-ref.sh            | 20 ++++++++++++++++++\n 5 files changed, 74 insertions(+), 13 deletions(-)\n\ndiff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt\nindex ba757470059..9627b34b37f 100644\n--- a/Documentation/git-show-ref.txt\n+++ b/Documentation/git-show-ref.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n [verse]\n 'git show-ref' [--head] [-d | --dereference]\n \t     [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n-\t     [--heads] [--] [<pattern>...]\n+\t     [--heads] [--symbolic-name] [--] [<pattern>...]\n 'git show-ref' --verify [-q | --quiet] [-d | --dereference]\n \t     [-s | --hash[=<n>]] [--abbrev[=<n>]]\n \t     [--] [<ref>...]\n@@ -58,6 +58,11 @@ OPTIONS\n \tDereference tags into object IDs as well. They will be shown with `^{}`\n \tappended.\n \n+--symbolic-name::\n+\n+\tPrint out the value the reference points to without dereferencing. This\n+\tis useful to know the reference that a symbolic ref is pointing to.\n+\n -s::\n --hash[=<n>]::\n \n@@ -146,6 +151,20 @@ $ git show-ref --heads --hash\n ...\n -----------------------------------------------------------------------------\n \n+When using `--symbolic-name`, the output is in the format:\n+\n+-----------\n+<oid> SP <ref> SP <symbolic-name>\n+-----------\n+\n+For example,\n+\n+-----------------------------------------------------------------------------\n+$ git show-ref --symbolic-name\n+b75428bae1d090f60bdd4b67185f814bc8f0819d refs/heads/SYMBOLIC_REF ref:refs/heads/main\n+...\n+-----------------------------------------------------------------------------\n+\n EXAMPLES\n --------\n \ndiff --git a/builtin/show-ref.c b/builtin/show-ref.c\nindex 1c15421e600..1d681505eac 100644\n--- a/builtin/show-ref.c\n+++ b/builtin/show-ref.c\n@@ -12,7 +12,7 @@\n static const char * const show_ref_usage[] = {\n \tN_(\"git show-ref [--head] [-d | --dereference]\\n\"\n \t   \"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\\n\"\n-\t   \"             [--heads] [--] [<pattern>...]\"),\n+\t   \"             [--heads] [--symbolic-name] [--] [<pattern>...]\"),\n \tN_(\"git show-ref --verify [-q | --quiet] [-d | --dereference]\\n\"\n \t   \"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\\n\"\n \t   \"             [--] [<ref>...]\"),\n@@ -26,10 +26,13 @@ struct show_one_options {\n \tint hash_only;\n \tint abbrev;\n \tint deref_tags;\n+\tint symbolic_name;\n };\n \n static void show_one(const struct show_one_options *opts,\n-\t\t     const char *refname, const struct object_id *oid)\n+\t\t     const char *refname,\n+\t\t     const char *referent,\n+\t\t     const struct object_id *oid, const int is_symref)\n {\n \tconst char *hex;\n \tstruct object_id peeled;\n@@ -44,7 +47,9 @@ static void show_one(const struct show_one_options *opts,\n \thex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev);\n \tif (opts->hash_only)\n \t\tprintf(\"%s\\n\", hex);\n-\telse\n+\telse if (opts->symbolic_name & is_symref) {\n+\t\tprintf(\"%s %s ref:%s\\n\", hex, refname, referent);\n+\t} else\n \t\tprintf(\"%s %s\\n\", hex, refname);\n \n \tif (!opts->deref_tags)\n@@ -63,8 +68,11 @@ struct show_ref_data {\n \tint show_head;\n };\n \n-static int show_ref(const char *refname, const struct object_id *oid,\n-\t\t    int flag UNUSED, void *cbdata)\n+static int show_ref_referent(struct repository *repo UNUSED,\n+\t\t\t     const char *refname,\n+\t\t\t     const char *referent,\n+\t\t\t     const struct object_id *oid,\n+\t\t\t     int flag, void *cbdata)\n {\n \tstruct show_ref_data *data = cbdata;\n \n@@ -91,11 +99,17 @@ static int show_ref(const char *refname, const struct object_id *oid,\n match:\n \tdata->found_match++;\n \n-\tshow_one(data->show_one_opts, refname, oid);\n+\tshow_one(data->show_one_opts, refname, referent, oid, flag & REF_ISSYMREF);\n \n \treturn 0;\n }\n \n+static int show_ref(const char *refname, const struct object_id *oid,\n+\t\t    int flag, void *cbdata)\n+{\n+\treturn show_ref_referent(NULL, refname, NULL, oid, flag, cbdata);\n+}\n+\n static int add_existing(const char *refname,\n \t\t\tconst struct object_id *oid UNUSED,\n \t\t\tint flag UNUSED, void *cbdata)\n@@ -171,10 +185,11 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,\n \n \twhile (*refs) {\n \t\tstruct object_id oid;\n+\t\tint flags = 0;\n \n \t\tif ((starts_with(*refs, \"refs/\") || refname_is_safe(*refs)) &&\n-\t\t    !read_ref(*refs, &oid)) {\n-\t\t\tshow_one(show_one_opts, *refs, &oid);\n+\t\t    !read_ref_full(*refs, 0, &oid, &flags)) {\n+\t\t\tshow_one(show_one_opts, *refs, NULL, &oid, flags & REF_ISSYMREF);\n \t\t}\n \t\telse if (!show_one_opts->quiet)\n \t\t\tdie(\"'%s' - not a valid ref\", *refs);\n@@ -208,11 +223,11 @@ static int cmd_show_ref__patterns(const struct patterns_options *opts,\n \t\thead_ref(show_ref, &show_ref_data);\n \tif (opts->heads_only || opts->tags_only) {\n \t\tif (opts->heads_only)\n-\t\t\tfor_each_fullref_in(\"refs/heads/\", show_ref, &show_ref_data);\n+\t\t\tfor_each_ref_all(\"refs/heads/\", show_ref_referent, &show_ref_data);\n \t\tif (opts->tags_only)\n-\t\t\tfor_each_fullref_in(\"refs/tags/\", show_ref, &show_ref_data);\n+\t\t\tfor_each_ref_all(\"refs/tags/\", show_ref_referent, &show_ref_data);\n \t} else {\n-\t\tfor_each_ref(show_ref, &show_ref_data);\n+\t\tfor_each_ref_all(\"\", show_ref_referent, &show_ref_data);\n \t}\n \tif (!show_ref_data.found_match)\n \t\treturn 1;\n@@ -289,6 +304,7 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL(0, \"tags\", &patterns_opts.tags_only, N_(\"only show tags (can be combined with heads)\")),\n \t\tOPT_BOOL(0, \"heads\", &patterns_opts.heads_only, N_(\"only show heads (can be combined with tags)\")),\n \t\tOPT_BOOL(0, \"exists\", &exists, N_(\"check for reference existence without resolving\")),\n+\t\tOPT_BOOL(0, \"symbolic-name\", &show_one_opts.symbolic_name, N_(\"print out symbolic reference values\")),\n \t\tOPT_BOOL(0, \"verify\", &verify, N_(\"stricter reference checking, \"\n \t\t\t    \"requires exact ref path\")),\n \t\tOPT_HIDDEN_BOOL('h', NULL, &patterns_opts.show_head,\ndiff --git a/refs.c b/refs.c\nindex 77ae38ea214..9488ad9594d 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1734,6 +1734,12 @@ int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_dat\n \t\t\t\t    DO_FOR_EACH_INCLUDE_BROKEN, cb_data);\n }\n \n+int for_each_ref_all(const char *prefix, each_repo_ref_fn fn, void *cb_data)\n+{\n+\treturn do_for_each_repo_ref(the_repository, prefix, fn, 0,\n+\t\t\t\t    0, cb_data);\n+}\n+\n int for_each_namespaced_ref(const char **exclude_patterns,\n \t\t\t    each_ref_fn fn, void *cb_data)\n {\ndiff --git a/refs.h b/refs.h\nindex 23e5aaba2e9..54b459375be 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -337,7 +337,6 @@ int refs_for_each_branch_ref(struct ref_store *refs,\n \t\t\t     each_ref_fn fn, void *cb_data);\n int refs_for_each_remote_ref(struct ref_store *refs,\n \t\t\t     each_ref_fn fn, void *cb_data);\n-\n /* just iterates the head ref. */\n int head_ref(each_ref_fn fn, void *cb_data);\n \n@@ -381,6 +380,7 @@ int for_each_tag_ref(each_ref_fn fn, void *cb_data);\n int for_each_branch_ref(each_ref_fn fn, void *cb_data);\n int for_each_remote_ref(each_ref_fn fn, void *cb_data);\n int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data);\n+int for_each_ref_all(const char *prefix, each_repo_ref_fn fn, void *cb_data);\n \n /* iterates all refs that match the specified glob pattern. */\n int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);\ndiff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh\nindex 33fb7a38fff..0aebe709dca 100755\n--- a/t/t1403-show-ref.sh\n+++ b/t/t1403-show-ref.sh\n@@ -286,4 +286,24 @@ test_expect_success '--exists with existing special ref' '\n \tgit show-ref --exists FETCH_HEAD\n '\n \n+test_expect_success '--symbolic-name with a non symbolic ref' '\n+\tcommit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) &&\n+\tcat >expect <<-EOF &&\n+\t$commit_oid refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n+\tEOF\n+\tgit show-ref --symbolic-name refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--symbolic-name with symbolic ref' '\n+\ttest_when_finished \"git symbolic-ref -d refs/heads/SYMBOLIC_REF_A\" &&\n+\tcommit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) &&\n+\tcat >expect <<-EOF &&\n+\t$commit_oid refs/heads/SYMBOLIC_REF_A ref:refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n+\tEOF\n+\tgit symbolic-ref refs/heads/SYMBOLIC_REF_A refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&\n+\tgit show-ref --symbolic-name SYMBOLIC_REF_A >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \ngitgitgadget\n"},{"id":"492591","messageId":"xmqq34rv791a.fsf@gitster.g","threadId":"61049","inReplyTo":"6adc9dd26da4459d246591ce148c960b33bde336.1712597893.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 1/3] refs: keep track of unresolved reference value in iterator","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-04-08T23:02:25Z","receivedAt":"2024-04-08T23:02:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"> diff --git a/refs.h b/refs.h\n> index 298caf6c618..2e740c692ac 100644\n> --- a/refs.h\n> +++ b/refs.h\n> @@ -71,9 +71,10 @@ struct pack_refs_opts {\n>  \tstruct ref_exclusions *exclusions;\n>  \tstruct string_list *includes;\n>  };\n> -\n>  const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n> +\n>  \t\t\t\t    const char *refname,\n> +\t\t\t\t    char **referent,\n>  \t\t\t\t    int resolve_flags,\n>  \t\t\t\t    struct object_id *oid,\n>  \t\t\t\t    int *flags);\n\nIf referent is meant to be an out-parameter, it should sit next to\noid that is also an out-parameter.  And as a late-comer sibling, it\nshould sit after its elder brother.\n\nAlso, I do not see the reason for the shuffling of blank lines.\nShouldn't it be the other way around, even?  After an unrelated\ndefinition of \"struct pack_refs_opts\", there is (and should be)\na blank line, then the first line of the declaration of function.\n\nPerhaps some fat-fingering.\n\n> @@ -1928,6 +1928,7 @@ int refs_read_symbolic_ref(struct ref_store *ref_store, const char *refname,\n>  \n>  const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n>  \t\t\t\t    const char *refname,\n> +\t\t\t\t    char **referent,\n>  \t\t\t\t    int resolve_flags,\n>  \t\t\t\t    struct object_id *oid,\n>  \t\t\t\t    int *flags)\n> @@ -1989,6 +1990,8 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n>  \t\t}\n>  \n>  \t\t*flags |= read_flags;\n> +\t\tif (referent && read_flags & REF_ISSYMREF && sb_refname.len > 0)\n> +\t\t\t*referent = sb_refname.buf;\n\nIs this safe?  After this assignment, which \"return\" in this loop\nare you expecting to return from this function?  If you fail to\nreturn from the function during this iteration, you'll clobber the\nsame strbuf with the next refs_read_raw_ref(), but I do not see how\nyou are ensuring that you'll return from the function without such\ncorruption happening.\n\nThis assignment happens only when read_flags has REF_ISSYMREF set,\nso the next \"if it is not, then return refname\" would not even\ntrigger.  If RESOLVE_REF_NO_RECURSE bit is on in resolve_flags,\nthen we'd return without further dereferencing, but if that is the\nonly safe exit from the function, shouldn't you be guarding the\nfunction with something like\n\n\tif (referent && !(resolve_flags & RESOLVE_REF_NO_RECURSE))\n\t\tBUG(\"recursive dereference can will clobber *referent\");\n\nto protect its callers from their own mistakes?\n\nAnother return before we start the next iteration of the loop and\nclobber sb_refname with another call to refs_read_raw_ref() is the\nerror return codepath at the end of the loop, but that is a totally\nuninteresting case.\n\nOr am I totally confused?\n"},{"id":"492635","messageId":"a9ccd0db-2e52-4521-9d72-49d2a85656f5@gmail.com","threadId":"61049","inReplyTo":"a9e6644327a04f1d309eca812ace9c4159781353.1712597893.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 3/3] show-ref: add --symbolic-name option","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-04-09T15:25:13Z","receivedAt":"2024-04-09T15:25:17Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi John\n\nOn 08/04/2024 18:38, John Cai via GitGitGadget wrote:\n > +--symbolic-name::\n > +\n > +\tPrint out the value the reference points to without dereferencing. This\n > +\tis useful to know the reference that a symbolic ref is pointing to.\n\nIt would be helpful to clarify that this prints the contents of the \nsymbolic ref without dereferencing but also prints the OID after \ndereferencing the given ref.\n\n > +When using `--symbolic-name`, the output is in the format:\n > +\n > +-----------\n > +<oid> SP <ref> SP <symbolic-name>\n > +-----------\n > +\n > +For example,\n > +\n > \n+-----------------------------------------------------------------------------\n > +$ git show-ref --symbolic-name\n > +b75428bae1d090f60bdd4b67185f814bc8f0819d refs/heads/SYMBOLIC_REF \nref:refs/heads/main\n\nDo we really need the \"ref:\" prefix? It is not specified above and I \nthink anyone calling this would have to remove the prefix before they \ncould use the value.\n\n\n> +test_expect_success '--symbolic-name with symbolic ref' '\n> +\ttest_when_finished \"git symbolic-ref -d refs/heads/SYMBOLIC_REF_A\" &&\n> +\tcommit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) &&\n> +\tcat >expect <<-EOF &&\n> +\t$commit_oid refs/heads/SYMBOLIC_REF_A ref:refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n> +\tEOF\n> +\tgit symbolic-ref refs/heads/SYMBOLIC_REF_A refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&\n> +\tgit show-ref --symbolic-name SYMBOLIC_REF_A >actual &&\n> +\ttest_cmp expect actual\n> +'\n\nI think it would be nice to see a test along the lines of\n\n\tgit symbolic-ref refs/symref-c refs/heads/master\n\tgit symbolic-ref refs/symref-b refs/symref-c &&\n\tgit symbolic-ref refs/symref-a refs/symref-b &&\n\tgit show-ref --symbolic-name refs/symref-a >actual &&\n\tcat >expect <<\\EOF &&\n\t$(git show-ref -s --verify refs/heads/master) refs/heads/symref-a \nrefs/heads/symref-b\n\tEOF\n\ttest_cmp expect actual\n\nto show what this command is expected to return when there is a chain of \nsymbolic references.\n\nBest Wishes\n\nPhillip\n"},{"id":"492646","messageId":"DDF183A6-2840-4225-B459-8D9B4287D98F@gmail.com","threadId":"61049","inReplyTo":"xmqq34rv791a.fsf@gitster.g","subject":"Re: [PATCH v2 1/3] refs: keep track of unresolved reference value in iterator","fromName":"John Cai","fromEmail":"johncai86@gmail.com","sentAt":"2024-04-09T20:29:48Z","receivedAt":"2024-04-09T20:29:51Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"Hi Junio\n\nOn 8 Apr 2024, at 19:02, Junio C Hamano wrote:\n\n>> diff --git a/refs.h b/refs.h\n>> index 298caf6c618..2e740c692ac 100644\n>> --- a/refs.h\n>> +++ b/refs.h\n>> @@ -71,9 +71,10 @@ struct pack_refs_opts {\n>>  \tstruct ref_exclusions *exclusions;\n>>  \tstruct string_list *includes;\n>>  };\n>> -\n>>  const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n>> +\n>>  \t\t\t\t    const char *refname,\n>> +\t\t\t\t    char **referent,\n>>  \t\t\t\t    int resolve_flags,\n>>  \t\t\t\t    struct object_id *oid,\n>>  \t\t\t\t    int *flags);\n>\n> If referent is meant to be an out-parameter, it should sit next to\n> oid that is also an out-parameter.  And as a late-comer sibling, it\n> should sit after its elder brother.\n>\n> Also, I do not see the reason for the shuffling of blank lines.\n> Shouldn't it be the other way around, even?  After an unrelated\n> definition of \"struct pack_refs_opts\", there is (and should be)\n> a blank line, then the first line of the declaration of function.\n>\n> Perhaps some fat-fingering.\n\nThis was indeed a case of fat-fingering.\n\n>\n>> @@ -1928,6 +1928,7 @@ int refs_read_symbolic_ref(struct ref_store *ref_store, const char *refname,\n>>\n>>  const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n>>  \t\t\t\t    const char *refname,\n>> +\t\t\t\t    char **referent,\n>>  \t\t\t\t    int resolve_flags,\n>>  \t\t\t\t    struct object_id *oid,\n>>  \t\t\t\t    int *flags)\n>> @@ -1989,6 +1990,8 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n>>  \t\t}\n>>\n>>  \t\t*flags |= read_flags;\n>> +\t\tif (referent && read_flags & REF_ISSYMREF && sb_refname.len > 0)\n>> +\t\t\t*referent = sb_refname.buf;\n>\n> Is this safe?  After this assignment, which \"return\" in this loop\n> are you expecting to return from this function?  If you fail to\n> return from the function during this iteration, you'll clobber the\n> same strbuf with the next refs_read_raw_ref(), but I do not see how\n> you are ensuring that you'll return from the function without such\n> corruption happening.\n>\n> This assignment happens only when read_flags has REF_ISSYMREF set,\n> so the next \"if it is not, then return refname\" would not even\n> trigger.  If RESOLVE_REF_NO_RECURSE bit is on in resolve_flags,\n> then we'd return without further dereferencing, but if that is the\n> only safe exit from the function, shouldn't you be guarding the\n> function with something like\n>\n> \tif (referent && !(resolve_flags & RESOLVE_REF_NO_RECURSE))\n> \t\tBUG(\"recursive dereference can will clobber *referent\");\n>\n> to protect its callers from their own mistakes?\n>\n> Another return before we start the next iteration of the loop and\n> clobber sb_refname with another call to refs_read_raw_ref() is the\n> error return codepath at the end of the loop, but that is a totally\n> uninteresting case.\n>\n> Or am I totally confused?\n\nYeah good point. This probably not a good idea. In fact, perhaps adding another\nargument to this function is unnecessary. We already have refs_read_symbolic_ref\nand we can just make a separate call to that once we know that a ref is a\nsymbolic reference. Though a separate call is less efficient, I'm not sure it's\nworth adding this parameter.\n\nthanks\nJohn\n"},{"id":"492684","messageId":"ZhY3SUdrX2WifyZf@tanuki","threadId":"61049","inReplyTo":"6adc9dd26da4459d246591ce148c960b33bde336.1712597893.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 1/3] refs: keep track of unresolved reference value in iterator","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-04-10T06:52:57Z","receivedAt":"2024-04-10T06:53:05Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Mon, Apr 08, 2024 at 05:38:11PM +0000, John Cai via GitGitGadget wrote:\n> From: John Cai <johncai86@gmail.com>\n[snip]\n> @@ -1928,6 +1928,7 @@ int refs_read_symbolic_ref(struct ref_store *ref_store, const char *refname,\n>  \n>  const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n>  \t\t\t\t    const char *refname,\n> +\t\t\t\t    char **referent,\n>  \t\t\t\t    int resolve_flags,\n>  \t\t\t\t    struct object_id *oid,\n>  \t\t\t\t    int *flags)\n\nI wonder whether this really should be a `char **`. You don't seem to be\nfree'ing returned pointers anywhere, so this should probably at least be\n`const char **` to indicate that memory is owned by the ref store. But\nthat would require the ref store to inevitably release it, which may\neasily lead to bugs when the caller expects a different lifetime.\n\nSo how about we instead make this a `struct strbuf *referent`? This\nmakes it quite clear who owns the memory. Furthermore, if the caller\nwants to resolve multiple refs, it would allow them to reuse the buffer\nfor better-optimized allocation patterns.\n\n[snip]\n> diff --git a/refs/ref-cache.c b/refs/ref-cache.c\n> index 9f9797209a4..4c23b92414a 100644\n> --- a/refs/ref-cache.c\n> +++ b/refs/ref-cache.c\n> @@ -34,12 +34,14 @@ struct ref_dir *get_ref_dir(struct ref_entry *entry)\n>  }\n>  \n>  struct ref_entry *create_ref_entry(const char *refname,\n> +\t\t\t\t   const char *referent,\n>  \t\t\t\t   const struct object_id *oid, int flag)\n>  {\n>  \tstruct ref_entry *ref;\n>  \n>  \tFLEX_ALLOC_STR(ref, name, refname);\n>  \toidcpy(&ref->u.value.oid, oid);\n> +\tref->u.value.referent = referent;\n>  \tref->flag = flag;\n>  \treturn ref;\n>  }\n\nIt's kind of hard to spot the actual changes inbetween all those changes\nto callsites. Is it possible to split the patch up such that we have one\npatch that changes all the callsites and one patch that does the actual\nchanges to implement this?\n\nPatrick\n"},{"id":"492685","messageId":"ZhY3UCwJeMcG4fH1@tanuki","threadId":"61049","inReplyTo":"a9e6644327a04f1d309eca812ace9c4159781353.1712597893.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 3/3] show-ref: add --symbolic-name option","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-04-10T06:53:04Z","receivedAt":"2024-04-10T06:53:09Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Mon, Apr 08, 2024 at 05:38:13PM +0000, John Cai via GitGitGadget wrote:\n> From: John Cai <johncai86@gmail.com>\n> \n> For reftable development, it would be handy to have a tool to provide\n> the direct value of any ref whether it be a symbolic ref or not.\n> Currently there is git-symbolic-ref, which only works for symbolic refs,\n> and git-rev-parse, which will resolve the ref. Let's teach show-ref a\n> --symbolic-name option that will cause git-show-ref(1) to print out the\n> value symbolic references points to.\n\nI think it was Peff who shared a way to achieve this without actually\nintroducing a new option via `git for-each-ref --format=`. Can we maybe\nprovide some benchmarks to demonstrate that this variant is preferable\nover what's already possible?\n\nPatrick\n\n> Signed-off-by: John Cai <johncai86@gmail.com>\n> ---\n>  Documentation/git-show-ref.txt | 21 ++++++++++++++++++-\n>  builtin/show-ref.c             | 38 ++++++++++++++++++++++++----------\n>  refs.c                         |  6 ++++++\n>  refs.h                         |  2 +-\n>  t/t1403-show-ref.sh            | 20 ++++++++++++++++++\n>  5 files changed, 74 insertions(+), 13 deletions(-)\n> \n> diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt\n> index ba757470059..9627b34b37f 100644\n> --- a/Documentation/git-show-ref.txt\n> +++ b/Documentation/git-show-ref.txt\n> @@ -10,7 +10,7 @@ SYNOPSIS\n>  [verse]\n>  'git show-ref' [--head] [-d | --dereference]\n>  \t     [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n> -\t     [--heads] [--] [<pattern>...]\n> +\t     [--heads] [--symbolic-name] [--] [<pattern>...]\n>  'git show-ref' --verify [-q | --quiet] [-d | --dereference]\n>  \t     [-s | --hash[=<n>]] [--abbrev[=<n>]]\n>  \t     [--] [<ref>...]\n> @@ -58,6 +58,11 @@ OPTIONS\n>  \tDereference tags into object IDs as well. They will be shown with `^{}`\n>  \tappended.\n>  \n> +--symbolic-name::\n> +\n> +\tPrint out the value the reference points to without dereferencing. This\n> +\tis useful to know the reference that a symbolic ref is pointing to.\n> +\n>  -s::\n>  --hash[=<n>]::\n>  \n> @@ -146,6 +151,20 @@ $ git show-ref --heads --hash\n>  ...\n>  -----------------------------------------------------------------------------\n>  \n> +When using `--symbolic-name`, the output is in the format:\n> +\n> +-----------\n> +<oid> SP <ref> SP <symbolic-name>\n> +-----------\n> +\n> +For example,\n> +\n> +-----------------------------------------------------------------------------\n> +$ git show-ref --symbolic-name\n> +b75428bae1d090f60bdd4b67185f814bc8f0819d refs/heads/SYMBOLIC_REF ref:refs/heads/main\n> +...\n> +-----------------------------------------------------------------------------\n> +\n>  EXAMPLES\n>  --------\n>  \n> diff --git a/builtin/show-ref.c b/builtin/show-ref.c\n> index 1c15421e600..1d681505eac 100644\n> --- a/builtin/show-ref.c\n> +++ b/builtin/show-ref.c\n> @@ -12,7 +12,7 @@\n>  static const char * const show_ref_usage[] = {\n>  \tN_(\"git show-ref [--head] [-d | --dereference]\\n\"\n>  \t   \"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\\n\"\n> -\t   \"             [--heads] [--] [<pattern>...]\"),\n> +\t   \"             [--heads] [--symbolic-name] [--] [<pattern>...]\"),\n>  \tN_(\"git show-ref --verify [-q | --quiet] [-d | --dereference]\\n\"\n>  \t   \"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\\n\"\n>  \t   \"             [--] [<ref>...]\"),\n> @@ -26,10 +26,13 @@ struct show_one_options {\n>  \tint hash_only;\n>  \tint abbrev;\n>  \tint deref_tags;\n> +\tint symbolic_name;\n>  };\n>  \n>  static void show_one(const struct show_one_options *opts,\n> -\t\t     const char *refname, const struct object_id *oid)\n> +\t\t     const char *refname,\n> +\t\t     const char *referent,\n> +\t\t     const struct object_id *oid, const int is_symref)\n>  {\n>  \tconst char *hex;\n>  \tstruct object_id peeled;\n> @@ -44,7 +47,9 @@ static void show_one(const struct show_one_options *opts,\n>  \thex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev);\n>  \tif (opts->hash_only)\n>  \t\tprintf(\"%s\\n\", hex);\n> -\telse\n> +\telse if (opts->symbolic_name & is_symref) {\n> +\t\tprintf(\"%s %s ref:%s\\n\", hex, refname, referent);\n> +\t} else\n>  \t\tprintf(\"%s %s\\n\", hex, refname);\n>  \n>  \tif (!opts->deref_tags)\n> @@ -63,8 +68,11 @@ struct show_ref_data {\n>  \tint show_head;\n>  };\n>  \n> -static int show_ref(const char *refname, const struct object_id *oid,\n> -\t\t    int flag UNUSED, void *cbdata)\n> +static int show_ref_referent(struct repository *repo UNUSED,\n> +\t\t\t     const char *refname,\n> +\t\t\t     const char *referent,\n> +\t\t\t     const struct object_id *oid,\n> +\t\t\t     int flag, void *cbdata)\n>  {\n>  \tstruct show_ref_data *data = cbdata;\n>  \n> @@ -91,11 +99,17 @@ static int show_ref(const char *refname, const struct object_id *oid,\n>  match:\n>  \tdata->found_match++;\n>  \n> -\tshow_one(data->show_one_opts, refname, oid);\n> +\tshow_one(data->show_one_opts, refname, referent, oid, flag & REF_ISSYMREF);\n>  \n>  \treturn 0;\n>  }\n>  \n> +static int show_ref(const char *refname, const struct object_id *oid,\n> +\t\t    int flag, void *cbdata)\n> +{\n> +\treturn show_ref_referent(NULL, refname, NULL, oid, flag, cbdata);\n> +}\n> +\n>  static int add_existing(const char *refname,\n>  \t\t\tconst struct object_id *oid UNUSED,\n>  \t\t\tint flag UNUSED, void *cbdata)\n> @@ -171,10 +185,11 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,\n>  \n>  \twhile (*refs) {\n>  \t\tstruct object_id oid;\n> +\t\tint flags = 0;\n>  \n>  \t\tif ((starts_with(*refs, \"refs/\") || refname_is_safe(*refs)) &&\n> -\t\t    !read_ref(*refs, &oid)) {\n> -\t\t\tshow_one(show_one_opts, *refs, &oid);\n> +\t\t    !read_ref_full(*refs, 0, &oid, &flags)) {\n> +\t\t\tshow_one(show_one_opts, *refs, NULL, &oid, flags & REF_ISSYMREF);\n>  \t\t}\n>  \t\telse if (!show_one_opts->quiet)\n>  \t\t\tdie(\"'%s' - not a valid ref\", *refs);\n> @@ -208,11 +223,11 @@ static int cmd_show_ref__patterns(const struct patterns_options *opts,\n>  \t\thead_ref(show_ref, &show_ref_data);\n>  \tif (opts->heads_only || opts->tags_only) {\n>  \t\tif (opts->heads_only)\n> -\t\t\tfor_each_fullref_in(\"refs/heads/\", show_ref, &show_ref_data);\n> +\t\t\tfor_each_ref_all(\"refs/heads/\", show_ref_referent, &show_ref_data);\n>  \t\tif (opts->tags_only)\n> -\t\t\tfor_each_fullref_in(\"refs/tags/\", show_ref, &show_ref_data);\n> +\t\t\tfor_each_ref_all(\"refs/tags/\", show_ref_referent, &show_ref_data);\n>  \t} else {\n> -\t\tfor_each_ref(show_ref, &show_ref_data);\n> +\t\tfor_each_ref_all(\"\", show_ref_referent, &show_ref_data);\n>  \t}\n>  \tif (!show_ref_data.found_match)\n>  \t\treturn 1;\n> @@ -289,6 +304,7 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)\n>  \t\tOPT_BOOL(0, \"tags\", &patterns_opts.tags_only, N_(\"only show tags (can be combined with heads)\")),\n>  \t\tOPT_BOOL(0, \"heads\", &patterns_opts.heads_only, N_(\"only show heads (can be combined with tags)\")),\n>  \t\tOPT_BOOL(0, \"exists\", &exists, N_(\"check for reference existence without resolving\")),\n> +\t\tOPT_BOOL(0, \"symbolic-name\", &show_one_opts.symbolic_name, N_(\"print out symbolic reference values\")),\n>  \t\tOPT_BOOL(0, \"verify\", &verify, N_(\"stricter reference checking, \"\n>  \t\t\t    \"requires exact ref path\")),\n>  \t\tOPT_HIDDEN_BOOL('h', NULL, &patterns_opts.show_head,\n> diff --git a/refs.c b/refs.c\n> index 77ae38ea214..9488ad9594d 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -1734,6 +1734,12 @@ int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_dat\n>  \t\t\t\t    DO_FOR_EACH_INCLUDE_BROKEN, cb_data);\n>  }\n>  \n> +int for_each_ref_all(const char *prefix, each_repo_ref_fn fn, void *cb_data)\n> +{\n> +\treturn do_for_each_repo_ref(the_repository, prefix, fn, 0,\n> +\t\t\t\t    0, cb_data);\n> +}\n> +\n>  int for_each_namespaced_ref(const char **exclude_patterns,\n>  \t\t\t    each_ref_fn fn, void *cb_data)\n>  {\n> diff --git a/refs.h b/refs.h\n> index 23e5aaba2e9..54b459375be 100644\n> --- a/refs.h\n> +++ b/refs.h\n> @@ -337,7 +337,6 @@ int refs_for_each_branch_ref(struct ref_store *refs,\n>  \t\t\t     each_ref_fn fn, void *cb_data);\n>  int refs_for_each_remote_ref(struct ref_store *refs,\n>  \t\t\t     each_ref_fn fn, void *cb_data);\n> -\n>  /* just iterates the head ref. */\n>  int head_ref(each_ref_fn fn, void *cb_data);\n>  \n> @@ -381,6 +380,7 @@ int for_each_tag_ref(each_ref_fn fn, void *cb_data);\n>  int for_each_branch_ref(each_ref_fn fn, void *cb_data);\n>  int for_each_remote_ref(each_ref_fn fn, void *cb_data);\n>  int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data);\n> +int for_each_ref_all(const char *prefix, each_repo_ref_fn fn, void *cb_data);\n>  \n>  /* iterates all refs that match the specified glob pattern. */\n>  int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);\n> diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh\n> index 33fb7a38fff..0aebe709dca 100755\n> --- a/t/t1403-show-ref.sh\n> +++ b/t/t1403-show-ref.sh\n> @@ -286,4 +286,24 @@ test_expect_success '--exists with existing special ref' '\n>  \tgit show-ref --exists FETCH_HEAD\n>  '\n>  \n> +test_expect_success '--symbolic-name with a non symbolic ref' '\n> +\tcommit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) &&\n> +\tcat >expect <<-EOF &&\n> +\t$commit_oid refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n> +\tEOF\n> +\tgit show-ref --symbolic-name refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success '--symbolic-name with symbolic ref' '\n> +\ttest_when_finished \"git symbolic-ref -d refs/heads/SYMBOLIC_REF_A\" &&\n> +\tcommit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) &&\n> +\tcat >expect <<-EOF &&\n> +\t$commit_oid refs/heads/SYMBOLIC_REF_A ref:refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n> +\tEOF\n> +\tgit symbolic-ref refs/heads/SYMBOLIC_REF_A refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&\n> +\tgit show-ref --symbolic-name SYMBOLIC_REF_A >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n>  test_done\n> -- \n> gitgitgadget\n"},{"id":"492717","messageId":"xmqqwmp5xmqy.fsf@gitster.g","threadId":"61049","inReplyTo":"ZhY3SUdrX2WifyZf@tanuki","subject":"Re: [PATCH v2 1/3] refs: keep track of unresolved reference value in iterator","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-04-10T15:26:13Z","receivedAt":"2024-04-10T15:26:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n>>  const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n>>  \t\t\t\t    const char *refname,\n>> +\t\t\t\t    char **referent,\n>>  \t\t\t\t    int resolve_flags,\n>>  \t\t\t\t    struct object_id *oid,\n>>  \t\t\t\t    int *flags)\n>\n> I wonder whether this really should be a `char **`. You don't seem to be\n> free'ing returned pointers anywhere, so this should probably at least be\n> `const char **` to indicate that memory is owned by the ref store. But\n> that would require the ref store to inevitably release it, which may\n> easily lead to bugs when the caller expects a different lifetime.\n>\n> So how about we instead make this a `struct strbuf *referent`? This\n> makes it quite clear who owns the memory. Furthermore, if the caller\n> wants to resolve multiple refs, it would allow them to reuse the buffer\n> for better-optimized allocation patterns.\n\nOr return an allocated piece of memory via \"char **\".  I think an\ninterface that _requires_ the caller to use strbuf is not nice, if\nthe caller is not expected to further _edit_ the returned contents\nusing the strbuf API.  If it is likely that the caller would want to\nperform further post-processing on the result, an interface based on\nstrbuf is nice, but I do not think it applies to this case.\n"},{"id":"492718","messageId":"xmqqseztxmp9.fsf@gitster.g","threadId":"61049","inReplyTo":"ZhY3UCwJeMcG4fH1@tanuki","subject":"Re: [PATCH v2 3/3] show-ref: add --symbolic-name option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-04-10T15:27:14Z","receivedAt":"2024-04-10T15:27:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> On Mon, Apr 08, 2024 at 05:38:13PM +0000, John Cai via GitGitGadget wrote:\n>> From: John Cai <johncai86@gmail.com>\n>> \n>> For reftable development, it would be handy to have a tool to provide\n>> the direct value of any ref whether it be a symbolic ref or not.\n>> Currently there is git-symbolic-ref, which only works for symbolic refs,\n>> and git-rev-parse, which will resolve the ref. Let's teach show-ref a\n>> --symbolic-name option that will cause git-show-ref(1) to print out the\n>> value symbolic references points to.\n>\n> I think it was Peff who shared a way to achieve this without actually\n> introducing a new option via `git for-each-ref --format=`. Can we maybe\n> provide some benchmarks to demonstrate that this variant is preferable\n> over what's already possible?\n\nYes, I recall that discussion, and in fact I was a bit surprised to\nsee that this iteration still went to the show-ref route.\n\nThanks for bringing it up.\n"},{"id":"492777","messageId":"ZhepXm4G1OJqo6jq@tanuki","threadId":"61049","inReplyTo":"xmqqwmp5xmqy.fsf@gitster.g","subject":"Re: [PATCH v2 1/3] refs: keep track of unresolved reference value in iterator","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-04-11T09:11:58Z","receivedAt":"2024-04-11T09:12:03Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Apr 10, 2024 at 08:26:13AM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> >>  const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n> >>  \t\t\t\t    const char *refname,\n> >> +\t\t\t\t    char **referent,\n> >>  \t\t\t\t    int resolve_flags,\n> >>  \t\t\t\t    struct object_id *oid,\n> >>  \t\t\t\t    int *flags)\n> >\n> > I wonder whether this really should be a `char **`. You don't seem to be\n> > free'ing returned pointers anywhere, so this should probably at least be\n> > `const char **` to indicate that memory is owned by the ref store. But\n> > that would require the ref store to inevitably release it, which may\n> > easily lead to bugs when the caller expects a different lifetime.\n> >\n> > So how about we instead make this a `struct strbuf *referent`? This\n> > makes it quite clear who owns the memory. Furthermore, if the caller\n> > wants to resolve multiple refs, it would allow them to reuse the buffer\n> > for better-optimized allocation patterns.\n> \n> Or return an allocated piece of memory via \"char **\".  I think an\n> interface that _requires_ the caller to use strbuf is not nice, if\n> the caller is not expected to further _edit_ the returned contents\n> using the strbuf API.  If it is likely that the caller would want to\n> perform further post-processing on the result, an interface based on\n> strbuf is nice, but I do not think it applies to this case.\n\nWhen iterating through refs this would incur one allocation per ref\nthough, whereas using a `struct strbuf` would only require a single one.\n\nPatrick\n"},{"id":"492800","messageId":"F53A2B72-D089-4C0A-92D0-E96EDD410A74@gmail.com","threadId":"61049","inReplyTo":"a9ccd0db-2e52-4521-9d72-49d2a85656f5@gmail.com","subject":"Re: [PATCH v2 3/3] show-ref: add --symbolic-name option","fromName":"John Cai","fromEmail":"johncai86@gmail.com","sentAt":"2024-04-11T19:57:49Z","receivedAt":"2024-04-11T19:57:51Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"Hi Phillip,\n\nOn 9 Apr 2024, at 11:25, Phillip Wood wrote:\n\n> Hi John\n>\n> On 08/04/2024 18:38, John Cai via GitGitGadget wrote:\n>> +--symbolic-name::\n>> +\n>> +\tPrint out the value the reference points to without dereferencing. This\n>> +\tis useful to know the reference that a symbolic ref is pointing to.\n>\n> It would be helpful to clarify that this prints the contents of the symbolic ref without dereferencing but also prints the OID after dereferencing the given ref.\n\nThanks, will clarify in the next version.\n\n>\n>> +When using `--symbolic-name`, the output is in the format:\n>> +\n>> +-----------\n>> +<oid> SP <ref> SP <symbolic-name>\n>> +-----------\n>> +\n>> +For example,\n>> +\n>> +-----------------------------------------------------------------------------\n>> +$ git show-ref --symbolic-name\n>> +b75428bae1d090f60bdd4b67185f814bc8f0819d refs/heads/SYMBOLIC_REF ref:refs/heads/main\n>\n> Do we really need the \"ref:\" prefix? It is not specified above and I think anyone calling this would have to remove the prefix before they could use the value.\n\nI can see how it would be more ergonimic to just have the value without the\n\"ref: \" prefix. I kept it because that's how the refs are represented on disk\nand git-symbolic-ref prints them out with the \"ref: \" prefix.\n\nI don't have a strong preference, but I lean a bit towards keeping it consistent\nwith the output of other commands.\n\n>\n>\n>> +test_expect_success '--symbolic-name with symbolic ref' '\n>> +\ttest_when_finished \"git symbolic-ref -d refs/heads/SYMBOLIC_REF_A\" &&\n>> +\tcommit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) &&\n>> +\tcat >expect <<-EOF &&\n>> +\t$commit_oid refs/heads/SYMBOLIC_REF_A ref:refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>> +\tEOF\n>> +\tgit symbolic-ref refs/heads/SYMBOLIC_REF_A refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&\n>> +\tgit show-ref --symbolic-name SYMBOLIC_REF_A >actual &&\n>> +\ttest_cmp expect actual\n>> +'\n>\n> I think it would be nice to see a test along the lines of\n>\n> \tgit symbolic-ref refs/symref-c refs/heads/master\n> \tgit symbolic-ref refs/symref-b refs/symref-c &&\n> \tgit symbolic-ref refs/symref-a refs/symref-b &&\n> \tgit show-ref --symbolic-name refs/symref-a >actual &&\n> \tcat >expect <<\\EOF &&\n> \t$(git show-ref -s --verify refs/heads/master) refs/heads/symref-a refs/heads/symref-b\n> \tEOF\n> \ttest_cmp expect actual\n>\n> to show what this command is expected to return when there is a chain of symbolic references.\n\ngood point, will add this in the next series.\n\n>\n> Best Wishes\n>\n> Phillip\n\nthanks for the review!\nJohn\n"},{"id":"492858","messageId":"4c0b6da2-56d3-4441-aaed-8d9a2d28d9c0@gmail.com","threadId":"61049","inReplyTo":"F53A2B72-D089-4C0A-92D0-E96EDD410A74@gmail.com","subject":"Re: [PATCH v2 3/3] show-ref: add --symbolic-name option","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-04-12T09:37:16Z","receivedAt":"2024-04-12T09:37:18Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi John\n\nOn 11/04/2024 20:57, John Cai wrote:\n> On 9 Apr 2024, at 11:25, Phillip Wood wrote:\n>>> +When using `--symbolic-name`, the output is in the format:\n>>> +\n>>> +-----------\n>>> +<oid> SP <ref> SP <symbolic-name>\n>>> +-----------\n>>> +\n>>> +For example,\n>>> +\n>>> +-----------------------------------------------------------------------------\n>>> +$ git show-ref --symbolic-name\n>>> +b75428bae1d090f60bdd4b67185f814bc8f0819d refs/heads/SYMBOLIC_REF ref:refs/heads/main\n>>\n>> Do we really need the \"ref:\" prefix? It is not specified above and I think anyone calling this would have to remove the prefix before they could use the value.\n> \n> I can see how it would be more ergonimic to just have the value without the\n> \"ref: \" prefix. I kept it because that's how the refs are represented on disk\n> and git-symbolic-ref prints them out with the \"ref: \" prefix.\n> \n> I don't have a strong preference, but I lean a bit towards keeping it consistent\n> with the output of other commands.\n\nI agree it is a good idea to keep things consistent, and dropping the \n\"ref:\" prefix is consistent with other commands:\n\n$ git symbolic-ref HEAD\nrefs/heads/rebase-fix-signoff\n\nBest Wishes\n\nPhillip\n\n> \n>>\n>>\n>>> +test_expect_success '--symbolic-name with symbolic ref' '\n>>> +\ttest_when_finished \"git symbolic-ref -d refs/heads/SYMBOLIC_REF_A\" &&\n>>> +\tcommit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) &&\n>>> +\tcat >expect <<-EOF &&\n>>> +\t$commit_oid refs/heads/SYMBOLIC_REF_A ref:refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>>> +\tEOF\n>>> +\tgit symbolic-ref refs/heads/SYMBOLIC_REF_A refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&\n>>> +\tgit show-ref --symbolic-name SYMBOLIC_REF_A >actual &&\n>>> +\ttest_cmp expect actual\n>>> +'\n>>\n>> I think it would be nice to see a test along the lines of\n>>\n>> \tgit symbolic-ref refs/symref-c refs/heads/master\n>> \tgit symbolic-ref refs/symref-b refs/symref-c &&\n>> \tgit symbolic-ref refs/symref-a refs/symref-b &&\n>> \tgit show-ref --symbolic-name refs/symref-a >actual &&\n>> \tcat >expect <<\\EOF &&\n>> \t$(git show-ref -s --verify refs/heads/master) refs/heads/symref-a refs/heads/symref-b\n>> \tEOF\n>> \ttest_cmp expect actual\n>>\n>> to show what this command is expected to return when there is a chain of symbolic references.\n> \n> good point, will add this in the next series.\n> \n>>\n>> Best Wishes\n>>\n>> Phillip\n> \n> thanks for the review!\n> John\n"},{"id":"492872","messageId":"2E94E03A-CBDE-406D-AB65-DEAC4BB26761@gmail.com","threadId":"61049","inReplyTo":"ZhY3UCwJeMcG4fH1@tanuki","subject":"Re: [PATCH v2 3/3] show-ref: add --symbolic-name option","fromName":"John Cai","fromEmail":"johncai86@gmail.com","sentAt":"2024-04-12T15:23:35Z","receivedAt":"2024-04-12T15:23:37Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"Hi Patrick,\n\nOn 10 Apr 2024, at 2:53, Patrick Steinhardt wrote:\n\n> On Mon, Apr 08, 2024 at 05:38:13PM +0000, John Cai via GitGitGadget wrote:\n>> From: John Cai <johncai86@gmail.com>\n>>\n>> For reftable development, it would be handy to have a tool to provide\n>> the direct value of any ref whether it be a symbolic ref or not.\n>> Currently there is git-symbolic-ref, which only works for symbolic refs,\n>> and git-rev-parse, which will resolve the ref. Let's teach show-ref a\n>> --symbolic-name option that will cause git-show-ref(1) to print out the\n>> value symbolic references points to.\n>\n> I think it was Peff who shared a way to achieve this without actually\n> introducing a new option via `git for-each-ref --format=`. Can we maybe\n> provide some benchmarks to demonstrate that this variant is preferable\n> over what's already possible?\n\nYes, it would be better to not introduce a new option. I've done a quick\nbenchmark including my changes to add the unresolved symref to the iterator, and\nsome changes to integrate this into ref-filter.c. Here are the results:\n\n$ hyperfine --warmup 5 \"git for-each-ref \\\n--format='%(refname) %(objectname) %(symref)'\" \"./git for-each-ref \\\n--format='%(refname) %(objectname) %(symref)'\"\n\nBenchmark 1: git for-each-ref --format='%(refname) %(objectname) %(symref)'\n  Time (mean ± σ):     213.5 ms ±   9.9 ms    [User: 7.5 ms, System: 14.3 ms]\n  Range (min … max):   202.7 ms … 236.3 ms    14 runs\n\nBenchmark 2: ./git for-each-ref --format='%(refname) %(objectname) %(symref)'\n  Time (mean ± σ):      10.8 ms ±   1.3 ms    [User: 4.4 ms, System: 6.2 ms]\n  Range (min … max):     9.5 ms …  17.5 ms    189 runs\n\nSummary\n  ./git for-each-ref --format='%(refname) %(objectname) %(symref)' ran\n   19.72 ± 2.62 times faster than git for-each-ref --format='%(refname) %(objectname) %(symref)'\n\nIt looks like this gives us a nice speedup. I will send up a new version that improves git-for-each-ref\ninstead.\n\nthanks\nJohn\n\n>\n> Patrick\n>\n>> Signed-off-by: John Cai <johncai86@gmail.com>\n>> ---\n>>  Documentation/git-show-ref.txt | 21 ++++++++++++++++++-\n>>  builtin/show-ref.c             | 38 ++++++++++++++++++++++++----------\n>>  refs.c                         |  6 ++++++\n>>  refs.h                         |  2 +-\n>>  t/t1403-show-ref.sh            | 20 ++++++++++++++++++\n>>  5 files changed, 74 insertions(+), 13 deletions(-)\n>>\n>> diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt\n>> index ba757470059..9627b34b37f 100644\n>> --- a/Documentation/git-show-ref.txt\n>> +++ b/Documentation/git-show-ref.txt\n>> @@ -10,7 +10,7 @@ SYNOPSIS\n>>  [verse]\n>>  'git show-ref' [--head] [-d | --dereference]\n>>  \t     [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n>> -\t     [--heads] [--] [<pattern>...]\n>> +\t     [--heads] [--symbolic-name] [--] [<pattern>...]\n>>  'git show-ref' --verify [-q | --quiet] [-d | --dereference]\n>>  \t     [-s | --hash[=<n>]] [--abbrev[=<n>]]\n>>  \t     [--] [<ref>...]\n>> @@ -58,6 +58,11 @@ OPTIONS\n>>  \tDereference tags into object IDs as well. They will be shown with `^{}`\n>>  \tappended.\n>>\n>> +--symbolic-name::\n>> +\n>> +\tPrint out the value the reference points to without dereferencing. This\n>> +\tis useful to know the reference that a symbolic ref is pointing to.\n>> +\n>>  -s::\n>>  --hash[=<n>]::\n>>\n>> @@ -146,6 +151,20 @@ $ git show-ref --heads --hash\n>>  ...\n>>  -----------------------------------------------------------------------------\n>>\n>> +When using `--symbolic-name`, the output is in the format:\n>> +\n>> +-----------\n>> +<oid> SP <ref> SP <symbolic-name>\n>> +-----------\n>> +\n>> +For example,\n>> +\n>> +-----------------------------------------------------------------------------\n>> +$ git show-ref --symbolic-name\n>> +b75428bae1d090f60bdd4b67185f814bc8f0819d refs/heads/SYMBOLIC_REF ref:refs/heads/main\n>> +...\n>> +-----------------------------------------------------------------------------\n>> +\n>>  EXAMPLES\n>>  --------\n>>\n>> diff --git a/builtin/show-ref.c b/builtin/show-ref.c\n>> index 1c15421e600..1d681505eac 100644\n>> --- a/builtin/show-ref.c\n>> +++ b/builtin/show-ref.c\n>> @@ -12,7 +12,7 @@\n>>  static const char * const show_ref_usage[] = {\n>>  \tN_(\"git show-ref [--head] [-d | --dereference]\\n\"\n>>  \t   \"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\\n\"\n>> -\t   \"             [--heads] [--] [<pattern>...]\"),\n>> +\t   \"             [--heads] [--symbolic-name] [--] [<pattern>...]\"),\n>>  \tN_(\"git show-ref --verify [-q | --quiet] [-d | --dereference]\\n\"\n>>  \t   \"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\\n\"\n>>  \t   \"             [--] [<ref>...]\"),\n>> @@ -26,10 +26,13 @@ struct show_one_options {\n>>  \tint hash_only;\n>>  \tint abbrev;\n>>  \tint deref_tags;\n>> +\tint symbolic_name;\n>>  };\n>>\n>>  static void show_one(const struct show_one_options *opts,\n>> -\t\t     const char *refname, const struct object_id *oid)\n>> +\t\t     const char *refname,\n>> +\t\t     const char *referent,\n>> +\t\t     const struct object_id *oid, const int is_symref)\n>>  {\n>>  \tconst char *hex;\n>>  \tstruct object_id peeled;\n>> @@ -44,7 +47,9 @@ static void show_one(const struct show_one_options *opts,\n>>  \thex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev);\n>>  \tif (opts->hash_only)\n>>  \t\tprintf(\"%s\\n\", hex);\n>> -\telse\n>> +\telse if (opts->symbolic_name & is_symref) {\n>> +\t\tprintf(\"%s %s ref:%s\\n\", hex, refname, referent);\n>> +\t} else\n>>  \t\tprintf(\"%s %s\\n\", hex, refname);\n>>\n>>  \tif (!opts->deref_tags)\n>> @@ -63,8 +68,11 @@ struct show_ref_data {\n>>  \tint show_head;\n>>  };\n>>\n>> -static int show_ref(const char *refname, const struct object_id *oid,\n>> -\t\t    int flag UNUSED, void *cbdata)\n>> +static int show_ref_referent(struct repository *repo UNUSED,\n>> +\t\t\t     const char *refname,\n>> +\t\t\t     const char *referent,\n>> +\t\t\t     const struct object_id *oid,\n>> +\t\t\t     int flag, void *cbdata)\n>>  {\n>>  \tstruct show_ref_data *data = cbdata;\n>>\n>> @@ -91,11 +99,17 @@ static int show_ref(const char *refname, const struct object_id *oid,\n>>  match:\n>>  \tdata->found_match++;\n>>\n>> -\tshow_one(data->show_one_opts, refname, oid);\n>> +\tshow_one(data->show_one_opts, refname, referent, oid, flag & REF_ISSYMREF);\n>>\n>>  \treturn 0;\n>>  }\n>>\n>> +static int show_ref(const char *refname, const struct object_id *oid,\n>> +\t\t    int flag, void *cbdata)\n>> +{\n>> +\treturn show_ref_referent(NULL, refname, NULL, oid, flag, cbdata);\n>> +}\n>> +\n>>  static int add_existing(const char *refname,\n>>  \t\t\tconst struct object_id *oid UNUSED,\n>>  \t\t\tint flag UNUSED, void *cbdata)\n>> @@ -171,10 +185,11 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,\n>>\n>>  \twhile (*refs) {\n>>  \t\tstruct object_id oid;\n>> +\t\tint flags = 0;\n>>\n>>  \t\tif ((starts_with(*refs, \"refs/\") || refname_is_safe(*refs)) &&\n>> -\t\t    !read_ref(*refs, &oid)) {\n>> -\t\t\tshow_one(show_one_opts, *refs, &oid);\n>> +\t\t    !read_ref_full(*refs, 0, &oid, &flags)) {\n>> +\t\t\tshow_one(show_one_opts, *refs, NULL, &oid, flags & REF_ISSYMREF);\n>>  \t\t}\n>>  \t\telse if (!show_one_opts->quiet)\n>>  \t\t\tdie(\"'%s' - not a valid ref\", *refs);\n>> @@ -208,11 +223,11 @@ static int cmd_show_ref__patterns(const struct patterns_options *opts,\n>>  \t\thead_ref(show_ref, &show_ref_data);\n>>  \tif (opts->heads_only || opts->tags_only) {\n>>  \t\tif (opts->heads_only)\n>> -\t\t\tfor_each_fullref_in(\"refs/heads/\", show_ref, &show_ref_data);\n>> +\t\t\tfor_each_ref_all(\"refs/heads/\", show_ref_referent, &show_ref_data);\n>>  \t\tif (opts->tags_only)\n>> -\t\t\tfor_each_fullref_in(\"refs/tags/\", show_ref, &show_ref_data);\n>> +\t\t\tfor_each_ref_all(\"refs/tags/\", show_ref_referent, &show_ref_data);\n>>  \t} else {\n>> -\t\tfor_each_ref(show_ref, &show_ref_data);\n>> +\t\tfor_each_ref_all(\"\", show_ref_referent, &show_ref_data);\n>>  \t}\n>>  \tif (!show_ref_data.found_match)\n>>  \t\treturn 1;\n>> @@ -289,6 +304,7 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)\n>>  \t\tOPT_BOOL(0, \"tags\", &patterns_opts.tags_only, N_(\"only show tags (can be combined with heads)\")),\n>>  \t\tOPT_BOOL(0, \"heads\", &patterns_opts.heads_only, N_(\"only show heads (can be combined with tags)\")),\n>>  \t\tOPT_BOOL(0, \"exists\", &exists, N_(\"check for reference existence without resolving\")),\n>> +\t\tOPT_BOOL(0, \"symbolic-name\", &show_one_opts.symbolic_name, N_(\"print out symbolic reference values\")),\n>>  \t\tOPT_BOOL(0, \"verify\", &verify, N_(\"stricter reference checking, \"\n>>  \t\t\t    \"requires exact ref path\")),\n>>  \t\tOPT_HIDDEN_BOOL('h', NULL, &patterns_opts.show_head,\n>> diff --git a/refs.c b/refs.c\n>> index 77ae38ea214..9488ad9594d 100644\n>> --- a/refs.c\n>> +++ b/refs.c\n>> @@ -1734,6 +1734,12 @@ int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_dat\n>>  \t\t\t\t    DO_FOR_EACH_INCLUDE_BROKEN, cb_data);\n>>  }\n>>\n>> +int for_each_ref_all(const char *prefix, each_repo_ref_fn fn, void *cb_data)\n>> +{\n>> +\treturn do_for_each_repo_ref(the_repository, prefix, fn, 0,\n>> +\t\t\t\t    0, cb_data);\n>> +}\n>> +\n>>  int for_each_namespaced_ref(const char **exclude_patterns,\n>>  \t\t\t    each_ref_fn fn, void *cb_data)\n>>  {\n>> diff --git a/refs.h b/refs.h\n>> index 23e5aaba2e9..54b459375be 100644\n>> --- a/refs.h\n>> +++ b/refs.h\n>> @@ -337,7 +337,6 @@ int refs_for_each_branch_ref(struct ref_store *refs,\n>>  \t\t\t     each_ref_fn fn, void *cb_data);\n>>  int refs_for_each_remote_ref(struct ref_store *refs,\n>>  \t\t\t     each_ref_fn fn, void *cb_data);\n>> -\n>>  /* just iterates the head ref. */\n>>  int head_ref(each_ref_fn fn, void *cb_data);\n>>\n>> @@ -381,6 +380,7 @@ int for_each_tag_ref(each_ref_fn fn, void *cb_data);\n>>  int for_each_branch_ref(each_ref_fn fn, void *cb_data);\n>>  int for_each_remote_ref(each_ref_fn fn, void *cb_data);\n>>  int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data);\n>> +int for_each_ref_all(const char *prefix, each_repo_ref_fn fn, void *cb_data);\n>>\n>>  /* iterates all refs that match the specified glob pattern. */\n>>  int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);\n>> diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh\n>> index 33fb7a38fff..0aebe709dca 100755\n>> --- a/t/t1403-show-ref.sh\n>> +++ b/t/t1403-show-ref.sh\n>> @@ -286,4 +286,24 @@ test_expect_success '--exists with existing special ref' '\n>>  \tgit show-ref --exists FETCH_HEAD\n>>  '\n>>\n>> +test_expect_success '--symbolic-name with a non symbolic ref' '\n>> +\tcommit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) &&\n>> +\tcat >expect <<-EOF &&\n>> +\t$commit_oid refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>> +\tEOF\n>> +\tgit show-ref --symbolic-name refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME >actual &&\n>> +\ttest_cmp expect actual\n>> +'\n>> +\n>> +test_expect_success '--symbolic-name with symbolic ref' '\n>> +\ttest_when_finished \"git symbolic-ref -d refs/heads/SYMBOLIC_REF_A\" &&\n>> +\tcommit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) &&\n>> +\tcat >expect <<-EOF &&\n>> +\t$commit_oid refs/heads/SYMBOLIC_REF_A ref:refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>> +\tEOF\n>> +\tgit symbolic-ref refs/heads/SYMBOLIC_REF_A refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&\n>> +\tgit show-ref --symbolic-name SYMBOLIC_REF_A >actual &&\n>> +\ttest_cmp expect actual\n>> +'\n>> +\n>>  test_done\n>> -- \n>> gitgitgadget\n"}]}