{"thread":{"id":"61598","subject":"[PATCH 0/4] keep track of unresolved value of symbolic-ref in ref iterators","startedAt":"2024-06-06T17:26:45Z","lastAt":"2024-11-23T08:24:08Z","messageCount":44,"participants":["John Cai via GitGitGadget","Junio C Hamano","John Cai","Patrick Steinhardt","Jeff King","Kristoffer Haugsbakk","Linus Arver","shejialuo"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"496548","messageId":"pull.1712.git.git.1717694800.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":null,"subject":"[PATCH 0/4] keep track of unresolved value of symbolic-ref in ref iterators","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-06-06T17:26:36Z","receivedAt":"2024-06-06T17:26:45Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"For reftable development, it's useful to be able to print out the direct\nvalue of a symbolic reference before resolution. This is currently possible\nwith git-for-each-ref, but since the iterators do not keep track of the\nvalue of the symbolic ref, a separate call needs to be made each time making\nit inefficient.\n\nAddress this inefficiency by keeping track of the value of the symbolic\nreference in the ref iterator. This patch series also ends with a commit to\nuse this value in the iterator through callbacks in ref-filter.c.\n\nThis series started with [1] but I decided to send a separate patch series\nsince it is substantially different.\n\nBenchmarking shows that with these changes, we experience a significant\nspeedup in git-for-each-ref(1):\n\n$ hyperfine --warmup 5 \"git for-each-ref --format='%(refname) %(objectname)\n%(symref)'\" \"~/Projects/git/git for-each-ref --format='%(refname)\n%(objectname) %(symref)'\"\n\nBenchmark 1: git for-each-ref --format='%(refname) %(objectname) %(symref)'\nTime (mean ± σ): 210.0 ms ± 9.1 ms [User: 5.8 ms, System: 11.8 ms] Range\n(min … max): 203.4 ms … 228.8 ms 12 runs\n\nBenchmark 2: ~/Projects/git/git for-each-ref --format='%(refname)\n%(objectname) %(symref)' Time (mean ± σ): 7.4 ms ± 0.7 ms [User: 2.6 ms,\nSystem: 3.9 ms] Range (min … max): 5.9 ms … 11.7 ms 273 runs\n\nSummary ~/Projects/git/git for-each-ref --format='%(refname) %(objectname)\n%(symref)' ran 28.47 ± 2.86 times faster than git for-each-ref\n--format='%(refname) %(objectname) %(symref)'\n\n 1. https://lore.kernel.org/git/pull.1684.git.git.1709592718743.gitgitgadget@gmail.com/\n\nJohn Cai (4):\n  refs: add referent parameter to refs_resolve_ref_unsafe\n  refs: keep track of unresolved reference value in iterators\n  refs: add referent to each_ref_fn\n  ref-filter: populate symref from iterator\n\n add-interactive.c           |  6 +++---\n bisect.c                    |  3 ++-\n blame.c                     |  4 ++--\n builtin/bisect.c            |  6 ++++--\n builtin/blame.c             |  2 +-\n builtin/branch.c            |  2 +-\n builtin/checkout.c          |  2 +-\n builtin/describe.c          |  2 +-\n builtin/fetch.c             |  3 ++-\n builtin/fsck.c              |  6 +++---\n builtin/gc.c                |  1 +\n builtin/log.c               |  1 +\n builtin/name-rev.c          |  2 +-\n builtin/pack-objects.c      |  5 +++--\n builtin/rebase.c            |  6 +++---\n builtin/receive-pack.c      |  4 ++--\n builtin/remote.c            |  6 ++++--\n builtin/repack.c            |  1 +\n builtin/replace.c           |  1 +\n builtin/rev-parse.c         |  4 ++--\n builtin/show-branch.c       |  8 ++++----\n builtin/show-ref.c          |  3 ++-\n builtin/stash.c             |  2 +-\n builtin/submodule--helper.c |  5 +++--\n builtin/symbolic-ref.c      |  2 +-\n builtin/worktree.c          |  1 +\n commit-graph.c              |  1 +\n config.c                    |  2 +-\n delta-islands.c             |  2 +-\n fetch-pack.c                |  2 ++\n help.c                      |  2 +-\n http-backend.c              |  5 +++--\n log-tree.c                  |  4 ++--\n ls-refs.c                   |  7 ++++---\n midx-write.c                |  2 +-\n negotiator/default.c        |  2 +-\n negotiator/skipping.c       |  2 +-\n notes.c                     |  2 +-\n object-name.c               |  2 +-\n reachable.c                 |  2 +-\n ref-filter.c                | 11 ++++++-----\n reflog.c                    |  1 +\n refs.c                      | 32 ++++++++++++++++++--------------\n refs.h                      |  3 ++-\n refs/files-backend.c        | 24 ++++++++++++++----------\n refs/iterator.c             |  5 ++++-\n refs/ref-cache.c            |  6 ++++++\n refs/ref-cache.h            |  2 ++\n refs/refs-internal.h        |  1 +\n refs/reftable-backend.c     |  6 +++---\n remote.c                    | 10 +++++-----\n replace-object.c            |  1 +\n revision.c                  |  4 ++--\n sequencer.c                 |  9 +++++----\n server-info.c               |  2 +-\n shallow.c                   |  2 ++\n submodule.c                 |  2 ++\n t/helper/test-ref-store.c   |  4 ++--\n transport-helper.c          |  1 +\n transport.c                 |  2 +-\n upload-pack.c               | 10 +++++-----\n walker.c                    |  1 +\n worktree.c                  |  6 ++++--\n 63 files changed, 161 insertions(+), 109 deletions(-)\n\n\nbase-commit: 7b0defb3915eaa0bd118f0996e8c00b4eb2dc1ca\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1712%2Fjohn-cai%2Fjc%2Fsymbolic-ref-in-iterator-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1712/john-cai/jc/symbolic-ref-in-iterator-v1\nPull-Request: https://github.com/git/git/pull/1712\n-- \ngitgitgadget\n"},{"id":"496549","messageId":"7202ada0542b6f014647785945094a13c9d885c7.1717694802.git.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":"pull.1712.git.git.1717694800.gitgitgadget@gmail.com","subject":"[PATCH 2/4] refs: keep track of unresolved reference value in iterators","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-06-06T17:26:38Z","receivedAt":"2024-06-06T17:26:46Z","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 iterators for both the\nfiles backend and the reftable backend.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n refs/files-backend.c    | 8 ++++++--\n refs/iterator.c         | 3 +++\n refs/ref-cache.c        | 6 ++++++\n refs/ref-cache.h        | 2 ++\n refs/refs-internal.h    | 1 +\n refs/reftable-backend.c | 2 +-\n 6 files changed, 19 insertions(+), 3 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex bf2ffe062ea..a963d796a29 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -243,8 +243,9 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n {\n \tstruct object_id oid;\n \tint flag;\n+\tconst char* referent = NULL;\n \n-\tif (!refs_resolve_ref_unsafe(&refs->base, refname, NULL, 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@@ -266,7 +267,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@@ -854,6 +855,9 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \t\titer->base.refname = iter->iter0->refname;\n \t\titer->base.oid = iter->iter0->oid;\n \t\titer->base.flags = iter->iter0->flags;\n+\t\tif (iter->iter0->flags & REF_ISSYMREF)\n+\t\t\titer->base.referent = iter->iter0->referent;\n+\n \t\treturn ITER_OK;\n \t}\n \ndiff --git a/refs/iterator.c b/refs/iterator.c\nindex d355ebf0d59..26acb6bd640 100644\n--- a/refs/iterator.c\n+++ b/refs/iterator.c\n@@ -7,6 +7,7 @@\n #include \"refs.h\"\n #include \"refs/refs-internal.h\"\n #include \"iterator.h\"\n+#include \"strbuf.h\"\n \n int ref_iterator_advance(struct ref_iterator *ref_iterator)\n {\n@@ -29,6 +30,7 @@ void base_ref_iterator_init(struct ref_iterator *iter,\n {\n \titer->vtable = vtable;\n \titer->refname = NULL;\n+\titer->referent = NULL;\n \titer->oid = NULL;\n \titer->flags = 0;\n }\n@@ -199,6 +201,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 b6c53fc8ed2..e8ce5822cc1 100644\n--- a/refs/ref-cache.c\n+++ b/refs/ref-cache.c\n@@ -5,6 +5,7 @@\n #include \"refs-internal.h\"\n #include \"ref-cache.h\"\n #include \"../iterator.h\"\n+#include \"../strbuf.h\"\n \n void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry)\n {\n@@ -34,12 +35,16 @@ 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+\tif (referent)\n+\t\tref->u.value.referent = xstrdup(referent);\n+\n \tref->flag = flag;\n \treturn ref;\n }\n@@ -429,6 +434,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \t\t\tlevel->index = -1;\n \t\t} else {\n \t\t\titer->base.refname = entry->name;\n+\t\t\titer->base.referent = entry->u.value.referent;\n \t\t\titer->base.oid = &entry->u.value.oid;\n \t\t\titer->base.flags = entry->flag;\n \t\t\treturn ITER_OK;\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 33749fbd839..07754514355 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -299,6 +299,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 9e03582e7da..6328c0f77dc 100644\n--- a/refs/reftable-backend.c\n+++ b/refs/reftable-backend.c\n@@ -438,7 +438,7 @@ 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, NULL,\n+\t\t\tif (!refs_resolve_ref_unsafe(&iter->refs->base, iter->ref.refname, iter->base.referent,\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-- \ngitgitgadget\n\n"},{"id":"496550","messageId":"011c10f488610b0a795a843bff66723477783761.1717694801.git.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":"pull.1712.git.git.1717694800.gitgitgadget@gmail.com","subject":"[PATCH 1/4] refs: add referent parameter to refs_resolve_ref_unsafe","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-06-06T17:26:37Z","receivedAt":"2024-06-06T17:26:46Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"From: John Cai <johncai86@gmail.com>\n\nrefs_resolve_ref_unsafe retrieves the referent, the unresolved value of\na reference. Add a parameter to allow refs_resolve_ref_unsafe to pass up\nthe value of referent to the caller so it can save this value in ref\niterators for more efficient access.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n add-interactive.c           |  6 +++---\n blame.c                     |  4 ++--\n builtin/bisect.c            |  2 +-\n builtin/blame.c             |  2 +-\n builtin/branch.c            |  2 +-\n builtin/fsck.c              |  2 +-\n builtin/log.c               |  1 +\n builtin/rebase.c            |  6 +++---\n builtin/receive-pack.c      |  2 +-\n builtin/remote.c            |  2 +-\n builtin/stash.c             |  2 +-\n builtin/submodule--helper.c |  4 ++--\n builtin/symbolic-ref.c      |  2 +-\n config.c                    |  2 +-\n http-backend.c              |  1 +\n log-tree.c                  |  2 +-\n ls-refs.c                   |  3 ++-\n refs.c                      | 20 ++++++++++++--------\n refs.h                      |  1 +\n refs/files-backend.c        | 16 ++++++++--------\n refs/reftable-backend.c     |  6 +++---\n remote.c                    |  6 +++---\n revision.c                  |  2 +-\n sequencer.c                 |  9 +++++----\n t/helper/test-ref-store.c   |  2 +-\n transport-helper.c          |  1 +\n transport.c                 |  2 +-\n upload-pack.c               |  2 +-\n worktree.c                  |  4 +++-\n 29 files changed, 64 insertions(+), 52 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex b5d6cd689a1..041d30cf2b3 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -533,7 +533,7 @@ static int get_modified_files(struct repository *r,\n {\n \tstruct object_id head_oid;\n \tint is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t\t  \"HEAD\", RESOLVE_REF_READING,\n+\t\t\t\t\t\t  \"HEAD\", NULL, RESOLVE_REF_READING,\n \t\t\t\t\t\t  &head_oid, NULL);\n \tstruct collection_status s = { 0 };\n \tint i;\n@@ -763,7 +763,7 @@ static int run_revert(struct add_i_state *s, const struct pathspec *ps,\n \n \tstruct object_id oid;\n \tint is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t\t  \"HEAD\", RESOLVE_REF_READING,\n+\t\t\t\t\t\t  \"HEAD\", NULL, RESOLVE_REF_READING,\n \t\t\t\t\t\t  &oid,\n \t\t\t\t\t\t  NULL);\n \tstruct lock_file index_lock;\n@@ -994,7 +994,7 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps,\n \n \tstruct object_id oid;\n \tint is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t\t  \"HEAD\", RESOLVE_REF_READING,\n+\t\t\t\t\t\t  \"HEAD\", NULL, RESOLVE_REF_READING,\n \t\t\t\t\t\t  &oid,\n \t\t\t\t\t\t  NULL);\n \tif (get_modified_files(s->r, INDEX_ONLY, files, ps, NULL, NULL) < 0)\ndiff --git a/blame.c b/blame.c\nindex 33586b97772..9e5d0cd788f 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -2700,7 +2700,7 @@ static struct commit *dwim_reverse_initial(struct rev_info *revs,\n \t\treturn NULL;\n \n \t/* Do we have HEAD? */\n-\tif (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), \"HEAD\", RESOLVE_REF_READING, &head_oid, NULL))\n+\tif (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), \"HEAD\", NULL, RESOLVE_REF_READING, &head_oid, NULL))\n \t\treturn NULL;\n \thead_commit = lookup_commit_reference_gently(revs->repo,\n \t\t\t\t\t\t     &head_oid, 1);\n@@ -2803,7 +2803,7 @@ void setup_scoreboard(struct blame_scoreboard *sb,\n \t\tif (sb->final) {\n \t\t\tparent_oid = &sb->final->object.oid;\n \t\t} else {\n-\t\t\tif (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), \"HEAD\", RESOLVE_REF_READING, &head_oid, NULL))\n+\t\t\tif (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), \"HEAD\", NULL, RESOLVE_REF_READING, &head_oid, NULL))\n \t\t\t\tdie(\"no such ref: HEAD\");\n \t\t\tparent_oid = &head_oid;\n \t\t}\ndiff --git a/builtin/bisect.c b/builtin/bisect.c\nindex a58432b9d90..76ce5f0e0df 100644\n--- a/builtin/bisect.c\n+++ b/builtin/bisect.c\n@@ -783,7 +783,7 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc,\n \t * Verify HEAD\n \t */\n \thead = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t       \"HEAD\", 0, &head_oid, &flags);\n+\t\t\t\t       \"HEAD\", NULL, 0, &head_oid, &flags);\n \tif (!head)\n \t\tif (repo_get_oid(the_repository, \"HEAD\", &head_oid))\n \t\t\treturn error(_(\"bad HEAD - I need a HEAD\"));\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex fadba1a5304..1504a2ed99d 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1092,7 +1092,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\tstruct commit *head_commit;\n \t\tstruct object_id head_oid;\n \n-\t\tif (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), \"HEAD\", RESOLVE_REF_READING,\n+\t\tif (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), \"HEAD\", NULL, RESOLVE_REF_READING,\n \t\t\t\t\t     &head_oid, NULL) ||\n \t\t    !(head_commit = lookup_commit_reference_gently(revs.repo,\n \t\t\t\t\t\t\t     &head_oid, 1)))\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 48cac74f97f..dd871d44f2d 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -503,7 +503,7 @@ static void print_current_branch_name(void)\n {\n \tint flags;\n \tconst char *refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t\t      \"HEAD\", 0, NULL, &flags);\n+\t\t\t\t\t\t      \"HEAD\", NULL, 0, NULL, &flags);\n \tconst char *shortname;\n \tif (!refname)\n \t\tdie(_(\"could not resolve HEAD\"));\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex d13a226c2ed..2c0ac6653ca 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -716,7 +716,7 @@ static int fsck_head_link(const char *head_ref_name,\n \t\tfprintf_ln(stderr, _(\"Checking %s link\"), head_ref_name);\n \n \t*head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t\t  head_ref_name, 0, head_oid,\n+\t\t\t\t\t\t  head_ref_name, NULL, 0, head_oid,\n \t\t\t\t\t\t  NULL);\n \tif (!*head_points_at) {\n \t\terrors_found |= ERROR_REFS;\ndiff --git a/builtin/log.c b/builtin/log.c\nindex c8ce0c0d88a..337f367e974 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -2228,6 +2228,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\tconst char *ref, *v;\n \t\t\tref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n \t\t\t\t\t\t      \"HEAD\",\n+\t\t\t\t\t\t      NULL,\n \t\t\t\t\t\t      RESOLVE_REF_READING,\n \t\t\t\t\t\t      NULL, NULL);\n \t\t\tif (ref && skip_prefix(ref, \"refs/heads/\", &v))\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 0466d9414af..fd14b1e4505 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1641,7 +1641,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t} else if (argc == 0) {\n \t\t/* Do not need to switch branches, we are already on it. */\n \t\toptions.head_name =\n-\t\t\txstrdup_or_null(refs_resolve_ref_unsafe(get_main_ref_store(the_repository), \"HEAD\", 0, NULL,\n+\t\t\txstrdup_or_null(refs_resolve_ref_unsafe(get_main_ref_store(the_repository), \"HEAD\", NULL, 0, NULL,\n \t\t\t\t\t\t\t\t&flags));\n \t\tif (!options.head_name)\n \t\t\tdie(_(\"No such ref: %s\"), \"HEAD\");\n@@ -1736,7 +1736,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\tif (!(options.flags & REBASE_NO_QUIET))\n \t\t\t\t; /* be quiet */\n \t\t\telse if (!strcmp(branch_name, \"HEAD\") &&\n-\t\t\t\t refs_resolve_ref_unsafe(get_main_ref_store(the_repository), \"HEAD\", 0, NULL, &flag))\n+\t\t\t\t refs_resolve_ref_unsafe(get_main_ref_store(the_repository), \"HEAD\", NULL, 0, NULL, &flag))\n \t\t\t\tputs(_(\"HEAD is up to date.\"));\n \t\t\telse\n \t\t\t\tprintf(_(\"Current branch %s is up to date.\\n\"),\n@@ -1746,7 +1746,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t} else if (!(options.flags & REBASE_NO_QUIET))\n \t\t\t; /* be quiet */\n \t\telse if (!strcmp(branch_name, \"HEAD\") &&\n-\t\t\t refs_resolve_ref_unsafe(get_main_ref_store(the_repository), \"HEAD\", 0, NULL, &flag))\n+\t\t\t refs_resolve_ref_unsafe(get_main_ref_store(the_repository), \"HEAD\", NULL, 0, NULL, &flag))\n \t\t\tputs(_(\"HEAD is up to date, rebase forced.\"));\n \t\telse\n \t\t\tprintf(_(\"Current branch %s is up to date, rebase \"\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex be8969a84a8..d9e2c4bbe39 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -1695,7 +1695,7 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)\n \n \tstrbuf_addf(&buf, \"%s%s\", get_git_namespace(), cmd->ref_name);\n \tdst_name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t   buf.buf, 0, NULL, &flag);\n+\t\t\t\t\t   buf.buf, NULL, 0, NULL, &flag);\n \tcheck_aliased_update_internal(cmd, list, dst_name, flag);\n \tstrbuf_release(&buf);\n }\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 447ef1d3c92..039d1d6c55a 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -599,7 +599,7 @@ static int read_remote_branches(const char *refname,\n \tif (starts_with(refname, buf.buf)) {\n \t\titem = string_list_append(rename->remote_branches, refname);\n \t\tsymref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t\t refname, RESOLVE_REF_READING,\n+\t\t\t\t\t\t refname, NULL, RESOLVE_REF_READING,\n \t\t\t\t\t\t NULL, &flag);\n \t\tif (symref && (flag & REF_ISSYMREF)) {\n \t\t\titem->util = xstrdup(symref);\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 7859bc0866a..b733492cead 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1386,7 +1386,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b\n \t}\n \n \tbranch_ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t     \"HEAD\", 0, NULL, &flags);\n+\t\t\t\t\t     \"HEAD\", NULL, 0, NULL, &flags);\n \tif (flags & REF_ISSYMREF)\n \t\tskip_prefix(branch_ref, \"refs/heads/\", &branch_name);\n \thead_short_sha1 = repo_find_unique_abbrev(the_repository,\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 897f19868e8..e3e1f08a58c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -44,7 +44,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)\n@@ -2459,7 +2459,7 @@ static int remote_submodule_branch(const char *path, const char **branch)\n \n \tif (!strcmp(*branch, \".\")) {\n \t\tconst char *refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t\t\t      \"HEAD\", 0, NULL,\n+\t\t\t\t\t\t\t      \"HEAD\", NULL, 0, NULL,\n \t\t\t\t\t\t\t      NULL);\n \n \t\tif (!refname)\ndiff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c\nindex 81abdd170fe..96fa311b075 100644\n--- a/builtin/symbolic-ref.c\n+++ b/builtin/symbolic-ref.c\n@@ -19,7 +19,7 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int recurse, i\n \n \tresolve_flags = (recurse ? 0 : RESOLVE_REF_NO_RECURSE);\n \trefname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t  HEAD, resolve_flags, NULL, &flag);\n+\t\t\t\t\t  HEAD, NULL, resolve_flags, NULL, &flag);\n \n \tif (!refname)\n \t\tdie(\"No such ref: %s\", HEAD);\ndiff --git a/config.c b/config.c\nindex 14461312b33..3cc1dab0fb9 100644\n--- a/config.c\n+++ b/config.c\n@@ -304,7 +304,7 @@ static int include_by_branch(const char *cond, size_t cond_len)\n \tstruct strbuf pattern = STRBUF_INIT;\n \tconst char *refname = !the_repository->gitdir ?\n \t\tNULL : refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t       \"HEAD\", 0, NULL, &flags);\n+\t\t\t\t\t       \"HEAD\", NULL, 0, NULL, &flags);\n \tconst char *shortname;\n \n \tif (!refname || !(flags & REF_ISSYMREF)\t||\ndiff --git a/http-backend.c b/http-backend.c\nindex 5b65287ac90..20c3ff8fa95 100644\n--- a/http-backend.c\n+++ b/http-backend.c\n@@ -574,6 +574,7 @@ static int show_head_ref(const char *refname, const struct object_id *oid,\n \tif (flag & REF_ISSYMREF) {\n \t\tconst char *target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n \t\t\t\t\t\t\t     refname,\n+\t\t\t\t\t\t\t     NULL,\n \t\t\t\t\t\t\t     RESOLVE_REF_READING,\n \t\t\t\t\t\t\t     NULL, NULL);\n \ndiff --git a/log-tree.c b/log-tree.c\nindex 41416de4e3f..da06c6e982f 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -280,7 +280,7 @@ static const struct name_decoration *current_pointed_by_HEAD(const struct name_d\n \n \t/* Now resolve and find the matching current branch */\n \tbranch_name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t      \"HEAD\", 0, NULL, &rru_flags);\n+\t\t\t\t\t      \"HEAD\", NULL, 0, NULL, &rru_flags);\n \tif (!branch_name || !(rru_flags & REF_ISSYMREF))\n \t\treturn NULL;\n \ndiff --git a/ls-refs.c b/ls-refs.c\nindex 398afe4ce39..3d047bbc64f 100644\n--- a/ls-refs.c\n+++ b/ls-refs.c\n@@ -97,6 +97,7 @@ static int send_ref(const char *refname, const struct object_id *oid,\n \t\tstruct object_id unused;\n \t\tconst char *symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n \t\t\t\t\t\t\t\t    refname,\n+\t\t\t\t\t\t\t\t    NULL,\n \t\t\t\t\t\t\t\t    0,\n \t\t\t\t\t\t\t\t    &unused,\n \t\t\t\t\t\t\t\t    &flag);\n@@ -128,7 +129,7 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)\n \tint oid_is_null;\n \n \tstrbuf_addf(&namespaced, \"%sHEAD\", get_git_namespace());\n-\tif (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), namespaced.buf, 0, &oid, &flag))\n+\tif (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), namespaced.buf, NULL, 0, &oid, &flag))\n \t\treturn; /* bad ref */\n \toid_is_null = is_null_oid(&oid);\n \tif (!oid_is_null ||\ndiff --git a/refs.c b/refs.c\nindex 31032588e0e..2c592a9ae29 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -378,7 +378,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@@ -394,7 +394,7 @@ struct for_each_ref_filter {\n int refs_read_ref_full(struct ref_store *refs, const char *refname,\n \t\t       int resolve_flags, struct object_id *oid, int *flags)\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@@ -407,7 +407,7 @@ int refs_read_ref(struct ref_store *refs, const char *refname, struct object_id\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@@ -442,7 +442,7 @@ static int warn_if_dangling_symref(const char *refname,\n \tif (!(flags & REF_ISSYMREF))\n \t\treturn 0;\n \n-\tresolves_to = refs_resolve_ref_unsafe(d->refs, refname, 0, NULL, NULL);\n+\tresolves_to = refs_resolve_ref_unsafe(d->refs, refname, NULL, 0, NULL, NULL);\n \tif (!resolves_to\n \t    || (d->refname\n \t\t? strcmp(resolves_to, d->refname)\n@@ -716,7 +716,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@@ -750,7 +750,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@@ -1522,7 +1522,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@@ -1761,6 +1761,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    const 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@@ -1822,6 +1823,9 @@ 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) &&\n+\t\t    sb_refname.len > 0)\n+\t\t\treferent = sb_refname.buf;\n \n \t\tif (!(read_flags & REF_ISSYMREF)) {\n \t\t\tif (*flags & REF_BAD_NAME) {\n@@ -1865,7 +1869,7 @@ int repo_resolve_gitlink_ref(struct repository *r,\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 fe7f0db35e6..ea4a3217658 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -68,6 +68,7 @@ const char *ref_storage_format_to_name(unsigned int ref_storage_format);\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    const 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 324c59b096c..bf2ffe062ea 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -244,7 +244,7 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n \tstruct object_id oid;\n \tint flag;\n \n-\tif (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,\n+\tif (!refs_resolve_ref_unsafe(&refs->base, refname, NULL, RESOLVE_REF_READING,\n \t\t\t\t     &oid, &flag)) {\n \t\toidclr(&oid);\n \t\tflag |= REF_ISBROKEN;\n@@ -1118,7 +1118,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@@ -1455,7 +1455,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@@ -1501,7 +1501,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@@ -1875,7 +1875,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@@ -2464,7 +2464,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@@ -2823,7 +2823,7 @@ static int parse_and_write_reflog(struct files_ref_store *refs,\n \t\t * We want to get the resolved OID for the target, to ensure\n \t\t * that the correct value is added to the reflog.\n \t\t */\n-\t\tif (!refs_resolve_ref_unsafe(&refs->base, update->new_target,\n+\t\tif (!refs_resolve_ref_unsafe(&refs->base, update->new_target, NULL,\n \t\t\t\t\t     RESOLVE_REF_READING,\n \t\t\t\t\t     &update->new_oid, NULL)) {\n \t\t\t/*\n@@ -3229,7 +3229,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/reftable-backend.c b/refs/reftable-backend.c\nindex 438b5c478b4..9e03582e7da 100644\n--- a/refs/reftable-backend.c\n+++ b/refs/reftable-backend.c\n@@ -438,7 +438,7 @@ 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 (!refs_resolve_ref_unsafe(&iter->refs->base, iter->ref.refname, NULL,\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@@ -926,7 +926,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@@ -1148,7 +1148,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data\n \t\t\tint create_reflog = 1;\n \n \t\t\tif (u->new_target) {\n-\t\t\t\tif (!refs_resolve_ref_unsafe(&arg->refs->base, u->new_target,\n+\t\t\t\tif (!refs_resolve_ref_unsafe(&arg->refs->base, u->new_target, NULL,\n \t\t\t\t\t\t\t     RESOLVE_REF_READING, &u->new_oid, NULL)) {\n \t\t\t\t\t/*\n \t\t\t\t\t * TODO: currently we skip creating reflogs for dangling\ndiff --git a/remote.c b/remote.c\nindex 5898da2bb5c..c9718ee0ca1 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -520,7 +520,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(\n@@ -1201,7 +1201,7 @@ static char *guess_ref(const char *name, struct ref *peer)\n \tstruct strbuf buf = STRBUF_INIT;\n \n \tconst char *r = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t\tpeer->name,\n+\t\t\t\t\t\tpeer->name, NULL,\n \t\t\t\t\t\tRESOLVE_REF_READING,\n \t\t\t\t\t\tNULL, NULL);\n \tif (!r)\n@@ -1321,7 +1321,7 @@ static int match_explicit(struct ref *src, struct ref *dst,\n \t\tint flag;\n \n \t\tdst_value = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t\t    matched_src->name,\n+\t\t\t\t\t\t    matched_src->name, NULL,\n \t\t\t\t\t\t    RESOLVE_REF_READING,\n \t\t\t\t\t\t    NULL, &flag);\n \t\tif (!dst_value ||\ndiff --git a/revision.c b/revision.c\nindex 7ddf0f151a3..6aca4f42303 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2920,7 +2920,7 @@ static void NORETURN diagnose_missing_default(const char *def)\n \tconst char *refname;\n \n \trefname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t  def, 0, NULL, &flags);\n+\t\t\t\t\t  def, NULL, 0, NULL, &flags);\n \tif (!refname || !(flags & REF_ISSYMREF) || (flags & REF_ISBROKEN))\n \t\tdie(_(\"your current branch appears to be broken\"));\n \ndiff --git a/sequencer.c b/sequencer.c\nindex aa2a2398357..cf7a2a9a112 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -839,10 +839,10 @@ static int is_index_unchanged(struct repository *r)\n \tstruct index_state *istate = r->index;\n \tconst char *head_name;\n \n-\tif (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), \"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n+\tif (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), \"HEAD\", NULL, RESOLVE_REF_READING, &head_oid, NULL)) {\n \t\t/* Check to see if this is an unborn branch */\n \t\thead_name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t\t    \"HEAD\",\n+\t\t\t\t\t\t    \"HEAD\", NULL,\n \t\t\t\t\t\t    RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n \t\t\t\t\t\t    &head_oid, NULL);\n \t\tif (!head_name ||\n@@ -1480,7 +1480,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@@ -4715,7 +4715,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)\n@@ -6213,6 +6213,7 @@ static int add_decorations_to_list(const struct commit *commit,\n \tconst struct name_decoration *decoration = get_name_decoration(&commit->object);\n \tconst char *head_ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n \t\t\t\t\t\t       \"HEAD\",\n+\t\t\t\t\t\t       NULL,\n \t\t\t\t\t\t       RESOLVE_REF_READING,\n \t\t\t\t\t\t       NULL,\n \t\t\t\t\t\t       NULL);\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex c9efd74c2b5..ef1d6acbfae 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -184,7 +184,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/transport-helper.c b/transport-helper.c\nindex 780fcaf5292..27d85748b60 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -1110,6 +1110,7 @@ static int push_refs_with_export(struct transport *transport,\n \t\t\t\t\t/* Follow symbolic refs (mainly for HEAD). */\n \t\t\t\t\tname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n \t\t\t\t\t\t\t\t       ref->peer_ref->name,\n+\t\t\t\t\t\t\t\t       NULL,\n \t\t\t\t\t\t\t\t       RESOLVE_REF_READING,\n \t\t\t\t\t\t\t\t       &oid,\n \t\t\t\t\t\t\t\t       &flag);\ndiff --git a/transport.c b/transport.c\nindex 0ad04b77fd2..8818ba75e7a 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -101,7 +101,7 @@ static void set_upstreams(struct transport *transport, struct ref *refs,\n \t\tlocalname = ref->peer_ref->name;\n \t\tremotename = ref->name;\n \t\ttmp = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t      localname, RESOLVE_REF_READING,\n+\t\t\t\t\t      localname, NULL, RESOLVE_REF_READING,\n \t\t\t\t\t      NULL, &flag);\n \t\tif (tmp && flag & REF_ISSYMREF &&\n \t\t\tstarts_with(tmp, \"refs/heads/\"))\ndiff --git a/upload-pack.c b/upload-pack.c\nindex bbfb04c8bd8..4fddc081237 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -1291,7 +1291,7 @@ static int find_symref(const char *refname,\n \tif ((flag & REF_ISSYMREF) == 0)\n \t\treturn 0;\n \tsymref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n-\t\t\t\t\t\trefname, 0, NULL, &flag);\n+\t\t\t\t\t\trefname, NULL, 0, NULL, &flag);\n \tif (!symref_target || (flag & REF_ISSYMREF) == 0)\n \t\tdie(\"'%s' is a symref but it is not?\", refname);\n \titem = string_list_append(cb_data, strip_namespace(refname));\ndiff --git a/worktree.c b/worktree.c\nindex 12eadacc618..ed89bab481d 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@@ -448,7 +449,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@@ -549,6 +550,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":"496551","messageId":"6d5b1b62e6c59ecc0f49d11e08732c6dc4d573fa.1717694802.git.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":"pull.1712.git.git.1717694800.gitgitgadget@gmail.com","subject":"[PATCH 4/4] ref-filter: populate symref from iterator","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-06-06T17:26:40Z","receivedAt":"2024-06-06T17:26:48Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"From: John Cai <johncai86@gmail.com>\n\nWith a previous commit, the reference the symbolic ref points is saved\nin the ref iterator records. Instead of making a separate call to\nresolve_refdup() each time, we can just populate the ref_array_item with\nthe value from the iterator.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n ref-filter.c | 11 ++++++-----\n 1 file changed, 6 insertions(+), 5 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 0b4b6bb8d71..c7c2117f9cd 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -2781,7 +2781,7 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)\n \treturn ref_kind_from_refname(refname);\n }\n \n-static struct ref_array_item *apply_ref_filter(const char *refname, const struct object_id *oid,\n+static struct ref_array_item *apply_ref_filter(const char *refname, const char *referent, const struct object_id *oid,\n \t\t\t    int flag, struct ref_filter *filter)\n {\n \tstruct ref_array_item *ref;\n@@ -2850,6 +2850,7 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const struct\n \tref->commit = commit;\n \tref->flag = flag;\n \tref->kind = kind;\n+\tref->symref = referent;\n \n \treturn ref;\n }\n@@ -2863,12 +2864,12 @@ struct ref_filter_cbdata {\n  * A call-back given to for_each_ref().  Filter refs and keep them for\n  * later object processing.\n  */\n-static int filter_one(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\n \n-\tref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);\n+\tref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);\n \tif (ref)\n \t\tref_array_append(ref_cbdata->array, ref);\n \n@@ -2898,13 +2899,13 @@ struct ref_filter_and_format_cbdata {\n \t} internal;\n };\n \n-static int filter_and_format_one(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_and_format_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_and_format_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\n \tstruct strbuf output = STRBUF_INIT, err = STRBUF_INIT;\n \n-\tref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);\n+\tref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);\n \tif (!ref)\n \t\treturn 0;\n \n-- \ngitgitgadget\n"},{"id":"496552","messageId":"33914f84a649fed5bc985d60e1947d1e414e65aa.1717694802.git.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":"pull.1712.git.git.1717694800.gitgitgadget@gmail.com","subject":"[PATCH 3/4] refs: add referent to each_ref_fn","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-06-06T17:26:39Z","receivedAt":"2024-06-06T17:26:48Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"From: John Cai <johncai86@gmail.com>\n\nAdd a parameter to each_ref_fn so that callers to the ref APIs\nthat use this function as a callback can have acess to the\nunresolved value of a symbolic ref.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n bisect.c                    |  3 ++-\n builtin/bisect.c            |  4 +++-\n builtin/checkout.c          |  2 +-\n builtin/describe.c          |  2 +-\n builtin/fetch.c             |  3 ++-\n builtin/fsck.c              |  4 ++--\n builtin/gc.c                |  1 +\n builtin/name-rev.c          |  2 +-\n builtin/pack-objects.c      |  5 +++--\n builtin/receive-pack.c      |  2 +-\n builtin/remote.c            |  4 +++-\n builtin/repack.c            |  1 +\n builtin/replace.c           |  1 +\n builtin/rev-parse.c         |  4 ++--\n builtin/show-branch.c       |  8 ++++----\n builtin/show-ref.c          |  3 ++-\n builtin/submodule--helper.c |  1 +\n builtin/worktree.c          |  1 +\n commit-graph.c              |  1 +\n delta-islands.c             |  2 +-\n fetch-pack.c                |  2 ++\n help.c                      |  2 +-\n http-backend.c              |  4 ++--\n log-tree.c                  |  2 +-\n ls-refs.c                   |  4 ++--\n midx-write.c                |  2 +-\n negotiator/default.c        |  2 +-\n negotiator/skipping.c       |  2 +-\n notes.c                     |  2 +-\n object-name.c               |  2 +-\n reachable.c                 |  2 +-\n ref-filter.c                |  4 ++--\n reflog.c                    |  1 +\n refs.c                      | 12 ++++++------\n refs.h                      |  2 +-\n refs/files-backend.c        |  2 +-\n refs/iterator.c             |  2 +-\n remote.c                    |  4 ++--\n replace-object.c            |  1 +\n revision.c                  |  2 +-\n server-info.c               |  2 +-\n shallow.c                   |  2 ++\n submodule.c                 |  2 ++\n t/helper/test-ref-store.c   |  2 +-\n upload-pack.c               |  8 ++++----\n walker.c                    |  1 +\n worktree.c                  |  2 +-\n 47 files changed, 76 insertions(+), 53 deletions(-)\n\ndiff --git a/bisect.c b/bisect.c\nindex 4ea703bec11..e22b3acf082 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -446,7 +446,7 @@ void find_bisection(struct commit_list **commit_list, int *reaches,\n \tclear_commit_weight(&commit_weight);\n }\n \n-static int register_ref(const char *refname, const struct object_id *oid,\n+static int register_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\tint flags UNUSED, void *cb_data UNUSED)\n {\n \tstruct strbuf good_prefix = STRBUF_INIT;\n@@ -1168,6 +1168,7 @@ int estimate_bisect_steps(int all)\n }\n \n static int mark_for_removal(const char *refname,\n+\t\t\t    const char *referent UNUSED,\n \t\t\t    const struct object_id *oid UNUSED,\n \t\t\t    int flag UNUSED, void *cb_data)\n {\ndiff --git a/builtin/bisect.c b/builtin/bisect.c\nindex 76ce5f0e0df..142e56d6020 100644\n--- a/builtin/bisect.c\n+++ b/builtin/bisect.c\n@@ -355,6 +355,7 @@ static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)\n }\n \n static int inc_nr(const char *refname UNUSED,\n+\t\t  const char *referent UNUSED,\n \t\t  const struct object_id *oid UNUSED,\n \t\t  int flag UNUSED, void *cb_data)\n {\n@@ -544,7 +545,7 @@ static int bisect_append_log_quoted(const char **argv)\n \treturn res;\n }\n \n-static int add_bisect_ref(const char *refname, const struct object_id *oid,\n+static int add_bisect_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flags UNUSED, void *cb)\n {\n \tstruct add_bisect_ref_data *data = cb;\n@@ -1161,6 +1162,7 @@ static int bisect_visualize(struct bisect_terms *terms, int argc,\n }\n \n static int get_first_good(const char *refname UNUSED,\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/builtin/checkout.c b/builtin/checkout.c\nindex f90a4ca4b7a..ccd27c277d9 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1045,7 +1045,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,\n \t\treport_tracking(new_branch_info);\n }\n \n-static int add_pending_uninteresting_ref(const char *refname,\n+static int add_pending_uninteresting_ref(const char *refname, const char *referent UNUSED,\n \t\t\t\t\t const struct object_id *oid,\n \t\t\t\t\t int flags UNUSED, void *cb_data)\n {\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex e5287eddf2d..d95a9109eed 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -145,7 +145,7 @@ static void add_to_known_names(const char *path,\n \t}\n }\n \n-static int get_name(const char *path, const struct object_id *oid,\n+static int get_name(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flag UNUSED, void *cb_data UNUSED)\n {\n \tint is_tag = 0;\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex a319954f9fd..b3e275ca5c8 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -286,7 +286,7 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,\n \treturn ent;\n }\n \n-static int add_one_refname(const char *refname,\n+static int add_one_refname(const char *refname, const char *referent UNUSED,\n \t\t\t   const struct object_id *oid,\n \t\t\t   int flag UNUSED, void *cbdata)\n {\n@@ -1459,6 +1459,7 @@ static void set_option(struct transport *transport, const char *name, const char\n \n \n static int add_oid(const char *refname UNUSED,\n+\t\t   const char *referent UNUSED,\n \t\t   const struct object_id *oid,\n \t\t   int flags UNUSED, void *cb_data)\n {\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 2c0ac6653ca..650dd0d839d 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -521,7 +521,7 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)\n \treturn 0;\n }\n \n-static int fsck_handle_ref(const char *refname, const struct object_id *oid,\n+static int fsck_handle_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t   int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object *obj;\n@@ -576,7 +576,7 @@ static void get_default_heads(void)\n \t\tstrbuf_worktree_ref(wt, &ref, \"HEAD\");\n \t\tfsck_head_link(ref.buf, &head_points_at, &head_oid);\n \t\tif (head_points_at && !is_null_oid(&head_oid))\n-\t\t\tfsck_handle_ref(ref.buf, &head_oid, 0, NULL);\n+\t\t\tfsck_handle_ref(ref.buf, NULL, &head_oid, 0, NULL);\n \t\tstrbuf_release(&ref);\n \n \t\tif (include_reflogs)\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex 72bac2554fc..65be7c31b33 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -836,6 +836,7 @@ struct cg_auto_data {\n };\n \n static int dfs_on_ref(const char *refname UNUSED,\n+\t\t      const char *referent UNUSED,\n \t\t      const struct object_id *oid,\n \t\t      int flags UNUSED,\n \t\t      void *cb_data)\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex 70e9ec4e477..52016f1cf7d 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -337,7 +337,7 @@ static int cmp_by_tag_and_age(const void *a_, const void *b_)\n \treturn a->taggerdate != b->taggerdate;\n }\n \n-static int name_ref(const char *path, const struct object_id *oid,\n+static int name_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flags UNUSED, void *cb_data)\n {\n \tstruct object *o = parse_object(the_repository, oid);\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 638f5c57f0d..a593fbfb3a3 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -771,7 +771,7 @@ static enum write_one_status write_one(struct hashfile *f,\n \treturn WRITE_ONE_WRITTEN;\n }\n \n-static int mark_tagged(const char *path UNUSED, const struct object_id *oid,\n+static int mark_tagged(const char *path UNUSED, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object_id peeled;\n@@ -3127,7 +3127,7 @@ static void add_tag_chain(const struct object_id *oid)\n \t}\n }\n \n-static int add_ref_tag(const char *tag UNUSED, const struct object_id *oid,\n+static int add_ref_tag(const char *tag UNUSED, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object_id peeled;\n@@ -4074,6 +4074,7 @@ static void record_recent_commit(struct commit *commit, void *data UNUSED)\n }\n \n static int mark_bitmap_preferred_tip(const char *refname,\n+\t\t\t\t     const char *referent UNUSED,\n \t\t\t\t     const struct object_id *oid,\n \t\t\t\t     int flags UNUSED,\n \t\t\t\t     void *data UNUSED)\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex d9e2c4bbe39..d3307730f39 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -300,7 +300,7 @@ static void show_ref(const char *path, const struct object_id *oid)\n \t}\n }\n \n-static int show_ref_cb(const char *path_full, const struct object_id *oid,\n+static int show_ref_cb(const char *path_full, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED, void *data)\n {\n \tstruct oidset *seen = data;\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 039d1d6c55a..fdc5954c6e0 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -543,6 +543,7 @@ struct branches_for_remote {\n };\n \n static int add_branch_for_removal(const char *refname,\n+\t\t\t\t  const char *referent UNUSED,\n \t\t\t\t  const struct object_id *oid UNUSED,\n \t\t\t\t  int flags UNUSED, void *cb_data)\n {\n@@ -585,7 +586,7 @@ struct rename_info {\n \tuint32_t symrefs_nr;\n };\n \n-static int read_remote_branches(const char *refname,\n+static int read_remote_branches(const char *refname, const char *referent UNUSED,\n \t\t\t\tconst struct object_id *oid UNUSED,\n \t\t\t\tint flags UNUSED, void *cb_data)\n {\n@@ -971,6 +972,7 @@ static void free_remote_ref_states(struct ref_states *states)\n }\n \n static int append_ref_to_tracked_list(const char *refname,\n+\t\t\t\t      const char *referent UNUSED,\n \t\t\t\t      const struct object_id *oid UNUSED,\n \t\t\t\t      int flags, void *cb_data)\n {\ndiff --git a/builtin/repack.c b/builtin/repack.c\nindex 58ad82dd97b..2c1218debcd 100644\n--- a/builtin/repack.c\n+++ b/builtin/repack.c\n@@ -667,6 +667,7 @@ struct midx_snapshot_ref_data {\n };\n \n static int midx_snapshot_ref_one(const char *refname UNUSED,\n+\t\t\t\t const char *referent UNUSED,\n \t\t\t\t const struct object_id *oid,\n \t\t\t\t int flag UNUSED, void *_data)\n {\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex ce9f6974d26..ab004979fbb 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -49,6 +49,7 @@ struct show_data {\n };\n \n static int show_reference(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/builtin/rev-parse.c b/builtin/rev-parse.c\nindex 1e2919fd81c..cc1a88cd933 100644\n--- a/builtin/rev-parse.c\n+++ b/builtin/rev-parse.c\n@@ -211,7 +211,7 @@ static int show_default(void)\n \treturn 0;\n }\n \n-static int show_reference(const char *refname, const struct object_id *oid,\n+static int show_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flag UNUSED, void *cb_data UNUSED)\n {\n \tif (ref_excluded(&ref_excludes, refname))\n@@ -220,7 +220,7 @@ static int show_reference(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n-static int anti_reference(const char *refname, const struct object_id *oid,\n+static int anti_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flag UNUSED, void *cb_data UNUSED)\n {\n \tshow_rev(REVERSED, oid, refname);\ndiff --git a/builtin/show-branch.c b/builtin/show-branch.c\nindex d72f4cb98de..3427c1572e5 100644\n--- a/builtin/show-branch.c\n+++ b/builtin/show-branch.c\n@@ -410,7 +410,7 @@ static int append_ref(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n-static int append_head_ref(const char *refname, const struct object_id *oid,\n+static int append_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t   int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object_id tmp;\n@@ -425,7 +425,7 @@ static int append_head_ref(const char *refname, const struct object_id *oid,\n \treturn append_ref(refname + ofs, oid, 0);\n }\n \n-static int append_remote_ref(const char *refname, const struct object_id *oid,\n+static int append_remote_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t     int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object_id tmp;\n@@ -451,7 +451,7 @@ static int append_tag_ref(const char *refname, const struct object_id *oid,\n static const char *match_ref_pattern = NULL;\n static int match_ref_slash = 0;\n \n-static int append_matching_ref(const char *refname, const struct object_id *oid,\n+static int append_matching_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t       int flag, void *cb_data)\n {\n \t/* we want to allow pattern hold/<asterisk> to show all\n@@ -468,7 +468,7 @@ static int append_matching_ref(const char *refname, const struct object_id *oid,\n \tif (wildmatch(match_ref_pattern, tail, 0))\n \t\treturn 0;\n \tif (starts_with(refname, \"refs/heads/\"))\n-\t\treturn append_head_ref(refname, oid, flag, cb_data);\n+\t\treturn append_head_ref(refname, NULL, oid, flag, cb_data);\n \tif (starts_with(refname, \"refs/tags/\"))\n \t\treturn append_tag_ref(refname, oid, flag, cb_data);\n \treturn append_ref(refname, oid, 0);\ndiff --git a/builtin/show-ref.c b/builtin/show-ref.c\nindex 3114bdc391a..3b9be14b61c 100644\n--- a/builtin/show-ref.c\n+++ b/builtin/show-ref.c\n@@ -63,7 +63,7 @@ struct show_ref_data {\n \tint show_head;\n };\n \n-static int show_ref(const char *refname, const struct object_id *oid,\n+static int show_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flag UNUSED, void *cbdata)\n {\n \tstruct show_ref_data *data = cbdata;\n@@ -97,6 +97,7 @@ static int show_ref(const char *refname, const struct object_id *oid,\n }\n \n static int add_existing(const char *refname,\n+\t\t\tconst char *referent UNUSED,\n \t\t\tconst struct object_id *oid UNUSED,\n \t\t\tint flag UNUSED, void *cbdata)\n {\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex e3e1f08a58c..d7c29383ccb 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -611,6 +611,7 @@ static void print_status(unsigned int flags, char state, const char *path,\n }\n \n static int handle_submodule_head_ref(const char *refname UNUSED,\n+\t\t\t\t     const char *referent UNUSED,\n \t\t\t\t     const struct object_id *oid,\n \t\t\t\t     int flags UNUSED,\n \t\t\t\t     void *cb_data)\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 893e973871e..14f9a525d6a 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -626,6 +626,7 @@ static void print_preparing_worktree_line(int detach,\n  * Returns 0 on failure and non-zero on success.\n  */\n static int first_valid_ref(const char *refname UNUSED,\n+\t\t\t   const char *referent UNUSED,\n \t\t\t   const struct object_id *oid UNUSED,\n \t\t\t   int flags UNUSED,\n \t\t\t   void *cb_data UNUSED)\ndiff --git a/commit-graph.c b/commit-graph.c\nindex e5dd3553dfe..b6906633b1b 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -1815,6 +1815,7 @@ struct refs_cb_data {\n };\n \n static int add_ref_to_set(const char *refname UNUSED,\n+\t\t\t  const char *referent UNUSED,\n \t\t\t  const struct object_id *oid,\n \t\t\t  int flags UNUSED, void *cb_data)\n {\ndiff --git a/delta-islands.c b/delta-islands.c\nindex 4ac3c10551a..8bafa4b807c 100644\n--- a/delta-islands.c\n+++ b/delta-islands.c\n@@ -388,7 +388,7 @@ static void add_ref_to_island(kh_str_t *remote_islands, const char *island_name,\n \trl->hash += sha_core;\n }\n \n-static int find_island_for_ref(const char *refname, const struct object_id *oid,\n+static int find_island_for_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t       int flags UNUSED, void *cb)\n {\n \tstruct island_load_data *ild = cb;\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 7d2aef21add..ae9615fad65 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -181,6 +181,7 @@ static int rev_list_insert_ref(struct fetch_negotiator *negotiator,\n }\n \n static int rev_list_insert_ref_oid(const char *refname UNUSED,\n+\t\t\t\t   const char *referent UNUSED,\n \t\t\t\t   const struct object_id *oid,\n \t\t\t\t   int flag UNUSED,\n \t\t\t\t   void *cb_data)\n@@ -608,6 +609,7 @@ static int mark_complete(const struct object_id *oid)\n }\n \n static int mark_complete_oid(const char *refname UNUSED,\n+\t\t\t     const char *referent UNUSED,\n \t\t\t     const struct object_id *oid,\n \t\t\t     int flag UNUSED,\n \t\t\t     void *cb_data UNUSED)\ndiff --git a/help.c b/help.c\nindex 1d057aa6073..385a98f81fa 100644\n--- a/help.c\n+++ b/help.c\n@@ -789,7 +789,7 @@ struct similar_ref_cb {\n \tstruct string_list *similar_refs;\n };\n \n-static int append_similar_ref(const char *refname,\n+static int append_similar_ref(const char *refname, const char *referent UNUSED,\n \t\t\t      const struct object_id *oid UNUSED,\n \t\t\t      int flags UNUSED, void *cb_data)\n {\ndiff --git a/http-backend.c b/http-backend.c\nindex 20c3ff8fa95..d9ed6405ed4 100644\n--- a/http-backend.c\n+++ b/http-backend.c\n@@ -510,7 +510,7 @@ static void run_service(const char **argv, int buffer_input)\n \t\texit(1);\n }\n \n-static int show_text_ref(const char *name, const struct object_id *oid,\n+static int show_text_ref(const char *name, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t int flag UNUSED, void *cb_data)\n {\n \tconst char *name_nons = strip_namespace(name);\n@@ -566,7 +566,7 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)\n \tstrbuf_release(&buf);\n }\n \n-static int show_head_ref(const char *refname, const struct object_id *oid,\n+static int show_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t int flag, void *cb_data)\n {\n \tstruct strbuf *buf = cb_data;\ndiff --git a/log-tree.c b/log-tree.c\nindex da06c6e982f..06176e34a2c 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -143,7 +143,7 @@ static int ref_filter_match(const char *refname,\n \treturn 1;\n }\n \n-static int add_ref_decoration(const char *refname, const struct object_id *oid,\n+static int add_ref_decoration(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t      int flags UNUSED,\n \t\t\t      void *cb_data)\n {\ndiff --git a/ls-refs.c b/ls-refs.c\nindex 3d047bbc64f..f0d9f8864d7 100644\n--- a/ls-refs.c\n+++ b/ls-refs.c\n@@ -75,7 +75,7 @@ struct ls_refs_data {\n \tunsigned unborn : 1;\n };\n \n-static int send_ref(const char *refname, const struct object_id *oid,\n+static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flag, void *cb_data)\n {\n \tstruct ls_refs_data *data = cb_data;\n@@ -134,7 +134,7 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)\n \toid_is_null = is_null_oid(&oid);\n \tif (!oid_is_null ||\n \t    (data->unborn && data->symrefs && (flag & REF_ISSYMREF)))\n-\t\tsend_ref(namespaced.buf, oid_is_null ? NULL : &oid, flag, data);\n+\t\tsend_ref(namespaced.buf, NULL, oid_is_null ? NULL : &oid, flag, data);\n \tstrbuf_release(&namespaced);\n }\n \ndiff --git a/midx-write.c b/midx-write.c\nindex 4998707eb8b..bac416d5e87 100644\n--- a/midx-write.c\n+++ b/midx-write.c\n@@ -651,7 +651,7 @@ static void prepare_midx_packing_data(struct packing_data *pdata,\n \ttrace2_region_leave(\"midx\", \"prepare_midx_packing_data\", the_repository);\n }\n \n-static int add_ref_to_pending(const char *refname,\n+static int add_ref_to_pending(const char *refname, const char *referent UNUSED,\n \t\t\t      const struct object_id *oid,\n \t\t\t      int flag, void *cb_data)\n {\ndiff --git a/negotiator/default.c b/negotiator/default.c\nindex 518b3c43b28..052c37f4ae2 100644\n--- a/negotiator/default.c\n+++ b/negotiator/default.c\n@@ -36,7 +36,7 @@ static void rev_list_push(struct negotiation_state *ns,\n \t}\n }\n \n-static int clear_marks(const char *refname, const struct object_id *oid,\n+static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED,\n \t\t       void *cb_data UNUSED)\n {\ndiff --git a/negotiator/skipping.c b/negotiator/skipping.c\nindex b7e008c2fda..0ad76b033ab 100644\n--- a/negotiator/skipping.c\n+++ b/negotiator/skipping.c\n@@ -73,7 +73,7 @@ static struct entry *rev_list_push(struct data *data, struct commit *commit, int\n \treturn entry;\n }\n \n-static int clear_marks(const char *refname, const struct object_id *oid,\n+static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED,\n \t\t       void *cb_data UNUSED)\n {\ndiff --git a/notes.c b/notes.c\nindex 53ca25c8147..77ac78ac08e 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -928,7 +928,7 @@ int combine_notes_cat_sort_uniq(struct object_id *cur_oid,\n \treturn ret;\n }\n \n-static int string_list_add_one_ref(const char *refname,\n+static int string_list_add_one_ref(const char *refname, const char *referent UNUSED,\n \t\t\t\t   const struct object_id *oid UNUSED,\n \t\t\t\t   int flag UNUSED, void *cb)\n {\ndiff --git a/object-name.c b/object-name.c\nindex 523af6f64f3..68b4105d614 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -1363,7 +1363,7 @@ struct handle_one_ref_cb {\n \tstruct commit_list **list;\n };\n \n-static int handle_one_ref(const char *path, const struct object_id *oid,\n+static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flag UNUSED,\n \t\t\t  void *cb_data)\n {\ndiff --git a/reachable.c b/reachable.c\nindex 1224b300086..1f09353bae3 100644\n--- a/reachable.c\n+++ b/reachable.c\n@@ -77,7 +77,7 @@ static void add_rebase_files(struct rev_info *revs)\n \tfree_worktrees(worktrees);\n }\n \n-static int add_one_ref(const char *path, const struct object_id *oid,\n+static int add_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag, void *cb_data)\n {\n \tstruct rev_info *revs = (struct rev_info *)cb_data;\ndiff --git a/ref-filter.c b/ref-filter.c\nindex f7fb0c7e0ec..0b4b6bb8d71 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -2863,7 +2863,7 @@ struct ref_filter_cbdata {\n  * A call-back given to for_each_ref().  Filter refs and keep them for\n  * later object processing.\n  */\n-static int filter_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_one(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\n@@ -2898,7 +2898,7 @@ struct ref_filter_and_format_cbdata {\n \t} internal;\n };\n \n-static int filter_and_format_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_and_format_one(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_and_format_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\ndiff --git a/reflog.c b/reflog.c\nindex 3c80950186b..744e5f2d982 100644\n--- a/reflog.c\n+++ b/reflog.c\n@@ -298,6 +298,7 @@ int should_expire_reflog_ent_verbose(struct object_id *ooid,\n }\n \n static int push_tip_to_list(const char *refname UNUSED,\n+\t\t\t    const char *referent UNUSED,\n \t\t\t    const struct object_id *oid,\n \t\t\t    int flags, void *cb_data)\n {\ndiff --git a/refs.c b/refs.c\nindex 2c592a9ae29..f75d6537bc7 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -411,7 +411,7 @@ int refs_ref_exists(struct ref_store *refs, const char *refname)\n \t\t\t\t\t NULL, NULL);\n }\n \n-static int for_each_filter_refs(const char *refname,\n+static int for_each_filter_refs(const char *refname, const char *referent,\n \t\t\t\tconst struct object_id *oid,\n \t\t\t\tint flags, void *data)\n {\n@@ -421,7 +421,7 @@ static int for_each_filter_refs(const char *refname,\n \t\treturn 0;\n \tif (filter->prefix)\n \t\tskip_prefix(refname, filter->prefix, &refname);\n-\treturn filter->fn(refname, oid, flags, filter->cb_data);\n+\treturn filter->fn(refname, referent, oid, flags, filter->cb_data);\n }\n \n struct warn_if_dangling_data {\n@@ -432,7 +432,7 @@ struct warn_if_dangling_data {\n \tconst char *msg_fmt;\n };\n \n-static int warn_if_dangling_symref(const char *refname,\n+static int warn_if_dangling_symref(const char *refname, const char *referent UNUSED,\n \t\t\t\t   const struct object_id *oid UNUSED,\n \t\t\t\t   int flags, void *cb_data)\n {\n@@ -503,7 +503,7 @@ int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_da\n \n \tstrbuf_addf(&buf, \"%sHEAD\", get_git_namespace());\n \tif (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag))\n-\t\tret = fn(buf.buf, &oid, flag, cb_data);\n+\t\tret = fn(buf.buf, NULL, &oid, flag, cb_data);\n \tstrbuf_release(&buf);\n \n \treturn ret;\n@@ -1524,7 +1524,7 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n \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+\t\treturn fn(\"HEAD\", NULL, &oid, flag, cb_data);\n \n \treturn 0;\n }\n@@ -2360,7 +2360,7 @@ struct do_for_each_reflog_help {\n \tvoid *cb_data;\n };\n \n-static int do_for_each_reflog_helper(const char *refname,\n+static int do_for_each_reflog_helper(const char *refname, 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 ea4a3217658..979494ce1f8 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -294,7 +294,7 @@ struct ref_transaction;\n  * arguments is only guaranteed to be valid for the duration of a\n  * single callback invocation.\n  */\n-typedef int each_ref_fn(const char *refname,\n+typedef int each_ref_fn(const char *refname, const char *referent,\n \t\t\tconst struct object_id *oid, int flags, void *cb_data);\n \n /*\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex a963d796a29..f4c892422bc 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -3002,7 +3002,7 @@ static int files_transaction_abort(struct ref_store *ref_store,\n \treturn 0;\n }\n \n-static int ref_present(const char *refname,\n+static int ref_present(const char *refname, const char *referent UNUSED,\n \t\t       const struct object_id *oid UNUSED,\n \t\t       int flags UNUSED,\n \t\t       void *cb_data)\ndiff --git a/refs/iterator.c b/refs/iterator.c\nindex 26acb6bd640..21beeb91e9e 100644\n--- a/refs/iterator.c\n+++ b/refs/iterator.c\n@@ -451,7 +451,7 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,\n \n \tcurrent_ref_iter = iter;\n \twhile ((ok = ref_iterator_advance(iter)) == ITER_OK) {\n-\t\tretval = fn(iter->refname, iter->oid, iter->flags, cb_data);\n+\t\tretval = fn(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/remote.c b/remote.c\nindex c9718ee0ca1..071c542ab31 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -2342,7 +2342,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,\n \treturn 1;\n }\n \n-static int one_local_ref(const char *refname, const struct object_id *oid,\n+static int one_local_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t int flag UNUSED,\n \t\t\t void *cb_data)\n {\n@@ -2427,7 +2427,7 @@ struct stale_heads_info {\n \tstruct refspec *rs;\n };\n \n-static int get_stale_heads_cb(const char *refname, const struct object_id *oid,\n+static int get_stale_heads_cb(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t      int flags, void *cb_data)\n {\n \tstruct stale_heads_info *info = cb_data;\ndiff --git a/replace-object.c b/replace-object.c\nindex 73f5acbcd9f..8ef561f1455 100644\n--- a/replace-object.c\n+++ b/replace-object.c\n@@ -9,6 +9,7 @@\n #include \"commit.h\"\n \n static int register_replace_ref(const 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)\ndiff --git a/revision.c b/revision.c\nindex 6aca4f42303..e37346fb52a 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1627,7 +1627,7 @@ struct all_refs_cb {\n \tstruct worktree *wt;\n };\n \n-static int handle_one_ref(const char *path, const struct object_id *oid,\n+static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flag UNUSED,\n \t\t\t  void *cb_data)\n {\ndiff --git a/server-info.c b/server-info.c\nindex 6feaa457c5c..3a0d6ad39ab 100644\n--- a/server-info.c\n+++ b/server-info.c\n@@ -151,7 +151,7 @@ static int update_info_file(char *path,\n \treturn ret;\n }\n \n-static int add_info_ref(const char *path, const struct object_id *oid,\n+static int add_info_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t\tint flag UNUSED,\n \t\t\tvoid *cb_data)\n {\ndiff --git a/shallow.c b/shallow.c\nindex a0b181ba8a1..a4d2c3e221b 100644\n--- a/shallow.c\n+++ b/shallow.c\n@@ -610,6 +610,7 @@ static void paint_down(struct paint_info *info, const struct object_id *oid,\n }\n \n static int mark_uninteresting(const char *refname UNUSED,\n+\t\t\t      const char *referent UNUSED,\n \t\t\t      const struct object_id *oid,\n \t\t\t      int flags UNUSED,\n \t\t\t      void *cb_data UNUSED)\n@@ -725,6 +726,7 @@ struct commit_array {\n };\n \n static int add_ref(const char *refname UNUSED,\n+\t\t  const char *referent UNUSED,\n \t\t   const struct object_id *oid,\n \t\t   int flags UNUSED,\n \t\t   void *cb_data)\ndiff --git a/submodule.c b/submodule.c\nindex 759cf1e1cda..bdba5a1ba11 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -951,6 +951,7 @@ static void free_submodules_data(struct string_list *submodules)\n }\n \n static int has_remote(const char *refname UNUSED,\n+\t\t      const char *referent UNUSED,\n \t\t      const struct object_id *oid UNUSED,\n \t\t      int flags UNUSED, void *cb_data UNUSED)\n {\n@@ -1271,6 +1272,7 @@ int push_unpushed_submodules(struct repository *r,\n }\n \n static int append_oid_to_array(const char *ref UNUSED,\n+\t\t\t       const char *referent UNUSED,\n \t\t\t       const struct object_id *oid,\n \t\t\t       int flags UNUSED, void *data)\n {\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex ef1d6acbfae..78cb873e6a0 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -153,7 +153,7 @@ static int cmd_rename_ref(struct ref_store *refs, const char **argv)\n \treturn refs_rename_ref(refs, oldref, newref, logmsg);\n }\n \n-static int each_ref(const char *refname, const struct object_id *oid,\n+static int each_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flags, void *cb_data UNUSED)\n {\n \tprintf(\"%s %s 0x%x\\n\", oid_to_hex(oid), refname, flags);\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 4fddc081237..e79e77570b7 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -855,7 +855,7 @@ static void send_unshallow(struct upload_pack_data *data)\n \t}\n }\n \n-static int check_ref(const char *refname_full, const struct object_id *oid,\n+static int check_ref(const char *refname_full, const char *referent UNUSED, const struct object_id *oid,\n \t\t     int flag, void *cb_data);\n static void deepen(struct upload_pack_data *data, int depth)\n {\n@@ -1206,7 +1206,7 @@ static int mark_our_ref(const char *refname, const char *refname_full,\n \treturn 0;\n }\n \n-static int check_ref(const char *refname_full, const struct object_id *oid,\n+static int check_ref(const char *refname_full, const char *referent UNUSED,const struct object_id *oid,\n \t\t     int flag UNUSED, void *cb_data)\n {\n \tconst char *refname = strip_namespace(refname_full);\n@@ -1274,14 +1274,14 @@ static void write_v0_ref(struct upload_pack_data *data,\n \treturn;\n }\n \n-static int send_ref(const char *refname, const struct object_id *oid,\n+static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flag UNUSED, void *cb_data)\n {\n \twrite_v0_ref(cb_data, refname, strip_namespace(refname), oid);\n \treturn 0;\n }\n \n-static int find_symref(const char *refname,\n+static int find_symref(const char *refname, const char *referent UNUSED,\n \t\t       const struct object_id *oid UNUSED,\n \t\t       int flag, void *cb_data)\n {\ndiff --git a/walker.c b/walker.c\nindex 946d86b04ee..918693221a9 100644\n--- a/walker.c\n+++ b/walker.c\n@@ -219,6 +219,7 @@ static int interpret_target(struct walker *walker, char *target, struct object_i\n }\n \n static int mark_complete(const char *path UNUSED,\n+\t\t\tconst char *referent UNUSED,\n \t\t\t const struct object_id *oid,\n \t\t\t int flag UNUSED,\n \t\t\t void *cb_data UNUSED)\ndiff --git a/worktree.c b/worktree.c\nindex ed89bab481d..9ab7e305d39 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -553,7 +553,7 @@ int other_head_refs(each_ref_fn fn, void *cb_data)\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+\t\t\tret = fn(refname.buf, NULL, &oid, flag, cb_data);\n \t\tif (ret)\n \t\t\tbreak;\n \t}\n-- \ngitgitgadget\n\n"},{"id":"496554","messageId":"xmqq34pqlyou.fsf@gitster.g","threadId":"61598","inReplyTo":"011c10f488610b0a795a843bff66723477783761.1717694801.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 1/4] refs: add referent parameter to refs_resolve_ref_unsafe","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-06T18:21:21Z","receivedAt":"2024-06-06T18:21:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"ADMINISTRIVIA.  Check the address you place on the CC: line.  What\nwe can see for this message at\n\nhttps://lore.kernel.org/git/011c10f488610b0a795a843bff66723477783761.1717694801.git.gitgitgadget@gmail.com/raw\n\nlooks like this.\n\n    Cc: \"Phillip Wood [ ]\" <phillip.wood123@gmail.com>,\n        Kristoffer Haugsbakk <[code@khaugsbakk.name]>,\n        \"Jeff King [ ]\" <peff@peff.net>,\n        \"Patrick Steinhardt [ ]\" <ps@pks.im>,\n        \"=?UTF-8?Q?Jean-No=C3=ABl?= Avila [ ]\" <avila.jn@gmail.com>,\n        John Cai <johncai86@gmail.com>,\n        John Cai <johncai86@gmail.com>\n\nI fixed them manually, but it wasn't pleasant.  I think we saw a\nsimilar breakage earlier coming via GGG, but I do not recall the\ndetails of how to cause such breakages (iow, what to avoid repeating\nthis).\n\nAnyway.\n\n\"John Cai via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n>  29 files changed, 64 insertions(+), 52 deletions(-)\n\nWow, the blast radius of this thing is rather big.  Among these\nexisting callers of refs_resolve_ref_unsafe(), how many of them\nwill benefit from being able to pass a non NULL parameter at the end\nof the series, and more importantly, in the future to take advantage\nof the new feature possibly with a separate series?\n\nI am assuming that this will benefit only a selected few and the\ncallers that would want to take advantage of the new feature will\nremain low.  Have you considered renaming refs_resolve_ref_unsafe()\nto a new name (say, refs_resolve_ref_unsafe_with_referent()) and\nimplement the new feature (which is only triggered when the new\nparameter gets a non NULL value), make refs_resolve_ref_unsafe() a\nvery thin wrapper that passes NULL to the new thing?\n\nThat way, you do not have to touch those existing callers that will\nnever benefit from the new capability in the future.  You won't risk\nconflicting with in flight topics semantically, either.\n\nOr will they also benefit from the new feature in the future?\n\nOffhand, I do not know how a caller like this ...\n\n> diff --git a/add-interactive.c b/add-interactive.c\n> index b5d6cd689a1..041d30cf2b3 100644\n> --- a/add-interactive.c\n> +++ b/add-interactive.c\n> @@ -533,7 +533,7 @@ static int get_modified_files(struct repository *r,\n>  {\n>  \tstruct object_id head_oid;\n>  \tint is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n> -\t\t\t\t\t\t  \"HEAD\", RESOLVE_REF_READING,\n> +\t\t\t\t\t\t  \"HEAD\", NULL, RESOLVE_REF_READING,\n>  \t\t\t\t\t\t  &head_oid, NULL);\n>  \tstruct collection_status s = { 0 };\n>  \tint i;\n\n... would be helped.\n\nThanks.\n"},{"id":"496555","messageId":"xmqqv82mkk15.fsf@gitster.g","threadId":"61598","inReplyTo":"xmqq34pqlyou.fsf@gitster.g","subject":"Re: [PATCH 1/4] refs: add referent parameter to refs_resolve_ref_unsafe","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-06T18:23:18Z","receivedAt":"2024-06-06T18:23:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> \"John Cai via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>>  29 files changed, 64 insertions(+), 52 deletions(-)\n>\n> Wow, the blast radius of this thing is rather big.  Among these\n> existing callers of refs_resolve_ref_unsafe(), how many of them\n> will benefit from being able to pass a non NULL parameter at the end\n> of the series, and more importantly, in the future to take advantage\n> of the new feature possibly with a separate series?\n> ...\n> That way, you do not have to touch those existing callers that will\n> never benefit from the new capability in the future.  You won't risk\n> conflicting with in flight topics semantically, either.\n\nThe same comment applies to [3/4], but I do not want to fix the Cc: header\nagain, so I am replying to this message.\n\nThanks.\n\n"},{"id":"496563","messageId":"F64F4F3A-EF82-4281-8A75-0DDC8FA65D4B@gmail.com","threadId":"61598","inReplyTo":"xmqq34pqlyou.fsf@gitster.g","subject":"Re: [PATCH 1/4] refs: add referent parameter to refs_resolve_ref_unsafe","fromName":"John Cai","fromEmail":"johncai86@gmail.com","sentAt":"2024-06-06T21:02:05Z","receivedAt":"2024-06-06T21:02:07Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"Hi Junio,\n\nOn 6 Jun 2024, at 14:21, Junio C Hamano wrote:\n\n> ADMINISTRIVIA.  Check the address you place on the CC: line.  What\n> we can see for this message at\n>\n> https://lore.kernel.org/git/011c10f488610b0a795a843bff66723477783761.1717694801.git.gitgitgadget@gmail.com/raw\n>\n> looks like this.\n>\n>     Cc: \"Phillip Wood [ ]\" <phillip.wood123@gmail.com>,\n>         Kristoffer Haugsbakk <[code@khaugsbakk.name]>,\n>         \"Jeff King [ ]\" <peff@peff.net>,\n>         \"Patrick Steinhardt [ ]\" <ps@pks.im>,\n>         \"=?UTF-8?Q?Jean-No=C3=ABl?= Avila [ ]\" <avila.jn@gmail.com>,\n>         John Cai <johncai86@gmail.com>,\n>         John Cai <johncai86@gmail.com>\n>\n> I fixed them manually, but it wasn't pleasant.  I think we saw a\n> similar breakage earlier coming via GGG, but I do not recall the\n> details of how to cause such breakages (iow, what to avoid repeating\n> this).\n\noof, apologies. Didn't notice that. I'll be more mindful about the cc line.\n\n>\n> Anyway.\n>\n> \"John Cai via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>>  29 files changed, 64 insertions(+), 52 deletions(-)\n>\n> Wow, the blast radius of this thing is rather big.  Among these\n> existing callers of refs_resolve_ref_unsafe(), how many of them\n> will benefit from being able to pass a non NULL parameter at the end\n> of the series, and more importantly, in the future to take advantage\n> of the new feature possibly with a separate series?\n>\n> I am assuming that this will benefit only a selected few and the\n> callers that would want to take advantage of the new feature will\n> remain low.  Have you considered renaming refs_resolve_ref_unsafe()\n> to a new name (say, refs_resolve_ref_unsafe_with_referent()) and\n> implement the new feature (which is only triggered when the new\n> parameter gets a non NULL value), make refs_resolve_ref_unsafe() a\n> very thin wrapper that passes NULL to the new thing?\n>\n> That way, you do not have to touch those existing callers that will\n> never benefit from the new capability in the future.  You won't risk\n> conflicting with in flight topics semantically, either.\n>\n> Or will they also benefit from the new feature in the future?\n>\n> Offhand, I do not know how a caller like this ...\n>\n>> diff --git a/add-interactive.c b/add-interactive.c\n>> index b5d6cd689a1..041d30cf2b3 100644\n>> --- a/add-interactive.c\n>> +++ b/add-interactive.c\n>> @@ -533,7 +533,7 @@ static int get_modified_files(struct repository *r,\n>>  {\n>>  \tstruct object_id head_oid;\n>>  \tint is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n>> -\t\t\t\t\t\t  \"HEAD\", RESOLVE_REF_READING,\n>> +\t\t\t\t\t\t  \"HEAD\", NULL, RESOLVE_REF_READING,\n>>  \t\t\t\t\t\t  &head_oid, NULL);\n>>  \tstruct collection_status s = { 0 };\n>>  \tint i;\n>\n> ... would be helped.\n\nGood point. I was sensing the same thing as I made all the callsite changes so\nthis feedback makes it clear what we should do.\n\n>\n> Thanks.\n"},{"id":"496571","messageId":"xmqqed99k7xo.fsf@gitster.g","threadId":"61598","inReplyTo":"F64F4F3A-EF82-4281-8A75-0DDC8FA65D4B@gmail.com","subject":"Re: [PATCH 1/4] refs: add referent parameter to refs_resolve_ref_unsafe","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-06T22:44:35Z","receivedAt":"2024-06-06T22:44:40Z","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> On 6 Jun 2024, at 14:21, Junio C Hamano wrote:\n>\n>> ADMINISTRIVIA.  Check the address you place on the CC: line.  What\n>> we can see for this message at\n>> ...\n>> I fixed them manually, but it wasn't pleasant.  I think we saw a\n>> similar breakage earlier coming via GGG, but I do not recall the\n>> details of how to cause such breakages (iow, what to avoid repeating\n>> this).\n>\n> oof, apologies. Didn't notice that. I'll be more mindful about the cc line.\n\nI found the previous occurrences of the same problem:\n\n  https://lore.kernel.org/git/xmqqjzm3qumx.fsf@gitster.g/\n  https://lore.kernel.org/git/xmqqh6hkxox6.fsf@gitster.g/\n\nThe last message in the thread\n\n  https://lore.kernel.org/git/CAMbn=B7J4ODf9ybJQpL1bZZ7qdWSDGaLEyTmVv+ZBiSeC9T+yw@mail.gmail.com/\n\nsays that the original user of GGG found what was wrong in the way\nthe user was using GGG to send and fixed it, but unfortunately we\ndidn't hear exactly *what* the breakage was and *how* it was fixed.\n\nAryan, do you remember what the problem was and more importantly\nwhat the fix was?\n\nThanks.\n"},{"id":"496646","messageId":"7FC6AD35-0DF4-4228-8708-790470D4B0EA@gmail.com","threadId":"61598","inReplyTo":"xmqqv82mkk15.fsf@gitster.g","subject":"Re: [PATCH 1/4] refs: add referent parameter to refs_resolve_ref_unsafe","fromName":"John Cai","fromEmail":"johncai86@gmail.com","sentAt":"2024-06-07T13:43:46Z","receivedAt":"2024-06-07T13:43:49Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"Hi Junio,\n\nOn 6 Jun 2024, at 14:23, Junio C Hamano wrote:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> \"John Cai via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>>\n>>>  29 files changed, 64 insertions(+), 52 deletions(-)\n>>\n>> Wow, the blast radius of this thing is rather big.  Among these\n>> existing callers of refs_resolve_ref_unsafe(), how many of them\n>> will benefit from being able to pass a non NULL parameter at the end\n>> of the series, and more importantly, in the future to take advantage\n>> of the new feature possibly with a separate series?\n>> ...\n>> That way, you do not have to touch those existing callers that will\n>> never benefit from the new capability in the future.  You won't risk\n>> conflicting with in flight topics semantically, either.\n>\n> The same comment applies to [3/4], but I do not want to fix the Cc: header\n> again, so I am replying to this message.\n\nYes, so for 3/4 I was exploring doing the same thing. However, each_repo_fn goes\npretty deep in the callstack and to provide an alternate set of functions that\nuse something like each_repo_referent_fn would still lead to a relatively large\nblast radius, eg, something like:\n\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex f7fb0c7e0e..770d3715c2 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -2631,12 +2631,12 @@ static int filter_exclude_match(struct ref_filter *filter, const char *refname)\n  * pattern match, so the callback still has to match each ref individually.\n  */\n static int for_each_fullref_in_pattern(struct ref_filter *filter,\n-\t\t\t\t       each_ref_fn cb,\n+\t\t\t\t       each_ref_with_referent_fn cb,\n \t\t\t\t       void *cb_data)\n {\n \tif (filter->kind & FILTER_REFS_ROOT_REFS) {\n \t\t/* In this case, we want to print all refs including root refs. */\n-\t\treturn refs_for_each_include_root_refs(get_main_ref_store(the_repository),\n+\t\treturn refs_for_each_include_root_refs_with_referent(get_main_ref_store(the_repository),\n \t\t\t\t\t\t       cb, cb_data);\n \t}\n\n@@ -2646,7 +2646,7 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,\n \t\t * prefixes like \"refs/heads/\" etc. are stripped off,\n \t\t * so we have to look at everything:\n \t\t */\n-\t\treturn refs_for_each_fullref_in(get_main_ref_store(the_repository),\n+\t\treturn refs_for_each_fullref_in_with_referent(get_main_ref_store(the_repository),\n \t\t\t\t\t\t\"\", NULL, cb, cb_data);\n \t}\n\n@@ -2656,17 +2656,17 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,\n \t\t * so just return everything and let the caller\n \t\t * sort it out.\n \t\t */\n-\t\treturn refs_for_each_fullref_in(get_main_ref_store(the_repository),\n+\t\treturn refs_for_each_fullref_in_with_referent(get_main_ref_store(the_repository),\n \t\t\t\t\t\t\"\", NULL, cb, cb_data);\n \t}\n\n \tif (!filter->name_patterns[0]) {\n \t\t/* no patterns; we have to look at everything */\n-\t\treturn refs_for_each_fullref_in(get_main_ref_store(the_repository),\n+\t\treturn refs_for_each_fullref_in_with_referent(get_main_ref_store(the_repository),\n \t\t\t\t\t\t \"\", filter->exclude.v, cb, cb_data);\n \t}\n\n-\treturn refs_for_each_fullref_in_prefixes(get_main_ref_store(the_repository),\n+\treturn refs_for_each_fullref_in_prefixes_with_referent(get_main_ref_store(the_repository),\n \t\t\t\t\t\t NULL, filter->name_patterns,\n \t\t\t\t\t\t filter->exclude.v,\n \t\t\t\t\t\t cb, cb_data);\n@@ -2781,7 +2781,7 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)\n \treturn ref_kind_from_refname(refname);\n }\n\n-static struct ref_array_item *apply_ref_filter(const char *refname, const struct object_id *oid,\n+static struct ref_array_item *apply_ref_filter(const char *refname, const char *referent, const struct object_id *oid,\n \t\t\t    int flag, struct ref_filter *filter)\n {\n \tstruct ref_array_item *ref;\n@@ -2850,6 +2850,7 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const struct\n \tref->commit = commit;\n \tref->flag = flag;\n \tref->kind = kind;\n+\tref->symref = referent;\n\n \treturn ref;\n }\n@@ -2863,12 +2864,12 @@ struct ref_filter_cbdata {\n  * A call-back given to for_each_ref().  Filter refs and keep them for\n  * later object processing.\n  */\n-static int filter_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\n\n-\tref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);\n+\tref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);\n \tif (ref)\n \t\tref_array_append(ref_cbdata->array, ref);\n\n@@ -2898,13 +2899,13 @@ struct ref_filter_and_format_cbdata {\n \t} internal;\n };\n\n-static int filter_and_format_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_and_format_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_and_format_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\n \tstruct strbuf output = STRBUF_INIT, err = STRBUF_INIT;\n\n-\tref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);\n+\tref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);\n \tif (!ref)\n \t\treturn 0;\n\n@@ -3050,7 +3051,7 @@ void filter_ahead_behind(struct repository *r,\n \tfree(commits);\n }\n\n-static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref_fn fn, void *cb_data)\n+static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref_with_referent_fn fn, void *cb_data)\n {\n \tint ret = 0;\n\n@@ -3070,15 +3071,15 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref\n \t\t * of filter_ref_kind().\n \t\t */\n \t\tif (filter->kind == FILTER_REFS_BRANCHES)\n-\t\t\tret = refs_for_each_fullref_in(get_main_ref_store(the_repository),\n+\t\t\tret = refs_for_each_fullref_in_with_referent(get_main_ref_store(the_repository),\n \t\t\t\t\t\t       \"refs/heads/\", NULL,\n \t\t\t\t\t\t       fn, cb_data);\n \t\telse if (filter->kind == FILTER_REFS_REMOTES)\n-\t\t\tret = refs_for_each_fullref_in(get_main_ref_store(the_repository),\n+\t\t\tret = refs_for_each_fullref_in_with_referent(get_main_ref_store(the_repository),\n \t\t\t\t\t\t       \"refs/remotes/\", NULL,\n \t\t\t\t\t\t       fn, cb_data);\n \t\telse if (filter->kind == FILTER_REFS_TAGS)\n-\t\t\tret = refs_for_each_fullref_in(get_main_ref_store(the_repository),\n+\t\t\tret = refs_for_each_fullref_in_with_referent(get_main_ref_store(the_repository),\n \t\t\t\t\t\t       \"refs/tags/\", NULL, fn,\n \t\t\t\t\t\t       cb_data);\n \t\telse if (filter->kind & FILTER_REFS_REGULAR)\n@@ -3090,7 +3091,7 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref\n \t\t */\n \t\tif (!ret && !(filter->kind & FILTER_REFS_ROOT_REFS) &&\n \t\t    (filter->kind & FILTER_REFS_DETACHED_HEAD))\n-\t\t\trefs_head_ref(get_main_ref_store(the_repository), fn,\n+\t\t\trefs_head_ref_referent(get_main_ref_store(the_repository), fn,\n \t\t\t\t      cb_data);\n \t}\n\ndiff --git a/refs.c b/refs.c\nindex 6dcb0288cb..4366f35586 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1529,6 +1529,19 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n \treturn 0;\n }\n\n+int refs_head_ref_referent(struct ref_store *refs, each_ref_with_referent_fn fn, void *cb_data)\n+{\n+\tstruct object_id oid;\n+\tint flag;\n+\tconst char *referent;\n+\n+\tif (refs_resolve_ref_unsafe_with_referent(refs, \"HEAD\", referent, RESOLVE_REF_READING,\n+\t\t\t\t    &oid, &flag))\n+\t\treturn fn(\"HEAD\", referent, &oid, flag, cb_data);\n+\n+\treturn 0;\n+}\n+\n struct ref_iterator *refs_ref_iterator_begin(\n \t\tstruct ref_store *refs,\n \t\tconst char *prefix,\n@@ -1560,6 +1573,22 @@ struct ref_iterator *refs_ref_iterator_begin(\n \treturn iter;\n }\n\n+static int do_for_each_ref_with_referent(struct ref_store *refs, const char *prefix,\n+\t\t\t   const char **exclude_patterns,\n+\t\t\t   each_ref_with_referent_fn fn, int trim,\n+\t\t\t   enum do_for_each_ref_flags flags, void *cb_data)\n+{\n+\tstruct ref_iterator *iter;\n+\n+\tif (!refs)\n+\t\treturn 0;\n+\n+\titer = refs_ref_iterator_begin(refs, prefix, exclude_patterns, trim,\n+\t\t\t\t       flags);\n+\n+\treturn do_for_each_ref_iterator_with_referent(iter, fn, cb_data);\n+}\n+\n static int do_for_each_ref(struct ref_store *refs, const char *prefix,\n \t\t\t   const char **exclude_patterns,\n \t\t\t   each_ref_fn fn, int trim,\n@@ -1594,6 +1623,13 @@ int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,\n \treturn do_for_each_ref(refs, prefix, exclude_patterns, fn, 0, 0, cb_data);\n }\n\n+int refs_for_each_fullref_in_with_referent(struct ref_store *refs, const char *prefix,\n+\t\t\t     const char **exclude_patterns,\n+\t\t\t     each_ref_with_referent_fn fn, void *cb_data)\n+{\n+\treturn do_for_each_ref_with_referent(refs, prefix, exclude_patterns, fn, 0, 0, cb_data);\n+}\n+\n int refs_for_each_replace_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n {\n \tconst char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref;\n@@ -1627,6 +1663,13 @@ int refs_for_each_include_root_refs(struct ref_store *refs, each_ref_fn fn,\n \t\t\t       DO_FOR_EACH_INCLUDE_ROOT_REFS, cb_data);\n }\n\n+int refs_for_each_include_root_refs_with_referent(struct ref_store *refs, each_ref_with_referent_fn fn,\n+\t\t\t\t    void *cb_data)\n+{\n+\treturn do_for_each_ref_with_referent(refs, \"\", NULL, fn, 0,\n+\t\t\t       DO_FOR_EACH_INCLUDE_ROOT_REFS, cb_data);\n+}\n+\n static int qsort_strcmp(const void *va, const void *vb)\n {\n \tconst char *a = *(const char **)va;\n@@ -1716,6 +1759,37 @@ int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store,\n \treturn ret;\n }\n\n+int refs_for_each_fullref_in_prefixes_with_referent(struct ref_store *ref_store,\n+\t\t\t\t      const char *namespace,\n+\t\t\t\t      const char **patterns,\n+\t\t\t\t      const char **exclude_patterns,\n+\t\t\t\t      each_ref_with_referent_fn fn, void *cb_data)\n+{\n+\tstruct string_list prefixes = STRING_LIST_INIT_DUP;\n+\tstruct string_list_item *prefix;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint ret = 0, namespace_len;\n+\n+\tfind_longest_prefixes(&prefixes, patterns);\n+\n+\tif (namespace)\n+\t\tstrbuf_addstr(&buf, namespace);\n+\tnamespace_len = buf.len;\n+\n+\tfor_each_string_list_item(prefix, &prefixes) {\n+\t\tstrbuf_addstr(&buf, prefix->string);\n+\t\tret = refs_for_each_fullref_in_with_referent(ref_store, buf.buf,\n+\t\t\t\t\t       exclude_patterns, fn, cb_data);\n+\t\tif (ret)\n+\t\t\tbreak;\n+\t\tstrbuf_setlen(&buf, namespace_len);\n+\t}\n+\n+\tstring_list_clear(&prefixes, 0);\n+\tstrbuf_release(&buf);\n+\treturn ret;\n+}\n+\n static int refs_read_special_head(struct ref_store *ref_store,\n \t\t\t\t  const char *refname, struct object_id *oid,\n \t\t\t\t  struct strbuf *referent, unsigned int *type,\ndiff --git a/refs.h b/refs.h\nindex ba6d0e0753..d8387c6296 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -304,6 +304,9 @@ struct ref_transaction;\n typedef int each_ref_fn(const char *refname,\n \t\t\tconst struct object_id *oid, int flags, void *cb_data);\n\n+typedef int each_ref_with_referent_fn(const char *refname, const char *referent,\n+\t\t\tconst struct object_id *oid, int flags, void *cb_data);\n+\n /*\n  * The following functions invoke the specified callback function for\n  * each reference indicated.  If the function ever returns a nonzero\n@@ -315,6 +318,8 @@ typedef int each_ref_fn(const char *refname,\n  */\n int refs_head_ref(struct ref_store *refs,\n \t\t  each_ref_fn fn, void *cb_data);\n+int refs_head_ref_referent(struct ref_store *refs,\n+\t\t  each_ref_with_referent_fn fn, void *cb_data);\n int refs_for_each_ref(struct ref_store *refs,\n \t\t      each_ref_fn fn, void *cb_data);\n int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,\n@@ -351,6 +356,17 @@ int refs_for_each_fullref_in_prefixes(struct ref_store *refs,\n \t\t\t\t      const char **exclude_patterns,\n \t\t\t\t      each_ref_fn fn, void *cb_data);\n\n+int refs_for_each_fullref_in_prefixes_with_referent(struct ref_store *refs,\n+\t\t\t\t      const char *namespace,\n+\t\t\t\t      const char **patterns,\n+\t\t\t\t      const char **exclude_patterns,\n+\t\t\t\t      each_ref_with_referent_fn fn, void *cb_data);\n+\n+int refs_for_each_fullref_in_with_referent(struct ref_store *refs, const char *prefix,\n+\t\t\t     const char **exclude_patterns,\n+\t\t\t     each_ref_with_referent_fn fn, void *cb_data);\n+\n+\n /* iterates all refs that match the specified glob pattern. */\n int refs_for_each_glob_ref(struct ref_store *refs, each_ref_fn fn,\n \t\t\t   const char *pattern, void *cb_data);\n@@ -377,6 +393,10 @@ int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);\n int refs_for_each_include_root_refs(struct ref_store *refs, each_ref_fn fn,\n \t\t\t\t    void *cb_data);\n\n+int refs_for_each_include_root_refs_with_referent(struct ref_store *refs, each_ref_with_referent_fn fn,\n+\t\t\t\t    void *cb_data);\n+\n+\n /*\n  * Normalizes partial refs to their fully qualified form.\n  * Will prepend <prefix> to the <pattern> if it doesn't start with 'refs/'.\ndiff --git a/refs/iterator.c b/refs/iterator.c\nindex 26acb6bd64..26c38ec6de 100644\n--- a/refs/iterator.c\n+++ b/refs/iterator.c\n@@ -469,3 +469,30 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,\n \t\treturn -1;\n \treturn retval;\n }\n+\n+int do_for_each_ref_iterator_with_referent(struct ref_iterator *iter,\n+\t\t\t     each_ref_with_referent_fn fn, void *cb_data)\n+{\n+\tint retval = 0, ok;\n+\tstruct ref_iterator *old_ref_iter = current_ref_iter;\n+\n+\tcurrent_ref_iter = iter;\n+\twhile ((ok = ref_iterator_advance(iter)) == ITER_OK) {\n+\t\tretval = fn(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,\n+\t\t\t * we ignore that error in deference to the\n+\t\t\t * callback function's return value.\n+\t\t\t */\n+\t\t\tref_iterator_abort(iter);\n+\t\t\tgoto out;\n+\t\t}\n+\t}\n+\n+out:\n+\tcurrent_ref_iter = old_ref_iter;\n+\tif (ok == ITER_ERROR)\n+\t\treturn -1;\n+\treturn retval;\n+}\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 0775451435..ebec407468 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -486,6 +486,9 @@ extern struct ref_iterator *current_ref_iter;\n  */\n int do_for_each_ref_iterator(struct ref_iterator *iter,\n \t\t\t     each_ref_fn fn, void *cb_data);\n+int do_for_each_ref_iterator_with_referent(struct ref_iterator *iter,\n+\t\t\t     each_ref_with_referent_fn fn, void *cb_data);\n+\n\n struct ref_store;\n\n\nWhich is a little bit unseemly in my view...so between the two options I would be\ninclined to go with modifying each_repo_fn than create a whole subset set of\nhelpers. wdyt?\n\nthanks\nJohn\n\n>\n> Thanks.\n\n"},{"id":"496648","messageId":"xmqqfrtohj80.fsf@gitster.g","threadId":"61598","inReplyTo":"7FC6AD35-0DF4-4228-8708-790470D4B0EA@gmail.com","subject":"Re: [PATCH 1/4] refs: add referent parameter to refs_resolve_ref_unsafe","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-07T15:21:19Z","receivedAt":"2024-06-07T15:21:28Z","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> Yes, so for 3/4 I was exploring doing the same thing. However, each_repo_fn goes\n> pretty deep in the callstack and to provide an alternate set of functions that\n> use something like each_repo_referent_fn would still lead to a relatively large\n> blast radius, eg, something like:\n\nHmph.  You only care about teaching referent to calls to\nrefs_for_each_ref_in() and friends in apply_ref_filter()\nand nowhere else.  So for example, to preserve the current\ncalling pattern for this pair of caller/callback (and you have\ndozens more exactly like this one you touch in [3/4]):\n\n\tstatic int register_ref(const char *refname,\n                                const struct object_id *oid,\n\t\t\t\tint flags,\n\t\t\t\tvoid *cb_data);\n\n\tstatic int read_bisect_refs(void)\n\t{\n\t\treturn refs_for_each_ref_in(get_main_ref_store(the_repository),\n\t\t\t\t\t    \"refs/bisect\", register_ref, NULL);\n\t}\n\nyou could arrange your _new_ API the way you want, e.g.,\n\n\ttypedef int each_ref_with_referent_fn(const char *refname,\n\t                                      const char *referent,\n                                              const struct object_id *oid,\n\t\t\t\t\t      int flags,\n\t\t\t\t\t      void *cb_data);\n\n\tint refs_for_each_ref_with_referent_in(struct ref_store *refs,\n\t\t\t\t\t       const char *prefix,\n                                               each_ref_with_referent_fn fn,\n\t\t\t\t\t       void *cb_data);\n\nto help the single caller, i.e., apply_ref_filter(), and rebuild the\nexisting API on top as a thin wrapper, e.g.\n\n\t/* This cannot change without butchering existing callers */\n\ttypedef int each_ref_fn(const char *refname,\n                                const struct object_id *oid,\n\t\t\t\tint flags,\n\t\t\t\tvoid *cb_data);\n\n\t/* Hence we introduce an adapter */\n\tstruct each_ref_fn_adapter_cbdata {\n\t\teach_ref_fn user_each_ref_fn;\n\t\tvoid *user_cb_data;\n\t};\n\n\t/* This is designed to work as an each_ref_with_referent_fn */\n\tstatic int each_ref_adapter_fn(const char *refname,\n\t\t\t\t       const char *referent,\n                                       const struct object_id *oid,\n\t\t\t\t       int flags,\n\t\t\t\t       void *cb_data)\n\t{\n\t\tstruct each_ref_fn_adapter_cbdata *adapter_cbdata = cbdata;\n\n\t\t/* the callers have no need for referent */\n                return adapter_cbdata->user_each_ref_fn(refname, oid, flags,\n\t\t\t\t\t                adapter_cbdata->user_cbdata);\n\t}\n\n\t/*\n         * The function signature must stay the same to help existing,\n         * callers, but the implementation is now a thin wrapper.\n\t */\n\tint refs_for_each_ref_in(struct ref_store *refs,\n\t\t\t\t const char *prefix,\n                                 each_ref_fn fn,\n\t\t\t\t void *cb_data)\n        {\n\t\tstruct each_ref_fn_adapter_cbdata adapter_cbdata = {\n                \t.user_each_ref_fn = fn,\n\t\t\t.user_cb_data = cb_data,\n\t\t};\n\t\treturn refs_for_each_ref_with_referetnt_in(refs, prefix,\n\t\t\t\t\t\t\t   each_ref_adapter_fn,\n                                                           &adapter_cbdata);\n\t}\n\nno?\n\nYou'd need to pass through the new parameter \"referent\" through the\ncode paths that implement refs_for_each_ref_in() and friends to\nupdate them to refs_for_each_ref_with_referent_in() and friends no\nmatter what, but there are limited number of the top-level\nrefs_for_each_ref_in() and friends that are unaware of the\n\"referent\", and each of them would need the above ~20 lines (couting\nthe comment) adapter function that all can share the single\neach_ref_adapter_fn() callback function.\n\nOr am I missing some other intricacy in the existing for-each-* API?\n"},{"id":"496757","messageId":"ZmarVcF5JjsZx0dl@tanuki","threadId":"61598","inReplyTo":"xmqqv82mkk15.fsf@gitster.g","subject":"Re: [PATCH 1/4] refs: add referent parameter to refs_resolve_ref_unsafe","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-06-10T07:29:25Z","receivedAt":"2024-06-10T07:29:32Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Jun 06, 2024 at 11:23:18AM -0700, Junio C Hamano wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> > \"John Cai via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> >\n> >>  29 files changed, 64 insertions(+), 52 deletions(-)\n> >\n> > Wow, the blast radius of this thing is rather big.  Among these\n> > existing callers of refs_resolve_ref_unsafe(), how many of them\n> > will benefit from being able to pass a non NULL parameter at the end\n> > of the series, and more importantly, in the future to take advantage\n> > of the new feature possibly with a separate series?\n> > ...\n> > That way, you do not have to touch those existing callers that will\n> > never benefit from the new capability in the future.  You won't risk\n> > conflicting with in flight topics semantically, either.\n> \n> The same comment applies to [3/4], but I do not want to fix the Cc: header\n> again, so I am replying to this message.\n\nI wonder whether we can future-proof the code a bit more by introducing\na struct that contains everything we want to pass to the callback\nfunction. That wouldn't fix the blast radius of this patch, but would\nmean that we can easily add additional fielids to that struct in the\nfuture without having to adapt any callers at all anymore.\n\nSomething like:\n\n    struct ref_data {\n        const char *refname;\n        const struct object_id *oid;\n        const char *referent;\n        int flags;\n    };\n\nThis would also allow us to get rid of this awful `peel_iterated_oid()`\nfunction that relies on global state in many places, as we can put the\npeeled OID into that structure, as well.\n\nPatrick\n"},{"id":"496782","messageId":"xmqqy17c3c1v.fsf@gitster.g","threadId":"61598","inReplyTo":"ZmarVcF5JjsZx0dl@tanuki","subject":"Re: [PATCH 1/4] refs: add referent parameter to refs_resolve_ref_unsafe","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-10T18:09:00Z","receivedAt":"2024-06-10T18:09:06Z","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> I wonder whether we can future-proof the code a bit more by introducing\n> a struct that contains everything we want to pass to the callback\n> function.\n\nThat would hopefully make a change with a large blast a one-time\nevent.  But at the same time, it may end up making it too opaque and\nhard to verify if all the API functions are using/updating/verifying\nall the members of the struct as they should.  Compared to that,\nunused parameters are easier to verify mechanically by compilers.\n\n> This would also allow us to get rid of this awful `peel_iterated_oid()`\n> function that relies on global state in many places, as we can put the\n> peeled OID into that structure, as well.\n\nYes, such a benefit may justify a one-time \"affect many callers\"\nevent.  Or the underlying for_each_*() friend of functions can be\nupdated to use a single struct and then the current \"only selected\nparameters that are used ar passed\" API can be made into a set of\nthin wrappers around it, and then callers can be converted one step\nat a time, in a multi-step series, perhaps.\n\n"},{"id":"496854","messageId":"20240611085058.GJ3248245@coredump.intra.peff.net","threadId":"61598","inReplyTo":"011c10f488610b0a795a843bff66723477783761.1717694801.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 1/4] refs: add referent parameter to refs_resolve_ref_unsafe","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2024-06-11T08:50:58Z","receivedAt":"2024-06-11T08:51:00Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jun 06, 2024 at 05:26:37PM +0000, John Cai via GitGitGadget wrote:\n\n> From: John Cai <johncai86@gmail.com>\n> \n> refs_resolve_ref_unsafe retrieves the referent, the unresolved value of\n> a reference. Add a parameter to allow refs_resolve_ref_unsafe to pass up\n> the value of referent to the caller so it can save this value in ref\n> iterators for more efficient access.\n\nThis commit message left me with a lot of questions.\n\nFor one, it wasn't immediately obvious to me what a \"referent\" is. ;) I\nthink an example could help. If I understand, you mean that if you have\na situation like:\n\n  - refs/heads/one is a symref pointing to refs/heads/two\n  - refs/heads/two is a regular ref\n\nand we resolve \"one\", then \"two\" is the referent? And the caller might\nwant to know that?\n\nBut I think we already pass that out as the return value from\nrefs_resolve_ref_unsafe(). That is how something like \"rev-parse\n--symbolic-full-name\" works now.\n\nBut there are some subtleties. In a chain of symbolic refs (say, \"two\"\nis a symbolic ref to \"three\"), we return only the final name (\"three\").\nAnd you might want to know about \"two\".\n\nYou can pass RESOLVE_REF_NO_RECURSE to inhibit this, and get back just\n\"two\". You can see that now with \"git symbolic-ref --no-recurse\". The\ndownside is that we never look at the referent at all, so you get only\nthe symref value (and no information about the actual oid, or if the\nreferent even exists). You would still get an oid for any non-symrefs\nyou examine.\n\nSo reading between the lines, you have a caller in mind which wants to\nknow the immediate referent in addition to the final recursive oid?\n\nLooking at the rest of your series, I guess that caller is the one in\nloose_fill_ref_dir_regular_file(), so that it can get passed to the\nfor-each-ref callback. But why is it right thing for it to record and\npass along the immediate referent there, and not the final one? For that\nmatter, would a caller ever want to see the whole chain of\none/two/three?\n\n> @@ -1761,6 +1761,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    const 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\nUnless I am misunderstanding the purpose of your patch completely, this\n\"referent\" is meant to be an out-parameter, right? In which case,\nshouldn't it be \"const char **referent\"?\n\nAs the code is now:\n\n> @@ -1822,6 +1823,9 @@ 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) &&\n> +\t\t    sb_refname.len > 0)\n> +\t\t\treferent = sb_refname.buf;\n>  \n>  \t\tif (!(read_flags & REF_ISSYMREF)) {\n>  \t\t\tif (*flags & REF_BAD_NAME) {\n\n...we'd assign the local \"referent\" pointer to our refname buf, but\nthe caller would never see that. Plus doing so would not help you\nanyway, since sb_refname will be used again as we recurse. So at best,\nyou end up with the final name in the chain anyway. Or at worst,\nsb_refname gets reallocated and \"referent\" is left as a dangling\npointer.\n\n-Peff\n"},{"id":"496855","messageId":"20240611090156.GK3248245@coredump.intra.peff.net","threadId":"61598","inReplyTo":"7202ada0542b6f014647785945094a13c9d885c7.1717694802.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 2/4] refs: keep track of unresolved reference value in iterators","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2024-06-11T09:01:56Z","receivedAt":"2024-06-11T09:01:57Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jun 06, 2024 at 05:26:38PM +0000, John Cai via GitGitGadget wrote:\n\n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index bf2ffe062ea..a963d796a29 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> @@ -243,8 +243,9 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n>  {\n>  \tstruct object_id oid;\n>  \tint flag;\n> +\tconst char* referent = NULL;\n>  \n> -\tif (!refs_resolve_ref_unsafe(&refs->base, refname, NULL, 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\nHere we pass in NULL, so the code in refs_resolve_ref_unsafe() won't do\nanything. And our copy of \"referent\" here will remain NULL, so the rest\nof this patch also does nothing. Again, I think that the function should\ntake a \"char **\", and you'd pass in &referent here?\n\nThough if we are OK with surfacing just the final value in a\nmulti-element chain, then you could just use the existing return value,\nlike:\n\n  referent = refs_resolve_ref_unsafe(&refs->base, refname,\n\t\t\t\t     RESOLVE_REF_REAEDING, &oid, &flags);\n  if (!referent) {\n          oidclr(&oid);\n          flag |= REF_ISBROKEN;\n  }\n\nand then later pass \"referent\" to create_ref_entry() if flags contains\nREF_ISSYMREF (or since we pass it the flags, it could do that check\nitself).\n\n-Peff\n"},{"id":"497814","messageId":"f6abd59c-ea86-423f-9172-79779932418b@app.fastmail.com","threadId":"61598","inReplyTo":"xmqqed99k7xo.fsf@gitster.g","subject":"Re: [PATCH 1/4] refs: add referent parameter to refs_resolve_ref_unsafe","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2024-06-28T15:30:08Z","receivedAt":"2024-06-28T15:31:38Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"On Fri, Jun 7, 2024, at 00:44, Junio C Hamano wrote:\n> I found the previous occurrences of the same problem:\n>\n>   https://lore.kernel.org/git/xmqqjzm3qumx.fsf@gitster.g/\n>   https://lore.kernel.org/git/xmqqh6hkxox6.fsf@gitster.g/\n>\n> The last message in the thread\n>\n>\n> https://lore.kernel.org/git/CAMbn=B7J4ODf9ybJQpL1bZZ7qdWSDGaLEyTmVv+ZBiSeC9T+yw@mail.gmail.com/\n>\n> says that the original user of GGG found what was wrong in the way\n> the user was using GGG to send and fixed it, but unfortunately we\n> didn't hear exactly *what* the breakage was and *how* it was fixed.\n>\n> Aryan, do you remember what the problem was and more importantly\n> what the fix was?\n>\n> Thanks.\n\nYes, Aryan didn’t explain what the issue was. But Linus Arver (+CC) in\nthat first thread/link figured out what was happening in his case:[1]\n\n> I realize now that it's because I copy/pasted the \"Cc: ...\" lines in the PR\n> description from\n> https://github.com/gitgitgadget/git/pull/1632#issue-2068188239, such\n> that when I pasted those in for the PR description for this series at\n> https://github.com/gitgitgadget/git/pull/1694#issue-2187804953, it\n> carried over the email addresses as Markdown-formatted hyperlinks.\n> Currently it reads\n>\n>    Cc: Christian Couder [chriscool@tuxfamily.org](mailto:chriscool@tuxfamily.org)\n>    Cc: Junio C Hamano [gitster@pobox.com](mailto:gitster@pobox.com)\n>    Cc: Emily Shaffer [nasamuffin@google.com](mailto:nasamuffin@google.com)\n>    cc: Josh Steadmon [steadmon@google.com](mailto:steadmon@google.com)\n>    cc: Randall S. Becker [rsbecker@nexbridge.com](mailto:rsbecker@nexbridge.com)\n>    cc: Christian Couder [christian.couder@gmail.com](mailto:christian.couder@gmail.com)\n>    cc: \"Kristoffer Haugsbakk\" [code@khaugsbakk.name](mailto:code@khaugsbakk.name)\n>    cc: \"Kristoffer Haugsbakk\" <code@khaugsbakk.name>\n>\n> when I click on \"edit\", where the last line must be from your manual\n> fix which GGG picked up. I've cleaned up the PR description manually\n> now, and for this message I'm also attempting to clean up those square\n> brackets.\n\nWhen I read that I assumed that Aryan had made the same mistake.\n\n🔗 1: https://lore.kernel.org/git/owly4jd741ph.fsf@fine.c.googlers.com/\n\n-- \nKristoffer Haugsbakk\n\n"},{"id":"497828","messageId":"xmqqle2o26jy.fsf@gitster.g","threadId":"61598","inReplyTo":"f6abd59c-ea86-423f-9172-79779932418b@app.fastmail.com","subject":"Re: [PATCH 1/4] refs: add referent parameter to refs_resolve_ref_unsafe","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-28T19:47:29Z","receivedAt":"2024-06-28T19:47:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Kristoffer Haugsbakk\" <code@khaugsbakk.name> writes:\n\n>> ...\n>> https://github.com/gitgitgadget/git/pull/1694#issue-2187804953, it\n>> carried over the email addresses as Markdown-formatted hyperlinks.\n>> Currently it reads\n>>\n>>    Cc: Christian Couder [chriscool@tuxfamily.org](mailto:chriscool@tuxfamily.org)\n>>...\n>>    cc: \"Kristoffer Haugsbakk\" <code@khaugsbakk.name>\n>>\n>> when I click on \"edit\", where the last line must be from your manual\n>> fix which GGG picked up. I've cleaned up the PR description manually\n>> now, and for this message I'm also attempting to clean up those square\n>> brackets.\n>\n> When I read that I assumed that Aryan had made the same mistake.\n\nAh, I see.\n\nI wonder if there are things we can do to help reduce the pain in\nfuture users of GGG here.\n\n - Is there some documentation update we can make on our end,\n   perhaps in Documentation/MyFirstContribution (SubmittingPatches\n   does not talk about GGG other than refering readers to\n   MyFirstContribution)?\n\n - Or the welcome message GGG adds to the pull-request that begins\n   with \"Thanks for taking the time to contribute\" can mention\n   something about this?\n\n - Or possibly the handling of Cc: addresses done by GGG can be\n   tweaked not to cause this?\n\nAnything else?\n\nThanks.\n"},{"id":"497853","messageId":"CAMo6p=FMhw2TZ4MdowMjQM0wgb90Lkqa17AENsxNqj-eJk_5WQ@mail.gmail.com","threadId":"61598","inReplyTo":"xmqqle2o26jy.fsf@gitster.g","subject":"Re: [PATCH 1/4] refs: add referent parameter to refs_resolve_ref_unsafe","fromName":"Linus Arver","fromEmail":"linusarver@gmail.com","sentAt":"2024-06-30T10:12:28Z","receivedAt":"2024-06-30T10:12:29Z","isPatch":true,"sender":{"key":"linusarver@gmail.com","avatar":"https://avatars.githubusercontent.com/u/725613?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> \"Kristoffer Haugsbakk\" <code@khaugsbakk.name> writes:\n>\n>>> ...\n>>> https://github.com/gitgitgadget/git/pull/1694#issue-2187804953, it\n>>> carried over the email addresses as Markdown-formatted hyperlinks.\n>>> Currently it reads\n>>>\n>>>    Cc: Christian Couder [chriscool@tuxfamily.org](mailto:chriscool@tuxfamily.org)\n>>>...\n>>>    cc: \"Kristoffer Haugsbakk\" <code@khaugsbakk.name>\n>>>\n>>> when I click on \"edit\", where the last line must be from your manual\n>>> fix which GGG picked up. I've cleaned up the PR description manually\n>>> now, and for this message I'm also attempting to clean up those square\n>>> brackets.\n>>\n>> When I read that I assumed that Aryan had made the same mistake.\n>\n> Ah, I see.\n>\n> I wonder if there are things we can do to help reduce the pain in\n> future users of GGG here.\n>\n>  [...]\n>\n>  - Or the welcome message GGG adds to the pull-request that begins\n>    with \"Thanks for taking the time to contribute\" can mention\n>    something about this?\n\nI've created https://github.com/gitgitgadget/gitgitgadget/pull/1644 to\naddress this for now.\n>\n>  - Or possibly the handling of Cc: addresses done by GGG can be\n>    tweaked not to cause this?\n\nI wanted to do this first but was deterred by my lack of familiarity\nwith TypeScript.\n"},{"id":"497859","messageId":"xmqqv81qz41s.fsf@gitster.g","threadId":"61598","inReplyTo":"CAMo6p=FMhw2TZ4MdowMjQM0wgb90Lkqa17AENsxNqj-eJk_5WQ@mail.gmail.com","subject":"Re: [PATCH 1/4] refs: add referent parameter to refs_resolve_ref_unsafe","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-30T18:19:43Z","receivedAt":"2024-06-30T18:19:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Arver <linusarver@gmail.com> writes:\n\n>>  - Or the welcome message GGG adds to the pull-request that begins\n>>    with \"Thanks for taking the time to contribute\" can mention\n>>    something about this?\n>\n> I've created https://github.com/gitgitgadget/gitgitgadget/pull/1644 to\n> address this for now.\n\nThanks.\n"},{"id":"499611","messageId":"45124B2F-BDE0-404A-BA85-54361F5877DF@gmail.com","threadId":"61598","inReplyTo":"20240611085058.GJ3248245@coredump.intra.peff.net","subject":"Re: [PATCH 1/4] refs: add referent parameter to refs_resolve_ref_unsafe","fromName":"John Cai","fromEmail":"johncai86@gmail.com","sentAt":"2024-07-30T14:38:52Z","receivedAt":"2024-07-30T14:38:54Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"Hey Peff,\n\nOn 11 Jun 2024, at 4:50, Jeff King wrote:\n\n> On Thu, Jun 06, 2024 at 05:26:37PM +0000, John Cai via GitGitGadget wrote:\n>\n>> From: John Cai <johncai86@gmail.com>\n>>\n>> refs_resolve_ref_unsafe retrieves the referent, the unresolved value of\n>> a reference. Add a parameter to allow refs_resolve_ref_unsafe to pass up\n>> the value of referent to the caller so it can save this value in ref\n>> iterators for more efficient access.\n>\n> This commit message left me with a lot of questions.\n>\n> For one, it wasn't immediately obvious to me what a \"referent\" is. ;) I\n> think an example could help. If I understand, you mean that if you have\n> a situation like:\n>\n>   - refs/heads/one is a symref pointing to refs/heads/two\n>   - refs/heads/two is a regular ref\n>\n> and we resolve \"one\", then \"two\" is the referent? And the caller might\n> want to know that?\n>\n> But I think we already pass that out as the return value from\n> refs_resolve_ref_unsafe(). That is how something like \"rev-parse\n> --symbolic-full-name\" works now.\n\nYes, exactly. I think you're right that it'd be preferable to just use the\noutput of refs_resolve_ref_unsafe() to get the value of the referent.\n>\n> But there are some subtleties. In a chain of symbolic refs (say, \"two\"\n> is a symbolic ref to \"three\"), we return only the final name (\"three\").\n> And you might want to know about \"two\".\n>\n> You can pass RESOLVE_REF_NO_RECURSE to inhibit this, and get back just\n> \"two\". You can see that now with \"git symbolic-ref --no-recurse\". The\n> downside is that we never look at the referent at all, so you get only\n> the symref value (and no information about the actual oid, or if the\n> referent even exists). You would still get an oid for any non-symrefs\n> you examine.\n>\n> So reading between the lines, you have a caller in mind which wants to\n> know the immediate referent in addition to the final recursive oid?\n\nThe goal is to keep track of the value that %(symref) would need in the\niterator so that a separate call doesn't need to be made.\n\n>\n> Looking at the rest of your series, I guess that caller is the one in\n> loose_fill_ref_dir_regular_file(), so that it can get passed to the\n> for-each-ref callback. But why is it right thing for it to record and\n> pass along the immediate referent there, and not the final one? For that\n> matter, would a caller ever want to see the whole chain of\n> one/two/three?\n\nRight, the final referent is the right one to pass down.\n\n>\n>> @@ -1761,6 +1761,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    const 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> Unless I am misunderstanding the purpose of your patch completely, this\n> \"referent\" is meant to be an out-parameter, right? In which case,\n> shouldn't it be \"const char **referent\"?\n>\n> As the code is now:\n>\n>> @@ -1822,6 +1823,9 @@ 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) &&\n>> +\t\t    sb_refname.len > 0)\n>> +\t\t\treferent = sb_refname.buf;\n>>\n>>  \t\tif (!(read_flags & REF_ISSYMREF)) {\n>>  \t\t\tif (*flags & REF_BAD_NAME) {\n>\n> ...we'd assign the local \"referent\" pointer to our refname buf, but\n> the caller would never see that. Plus doing so would not help you\n> anyway, since sb_refname will be used again as we recurse. So at best,\n> you end up with the final name in the chain anyway. Or at worst,\n> sb_refname gets reallocated and \"referent\" is left as a dangling\n> pointer.\n\nGoing to include changes to remove the out-parameter which will simplify things\nquite a bit.\n\n>\n> -Peff\n\nthanks for the review!\nJohn\n\n"},{"id":"499852","messageId":"pull.1712.v2.git.git.1722524334.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":"pull.1712.git.git.1717694800.gitgitgadget@gmail.com","subject":"[PATCH v2 0/3] keep track of unresolved value of symbolic-ref in ref iterators","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-08-01T14:58:51Z","receivedAt":"2024-08-01T14:59:01Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"For reftable development, it's useful to be able to print out the direct\nvalue of a symbolic reference before resolution. This is currently possible\nwith git-for-each-ref, but since the iterators do not keep track of the\nvalue of the symbolic ref, a separate call needs to be made each time making\nit inefficient.\n\nAddress this inefficiency by keeping track of the value of the symbolic\nreference in the ref iterator. This patch series also ends with a commit to\nuse this value in the iterator through callbacks in ref-filter.c.\n\nThis series started with [1] but I decided to send a separate patch series\nsince it is substantially different.\n\nBenchmarking shows that with these changes, we experience a speedup in\ngit-for-each-ref(1) on a repository with many symbolic refs:\n\n$ hyperfine --warmup 5 \"git for-each-ref --format='%(refname) %(objectname)\n%(symref)'\" \"~/Projects/git/git for-each-ref --format='%(refname)\n%(objectname) %(symref)'\" Benchmark 1: git for-each-ref --format='%(refname)\n%(objectname) %(symref)' Time (mean ± σ): 905.1 ms ± 13.2 ms [User: 56.3 ms,\nSystem: 628.6 ms] Range (min … max): 893.4 ms … 936.9 ms 10 runs\n\nBenchmark 2: ~/Projects/git/git for-each-ref --format='%(refname)\n%(objectname) %(symref)' Time (mean ± σ): 482.2 ms ± 26.4 ms [User: 34.7 ms,\nSystem: 410.6 ms] Range (min … max): 459.4 ms … 541.8 ms 10 runs\n\nSummary ~/Projects/git/git for-each-ref --format='%(refname) %(objectname)\n%(symref)' ran 1.88 ± 0.11 times faster than git for-each-ref\n--format='%(refname) %(objectname) %(symref)'\n\nChanges since V1:\n\n * Use the return value from refs_resolve_ref_unsafe instead of using an out\n   parameter\n\n 1. https://lore.kernel.org/git/pull.1684.git.git.1709592718743.gitgitgadget@gmail.com/\n\nJohn Cai (3):\n  refs: keep track of unresolved reference value in iterators\n  refs: add referent to each_ref_fn\n  ref-filter: populate symref from iterator\n\n bisect.c                    |  3 ++-\n builtin/bisect.c            |  4 +++-\n builtin/checkout.c          |  2 +-\n builtin/describe.c          |  2 +-\n builtin/fetch.c             |  3 ++-\n builtin/fsck.c              |  4 ++--\n builtin/gc.c                |  1 +\n builtin/name-rev.c          |  2 +-\n builtin/pack-objects.c      |  5 +++--\n builtin/receive-pack.c      |  2 +-\n builtin/remote.c            |  4 +++-\n builtin/repack.c            |  1 +\n builtin/replace.c           |  1 +\n builtin/rev-parse.c         |  4 ++--\n builtin/show-branch.c       |  8 ++++----\n builtin/show-ref.c          |  3 ++-\n builtin/submodule--helper.c |  1 +\n builtin/worktree.c          |  1 +\n commit-graph.c              |  1 +\n delta-islands.c             |  2 +-\n fetch-pack.c                |  2 ++\n help.c                      |  2 +-\n http-backend.c              |  4 ++--\n log-tree.c                  |  2 +-\n ls-refs.c                   |  4 ++--\n midx-write.c                |  2 +-\n negotiator/default.c        |  2 +-\n negotiator/skipping.c       |  2 +-\n notes.c                     |  2 +-\n object-name.c               |  2 +-\n pseudo-merge.c              |  1 +\n reachable.c                 |  2 +-\n ref-filter.c                | 12 +++++++-----\n reflog.c                    |  1 +\n refs.c                      | 14 +++++++-------\n refs.h                      |  2 +-\n refs/files-backend.c        | 16 +++++++++++-----\n refs/iterator.c             |  5 ++++-\n refs/ref-cache.c            |  8 ++++++++\n refs/ref-cache.h            |  2 ++\n refs/refs-internal.h        |  1 +\n refs/reftable-backend.c     |  8 ++++++--\n remote.c                    |  4 ++--\n replace-object.c            |  1 +\n revision.c                  |  2 +-\n server-info.c               |  2 +-\n shallow.c                   |  2 ++\n submodule.c                 |  2 ++\n t/helper/test-ref-store.c   |  2 +-\n upload-pack.c               |  8 ++++----\n walker.c                    |  1 +\n worktree.c                  |  2 +-\n 52 files changed, 113 insertions(+), 63 deletions(-)\n\n\nbase-commit: 557ae147e6cdc9db121269b058c757ac5092f9c9\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1712%2Fjohn-cai%2Fjc%2Fsymbolic-ref-in-iterator-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1712/john-cai/jc/symbolic-ref-in-iterator-v2\nPull-Request: https://github.com/git/git/pull/1712\n\nRange-diff vs v1:\n\n 1:  011c10f4886 < -:  ----------- refs: add referent parameter to refs_resolve_ref_unsafe\n 2:  7202ada0542 ! 1:  ac0957c9e6a refs: keep track of unresolved reference value in iterators\n     @@ refs/files-backend.c: static void loose_fill_ref_dir_regular_file(struct files_r\n       {\n       \tstruct object_id oid;\n       \tint flag;\n     -+\tconst char* referent = NULL;\n     - \n     --\tif (!refs_resolve_ref_unsafe(&refs->base, refname, NULL, 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     +-\n     +-\tif (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,\n     +-\t\t\t\t     &oid, &flag)) {\n     ++\tconst char* referent = refs_resolve_ref_unsafe(&refs->base,\n     ++\t\t\t\t\t\t       refname,\n     ++\t\t\t\t\t\t       RESOLVE_REF_READING,\n     ++\t\t\t\t\t\t       &oid, &flag);\n     ++\tif (!referent) {\n     + \t\toidclr(&oid, the_repository->hash_algo);\n       \t\tflag |= REF_ISBROKEN;\n     + \t} else if (is_null_oid(&oid)) {\n      @@ refs/files-backend.c: static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n     - \t\toidclr(&oid);\n     + \t\toidclr(&oid, the_repository->hash_algo);\n       \t\tflag |= REF_BAD_NAME | REF_ISBROKEN;\n       \t}\n      -\tadd_entry_to_dir(dir, create_ref_entry(refname, &oid, flag));\n     ++\n      +\tadd_entry_to_dir(dir, create_ref_entry(refname, referent, &oid, flag));\n       }\n       \n     @@ refs/ref-cache.c: struct ref_dir *get_ref_dir(struct ref_entry *entry)\n       \t\t\t\t   const struct object_id *oid, int flag)\n       {\n       \tstruct ref_entry *ref;\n     - \n     +@@ refs/ref-cache.c: struct ref_entry *create_ref_entry(const char *refname,\n       \tFLEX_ALLOC_STR(ref, name, refname);\n       \toidcpy(&ref->u.value.oid, oid);\n     -+\tif (referent)\n     -+\t\tref->u.value.referent = xstrdup(referent);\n     -+\n       \tref->flag = flag;\n     ++\n     ++\tif (flag & REF_ISSYMREF)\n     ++\t\tref->u.value.referent = xstrdup_or_null(referent);\n     ++\n       \treturn ref;\n       }\n     + \n     +@@ refs/ref-cache.c: static void free_ref_entry(struct ref_entry *entry)\n     + \t\t */\n     + \t\tclear_ref_dir(&entry->u.subdir);\n     + \t}\n     ++\tfree(entry->u.value.referent);\n     + \tfree(entry);\n     + }\n     + \n      @@ refs/ref-cache.c: static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)\n       \t\t\tlevel->index = -1;\n       \t\t} else {\n     @@ refs/ref-cache.h: 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     ++\tchar *referent;\n       };\n       \n       /*\n     @@ refs/refs-internal.h: enum do_for_each_ref_flags {\n      \n       ## refs/reftable-backend.c ##\n      @@ refs/reftable-backend.c: 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\t\tthe_repository->hash_algo);\n       \t\t\tbreak;\n       \t\tcase REFTABLE_REF_SYMREF:\n     --\t\t\tif (!refs_resolve_ref_unsafe(&iter->refs->base, iter->ref.refname, NULL,\n     -+\t\t\tif (!refs_resolve_ref_unsafe(&iter->refs->base, iter->ref.refname, iter->base.referent,\n     - \t\t\t\t\t\t     RESOLVE_REF_READING, &iter->oid, &flags))\n     - \t\t\t\toidclr(&iter->oid);\n     +-\t\t\tif (!refs_resolve_ref_unsafe(&iter->refs->base, iter->ref.refname,\n     +-\t\t\t\t\t\t     RESOLVE_REF_READING, &iter->oid, &flags))\n     ++\t\t\titer->base.referent = refs_resolve_ref_unsafe(&iter->refs->base,\n     ++\t\t\t\t\t\t\t\t      iter->ref.refname,\n     ++\t\t\t\t\t\t\t\t      RESOLVE_REF_READING,\n     ++\t\t\t\t\t\t\t\t      &iter->oid,\n     ++\t\t\t\t\t\t\t\t      &flags);\n     ++\t\t\tif (!iter->base.referent)\n     + \t\t\t\toidclr(&iter->oid, the_repository->hash_algo);\n       \t\t\tbreak;\n     + \t\tdefault:\n 3:  33914f84a64 ! 2:  1f3a604fae7 refs: add referent to each_ref_fn\n     @@ object-name.c: struct handle_one_ref_cb {\n       \t\t\t  void *cb_data)\n       {\n      \n     + ## pseudo-merge.c ##\n     +@@ pseudo-merge.c: void load_pseudo_merges_from_config(struct string_list *list)\n     + }\n     + \n     + static int find_pseudo_merge_group_for_ref(const char *refname,\n     ++\t\t\t\t\t   const char *referent UNUSED,\n     + \t\t\t\t\t   const struct object_id *oid,\n     + \t\t\t\t\t   int flags UNUSED,\n     + \t\t\t\t\t   void *_data)\n     +\n       ## reachable.c ##\n      @@ reachable.c: static void add_rebase_files(struct rev_info *revs)\n       \tfree_worktrees(worktrees);\n     @@ refs.c: int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, voi\n       \treturn ret;\n      @@ refs.c: int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n       \n     - \tif (refs_resolve_ref_unsafe(refs, \"HEAD\", NULL, RESOLVE_REF_READING,\n     + \tif (refs_resolve_ref_unsafe(refs, \"HEAD\", RESOLVE_REF_READING,\n       \t\t\t\t    &oid, &flag))\n      -\t\treturn fn(\"HEAD\", &oid, flag, cb_data);\n      +\t\treturn fn(\"HEAD\", NULL, &oid, flag, cb_data);\n     @@ refs.c: struct do_for_each_reflog_help {\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)\n     +@@ refs.c: struct migration_data {\n     + \tstruct strbuf *errbuf;\n     + };\n     + \n     +-static int migrate_one_ref(const char *refname, const struct object_id *oid,\n     ++static int migrate_one_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n     + \t\t\t   int flags, void *cb_data)\n     + {\n     + \tstruct migration_data *data = cb_data;\n      \n       ## refs.h ##\n      @@ refs.h: struct ref_transaction;\n     @@ walker.c: static int interpret_target(struct walker *walker, char *target, struc\n      \n       ## worktree.c ##\n      @@ worktree.c: int other_head_refs(each_ref_fn fn, void *cb_data)\n     - \t\t\t\t\t    NULL,\n     + \t\t\t\t\t    refname.buf,\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 4:  6d5b1b62e6c ! 3:  3e147e7d850 ref-filter: populate symref from iterator\n     @@ Metadata\n       ## Commit message ##\n          ref-filter: populate symref from iterator\n      \n     -    With a previous commit, the reference the symbolic ref points is saved\n     +    With a previous commit, the reference the symbolic ref points to is saved\n          in the ref iterator records. Instead of making a separate call to\n          resolve_refdup() each time, we can just populate the ref_array_item with\n          the value from the iterator.\n     @@ ref-filter.c: static struct ref_array_item *apply_ref_filter(const char *refname\n       \tref->commit = commit;\n       \tref->flag = flag;\n       \tref->kind = kind;\n     -+\tref->symref = referent;\n     ++\tif (flag & REF_ISSYMREF)\n     ++\t\tref->symref = xstrdup_or_null(referent);\n       \n       \treturn ref;\n       }\n\n-- \ngitgitgadget\n"},{"id":"499853","messageId":"ac0957c9e6abdc2597900573703461833e9c9d69.1722524334.git.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":"pull.1712.v2.git.git.1722524334.gitgitgadget@gmail.com","subject":"[PATCH v2 1/3] refs: keep track of unresolved reference value in iterators","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-08-01T14:58:52Z","receivedAt":"2024-08-01T14:59:02Z","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 iterators for both the\nfiles backend and the reftable backend.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n refs/files-backend.c    | 14 ++++++++++----\n refs/iterator.c         |  3 +++\n refs/ref-cache.c        |  8 ++++++++\n refs/ref-cache.h        |  2 ++\n refs/refs-internal.h    |  1 +\n refs/reftable-backend.c |  8 ++++++--\n 6 files changed, 30 insertions(+), 6 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex aa52d9be7c7..7640318cba8 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -245,9 +245,11 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n {\n \tstruct object_id oid;\n \tint flag;\n-\n-\tif (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,\n-\t\t\t\t     &oid, &flag)) {\n+\tconst char* referent = refs_resolve_ref_unsafe(&refs->base,\n+\t\t\t\t\t\t       refname,\n+\t\t\t\t\t\t       RESOLVE_REF_READING,\n+\t\t\t\t\t\t       &oid, &flag);\n+\tif (!referent) {\n \t\toidclr(&oid, the_repository->hash_algo);\n \t\tflag |= REF_ISBROKEN;\n \t} else if (is_null_oid(&oid)) {\n@@ -268,7 +270,8 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n \t\toidclr(&oid, the_repository->hash_algo);\n \t\tflag |= REF_BAD_NAME | REF_ISBROKEN;\n \t}\n-\tadd_entry_to_dir(dir, create_ref_entry(refname, &oid, flag));\n+\n+\tadd_entry_to_dir(dir, create_ref_entry(refname, referent, &oid, flag));\n }\n \n /*\n@@ -886,6 +889,9 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \t\titer->base.refname = iter->iter0->refname;\n \t\titer->base.oid = iter->iter0->oid;\n \t\titer->base.flags = iter->iter0->flags;\n+\t\tif (iter->iter0->flags & REF_ISSYMREF)\n+\t\t\titer->base.referent = iter->iter0->referent;\n+\n \t\treturn ITER_OK;\n \t}\n \ndiff --git a/refs/iterator.c b/refs/iterator.c\nindex d355ebf0d59..26acb6bd640 100644\n--- a/refs/iterator.c\n+++ b/refs/iterator.c\n@@ -7,6 +7,7 @@\n #include \"refs.h\"\n #include \"refs/refs-internal.h\"\n #include \"iterator.h\"\n+#include \"strbuf.h\"\n \n int ref_iterator_advance(struct ref_iterator *ref_iterator)\n {\n@@ -29,6 +30,7 @@ void base_ref_iterator_init(struct ref_iterator *iter,\n {\n \titer->vtable = vtable;\n \titer->refname = NULL;\n+\titer->referent = NULL;\n \titer->oid = NULL;\n \titer->flags = 0;\n }\n@@ -199,6 +201,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 4ce519bbc85..da946d476cc 100644\n--- a/refs/ref-cache.c\n+++ b/refs/ref-cache.c\n@@ -5,6 +5,7 @@\n #include \"refs-internal.h\"\n #include \"ref-cache.h\"\n #include \"../iterator.h\"\n+#include \"../strbuf.h\"\n \n void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry)\n {\n@@ -34,6 +35,7 @@ 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@@ -41,6 +43,10 @@ struct ref_entry *create_ref_entry(const char *refname,\n \tFLEX_ALLOC_STR(ref, name, refname);\n \toidcpy(&ref->u.value.oid, oid);\n \tref->flag = flag;\n+\n+\tif (flag & REF_ISSYMREF)\n+\t\tref->u.value.referent = xstrdup_or_null(referent);\n+\n \treturn ref;\n }\n \n@@ -66,6 +72,7 @@ static void free_ref_entry(struct ref_entry *entry)\n \t\t */\n \t\tclear_ref_dir(&entry->u.subdir);\n \t}\n+\tfree(entry->u.value.referent);\n \tfree(entry);\n }\n \n@@ -431,6 +438,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \t\t\tlevel->index = -1;\n \t\t} else {\n \t\t\titer->base.refname = entry->name;\n+\t\t\titer->base.referent = entry->u.value.referent;\n \t\t\titer->base.oid = &entry->u.value.oid;\n \t\t\titer->base.flags = entry->flag;\n \t\t\treturn ITER_OK;\ndiff --git a/refs/ref-cache.h b/refs/ref-cache.h\nindex 31ebe24f6cf..5f04e518c37 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+\tchar *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 fa975d69aaa..117ec233848 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -299,6 +299,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 fbe74c239d3..9f724c3d632 100644\n--- a/refs/reftable-backend.c\n+++ b/refs/reftable-backend.c\n@@ -494,8 +494,12 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \t\t\t\tthe_repository->hash_algo);\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\t\t\t\t     RESOLVE_REF_READING, &iter->oid, &flags))\n+\t\t\titer->base.referent = refs_resolve_ref_unsafe(&iter->refs->base,\n+\t\t\t\t\t\t\t\t      iter->ref.refname,\n+\t\t\t\t\t\t\t\t      RESOLVE_REF_READING,\n+\t\t\t\t\t\t\t\t      &iter->oid,\n+\t\t\t\t\t\t\t\t      &flags);\n+\t\t\tif (!iter->base.referent)\n \t\t\t\toidclr(&iter->oid, the_repository->hash_algo);\n \t\t\tbreak;\n \t\tdefault:\n-- \ngitgitgadget\n\n"},{"id":"499854","messageId":"3e147e7d850773f44b48d1b86e89aef1415a0ccd.1722524334.git.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":"pull.1712.v2.git.git.1722524334.gitgitgadget@gmail.com","subject":"[PATCH v2 3/3] ref-filter: populate symref from iterator","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-08-01T14:58:54Z","receivedAt":"2024-08-01T14:59:04Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"From: John Cai <johncai86@gmail.com>\n\nWith a previous commit, the reference the symbolic ref points to is saved\nin the ref iterator records. Instead of making a separate call to\nresolve_refdup() each time, we can just populate the ref_array_item with\nthe value from the iterator.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n ref-filter.c | 12 +++++++-----\n 1 file changed, 7 insertions(+), 5 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 39997890feb..08997e59662 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -2783,7 +2783,7 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)\n \treturn ref_kind_from_refname(refname);\n }\n \n-static struct ref_array_item *apply_ref_filter(const char *refname, const struct object_id *oid,\n+static struct ref_array_item *apply_ref_filter(const char *refname, const char *referent, const struct object_id *oid,\n \t\t\t    int flag, struct ref_filter *filter)\n {\n \tstruct ref_array_item *ref;\n@@ -2852,6 +2852,8 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const struct\n \tref->commit = commit;\n \tref->flag = flag;\n \tref->kind = kind;\n+\tif (flag & REF_ISSYMREF)\n+\t\tref->symref = xstrdup_or_null(referent);\n \n \treturn ref;\n }\n@@ -2865,12 +2867,12 @@ struct ref_filter_cbdata {\n  * A call-back given to for_each_ref().  Filter refs and keep them for\n  * later object processing.\n  */\n-static int filter_one(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\n \n-\tref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);\n+\tref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);\n \tif (ref)\n \t\tref_array_append(ref_cbdata->array, ref);\n \n@@ -2900,13 +2902,13 @@ struct ref_filter_and_format_cbdata {\n \t} internal;\n };\n \n-static int filter_and_format_one(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_and_format_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_and_format_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\n \tstruct strbuf output = STRBUF_INIT, err = STRBUF_INIT;\n \n-\tref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);\n+\tref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);\n \tif (!ref)\n \t\treturn 0;\n \n-- \ngitgitgadget\n"},{"id":"499855","messageId":"1f3a604fae7b0fdc70ff060c423002577a63277b.1722524334.git.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":"pull.1712.v2.git.git.1722524334.gitgitgadget@gmail.com","subject":"[PATCH v2 2/3] refs: add referent to each_ref_fn","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-08-01T14:58:53Z","receivedAt":"2024-08-01T14:59:04Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"From: John Cai <johncai86@gmail.com>\n\nAdd a parameter to each_ref_fn so that callers to the ref APIs\nthat use this function as a callback can have acess to the\nunresolved value of a symbolic ref.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n bisect.c                    |  3 ++-\n builtin/bisect.c            |  4 +++-\n builtin/checkout.c          |  2 +-\n builtin/describe.c          |  2 +-\n builtin/fetch.c             |  3 ++-\n builtin/fsck.c              |  4 ++--\n builtin/gc.c                |  1 +\n builtin/name-rev.c          |  2 +-\n builtin/pack-objects.c      |  5 +++--\n builtin/receive-pack.c      |  2 +-\n builtin/remote.c            |  4 +++-\n builtin/repack.c            |  1 +\n builtin/replace.c           |  1 +\n builtin/rev-parse.c         |  4 ++--\n builtin/show-branch.c       |  8 ++++----\n builtin/show-ref.c          |  3 ++-\n builtin/submodule--helper.c |  1 +\n builtin/worktree.c          |  1 +\n commit-graph.c              |  1 +\n delta-islands.c             |  2 +-\n fetch-pack.c                |  2 ++\n help.c                      |  2 +-\n http-backend.c              |  4 ++--\n log-tree.c                  |  2 +-\n ls-refs.c                   |  4 ++--\n midx-write.c                |  2 +-\n negotiator/default.c        |  2 +-\n negotiator/skipping.c       |  2 +-\n notes.c                     |  2 +-\n object-name.c               |  2 +-\n pseudo-merge.c              |  1 +\n reachable.c                 |  2 +-\n ref-filter.c                |  4 ++--\n reflog.c                    |  1 +\n refs.c                      | 14 +++++++-------\n refs.h                      |  2 +-\n refs/files-backend.c        |  2 +-\n refs/iterator.c             |  2 +-\n remote.c                    |  4 ++--\n replace-object.c            |  1 +\n revision.c                  |  2 +-\n server-info.c               |  2 +-\n shallow.c                   |  2 ++\n submodule.c                 |  2 ++\n t/helper/test-ref-store.c   |  2 +-\n upload-pack.c               |  8 ++++----\n walker.c                    |  1 +\n worktree.c                  |  2 +-\n 48 files changed, 78 insertions(+), 54 deletions(-)\n\ndiff --git a/bisect.c b/bisect.c\nindex 135f94ba097..4406fc44cf9 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -448,7 +448,7 @@ void find_bisection(struct commit_list **commit_list, int *reaches,\n \tclear_commit_weight(&commit_weight);\n }\n \n-static int register_ref(const char *refname, const struct object_id *oid,\n+static int register_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\tint flags UNUSED, void *cb_data UNUSED)\n {\n \tstruct strbuf good_prefix = STRBUF_INIT;\n@@ -1170,6 +1170,7 @@ int estimate_bisect_steps(int all)\n }\n \n static int mark_for_removal(const char *refname,\n+\t\t\t    const char *referent UNUSED,\n \t\t\t    const struct object_id *oid UNUSED,\n \t\t\t    int flag UNUSED, void *cb_data)\n {\ndiff --git a/builtin/bisect.c b/builtin/bisect.c\nindex dabce9b542b..453a6cccd70 100644\n--- a/builtin/bisect.c\n+++ b/builtin/bisect.c\n@@ -356,6 +356,7 @@ static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)\n }\n \n static int inc_nr(const char *refname UNUSED,\n+\t\t  const char *referent UNUSED,\n \t\t  const struct object_id *oid UNUSED,\n \t\t  int flag UNUSED, void *cb_data)\n {\n@@ -545,7 +546,7 @@ finish:\n \treturn res;\n }\n \n-static int add_bisect_ref(const char *refname, const struct object_id *oid,\n+static int add_bisect_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flags UNUSED, void *cb)\n {\n \tstruct add_bisect_ref_data *data = cb;\n@@ -1162,6 +1163,7 @@ static int bisect_visualize(struct bisect_terms *terms, int argc,\n }\n \n static int get_first_good(const char *refname UNUSED,\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/builtin/checkout.c b/builtin/checkout.c\nindex 3cf44b4683a..562778885e1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1045,7 +1045,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,\n \t\treport_tracking(new_branch_info);\n }\n \n-static int add_pending_uninteresting_ref(const char *refname,\n+static int add_pending_uninteresting_ref(const char *refname, const char *referent UNUSED,\n \t\t\t\t\t const struct object_id *oid,\n \t\t\t\t\t int flags UNUSED, void *cb_data)\n {\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex e5287eddf2d..d95a9109eed 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -145,7 +145,7 @@ static void add_to_known_names(const char *path,\n \t}\n }\n \n-static int get_name(const char *path, const struct object_id *oid,\n+static int get_name(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flag UNUSED, void *cb_data UNUSED)\n {\n \tint is_tag = 0;\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 693f02b9580..c297569a473 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -286,7 +286,7 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,\n \treturn ent;\n }\n \n-static int add_one_refname(const char *refname,\n+static int add_one_refname(const char *refname, const char *referent UNUSED,\n \t\t\t   const struct object_id *oid,\n \t\t\t   int flag UNUSED, void *cbdata)\n {\n@@ -1464,6 +1464,7 @@ static void set_option(struct transport *transport, const char *name, const char\n \n \n static int add_oid(const char *refname UNUSED,\n+\t\t   const char *referent UNUSED,\n \t\t   const struct object_id *oid,\n \t\t   int flags UNUSED, void *cb_data)\n {\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex d13a226c2ed..ef6ee1268bd 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -521,7 +521,7 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)\n \treturn 0;\n }\n \n-static int fsck_handle_ref(const char *refname, const struct object_id *oid,\n+static int fsck_handle_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t   int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object *obj;\n@@ -576,7 +576,7 @@ static void get_default_heads(void)\n \t\tstrbuf_worktree_ref(wt, &ref, \"HEAD\");\n \t\tfsck_head_link(ref.buf, &head_points_at, &head_oid);\n \t\tif (head_points_at && !is_null_oid(&head_oid))\n-\t\t\tfsck_handle_ref(ref.buf, &head_oid, 0, NULL);\n+\t\t\tfsck_handle_ref(ref.buf, NULL, &head_oid, 0, NULL);\n \t\tstrbuf_release(&ref);\n \n \t\tif (include_reflogs)\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex 72bac2554fc..65be7c31b33 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -836,6 +836,7 @@ struct cg_auto_data {\n };\n \n static int dfs_on_ref(const char *refname UNUSED,\n+\t\t      const char *referent UNUSED,\n \t\t      const struct object_id *oid,\n \t\t      int flags UNUSED,\n \t\t      void *cb_data)\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex 70e9ec4e477..52016f1cf7d 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -337,7 +337,7 @@ static int cmp_by_tag_and_age(const void *a_, const void *b_)\n \treturn a->taggerdate != b->taggerdate;\n }\n \n-static int name_ref(const char *path, const struct object_id *oid,\n+static int name_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flags UNUSED, void *cb_data)\n {\n \tstruct object *o = parse_object(the_repository, oid);\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex f395488971f..c481feadbfa 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -771,7 +771,7 @@ static enum write_one_status write_one(struct hashfile *f,\n \treturn WRITE_ONE_WRITTEN;\n }\n \n-static int mark_tagged(const char *path UNUSED, const struct object_id *oid,\n+static int mark_tagged(const char *path UNUSED, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object_id peeled;\n@@ -3129,7 +3129,7 @@ static void add_tag_chain(const struct object_id *oid)\n \t}\n }\n \n-static int add_ref_tag(const char *tag UNUSED, const struct object_id *oid,\n+static int add_ref_tag(const char *tag UNUSED, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object_id peeled;\n@@ -4076,6 +4076,7 @@ static void record_recent_commit(struct commit *commit, void *data UNUSED)\n }\n \n static int mark_bitmap_preferred_tip(const char *refname,\n+\t\t\t\t     const char *referent UNUSED,\n \t\t\t\t     const struct object_id *oid,\n \t\t\t\t     int flags UNUSED,\n \t\t\t\t     void *data UNUSED)\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 339524ae2a8..6e0f462efbb 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -300,7 +300,7 @@ static void show_ref(const char *path, const struct object_id *oid)\n \t}\n }\n \n-static int show_ref_cb(const char *path_full, const struct object_id *oid,\n+static int show_ref_cb(const char *path_full, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED, void *data)\n {\n \tstruct oidset *seen = data;\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 08292498bde..9a3db1b0f13 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -543,6 +543,7 @@ struct branches_for_remote {\n };\n \n static int add_branch_for_removal(const char *refname,\n+\t\t\t\t  const char *referent UNUSED,\n \t\t\t\t  const struct object_id *oid UNUSED,\n \t\t\t\t  int flags UNUSED, void *cb_data)\n {\n@@ -585,7 +586,7 @@ struct rename_info {\n \tuint32_t symrefs_nr;\n };\n \n-static int read_remote_branches(const char *refname,\n+static int read_remote_branches(const char *refname, const char *referent UNUSED,\n \t\t\t\tconst struct object_id *oid UNUSED,\n \t\t\t\tint flags UNUSED, void *cb_data)\n {\n@@ -971,6 +972,7 @@ static void free_remote_ref_states(struct ref_states *states)\n }\n \n static int append_ref_to_tracked_list(const char *refname,\n+\t\t\t\t      const char *referent UNUSED,\n \t\t\t\t      const struct object_id *oid UNUSED,\n \t\t\t\t      int flags, void *cb_data)\n {\ndiff --git a/builtin/repack.c b/builtin/repack.c\nindex f0317fa94ad..d9ea6cecbf9 100644\n--- a/builtin/repack.c\n+++ b/builtin/repack.c\n@@ -667,6 +667,7 @@ struct midx_snapshot_ref_data {\n };\n \n static int midx_snapshot_ref_one(const char *refname UNUSED,\n+\t\t\t\t const char *referent UNUSED,\n \t\t\t\t const struct object_id *oid,\n \t\t\t\t int flag UNUSED, void *_data)\n {\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex 1ef833c07f5..34cc4672bc1 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -49,6 +49,7 @@ struct show_data {\n };\n \n static int show_reference(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/builtin/rev-parse.c b/builtin/rev-parse.c\nindex 2e64f5bda70..a7084ca157f 100644\n--- a/builtin/rev-parse.c\n+++ b/builtin/rev-parse.c\n@@ -211,7 +211,7 @@ static int show_default(void)\n \treturn 0;\n }\n \n-static int show_reference(const char *refname, const struct object_id *oid,\n+static int show_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flag UNUSED, void *cb_data UNUSED)\n {\n \tif (ref_excluded(&ref_excludes, refname))\n@@ -220,7 +220,7 @@ static int show_reference(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n-static int anti_reference(const char *refname, const struct object_id *oid,\n+static int anti_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flag UNUSED, void *cb_data UNUSED)\n {\n \tshow_rev(REVERSED, oid, refname);\ndiff --git a/builtin/show-branch.c b/builtin/show-branch.c\nindex d72f4cb98de..3427c1572e5 100644\n--- a/builtin/show-branch.c\n+++ b/builtin/show-branch.c\n@@ -410,7 +410,7 @@ static int append_ref(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n-static int append_head_ref(const char *refname, const struct object_id *oid,\n+static int append_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t   int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object_id tmp;\n@@ -425,7 +425,7 @@ static int append_head_ref(const char *refname, const struct object_id *oid,\n \treturn append_ref(refname + ofs, oid, 0);\n }\n \n-static int append_remote_ref(const char *refname, const struct object_id *oid,\n+static int append_remote_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t     int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object_id tmp;\n@@ -451,7 +451,7 @@ static int append_tag_ref(const char *refname, const struct object_id *oid,\n static const char *match_ref_pattern = NULL;\n static int match_ref_slash = 0;\n \n-static int append_matching_ref(const char *refname, const struct object_id *oid,\n+static int append_matching_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t       int flag, void *cb_data)\n {\n \t/* we want to allow pattern hold/<asterisk> to show all\n@@ -468,7 +468,7 @@ static int append_matching_ref(const char *refname, const struct object_id *oid,\n \tif (wildmatch(match_ref_pattern, tail, 0))\n \t\treturn 0;\n \tif (starts_with(refname, \"refs/heads/\"))\n-\t\treturn append_head_ref(refname, oid, flag, cb_data);\n+\t\treturn append_head_ref(refname, NULL, oid, flag, cb_data);\n \tif (starts_with(refname, \"refs/tags/\"))\n \t\treturn append_tag_ref(refname, oid, flag, cb_data);\n \treturn append_ref(refname, oid, 0);\ndiff --git a/builtin/show-ref.c b/builtin/show-ref.c\nindex 839a5c29f31..04e0db707c6 100644\n--- a/builtin/show-ref.c\n+++ b/builtin/show-ref.c\n@@ -63,7 +63,7 @@ struct show_ref_data {\n \tint show_head;\n };\n \n-static int show_ref(const char *refname, const struct object_id *oid,\n+static int show_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flag UNUSED, void *cbdata)\n {\n \tstruct show_ref_data *data = cbdata;\n@@ -97,6 +97,7 @@ match:\n }\n \n static int add_existing(const char *refname,\n+\t\t\tconst char *referent UNUSED,\n \t\t\tconst struct object_id *oid UNUSED,\n \t\t\tint flag UNUSED, void *cbdata)\n {\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 880ab4456ee..1cdcc465b1c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -609,6 +609,7 @@ static void print_status(unsigned int flags, char state, const char *path,\n }\n \n static int handle_submodule_head_ref(const char *refname UNUSED,\n+\t\t\t\t     const char *referent UNUSED,\n \t\t\t\t     const struct object_id *oid,\n \t\t\t\t     int flags UNUSED,\n \t\t\t\t     void *cb_data)\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 1d51e54fcdc..2e16218c655 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -626,6 +626,7 @@ static void print_preparing_worktree_line(int detach,\n  * Returns 0 on failure and non-zero on success.\n  */\n static int first_valid_ref(const char *refname UNUSED,\n+\t\t\t   const char *referent UNUSED,\n \t\t\t   const struct object_id *oid UNUSED,\n \t\t\t   int flags UNUSED,\n \t\t\t   void *cb_data UNUSED)\ndiff --git a/commit-graph.c b/commit-graph.c\nindex 79b0e72cc42..aa6e08eb39c 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -1847,6 +1847,7 @@ struct refs_cb_data {\n };\n \n static int add_ref_to_set(const char *refname UNUSED,\n+\t\t\t  const char *referent UNUSED,\n \t\t\t  const struct object_id *oid,\n \t\t\t  int flags UNUSED, void *cb_data)\n {\ndiff --git a/delta-islands.c b/delta-islands.c\nindex ffe1ca28144..84435512593 100644\n--- a/delta-islands.c\n+++ b/delta-islands.c\n@@ -390,7 +390,7 @@ static void add_ref_to_island(kh_str_t *remote_islands, const char *island_name,\n \trl->hash += sha_core;\n }\n \n-static int find_island_for_ref(const char *refname, const struct object_id *oid,\n+static int find_island_for_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t       int flags UNUSED, void *cb)\n {\n \tstruct island_load_data *ild = cb;\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 732511604b1..58b4581ad80 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -183,6 +183,7 @@ static int rev_list_insert_ref(struct fetch_negotiator *negotiator,\n }\n \n static int rev_list_insert_ref_oid(const char *refname UNUSED,\n+\t\t\t\t   const char *referent UNUSED,\n \t\t\t\t   const struct object_id *oid,\n \t\t\t\t   int flag UNUSED,\n \t\t\t\t   void *cb_data)\n@@ -610,6 +611,7 @@ static int mark_complete(const struct object_id *oid)\n }\n \n static int mark_complete_oid(const char *refname UNUSED,\n+\t\t\t     const char *referent UNUSED,\n \t\t\t     const struct object_id *oid,\n \t\t\t     int flag UNUSED,\n \t\t\t     void *cb_data UNUSED)\ndiff --git a/help.c b/help.c\nindex a6b4d3b1ebd..c03863f2265 100644\n--- a/help.c\n+++ b/help.c\n@@ -804,7 +804,7 @@ struct similar_ref_cb {\n \tstruct string_list *similar_refs;\n };\n \n-static int append_similar_ref(const char *refname,\n+static int append_similar_ref(const char *refname, const char *referent UNUSED,\n \t\t\t      const struct object_id *oid UNUSED,\n \t\t\t      int flags UNUSED, void *cb_data)\n {\ndiff --git a/http-backend.c b/http-backend.c\nindex 461424972b6..79ce097359b 100644\n--- a/http-backend.c\n+++ b/http-backend.c\n@@ -512,7 +512,7 @@ static void run_service(const char **argv, int buffer_input)\n \t\texit(1);\n }\n \n-static int show_text_ref(const char *name, const struct object_id *oid,\n+static int show_text_ref(const char *name, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t int flag UNUSED, void *cb_data)\n {\n \tconst char *name_nons = strip_namespace(name);\n@@ -568,7 +568,7 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)\n \tstrbuf_release(&buf);\n }\n \n-static int show_head_ref(const char *refname, const struct object_id *oid,\n+static int show_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t int flag, void *cb_data)\n {\n \tstruct strbuf *buf = cb_data;\ndiff --git a/log-tree.c b/log-tree.c\nindex 52feec4356a..40306434556 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -145,7 +145,7 @@ static int ref_filter_match(const char *refname,\n \treturn 1;\n }\n \n-static int add_ref_decoration(const char *refname, const struct object_id *oid,\n+static int add_ref_decoration(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t      int flags UNUSED,\n \t\t\t      void *cb_data)\n {\ndiff --git a/ls-refs.c b/ls-refs.c\nindex 2dd925b43d0..c824aea7146 100644\n--- a/ls-refs.c\n+++ b/ls-refs.c\n@@ -77,7 +77,7 @@ struct ls_refs_data {\n \tunsigned unborn : 1;\n };\n \n-static int send_ref(const char *refname, const struct object_id *oid,\n+static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flag, void *cb_data)\n {\n \tstruct ls_refs_data *data = cb_data;\n@@ -135,7 +135,7 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)\n \toid_is_null = is_null_oid(&oid);\n \tif (!oid_is_null ||\n \t    (data->unborn && data->symrefs && (flag & REF_ISSYMREF)))\n-\t\tsend_ref(namespaced.buf, oid_is_null ? NULL : &oid, flag, data);\n+\t\tsend_ref(namespaced.buf, NULL, oid_is_null ? NULL : &oid, flag, data);\n \tstrbuf_release(&namespaced);\n }\n \ndiff --git a/midx-write.c b/midx-write.c\nindex 478b42e7209..56d1717f9f1 100644\n--- a/midx-write.c\n+++ b/midx-write.c\n@@ -655,7 +655,7 @@ static void prepare_midx_packing_data(struct packing_data *pdata,\n \ttrace2_region_leave(\"midx\", \"prepare_midx_packing_data\", the_repository);\n }\n \n-static int add_ref_to_pending(const char *refname,\n+static int add_ref_to_pending(const char *refname, const char *referent UNUSED,\n \t\t\t      const struct object_id *oid,\n \t\t\t      int flag, void *cb_data)\n {\ndiff --git a/negotiator/default.c b/negotiator/default.c\nindex e3fa5c33249..c479da9b091 100644\n--- a/negotiator/default.c\n+++ b/negotiator/default.c\n@@ -38,7 +38,7 @@ static void rev_list_push(struct negotiation_state *ns,\n \t}\n }\n \n-static int clear_marks(const char *refname, const struct object_id *oid,\n+static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED,\n \t\t       void *cb_data UNUSED)\n {\ndiff --git a/negotiator/skipping.c b/negotiator/skipping.c\nindex f109928ad0b..f65d47858b4 100644\n--- a/negotiator/skipping.c\n+++ b/negotiator/skipping.c\n@@ -75,7 +75,7 @@ static struct entry *rev_list_push(struct data *data, struct commit *commit, int\n \treturn entry;\n }\n \n-static int clear_marks(const char *refname, const struct object_id *oid,\n+static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED,\n \t\t       void *cb_data UNUSED)\n {\ndiff --git a/notes.c b/notes.c\nindex 1ba6412aae8..da42df282d5 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -932,7 +932,7 @@ out:\n \treturn ret;\n }\n \n-static int string_list_add_one_ref(const char *refname,\n+static int string_list_add_one_ref(const char *refname, const char *referent UNUSED,\n \t\t\t\t   const struct object_id *oid UNUSED,\n \t\t\t\t   int flag UNUSED, void *cb)\n {\ndiff --git a/object-name.c b/object-name.c\nindex 527b853ac45..7cca43020f2 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -1365,7 +1365,7 @@ struct handle_one_ref_cb {\n \tstruct commit_list **list;\n };\n \n-static int handle_one_ref(const char *path, const struct object_id *oid,\n+static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flag UNUSED,\n \t\t\t  void *cb_data)\n {\ndiff --git a/pseudo-merge.c b/pseudo-merge.c\nindex f0fde13c471..77a83b9c5ce 100644\n--- a/pseudo-merge.c\n+++ b/pseudo-merge.c\n@@ -201,6 +201,7 @@ void load_pseudo_merges_from_config(struct string_list *list)\n }\n \n static int find_pseudo_merge_group_for_ref(const char *refname,\n+\t\t\t\t\t   const char *referent UNUSED,\n \t\t\t\t\t   const struct object_id *oid,\n \t\t\t\t\t   int flags UNUSED,\n \t\t\t\t\t   void *_data)\ndiff --git a/reachable.c b/reachable.c\nindex 46613a6bb6f..3e9b3dd0a46 100644\n--- a/reachable.c\n+++ b/reachable.c\n@@ -79,7 +79,7 @@ static void add_rebase_files(struct rev_info *revs)\n \tfree_worktrees(worktrees);\n }\n \n-static int add_one_ref(const char *path, const struct object_id *oid,\n+static int add_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag, void *cb_data)\n {\n \tstruct rev_info *revs = (struct rev_info *)cb_data;\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 8c5e673fc0a..39997890feb 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -2865,7 +2865,7 @@ struct ref_filter_cbdata {\n  * A call-back given to for_each_ref().  Filter refs and keep them for\n  * later object processing.\n  */\n-static int filter_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_one(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\n@@ -2900,7 +2900,7 @@ struct ref_filter_and_format_cbdata {\n \t} internal;\n };\n \n-static int filter_and_format_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_and_format_one(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_and_format_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\ndiff --git a/reflog.c b/reflog.c\nindex 5ca944529b3..d592c1cfb56 100644\n--- a/reflog.c\n+++ b/reflog.c\n@@ -300,6 +300,7 @@ int should_expire_reflog_ent_verbose(struct object_id *ooid,\n }\n \n static int push_tip_to_list(const char *refname UNUSED,\n+\t\t\t    const char *referent UNUSED,\n \t\t\t    const struct object_id *oid,\n \t\t\t    int flags, void *cb_data)\n {\ndiff --git a/refs.c b/refs.c\nindex bb90a188758..0a5c12cf16b 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -414,7 +414,7 @@ int refs_ref_exists(struct ref_store *refs, const char *refname)\n \t\t\t\t\t NULL, NULL);\n }\n \n-static int for_each_filter_refs(const char *refname,\n+static int for_each_filter_refs(const char *refname, const char *referent,\n \t\t\t\tconst struct object_id *oid,\n \t\t\t\tint flags, void *data)\n {\n@@ -424,7 +424,7 @@ static int for_each_filter_refs(const char *refname,\n \t\treturn 0;\n \tif (filter->prefix)\n \t\tskip_prefix(refname, filter->prefix, &refname);\n-\treturn filter->fn(refname, oid, flags, filter->cb_data);\n+\treturn filter->fn(refname, referent, oid, flags, filter->cb_data);\n }\n \n struct warn_if_dangling_data {\n@@ -435,7 +435,7 @@ struct warn_if_dangling_data {\n \tconst char *msg_fmt;\n };\n \n-static int warn_if_dangling_symref(const char *refname,\n+static int warn_if_dangling_symref(const char *refname, const char *referent UNUSED,\n \t\t\t\t   const struct object_id *oid UNUSED,\n \t\t\t\t   int flags, void *cb_data)\n {\n@@ -506,7 +506,7 @@ int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_da\n \n \tstrbuf_addf(&buf, \"%sHEAD\", get_git_namespace());\n \tif (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag))\n-\t\tret = fn(buf.buf, &oid, flag, cb_data);\n+\t\tret = fn(buf.buf, NULL, &oid, flag, cb_data);\n \tstrbuf_release(&buf);\n \n \treturn ret;\n@@ -1547,7 +1547,7 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n \n \tif (refs_resolve_ref_unsafe(refs, \"HEAD\", RESOLVE_REF_READING,\n \t\t\t\t    &oid, &flag))\n-\t\treturn fn(\"HEAD\", &oid, flag, cb_data);\n+\t\treturn fn(\"HEAD\", NULL, &oid, flag, cb_data);\n \n \treturn 0;\n }\n@@ -2387,7 +2387,7 @@ struct do_for_each_reflog_help {\n \tvoid *cb_data;\n };\n \n-static int do_for_each_reflog_helper(const char *refname,\n+static int do_for_each_reflog_helper(const char *refname, 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)\n@@ -2593,7 +2593,7 @@ struct migration_data {\n \tstruct strbuf *errbuf;\n };\n \n-static int migrate_one_ref(const char *refname, const struct object_id *oid,\n+static int migrate_one_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t   int flags, void *cb_data)\n {\n \tstruct migration_data *data = cb_data;\ndiff --git a/refs.h b/refs.h\nindex 0ecba21b4a7..158e0a12197 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -298,7 +298,7 @@ struct ref_transaction;\n  * arguments is only guaranteed to be valid for the duration of a\n  * single callback invocation.\n  */\n-typedef int each_ref_fn(const char *refname,\n+typedef int each_ref_fn(const char *refname, const char *referent,\n \t\t\tconst struct object_id *oid, int flags, void *cb_data);\n \n /*\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 7640318cba8..40c88652750 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -3041,7 +3041,7 @@ static int files_transaction_abort(struct ref_store *ref_store,\n \treturn 0;\n }\n \n-static int ref_present(const char *refname,\n+static int ref_present(const char *refname, const char *referent UNUSED,\n \t\t       const struct object_id *oid UNUSED,\n \t\t       int flags UNUSED,\n \t\t       void *cb_data)\ndiff --git a/refs/iterator.c b/refs/iterator.c\nindex 26acb6bd640..21beeb91e9e 100644\n--- a/refs/iterator.c\n+++ b/refs/iterator.c\n@@ -451,7 +451,7 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,\n \n \tcurrent_ref_iter = iter;\n \twhile ((ok = ref_iterator_advance(iter)) == ITER_OK) {\n-\t\tretval = fn(iter->refname, iter->oid, iter->flags, cb_data);\n+\t\tretval = fn(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/remote.c b/remote.c\nindex f43cf5e7a4d..4a1269a47ab 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -2354,7 +2354,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,\n \treturn 1;\n }\n \n-static int one_local_ref(const char *refname, const struct object_id *oid,\n+static int one_local_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t int flag UNUSED,\n \t\t\t void *cb_data)\n {\n@@ -2439,7 +2439,7 @@ struct stale_heads_info {\n \tstruct refspec *rs;\n };\n \n-static int get_stale_heads_cb(const char *refname, const struct object_id *oid,\n+static int get_stale_heads_cb(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t      int flags, void *cb_data)\n {\n \tstruct stale_heads_info *info = cb_data;\ndiff --git a/replace-object.c b/replace-object.c\nindex 59252d565ef..9a3cdd809a9 100644\n--- a/replace-object.c\n+++ b/replace-object.c\n@@ -9,6 +9,7 @@\n #include \"commit.h\"\n \n static int register_replace_ref(const 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)\ndiff --git a/revision.c b/revision.c\nindex 1c0192f5225..6b33bd814f7 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1648,7 +1648,7 @@ struct all_refs_cb {\n \tstruct worktree *wt;\n };\n \n-static int handle_one_ref(const char *path, const struct object_id *oid,\n+static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flag UNUSED,\n \t\t\t  void *cb_data)\n {\ndiff --git a/server-info.c b/server-info.c\nindex f61296a60d0..1508fa6f825 100644\n--- a/server-info.c\n+++ b/server-info.c\n@@ -147,7 +147,7 @@ out:\n \treturn ret;\n }\n \n-static int add_info_ref(const char *path, const struct object_id *oid,\n+static int add_info_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t\tint flag UNUSED,\n \t\t\tvoid *cb_data)\n {\ndiff --git a/shallow.c b/shallow.c\nindex 31a6ca40fe2..b8cd051e3b6 100644\n--- a/shallow.c\n+++ b/shallow.c\n@@ -612,6 +612,7 @@ static void paint_down(struct paint_info *info, const struct object_id *oid,\n }\n \n static int mark_uninteresting(const char *refname UNUSED,\n+\t\t\t      const char *referent UNUSED,\n \t\t\t      const struct object_id *oid,\n \t\t\t      int flags UNUSED,\n \t\t\t      void *cb_data UNUSED)\n@@ -727,6 +728,7 @@ struct commit_array {\n };\n \n static int add_ref(const char *refname UNUSED,\n+\t\t  const char *referent UNUSED,\n \t\t   const struct object_id *oid,\n \t\t   int flags UNUSED,\n \t\t   void *cb_data)\ndiff --git a/submodule.c b/submodule.c\nindex ab99a302530..7c0f5db861d 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -953,6 +953,7 @@ static void free_submodules_data(struct string_list *submodules)\n }\n \n static int has_remote(const char *refname UNUSED,\n+\t\t      const char *referent UNUSED,\n \t\t      const struct object_id *oid UNUSED,\n \t\t      int flags UNUSED, void *cb_data UNUSED)\n {\n@@ -1273,6 +1274,7 @@ int push_unpushed_submodules(struct repository *r,\n }\n \n static int append_oid_to_array(const char *ref UNUSED,\n+\t\t\t       const char *referent UNUSED,\n \t\t\t       const struct object_id *oid,\n \t\t\t       int flags UNUSED, void *data)\n {\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex 637b8b294e9..65346dee551 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -156,7 +156,7 @@ static int cmd_rename_ref(struct ref_store *refs, const char **argv)\n \treturn refs_rename_ref(refs, oldref, newref, logmsg);\n }\n \n-static int each_ref(const char *refname, const struct object_id *oid,\n+static int each_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flags, void *cb_data UNUSED)\n {\n \tprintf(\"%s %s 0x%x\\n\", oid_to_hex(oid), refname, flags);\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 0052c6a4dce..f03ba3e98be 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -857,7 +857,7 @@ static void send_unshallow(struct upload_pack_data *data)\n \t}\n }\n \n-static int check_ref(const char *refname_full, const struct object_id *oid,\n+static int check_ref(const char *refname_full, const char *referent UNUSED, const struct object_id *oid,\n \t\t     int flag, void *cb_data);\n static void deepen(struct upload_pack_data *data, int depth)\n {\n@@ -1208,7 +1208,7 @@ static int mark_our_ref(const char *refname, const char *refname_full,\n \treturn 0;\n }\n \n-static int check_ref(const char *refname_full, const struct object_id *oid,\n+static int check_ref(const char *refname_full, const char *referent UNUSED,const struct object_id *oid,\n \t\t     int flag UNUSED, void *cb_data)\n {\n \tconst char *refname = strip_namespace(refname_full);\n@@ -1276,14 +1276,14 @@ static void write_v0_ref(struct upload_pack_data *data,\n \treturn;\n }\n \n-static int send_ref(const char *refname, const struct object_id *oid,\n+static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flag UNUSED, void *cb_data)\n {\n \twrite_v0_ref(cb_data, refname, strip_namespace(refname), oid);\n \treturn 0;\n }\n \n-static int find_symref(const char *refname,\n+static int find_symref(const char *refname, const char *referent UNUSED,\n \t\t       const struct object_id *oid UNUSED,\n \t\t       int flag, void *cb_data)\n {\ndiff --git a/walker.c b/walker.c\nindex 0fafdc97cf1..807a7a38813 100644\n--- a/walker.c\n+++ b/walker.c\n@@ -221,6 +221,7 @@ static int interpret_target(struct walker *walker, char *target, struct object_i\n }\n \n static int mark_complete(const char *path UNUSED,\n+\t\t\tconst char *referent UNUSED,\n \t\t\t const struct object_id *oid,\n \t\t\t int flag UNUSED,\n \t\t\t void *cb_data UNUSED)\ndiff --git a/worktree.c b/worktree.c\nindex f3c4c8ec54d..fd05f3741c6 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -546,7 +546,7 @@ int other_head_refs(each_ref_fn fn, void *cb_data)\n \t\t\t\t\t    refname.buf,\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+\t\t\tret = fn(refname.buf, NULL, &oid, flag, cb_data);\n \t\tif (ret)\n \t\t\tbreak;\n \t}\n-- \ngitgitgadget\n\n"},{"id":"499873","messageId":"xmqqa5hww600.fsf@gitster.g","threadId":"61598","inReplyTo":"ac0957c9e6abdc2597900573703461833e9c9d69.1722524334.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 1/3] refs: keep track of unresolved reference value in iterators","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-01T16:41:03Z","receivedAt":"2024-08-01T16:41:12Z","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> @@ -245,9 +245,11 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n>  {\n>  \tstruct object_id oid;\n>  \tint flag;\n> -\n> -\tif (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,\n> -\t\t\t\t     &oid, &flag)) {\n> +\tconst char* referent = refs_resolve_ref_unsafe(&refs->base,\n\nStyle.  The asterisk sticks to the pointer variable, not the base type.\n\n> +\t\t\t\t\t\t       refname,\n> +\t\t\t\t\t\t       RESOLVE_REF_READING,\n> +\t\t\t\t\t\t       &oid, &flag);\n> +\tif (!referent) {\n>  \t\toidclr(&oid, the_repository->hash_algo);\n>  \t\tflag |= REF_ISBROKEN;\n>  \t} else if (is_null_oid(&oid)) {\n> @@ -268,7 +270,8 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n>  \t\toidclr(&oid, the_repository->hash_algo);\n>  \t\tflag |= REF_BAD_NAME | REF_ISBROKEN;\n>  \t}\n> -\tadd_entry_to_dir(dir, create_ref_entry(refname, &oid, flag));\n> +\n> +\tadd_entry_to_dir(dir, create_ref_entry(refname, referent, &oid, flag));\n>  }\n\nThis is a very straight-forward change, given the matching change to\nthe ref-entry, which now has a referent member.\n\n> @@ -886,6 +889,9 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)\n>  \t\titer->base.refname = iter->iter0->refname;\n>  \t\titer->base.oid = iter->iter0->oid;\n>  \t\titer->base.flags = iter->iter0->flags;\n> +\t\tif (iter->iter0->flags & REF_ISSYMREF)\n> +\t\t\titer->base.referent = iter->iter0->referent;\n\nPresumably base.referent is initialized to NULL so this \"if\"\nstatement does not need an else clause?  \n\nI am primarily wondering why this needs to be conditional.  We are\nmaking verbatim copy of the flags word from *iter->iter0 to\niter->base; if iter0 is symref we want to mark base also as symref,\nIf iter0 is not a symref, then we want to mark base also not a\nsymref, presumably.  So wouldn't it make sense to just say\n\n\t\titer->base.referent = iter->iter0->referent;\n\nhere, regardless of what iter->iter0->flags say about symref-ness of\nthe thing?  Because ...\n\n> diff --git a/refs/iterator.c b/refs/iterator.c\n> index d355ebf0d59..26acb6bd640 100644\n> --- a/refs/iterator.c\n> +++ b/refs/iterator.c\n> @@ -7,6 +7,7 @@\n>  #include \"refs.h\"\n>  #include \"refs/refs-internal.h\"\n>  #include \"iterator.h\"\n> +#include \"strbuf.h\"\n>  \n>  int ref_iterator_advance(struct ref_iterator *ref_iterator)\n>  {\n> @@ -29,6 +30,7 @@ void base_ref_iterator_init(struct ref_iterator *iter,\n>  {\n>  \titer->vtable = vtable;\n>  \titer->refname = NULL;\n> +\titer->referent = NULL;\n>  \titer->oid = NULL;\n>  \titer->flags = 0;\n>  }\n> @@ -199,6 +201,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;\n\n... other parts of the API seem to follow that philosophy.\n\nIn other words, the invariant of this code is that .referent is\nnon-NULL if and only if the ref is a symref, and that invariant is\ntrusted without checking with .flags member.  But the earlier hunk\nthat copied iter0 to base did not seem to be using that invariant,\nwhich made it look inconsistent.\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> @@ -41,6 +43,10 @@ struct ref_entry *create_ref_entry(const char *refname,\n>  \tFLEX_ALLOC_STR(ref, name, refname);\n>  \toidcpy(&ref->u.value.oid, oid);\n>  \tref->flag = flag;\n> +\n> +\tif (flag & REF_ISSYMREF)\n> +\t\tref->u.value.referent = xstrdup_or_null(referent);\n\nOK.  ref_value now has referent next to the existing oid, but that\nmember only makes sense when flag says it is a symref.\n\nCuriously, that information is missing from ref_value struct, so by\nlooking at a ref_value alone, we cannot tell if we should trust the\nvalue in the .referent member?\n\nMakes me wonder if we should follow the same \"ignore what the flag\nsays when filling the .referent member; if the ref is not a symref,\nthe referent variable is NULL, and if it is, referent is never NULL\"\npattern?  Then ref->u.value.referent is _always_ defined---the\ncurrent code says \"the u.value.referent member is undefined for ref\nthat is not a symref\", but with the suggested change, it will be\n\"the u.value.referent member is NULL for ref that is not a symref,\nand for a symref, it is the value of the symref\".\n\n>  \treturn ref;\n>  }\n>  \n> @@ -66,6 +72,7 @@ static void free_ref_entry(struct ref_entry *entry)\n>  \t\t */\n>  \t\tclear_ref_dir(&entry->u.subdir);\n>  \t}\n> +\tfree(entry->u.value.referent);\n\nAnd that would match what is done here.  We do not say \"entry->flag\nsays it is not a symref, so do not bother freeing u.value.referent\".\n\n> @@ -431,6 +438,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)\n>  \t\t\tlevel->index = -1;\n>  \t\t} else {\n>  \t\t\titer->base.refname = entry->name;\n> +\t\t\titer->base.referent = entry->u.value.referent;\n>  \t\t\titer->base.oid = &entry->u.value.oid;\n>  \t\t\titer->base.flags = entry->flag;\n>  \t\t\treturn ITER_OK;\n> diff --git a/refs/ref-cache.h b/refs/ref-cache.h\n> index 31ebe24f6cf..5f04e518c37 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> +\tchar *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>  /*\n> diff --git a/refs/refs-internal.h b/refs/refs-internal.h\n> index fa975d69aaa..117ec233848 100644\n> --- a/refs/refs-internal.h\n> +++ b/refs/refs-internal.h\n> @@ -299,6 +299,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>  };\n> diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c\n> index fbe74c239d3..9f724c3d632 100644\n> --- a/refs/reftable-backend.c\n> +++ b/refs/reftable-backend.c\n> @@ -494,8 +494,12 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)\n>  \t\t\t\tthe_repository->hash_algo);\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\t\t\t\t     RESOLVE_REF_READING, &iter->oid, &flags))\n> +\t\t\titer->base.referent = refs_resolve_ref_unsafe(&iter->refs->base,\n> +\t\t\t\t\t\t\t\t      iter->ref.refname,\n> +\t\t\t\t\t\t\t\t      RESOLVE_REF_READING,\n> +\t\t\t\t\t\t\t\t      &iter->oid,\n> +\t\t\t\t\t\t\t\t      &flags);\n> +\t\t\tif (!iter->base.referent)\n>  \t\t\t\toidclr(&iter->oid, the_repository->hash_algo);\n>  \t\t\tbreak;\n>  \t\tdefault:\n"},{"id":"499874","messageId":"xmqq4j84w5vu.fsf@gitster.g","threadId":"61598","inReplyTo":"3e147e7d850773f44b48d1b86e89aef1415a0ccd.1722524334.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 3/3] ref-filter: populate symref from iterator","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-01T16:43:33Z","receivedAt":"2024-08-01T16:43:37Z","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> @@ -2852,6 +2852,8 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const struct\n>  \tref->commit = commit;\n>  \tref->flag = flag;\n>  \tref->kind = kind;\n> +\tif (flag & REF_ISSYMREF)\n> +\t\tref->symref = xstrdup_or_null(referent);\n\nThe same reaction as [1/3].  Doesn't the null-ness of referent\nconvey the same information as the ISSYMREF bit in flag?  IOW,\ncan't we do this assignment unconditionally?\n\n"},{"id":"499875","messageId":"xmqqwml0uqxi.fsf@gitster.g","threadId":"61598","inReplyTo":"3e147e7d850773f44b48d1b86e89aef1415a0ccd.1722524334.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 3/3] ref-filter: populate symref from iterator","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-01T16:51:53Z","receivedAt":"2024-08-01T16:51:57Z","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> With a previous commit, the reference the symbolic ref points to is saved\n> in the ref iterator records. Instead of making a separate call to\n> resolve_refdup() each time, we can just populate the ref_array_item with\n> the value from the iterator.\n>\n> Signed-off-by: John Cai <johncai86@gmail.com>\n> ---\n>  ref-filter.c | 12 +++++++-----\n>  1 file changed, 7 insertions(+), 5 deletions(-)\n>\n> diff --git a/ref-filter.c b/ref-filter.c\n> index 39997890feb..08997e59662 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -2783,7 +2783,7 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)\n>  \treturn ref_kind_from_refname(refname);\n>  }\n>  \n> -static struct ref_array_item *apply_ref_filter(const char *refname, const struct object_id *oid,\n> +static struct ref_array_item *apply_ref_filter(const char *refname, const char *referent, const struct object_id *oid,\n>  \t\t\t    int flag, struct ref_filter *filter)\n>  {\n>  \tstruct ref_array_item *ref;\n> @@ -2852,6 +2852,8 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const struct\n>  \tref->commit = commit;\n>  \tref->flag = flag;\n>  \tref->kind = kind;\n> +\tif (flag & REF_ISSYMREF)\n> +\t\tref->symref = xstrdup_or_null(referent);\n>  \n>  \treturn ref;\n>  }\n\nWhat is curious is that we do not lose any code from\npopulate_value() with this change.\n\nIs that because of this piece of code near the beginning of it?\n\n\tCALLOC_ARRAY(ref->value, used_atom_cnt);\n\n\tif (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {\n\t\tref->symref = refs_resolve_refdup(get_main_ref_store(the_repository),\n\t\t\t\t\t\t  ref->refname,\n\t\t\t\t\t\t  RESOLVE_REF_READING,\n\t\t\t\t\t\t  NULL, NULL);\n\t\tif (!ref->symref)\n\t\t\tref->symref = xstrdup(\"\");\n\t}\n\nThat is, if we somehow know the value of ref->symref for a ref that\nis known to be a symbolic ref (and when we know we need symref\ninformation in the output), we do not bother calling refs_resolve\nhere to obtain the value.\n\nThanks.\n"},{"id":"499876","messageId":"xmqqsevouqsj.fsf@gitster.g","threadId":"61598","inReplyTo":"xmqqwml0uqxi.fsf@gitster.g","subject":"Re: [PATCH v2 3/3] ref-filter: populate symref from iterator","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-01T16:54:52Z","receivedAt":"2024-08-01T16:54:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> @@ -2852,6 +2852,8 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const struct\n>>  \tref->commit = commit;\n>>  \tref->flag = flag;\n>>  \tref->kind = kind;\n>> +\tif (flag & REF_ISSYMREF)\n>> +\t\tref->symref = xstrdup_or_null(referent);\n>>  \n>>  \treturn ref;\n>>  }\n>\n> What is curious is that we do not lose any code from\n> populate_value() with this change.\n>\n> Is that because of this piece of code near the beginning of it?\n>\n> \tCALLOC_ARRAY(ref->value, used_atom_cnt);\n>\n> \tif (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {\n> \t\tref->symref = refs_resolve_refdup(get_main_ref_store(the_repository),\n> \t\t\t\t\t\t  ref->refname,\n> \t\t\t\t\t\t  RESOLVE_REF_READING,\n> \t\t\t\t\t\t  NULL, NULL);\n> \t\tif (!ref->symref)\n> \t\t\tref->symref = xstrdup(\"\");\n> \t}\n>\n> That is, if we somehow know the value of ref->symref for a ref that\n> is known to be a symbolic ref (and when we know we need symref\n> information in the output), we do not bother calling refs_resolve\n> here to obtain the value.\n\nI forgot to ask the real question.  With your change in place, does\nthis \"lazily fill ref->symref if it hasn't been discovered yet\" code\nstill trigger?  Under what condition?  Or is this now a dead code?\n\nThanks.\n\n"},{"id":"500057","messageId":"ZrCwqqLKcwdOYclN@tanuki","threadId":"61598","inReplyTo":"xmqqa5hww600.fsf@gitster.g","subject":"Re: [PATCH v2 1/3] refs: keep track of unresolved reference value in iterators","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-08-05T10:59:54Z","receivedAt":"2024-08-05T11:00:01Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Aug 01, 2024 at 09:41:03AM -0700, Junio C Hamano wrote:\n> \"John Cai via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> > @@ -886,6 +889,9 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)\n> >  \t\titer->base.refname = iter->iter0->refname;\n> >  \t\titer->base.oid = iter->iter0->oid;\n> >  \t\titer->base.flags = iter->iter0->flags;\n> > +\t\tif (iter->iter0->flags & REF_ISSYMREF)\n> > +\t\t\titer->base.referent = iter->iter0->referent;\n> \n> Presumably base.referent is initialized to NULL so this \"if\"\n> statement does not need an else clause?\n\nThis function typically ends up being called in a loop though. So\nwithout the else clause, wouldn't we potentially leak the value of a\npreceding ref into subsequent iterations like this?\n\n> I am primarily wondering why this needs to be conditional.  We are\n> making verbatim copy of the flags word from *iter->iter0 to\n> iter->base; if iter0 is symref we want to mark base also as symref,\n> If iter0 is not a symref, then we want to mark base also not a\n> symref, presumably.  So wouldn't it make sense to just say\n> \n> \t\titer->base.referent = iter->iter0->referent;\n> \n> here, regardless of what iter->iter0->flags say about symref-ness of\n> the thing?  Because ...\n\n> > diff --git a/refs/iterator.c b/refs/iterator.c\n> > index d355ebf0d59..26acb6bd640 100644\n> > --- a/refs/iterator.c\n> > +++ b/refs/iterator.c\n> > @@ -7,6 +7,7 @@\n> >  #include \"refs.h\"\n> >  #include \"refs/refs-internal.h\"\n> >  #include \"iterator.h\"\n> > +#include \"strbuf.h\"\n> >  \n> >  int ref_iterator_advance(struct ref_iterator *ref_iterator)\n> >  {\n> > @@ -29,6 +30,7 @@ void base_ref_iterator_init(struct ref_iterator *iter,\n> >  {\n> >  \titer->vtable = vtable;\n> >  \titer->refname = NULL;\n> > +\titer->referent = NULL;\n> >  \titer->oid = NULL;\n> >  \titer->flags = 0;\n> >  }\n> > @@ -199,6 +201,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;\n> \n> ... other parts of the API seem to follow that philosophy.\n> \n> In other words, the invariant of this code is that .referent is\n> non-NULL if and only if the ref is a symref, and that invariant is\n> trusted without checking with .flags member.  But the earlier hunk\n> that copied iter0 to base did not seem to be using that invariant,\n> which made it look inconsistent.\n\nAgreed.\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> > @@ -41,6 +43,10 @@ struct ref_entry *create_ref_entry(const char *refname,\n> >  \tFLEX_ALLOC_STR(ref, name, refname);\n> >  \toidcpy(&ref->u.value.oid, oid);\n> >  \tref->flag = flag;\n> > +\n> > +\tif (flag & REF_ISSYMREF)\n> > +\t\tref->u.value.referent = xstrdup_or_null(referent);\n> \n> OK.  ref_value now has referent next to the existing oid, but that\n> member only makes sense when flag says it is a symref.\n> \n> Curiously, that information is missing from ref_value struct, so by\n> looking at a ref_value alone, we cannot tell if we should trust the\n> value in the .referent member?\n> \n> Makes me wonder if we should follow the same \"ignore what the flag\n> says when filling the .referent member; if the ref is not a symref,\n> the referent variable is NULL, and if it is, referent is never NULL\"\n> pattern?  Then ref->u.value.referent is _always_ defined---the\n> current code says \"the u.value.referent member is undefined for ref\n> that is not a symref\", but with the suggested change, it will be\n> \"the u.value.referent member is NULL for ref that is not a symref,\n> and for a symref, it is the value of the symref\".\n\nYeah, I think that would be preferable indeed.\n\nPatrick\n"},{"id":"500088","messageId":"xmqqh6bz567d.fsf@gitster.g","threadId":"61598","inReplyTo":"ZrCwqqLKcwdOYclN@tanuki","subject":"Re: [PATCH v2 1/3] refs: keep track of unresolved reference value in iterators","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-05T15:40:06Z","receivedAt":"2024-08-05T15:40:18Z","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 Thu, Aug 01, 2024 at 09:41:03AM -0700, Junio C Hamano wrote:\n>> \"John Cai via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>> > @@ -886,6 +889,9 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)\n>> >  \t\titer->base.refname = iter->iter0->refname;\n>> >  \t\titer->base.oid = iter->iter0->oid;\n>> >  \t\titer->base.flags = iter->iter0->flags;\n>> > +\t\tif (iter->iter0->flags & REF_ISSYMREF)\n>> > +\t\t\titer->base.referent = iter->iter0->referent;\n>> \n>> Presumably base.referent is initialized to NULL so this \"if\"\n>> statement does not need an else clause?\n>\n> This function typically ends up being called in a loop though. So\n> without the else clause, wouldn't we potentially leak the value of a\n> preceding ref into subsequent iterations like this?\n\nOK, so this does need to clear it when we tell the caller we have\nnon SYMREF, as we do want to show NULL as base.referent to the\ncaller in such a case.  Thanks.\n\nIt does reinforce my larger point, which was:\n\n>> Makes me wonder if we should follow the same \"ignore what the flag\n>> says when filling the .referent member; if the ref is not a symref,\n>> the referent variable is NULL, and if it is, referent is never NULL\"\n>> pattern?  Then ref->u.value.referent is _always_ defined---the\n>> current code says \"the u.value.referent member is undefined for ref\n>> that is not a symref\", but with the suggested change, it will be\n>> \"the u.value.referent member is NULL for ref that is not a symref,\n>> and for a symref, it is the value of the symref\".\n>\n> Yeah, I think that would be preferable indeed.\n\nIn other words, with .referent member introduced, checking for\n(.flags & REF_ISSYMREF) becomes a redundant&duplicated bit of\ninformation, as the bit should exactly match the non-NULL ness of\nthe .referent member.\n\nThanks.\n"},{"id":"500249","messageId":"A71B6F98-820D-4692-9712-C6D63F019DCE@gmail.com","threadId":"61598","inReplyTo":"xmqqsevouqsj.fsf@gitster.g","subject":"Re: [PATCH v2 3/3] ref-filter: populate symref from iterator","fromName":"John Cai","fromEmail":"johncai86@gmail.com","sentAt":"2024-08-06T19:49:43Z","receivedAt":"2024-08-06T19:49:46Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"Hi Junio,\n\nOn 1 Aug 2024, at 12:54, Junio C Hamano wrote:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>>> @@ -2852,6 +2852,8 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const struct\n>>>  \tref->commit = commit;\n>>>  \tref->flag = flag;\n>>>  \tref->kind = kind;\n>>> +\tif (flag & REF_ISSYMREF)\n>>> +\t\tref->symref = xstrdup_or_null(referent);\n>>>\n>>>  \treturn ref;\n>>>  }\n>>\n>> What is curious is that we do not lose any code from\n>> populate_value() with this change.\n>>\n>> Is that because of this piece of code near the beginning of it?\n>>\n>> \tCALLOC_ARRAY(ref->value, used_atom_cnt);\n>>\n>> \tif (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {\n>> \t\tref->symref = refs_resolve_refdup(get_main_ref_store(the_repository),\n>> \t\t\t\t\t\t  ref->refname,\n>> \t\t\t\t\t\t  RESOLVE_REF_READING,\n>> \t\t\t\t\t\t  NULL, NULL);\n>> \t\tif (!ref->symref)\n>> \t\t\tref->symref = xstrdup(\"\");\n>> \t}\n>>\n>> That is, if we somehow know the value of ref->symref for a ref that\n>> is known to be a symbolic ref (and when we know we need symref\n>> information in the output), we do not bother calling refs_resolve\n>> here to obtain the value.\n>\n> I forgot to ask the real question.  With your change in place, does\n> this \"lazily fill ref->symref if it hasn't been discovered yet\" code\n> still trigger?  Under what condition?  Or is this now a dead code?\n\nYes that's a good question. I took a look and it seems like in *most* cases by\nthe time populate_value() is called, apply_ref_filter() has already been called that\npopulates the symref member of ref_array_item.\n\npopulate_value() gets called by get_ref_atom_value() which gets called by both\n\n1. format_ref_array_item()\n2. cmp_ref_sorting()\n\nIn the case of [2], the callchain starts with filter_and_format_refs() which\ncalls ref_array_sort() that eventually calls populate_value(). Before\nref_array_sort() is called, filter_refs() is called which ends up calling\ndo_filter_refs() with filter_one(), leading to apply_ref_filter().\n\nIn the case of [1] however, there are a couple of code paths that call\npopulate_value() without apply_ref_filter() ever being called.\n\npretty_print_ref() directly calls format_ref_array_item() ->\nget_ref_atom_value() -> populate_value(). However, apply_ref_filter() is not\ncalled which means the symref will not be populated.\n\nLooking through the codebase, this function is only called in builtin/tag.c and\nbulitin/verify-tag.c in the `git tag -v` codepath. So it seems that if we got\nrid of this block of code in populate_value(), only in the case where `git\ntag -v --format='%(symref)'` on a symbolic ref pointing to a tag would the\nsymref be missing.\n\nBut I don't even know if this is possible. I tried this locally and got the\nerror:\n\n\n$ git tag -a -s -m \"version 1\" v1 refs/heads/master\n$ git symbolic-ref refs/tags/symbolic-v1 refs/tags/v1\n$ git tag -v --format='(%symref)'\n\nerror: tag 'refs/tags/symbolic-v1' not found.\n\n\nSo practically speaking, I think we are safe to remove. However, from a\nfuture-proof point of view, anyone in the future who calls pretty_print_ref() would also need to be sure to populate the symref member in ref_array_item.\n\nSo perhaps from a code durability standpoint we should keep that block?\n\n>\n> Thanks.\n"},{"id":"500250","messageId":"xmqqplqls8x8.fsf@gitster.g","threadId":"61598","inReplyTo":"A71B6F98-820D-4692-9712-C6D63F019DCE@gmail.com","subject":"Re: [PATCH v2 3/3] ref-filter: populate symref from iterator","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-06T20:17:23Z","receivedAt":"2024-08-06T20:17:27Z","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> Yes that's a good question. I took a look and it seems like in *most* cases by\n> the time populate_value() is called, apply_ref_filter() has already been called that\n> populates the symref member of ref_array_item.\n>\n> populate_value() gets called by get_ref_atom_value() which gets called by both\n>\n> 1. format_ref_array_item()\n> 2. cmp_ref_sorting()\n>\n> In the case of [2], the callchain starts with filter_and_format_refs() which\n> calls ref_array_sort() that eventually calls populate_value(). Before\n> ref_array_sort() is called, filter_refs() is called which ends up calling\n> do_filter_refs() with filter_one(), leading to apply_ref_filter().\n>\n> In the case of [1] however, there are a couple of code paths that call\n> populate_value() without apply_ref_filter() ever being called.\n>\n> pretty_print_ref() directly calls format_ref_array_item() ->\n> get_ref_atom_value() -> populate_value(). However, apply_ref_filter() is not\n> called which means the symref will not be populated.\n\nI am perfectly OK to keep the \"fallback\" code, even if you haven't\nfound a concrete reproduction to trigger it.  Removing it is not\npart of what this series is trying to do, anyway.  The theme of the\ntopic has always been \"optimize when we can\", at least to me.\n\nHaving said that, if we somehow can give the analysis you did above\nas hint to future developers, then they can start from there when\nthey consider if they can remove the \"fallback\" code.  It would help\neven it just said\n\n    /*\n     * NEEDSWORK: this might have become a dead code after\n     * optimization to grab symref target in apply_ref_filter().\n     */\n\nand nothing else.\n"},{"id":"500347","messageId":"pull.1712.v3.git.git.1723059768.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":"pull.1712.v2.git.git.1722524334.gitgitgadget@gmail.com","subject":"[PATCH v3 0/3] keep track of unresolved value of symbolic-ref in ref iterators","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-08-07T19:42:45Z","receivedAt":"2024-08-07T19:42:53Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"For reftable development, it's useful to be able to print out the direct\nvalue of a symbolic reference before resolution. This is currently possible\nwith git-for-each-ref, but since the iterators do not keep track of the\nvalue of the symbolic ref, a separate call needs to be made each time making\nit inefficient.\n\nAddress this inefficiency by keeping track of the value of the symbolic\nreference in the ref iterator. This patch series also ends with a commit to\nuse this value in the iterator through callbacks in ref-filter.c.\n\nThis series started with [1] but I decided to send a separate patch series\nsince it is substantially different.\n\nBenchmarking shows that with these changes, we experience a speedup in\ngit-for-each-ref(1) on a repository with many symbolic refs:\n\n$ hyperfine --warmup 5 \"git for-each-ref --format='%(refname) %(objectname)\n%(symref)'\" \"~/Projects/git/git for-each-ref --format='%(refname)\n%(objectname) %(symref)'\" Benchmark 1: git for-each-ref --format='%(refname)\n%(objectname) %(symref)' Time (mean ± σ): 905.1 ms ± 13.2 ms [User: 56.3 ms,\nSystem: 628.6 ms] Range (min … max): 893.4 ms … 936.9 ms 10 runs\n\nBenchmark 2: ~/Projects/git/git for-each-ref --format='%(refname)\n%(objectname) %(symref)' Time (mean ± σ): 482.2 ms ± 26.4 ms [User: 34.7 ms,\nSystem: 410.6 ms] Range (min … max): 459.4 ms … 541.8 ms 10 runs\n\nSummary ~/Projects/git/git for-each-ref --format='%(refname) %(objectname)\n%(symref)' ran 1.88 ± 0.11 times faster than git for-each-ref\n--format='%(refname) %(objectname) %(symref)'\n\nChanges since V2:\n\n * Style tweaks\n * Added a NEEDSWORK comment around fallback code\n * Stay consistent in the meaning of a non-NULL referent member in the\n   iterator\n\nChanges since V1\n\n * Use the return value from refs_resolve_ref_unsafe instead of using an out\n   parameter\n\n 1. https://lore.kernel.org/git/pull.1684.git.git.1709592718743.gitgitgadget@gmail.com/\n\nJohn Cai (3):\n  refs: keep track of unresolved reference value in iterators\n  refs: add referent to each_ref_fn\n  ref-filter: populate symref from iterator\n\n bisect.c                    |  3 ++-\n builtin/bisect.c            |  4 +++-\n builtin/checkout.c          |  2 +-\n builtin/describe.c          |  2 +-\n builtin/fetch.c             |  3 ++-\n builtin/fsck.c              |  4 ++--\n builtin/gc.c                |  1 +\n builtin/name-rev.c          |  2 +-\n builtin/pack-objects.c      |  5 +++--\n builtin/receive-pack.c      |  2 +-\n builtin/remote.c            |  4 +++-\n builtin/repack.c            |  1 +\n builtin/replace.c           |  1 +\n builtin/rev-parse.c         |  4 ++--\n builtin/show-branch.c       |  8 ++++----\n builtin/show-ref.c          |  3 ++-\n builtin/submodule--helper.c |  1 +\n builtin/worktree.c          |  1 +\n commit-graph.c              |  1 +\n delta-islands.c             |  2 +-\n fetch-pack.c                |  2 ++\n help.c                      |  2 +-\n http-backend.c              |  4 ++--\n log-tree.c                  |  2 +-\n ls-refs.c                   |  4 ++--\n midx-write.c                |  2 +-\n negotiator/default.c        |  2 +-\n negotiator/skipping.c       |  2 +-\n notes.c                     |  2 +-\n object-name.c               |  2 +-\n pseudo-merge.c              |  1 +\n reachable.c                 |  2 +-\n ref-filter.c                | 17 ++++++++++++-----\n reflog.c                    |  1 +\n refs.c                      | 14 +++++++-------\n refs.h                      |  2 +-\n refs/files-backend.c        | 21 ++++++++++++++++-----\n refs/iterator.c             |  4 +++-\n refs/ref-cache.c            |  6 ++++++\n refs/ref-cache.h            |  2 ++\n refs/refs-internal.h        |  1 +\n refs/reftable-backend.c     | 10 ++++++++--\n remote.c                    |  4 ++--\n replace-object.c            |  1 +\n revision.c                  |  2 +-\n server-info.c               |  2 +-\n shallow.c                   |  2 ++\n submodule.c                 |  2 ++\n t/helper/test-ref-store.c   |  2 +-\n upload-pack.c               |  8 ++++----\n walker.c                    |  1 +\n worktree.c                  |  2 +-\n 52 files changed, 122 insertions(+), 63 deletions(-)\n\n\nbase-commit: 557ae147e6cdc9db121269b058c757ac5092f9c9\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1712%2Fjohn-cai%2Fjc%2Fsymbolic-ref-in-iterator-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1712/john-cai/jc/symbolic-ref-in-iterator-v3\nPull-Request: https://github.com/git/git/pull/1712\n\nRange-diff vs v2:\n\n 1:  ac0957c9e6a ! 1:  fc3defd9c47 refs: keep track of unresolved reference value in iterators\n     @@ refs/files-backend.c: static void loose_fill_ref_dir_regular_file(struct files_r\n      -\n      -\tif (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,\n      -\t\t\t\t     &oid, &flag)) {\n     -+\tconst char* referent = refs_resolve_ref_unsafe(&refs->base,\n     ++\tconst char *referent = refs_resolve_ref_unsafe(&refs->base,\n      +\t\t\t\t\t\t       refname,\n      +\t\t\t\t\t\t       RESOLVE_REF_READING,\n      +\t\t\t\t\t\t       &oid, &flag);\n     @@ refs/files-backend.c: static void loose_fill_ref_dir_regular_file(struct files_r\n       \t}\n      -\tadd_entry_to_dir(dir, create_ref_entry(refname, &oid, flag));\n      +\n     ++\tif (!(flag & REF_ISSYMREF))\n     ++\t\treferent = NULL;\n     ++\n      +\tadd_entry_to_dir(dir, create_ref_entry(refname, referent, &oid, flag));\n       }\n       \n     @@ refs/files-backend.c: static int files_ref_iterator_advance(struct ref_iterator\n       \t\titer->base.flags = iter->iter0->flags;\n      +\t\tif (iter->iter0->flags & REF_ISSYMREF)\n      +\t\t\titer->base.referent = iter->iter0->referent;\n     ++\t\telse\n     ++\t\t\titer->base.referent = NULL;\n      +\n       \t\treturn ITER_OK;\n       \t}\n       \n      \n       ## refs/iterator.c ##\n     -@@\n     - #include \"refs.h\"\n     - #include \"refs/refs-internal.h\"\n     - #include \"iterator.h\"\n     -+#include \"strbuf.h\"\n     - \n     - int ref_iterator_advance(struct ref_iterator *ref_iterator)\n     - {\n      @@ refs/iterator.c: void base_ref_iterator_init(struct ref_iterator *iter,\n       {\n       \titer->vtable = vtable;\n     @@ refs/ref-cache.c: struct ref_entry *create_ref_entry(const char *refname,\n       \tFLEX_ALLOC_STR(ref, name, refname);\n       \toidcpy(&ref->u.value.oid, oid);\n       \tref->flag = flag;\n     -+\n     -+\tif (flag & REF_ISSYMREF)\n     -+\t\tref->u.value.referent = xstrdup_or_null(referent);\n     ++\tref->u.value.referent = xstrdup_or_null(referent);\n      +\n       \treturn ref;\n       }\n     @@ refs/refs-internal.h: enum do_for_each_ref_flags {\n       };\n      \n       ## refs/reftable-backend.c ##\n     +@@ refs/reftable-backend.c: static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)\n     + \tstruct reftable_ref_iterator *iter =\n     + \t\t(struct reftable_ref_iterator *)ref_iterator;\n     + \tstruct reftable_ref_store *refs = iter->refs;\n     ++\tconst char *referent = NULL;\n     + \n     + \twhile (!iter->err) {\n     + \t\tint flags = 0;\n      @@ refs/reftable-backend.c: static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)\n       \t\t\t\tthe_repository->hash_algo);\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\t\t\t\t     RESOLVE_REF_READING, &iter->oid, &flags))\n     -+\t\t\titer->base.referent = refs_resolve_ref_unsafe(&iter->refs->base,\n     ++\t\t\treferent = refs_resolve_ref_unsafe(&iter->refs->base,\n      +\t\t\t\t\t\t\t\t      iter->ref.refname,\n      +\t\t\t\t\t\t\t\t      RESOLVE_REF_READING,\n      +\t\t\t\t\t\t\t\t      &iter->oid,\n      +\t\t\t\t\t\t\t\t      &flags);\n     -+\t\t\tif (!iter->base.referent)\n     ++\t\t\tif (!referent)\n       \t\t\t\toidclr(&iter->oid, the_repository->hash_algo);\n       \t\t\tbreak;\n       \t\tdefault:\n     +@@ refs/reftable-backend.c: 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 = referent;\n     + \t\titer->base.oid = &iter->oid;\n     + \t\titer->base.flags = flags;\n     + \n 2:  1f3a604fae7 = 2:  0b6e732ad7e refs: add referent to each_ref_fn\n 3:  3e147e7d850 ! 3:  83b70ab8287 ref-filter: populate symref from iterator\n     @@ Commit message\n          Signed-off-by: John Cai <johncai86@gmail.com>\n      \n       ## ref-filter.c ##\n     +@@ ref-filter.c: static int populate_value(struct ref_array_item *ref, struct strbuf *err)\n     + \n     + \tCALLOC_ARRAY(ref->value, used_atom_cnt);\n     + \n     ++\t/**\n     ++\t * NEEDSWORK: The following code might be unncessary if all codepaths\n     ++\t * that call populate_value() populates the symref member of ref_array_item\n     ++\t * like in apply_ref_filter(). Currently pretty_print_ref() is the only codepath\n     ++\t * that calls populate_value() without first populating symref.\n     ++\t */\n     + \tif (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {\n     + \t\tref->symref = refs_resolve_refdup(get_main_ref_store(the_repository),\n     + \t\t\t\t\t\t  ref->refname,\n      @@ ref-filter.c: static int filter_ref_kind(struct ref_filter *filter, const char *refname)\n       \treturn ref_kind_from_refname(refname);\n       }\n     @@ ref-filter.c: static struct ref_array_item *apply_ref_filter(const char *refname\n       \tref->commit = commit;\n       \tref->flag = flag;\n       \tref->kind = kind;\n     -+\tif (flag & REF_ISSYMREF)\n     -+\t\tref->symref = xstrdup_or_null(referent);\n     ++\tref->symref = xstrdup_or_null(referent);\n       \n       \treturn ref;\n       }\n\n-- \ngitgitgadget\n"},{"id":"500348","messageId":"fc3defd9c47e32bb23ba0fcb5c885274f3706b23.1723059769.git.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":"pull.1712.v3.git.git.1723059768.gitgitgadget@gmail.com","subject":"[PATCH v3 1/3] refs: keep track of unresolved reference value in iterators","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-08-07T19:42:46Z","receivedAt":"2024-08-07T19:42:54Z","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 iterators for both the\nfiles backend and the reftable backend.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n refs/files-backend.c    | 19 +++++++++++++++----\n refs/iterator.c         |  2 ++\n refs/ref-cache.c        |  6 ++++++\n refs/ref-cache.h        |  2 ++\n refs/refs-internal.h    |  1 +\n refs/reftable-backend.c | 10 ++++++++--\n 6 files changed, 34 insertions(+), 6 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex aa52d9be7c7..5ed69c23f74 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -245,9 +245,11 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n {\n \tstruct object_id oid;\n \tint flag;\n-\n-\tif (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,\n-\t\t\t\t     &oid, &flag)) {\n+\tconst char *referent = refs_resolve_ref_unsafe(&refs->base,\n+\t\t\t\t\t\t       refname,\n+\t\t\t\t\t\t       RESOLVE_REF_READING,\n+\t\t\t\t\t\t       &oid, &flag);\n+\tif (!referent) {\n \t\toidclr(&oid, the_repository->hash_algo);\n \t\tflag |= REF_ISBROKEN;\n \t} else if (is_null_oid(&oid)) {\n@@ -268,7 +270,11 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n \t\toidclr(&oid, the_repository->hash_algo);\n \t\tflag |= REF_BAD_NAME | REF_ISBROKEN;\n \t}\n-\tadd_entry_to_dir(dir, create_ref_entry(refname, &oid, flag));\n+\n+\tif (!(flag & REF_ISSYMREF))\n+\t\treferent = NULL;\n+\n+\tadd_entry_to_dir(dir, create_ref_entry(refname, referent, &oid, flag));\n }\n \n /*\n@@ -886,6 +892,11 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \t\titer->base.refname = iter->iter0->refname;\n \t\titer->base.oid = iter->iter0->oid;\n \t\titer->base.flags = iter->iter0->flags;\n+\t\tif (iter->iter0->flags & REF_ISSYMREF)\n+\t\t\titer->base.referent = iter->iter0->referent;\n+\t\telse\n+\t\t\titer->base.referent = NULL;\n+\n \t\treturn ITER_OK;\n \t}\n \ndiff --git a/refs/iterator.c b/refs/iterator.c\nindex d355ebf0d59..75fbe5d72ab 100644\n--- a/refs/iterator.c\n+++ b/refs/iterator.c\n@@ -29,6 +29,7 @@ void base_ref_iterator_init(struct ref_iterator *iter,\n {\n \titer->vtable = vtable;\n \titer->refname = NULL;\n+\titer->referent = NULL;\n \titer->oid = NULL;\n \titer->flags = 0;\n }\n@@ -199,6 +200,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 4ce519bbc85..bf80a62af17 100644\n--- a/refs/ref-cache.c\n+++ b/refs/ref-cache.c\n@@ -5,6 +5,7 @@\n #include \"refs-internal.h\"\n #include \"ref-cache.h\"\n #include \"../iterator.h\"\n+#include \"../strbuf.h\"\n \n void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry)\n {\n@@ -34,6 +35,7 @@ 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@@ -41,6 +43,8 @@ struct ref_entry *create_ref_entry(const char *refname,\n \tFLEX_ALLOC_STR(ref, name, refname);\n \toidcpy(&ref->u.value.oid, oid);\n \tref->flag = flag;\n+\tref->u.value.referent = xstrdup_or_null(referent);\n+\n \treturn ref;\n }\n \n@@ -66,6 +70,7 @@ static void free_ref_entry(struct ref_entry *entry)\n \t\t */\n \t\tclear_ref_dir(&entry->u.subdir);\n \t}\n+\tfree(entry->u.value.referent);\n \tfree(entry);\n }\n \n@@ -431,6 +436,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \t\t\tlevel->index = -1;\n \t\t} else {\n \t\t\titer->base.refname = entry->name;\n+\t\t\titer->base.referent = entry->u.value.referent;\n \t\t\titer->base.oid = &entry->u.value.oid;\n \t\t\titer->base.flags = entry->flag;\n \t\t\treturn ITER_OK;\ndiff --git a/refs/ref-cache.h b/refs/ref-cache.h\nindex 31ebe24f6cf..5f04e518c37 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+\tchar *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 fa975d69aaa..117ec233848 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -299,6 +299,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 fbe74c239d3..50a072b97b0 100644\n--- a/refs/reftable-backend.c\n+++ b/refs/reftable-backend.c\n@@ -455,6 +455,7 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \tstruct reftable_ref_iterator *iter =\n \t\t(struct reftable_ref_iterator *)ref_iterator;\n \tstruct reftable_ref_store *refs = iter->refs;\n+\tconst char *referent = NULL;\n \n \twhile (!iter->err) {\n \t\tint flags = 0;\n@@ -494,8 +495,12 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \t\t\t\tthe_repository->hash_algo);\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\t\t\t\t     RESOLVE_REF_READING, &iter->oid, &flags))\n+\t\t\treferent = refs_resolve_ref_unsafe(&iter->refs->base,\n+\t\t\t\t\t\t\t\t      iter->ref.refname,\n+\t\t\t\t\t\t\t\t      RESOLVE_REF_READING,\n+\t\t\t\t\t\t\t\t      &iter->oid,\n+\t\t\t\t\t\t\t\t      &flags);\n+\t\t\tif (!referent)\n \t\t\t\toidclr(&iter->oid, the_repository->hash_algo);\n \t\t\tbreak;\n \t\tdefault:\n@@ -523,6 +528,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 = referent;\n \t\titer->base.oid = &iter->oid;\n \t\titer->base.flags = flags;\n \n-- \ngitgitgadget\n\n"},{"id":"500350","messageId":"0b6e732ad7e6c37dc02c53f6b48404846576c0b2.1723059769.git.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":"pull.1712.v3.git.git.1723059768.gitgitgadget@gmail.com","subject":"[PATCH v3 2/3] refs: add referent to each_ref_fn","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-08-07T19:42:47Z","receivedAt":"2024-08-07T19:42:55Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"From: John Cai <johncai86@gmail.com>\n\nAdd a parameter to each_ref_fn so that callers to the ref APIs\nthat use this function as a callback can have acess to the\nunresolved value of a symbolic ref.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n bisect.c                    |  3 ++-\n builtin/bisect.c            |  4 +++-\n builtin/checkout.c          |  2 +-\n builtin/describe.c          |  2 +-\n builtin/fetch.c             |  3 ++-\n builtin/fsck.c              |  4 ++--\n builtin/gc.c                |  1 +\n builtin/name-rev.c          |  2 +-\n builtin/pack-objects.c      |  5 +++--\n builtin/receive-pack.c      |  2 +-\n builtin/remote.c            |  4 +++-\n builtin/repack.c            |  1 +\n builtin/replace.c           |  1 +\n builtin/rev-parse.c         |  4 ++--\n builtin/show-branch.c       |  8 ++++----\n builtin/show-ref.c          |  3 ++-\n builtin/submodule--helper.c |  1 +\n builtin/worktree.c          |  1 +\n commit-graph.c              |  1 +\n delta-islands.c             |  2 +-\n fetch-pack.c                |  2 ++\n help.c                      |  2 +-\n http-backend.c              |  4 ++--\n log-tree.c                  |  2 +-\n ls-refs.c                   |  4 ++--\n midx-write.c                |  2 +-\n negotiator/default.c        |  2 +-\n negotiator/skipping.c       |  2 +-\n notes.c                     |  2 +-\n object-name.c               |  2 +-\n pseudo-merge.c              |  1 +\n reachable.c                 |  2 +-\n ref-filter.c                |  4 ++--\n reflog.c                    |  1 +\n refs.c                      | 14 +++++++-------\n refs.h                      |  2 +-\n refs/files-backend.c        |  2 +-\n refs/iterator.c             |  2 +-\n remote.c                    |  4 ++--\n replace-object.c            |  1 +\n revision.c                  |  2 +-\n server-info.c               |  2 +-\n shallow.c                   |  2 ++\n submodule.c                 |  2 ++\n t/helper/test-ref-store.c   |  2 +-\n upload-pack.c               |  8 ++++----\n walker.c                    |  1 +\n worktree.c                  |  2 +-\n 48 files changed, 78 insertions(+), 54 deletions(-)\n\ndiff --git a/bisect.c b/bisect.c\nindex 135f94ba097..4406fc44cf9 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -448,7 +448,7 @@ void find_bisection(struct commit_list **commit_list, int *reaches,\n \tclear_commit_weight(&commit_weight);\n }\n \n-static int register_ref(const char *refname, const struct object_id *oid,\n+static int register_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\tint flags UNUSED, void *cb_data UNUSED)\n {\n \tstruct strbuf good_prefix = STRBUF_INIT;\n@@ -1170,6 +1170,7 @@ int estimate_bisect_steps(int all)\n }\n \n static int mark_for_removal(const char *refname,\n+\t\t\t    const char *referent UNUSED,\n \t\t\t    const struct object_id *oid UNUSED,\n \t\t\t    int flag UNUSED, void *cb_data)\n {\ndiff --git a/builtin/bisect.c b/builtin/bisect.c\nindex dabce9b542b..453a6cccd70 100644\n--- a/builtin/bisect.c\n+++ b/builtin/bisect.c\n@@ -356,6 +356,7 @@ static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)\n }\n \n static int inc_nr(const char *refname UNUSED,\n+\t\t  const char *referent UNUSED,\n \t\t  const struct object_id *oid UNUSED,\n \t\t  int flag UNUSED, void *cb_data)\n {\n@@ -545,7 +546,7 @@ finish:\n \treturn res;\n }\n \n-static int add_bisect_ref(const char *refname, const struct object_id *oid,\n+static int add_bisect_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flags UNUSED, void *cb)\n {\n \tstruct add_bisect_ref_data *data = cb;\n@@ -1162,6 +1163,7 @@ static int bisect_visualize(struct bisect_terms *terms, int argc,\n }\n \n static int get_first_good(const char *refname UNUSED,\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/builtin/checkout.c b/builtin/checkout.c\nindex 3cf44b4683a..562778885e1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1045,7 +1045,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,\n \t\treport_tracking(new_branch_info);\n }\n \n-static int add_pending_uninteresting_ref(const char *refname,\n+static int add_pending_uninteresting_ref(const char *refname, const char *referent UNUSED,\n \t\t\t\t\t const struct object_id *oid,\n \t\t\t\t\t int flags UNUSED, void *cb_data)\n {\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex e5287eddf2d..d95a9109eed 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -145,7 +145,7 @@ static void add_to_known_names(const char *path,\n \t}\n }\n \n-static int get_name(const char *path, const struct object_id *oid,\n+static int get_name(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flag UNUSED, void *cb_data UNUSED)\n {\n \tint is_tag = 0;\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 693f02b9580..c297569a473 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -286,7 +286,7 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,\n \treturn ent;\n }\n \n-static int add_one_refname(const char *refname,\n+static int add_one_refname(const char *refname, const char *referent UNUSED,\n \t\t\t   const struct object_id *oid,\n \t\t\t   int flag UNUSED, void *cbdata)\n {\n@@ -1464,6 +1464,7 @@ static void set_option(struct transport *transport, const char *name, const char\n \n \n static int add_oid(const char *refname UNUSED,\n+\t\t   const char *referent UNUSED,\n \t\t   const struct object_id *oid,\n \t\t   int flags UNUSED, void *cb_data)\n {\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex d13a226c2ed..ef6ee1268bd 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -521,7 +521,7 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)\n \treturn 0;\n }\n \n-static int fsck_handle_ref(const char *refname, const struct object_id *oid,\n+static int fsck_handle_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t   int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object *obj;\n@@ -576,7 +576,7 @@ static void get_default_heads(void)\n \t\tstrbuf_worktree_ref(wt, &ref, \"HEAD\");\n \t\tfsck_head_link(ref.buf, &head_points_at, &head_oid);\n \t\tif (head_points_at && !is_null_oid(&head_oid))\n-\t\t\tfsck_handle_ref(ref.buf, &head_oid, 0, NULL);\n+\t\t\tfsck_handle_ref(ref.buf, NULL, &head_oid, 0, NULL);\n \t\tstrbuf_release(&ref);\n \n \t\tif (include_reflogs)\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex 72bac2554fc..65be7c31b33 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -836,6 +836,7 @@ struct cg_auto_data {\n };\n \n static int dfs_on_ref(const char *refname UNUSED,\n+\t\t      const char *referent UNUSED,\n \t\t      const struct object_id *oid,\n \t\t      int flags UNUSED,\n \t\t      void *cb_data)\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex 70e9ec4e477..52016f1cf7d 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -337,7 +337,7 @@ static int cmp_by_tag_and_age(const void *a_, const void *b_)\n \treturn a->taggerdate != b->taggerdate;\n }\n \n-static int name_ref(const char *path, const struct object_id *oid,\n+static int name_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flags UNUSED, void *cb_data)\n {\n \tstruct object *o = parse_object(the_repository, oid);\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex f395488971f..c481feadbfa 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -771,7 +771,7 @@ static enum write_one_status write_one(struct hashfile *f,\n \treturn WRITE_ONE_WRITTEN;\n }\n \n-static int mark_tagged(const char *path UNUSED, const struct object_id *oid,\n+static int mark_tagged(const char *path UNUSED, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object_id peeled;\n@@ -3129,7 +3129,7 @@ static void add_tag_chain(const struct object_id *oid)\n \t}\n }\n \n-static int add_ref_tag(const char *tag UNUSED, const struct object_id *oid,\n+static int add_ref_tag(const char *tag UNUSED, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object_id peeled;\n@@ -4076,6 +4076,7 @@ static void record_recent_commit(struct commit *commit, void *data UNUSED)\n }\n \n static int mark_bitmap_preferred_tip(const char *refname,\n+\t\t\t\t     const char *referent UNUSED,\n \t\t\t\t     const struct object_id *oid,\n \t\t\t\t     int flags UNUSED,\n \t\t\t\t     void *data UNUSED)\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 339524ae2a8..6e0f462efbb 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -300,7 +300,7 @@ static void show_ref(const char *path, const struct object_id *oid)\n \t}\n }\n \n-static int show_ref_cb(const char *path_full, const struct object_id *oid,\n+static int show_ref_cb(const char *path_full, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED, void *data)\n {\n \tstruct oidset *seen = data;\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 08292498bde..9a3db1b0f13 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -543,6 +543,7 @@ struct branches_for_remote {\n };\n \n static int add_branch_for_removal(const char *refname,\n+\t\t\t\t  const char *referent UNUSED,\n \t\t\t\t  const struct object_id *oid UNUSED,\n \t\t\t\t  int flags UNUSED, void *cb_data)\n {\n@@ -585,7 +586,7 @@ struct rename_info {\n \tuint32_t symrefs_nr;\n };\n \n-static int read_remote_branches(const char *refname,\n+static int read_remote_branches(const char *refname, const char *referent UNUSED,\n \t\t\t\tconst struct object_id *oid UNUSED,\n \t\t\t\tint flags UNUSED, void *cb_data)\n {\n@@ -971,6 +972,7 @@ static void free_remote_ref_states(struct ref_states *states)\n }\n \n static int append_ref_to_tracked_list(const char *refname,\n+\t\t\t\t      const char *referent UNUSED,\n \t\t\t\t      const struct object_id *oid UNUSED,\n \t\t\t\t      int flags, void *cb_data)\n {\ndiff --git a/builtin/repack.c b/builtin/repack.c\nindex f0317fa94ad..d9ea6cecbf9 100644\n--- a/builtin/repack.c\n+++ b/builtin/repack.c\n@@ -667,6 +667,7 @@ struct midx_snapshot_ref_data {\n };\n \n static int midx_snapshot_ref_one(const char *refname UNUSED,\n+\t\t\t\t const char *referent UNUSED,\n \t\t\t\t const struct object_id *oid,\n \t\t\t\t int flag UNUSED, void *_data)\n {\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex 1ef833c07f5..34cc4672bc1 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -49,6 +49,7 @@ struct show_data {\n };\n \n static int show_reference(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/builtin/rev-parse.c b/builtin/rev-parse.c\nindex 2e64f5bda70..a7084ca157f 100644\n--- a/builtin/rev-parse.c\n+++ b/builtin/rev-parse.c\n@@ -211,7 +211,7 @@ static int show_default(void)\n \treturn 0;\n }\n \n-static int show_reference(const char *refname, const struct object_id *oid,\n+static int show_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flag UNUSED, void *cb_data UNUSED)\n {\n \tif (ref_excluded(&ref_excludes, refname))\n@@ -220,7 +220,7 @@ static int show_reference(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n-static int anti_reference(const char *refname, const struct object_id *oid,\n+static int anti_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flag UNUSED, void *cb_data UNUSED)\n {\n \tshow_rev(REVERSED, oid, refname);\ndiff --git a/builtin/show-branch.c b/builtin/show-branch.c\nindex d72f4cb98de..3427c1572e5 100644\n--- a/builtin/show-branch.c\n+++ b/builtin/show-branch.c\n@@ -410,7 +410,7 @@ static int append_ref(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n-static int append_head_ref(const char *refname, const struct object_id *oid,\n+static int append_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t   int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object_id tmp;\n@@ -425,7 +425,7 @@ static int append_head_ref(const char *refname, const struct object_id *oid,\n \treturn append_ref(refname + ofs, oid, 0);\n }\n \n-static int append_remote_ref(const char *refname, const struct object_id *oid,\n+static int append_remote_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t     int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object_id tmp;\n@@ -451,7 +451,7 @@ static int append_tag_ref(const char *refname, const struct object_id *oid,\n static const char *match_ref_pattern = NULL;\n static int match_ref_slash = 0;\n \n-static int append_matching_ref(const char *refname, const struct object_id *oid,\n+static int append_matching_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t       int flag, void *cb_data)\n {\n \t/* we want to allow pattern hold/<asterisk> to show all\n@@ -468,7 +468,7 @@ static int append_matching_ref(const char *refname, const struct object_id *oid,\n \tif (wildmatch(match_ref_pattern, tail, 0))\n \t\treturn 0;\n \tif (starts_with(refname, \"refs/heads/\"))\n-\t\treturn append_head_ref(refname, oid, flag, cb_data);\n+\t\treturn append_head_ref(refname, NULL, oid, flag, cb_data);\n \tif (starts_with(refname, \"refs/tags/\"))\n \t\treturn append_tag_ref(refname, oid, flag, cb_data);\n \treturn append_ref(refname, oid, 0);\ndiff --git a/builtin/show-ref.c b/builtin/show-ref.c\nindex 839a5c29f31..04e0db707c6 100644\n--- a/builtin/show-ref.c\n+++ b/builtin/show-ref.c\n@@ -63,7 +63,7 @@ struct show_ref_data {\n \tint show_head;\n };\n \n-static int show_ref(const char *refname, const struct object_id *oid,\n+static int show_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flag UNUSED, void *cbdata)\n {\n \tstruct show_ref_data *data = cbdata;\n@@ -97,6 +97,7 @@ match:\n }\n \n static int add_existing(const char *refname,\n+\t\t\tconst char *referent UNUSED,\n \t\t\tconst struct object_id *oid UNUSED,\n \t\t\tint flag UNUSED, void *cbdata)\n {\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 880ab4456ee..1cdcc465b1c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -609,6 +609,7 @@ static void print_status(unsigned int flags, char state, const char *path,\n }\n \n static int handle_submodule_head_ref(const char *refname UNUSED,\n+\t\t\t\t     const char *referent UNUSED,\n \t\t\t\t     const struct object_id *oid,\n \t\t\t\t     int flags UNUSED,\n \t\t\t\t     void *cb_data)\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 1d51e54fcdc..2e16218c655 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -626,6 +626,7 @@ static void print_preparing_worktree_line(int detach,\n  * Returns 0 on failure and non-zero on success.\n  */\n static int first_valid_ref(const char *refname UNUSED,\n+\t\t\t   const char *referent UNUSED,\n \t\t\t   const struct object_id *oid UNUSED,\n \t\t\t   int flags UNUSED,\n \t\t\t   void *cb_data UNUSED)\ndiff --git a/commit-graph.c b/commit-graph.c\nindex 79b0e72cc42..aa6e08eb39c 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -1847,6 +1847,7 @@ struct refs_cb_data {\n };\n \n static int add_ref_to_set(const char *refname UNUSED,\n+\t\t\t  const char *referent UNUSED,\n \t\t\t  const struct object_id *oid,\n \t\t\t  int flags UNUSED, void *cb_data)\n {\ndiff --git a/delta-islands.c b/delta-islands.c\nindex ffe1ca28144..84435512593 100644\n--- a/delta-islands.c\n+++ b/delta-islands.c\n@@ -390,7 +390,7 @@ static void add_ref_to_island(kh_str_t *remote_islands, const char *island_name,\n \trl->hash += sha_core;\n }\n \n-static int find_island_for_ref(const char *refname, const struct object_id *oid,\n+static int find_island_for_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t       int flags UNUSED, void *cb)\n {\n \tstruct island_load_data *ild = cb;\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 732511604b1..58b4581ad80 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -183,6 +183,7 @@ static int rev_list_insert_ref(struct fetch_negotiator *negotiator,\n }\n \n static int rev_list_insert_ref_oid(const char *refname UNUSED,\n+\t\t\t\t   const char *referent UNUSED,\n \t\t\t\t   const struct object_id *oid,\n \t\t\t\t   int flag UNUSED,\n \t\t\t\t   void *cb_data)\n@@ -610,6 +611,7 @@ static int mark_complete(const struct object_id *oid)\n }\n \n static int mark_complete_oid(const char *refname UNUSED,\n+\t\t\t     const char *referent UNUSED,\n \t\t\t     const struct object_id *oid,\n \t\t\t     int flag UNUSED,\n \t\t\t     void *cb_data UNUSED)\ndiff --git a/help.c b/help.c\nindex a6b4d3b1ebd..c03863f2265 100644\n--- a/help.c\n+++ b/help.c\n@@ -804,7 +804,7 @@ struct similar_ref_cb {\n \tstruct string_list *similar_refs;\n };\n \n-static int append_similar_ref(const char *refname,\n+static int append_similar_ref(const char *refname, const char *referent UNUSED,\n \t\t\t      const struct object_id *oid UNUSED,\n \t\t\t      int flags UNUSED, void *cb_data)\n {\ndiff --git a/http-backend.c b/http-backend.c\nindex 461424972b6..79ce097359b 100644\n--- a/http-backend.c\n+++ b/http-backend.c\n@@ -512,7 +512,7 @@ static void run_service(const char **argv, int buffer_input)\n \t\texit(1);\n }\n \n-static int show_text_ref(const char *name, const struct object_id *oid,\n+static int show_text_ref(const char *name, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t int flag UNUSED, void *cb_data)\n {\n \tconst char *name_nons = strip_namespace(name);\n@@ -568,7 +568,7 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)\n \tstrbuf_release(&buf);\n }\n \n-static int show_head_ref(const char *refname, const struct object_id *oid,\n+static int show_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t int flag, void *cb_data)\n {\n \tstruct strbuf *buf = cb_data;\ndiff --git a/log-tree.c b/log-tree.c\nindex 52feec4356a..40306434556 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -145,7 +145,7 @@ static int ref_filter_match(const char *refname,\n \treturn 1;\n }\n \n-static int add_ref_decoration(const char *refname, const struct object_id *oid,\n+static int add_ref_decoration(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t      int flags UNUSED,\n \t\t\t      void *cb_data)\n {\ndiff --git a/ls-refs.c b/ls-refs.c\nindex 2dd925b43d0..c824aea7146 100644\n--- a/ls-refs.c\n+++ b/ls-refs.c\n@@ -77,7 +77,7 @@ struct ls_refs_data {\n \tunsigned unborn : 1;\n };\n \n-static int send_ref(const char *refname, const struct object_id *oid,\n+static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flag, void *cb_data)\n {\n \tstruct ls_refs_data *data = cb_data;\n@@ -135,7 +135,7 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)\n \toid_is_null = is_null_oid(&oid);\n \tif (!oid_is_null ||\n \t    (data->unborn && data->symrefs && (flag & REF_ISSYMREF)))\n-\t\tsend_ref(namespaced.buf, oid_is_null ? NULL : &oid, flag, data);\n+\t\tsend_ref(namespaced.buf, NULL, oid_is_null ? NULL : &oid, flag, data);\n \tstrbuf_release(&namespaced);\n }\n \ndiff --git a/midx-write.c b/midx-write.c\nindex 478b42e7209..56d1717f9f1 100644\n--- a/midx-write.c\n+++ b/midx-write.c\n@@ -655,7 +655,7 @@ static void prepare_midx_packing_data(struct packing_data *pdata,\n \ttrace2_region_leave(\"midx\", \"prepare_midx_packing_data\", the_repository);\n }\n \n-static int add_ref_to_pending(const char *refname,\n+static int add_ref_to_pending(const char *refname, const char *referent UNUSED,\n \t\t\t      const struct object_id *oid,\n \t\t\t      int flag, void *cb_data)\n {\ndiff --git a/negotiator/default.c b/negotiator/default.c\nindex e3fa5c33249..c479da9b091 100644\n--- a/negotiator/default.c\n+++ b/negotiator/default.c\n@@ -38,7 +38,7 @@ static void rev_list_push(struct negotiation_state *ns,\n \t}\n }\n \n-static int clear_marks(const char *refname, const struct object_id *oid,\n+static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED,\n \t\t       void *cb_data UNUSED)\n {\ndiff --git a/negotiator/skipping.c b/negotiator/skipping.c\nindex f109928ad0b..f65d47858b4 100644\n--- a/negotiator/skipping.c\n+++ b/negotiator/skipping.c\n@@ -75,7 +75,7 @@ static struct entry *rev_list_push(struct data *data, struct commit *commit, int\n \treturn entry;\n }\n \n-static int clear_marks(const char *refname, const struct object_id *oid,\n+static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED,\n \t\t       void *cb_data UNUSED)\n {\ndiff --git a/notes.c b/notes.c\nindex 1ba6412aae8..da42df282d5 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -932,7 +932,7 @@ out:\n \treturn ret;\n }\n \n-static int string_list_add_one_ref(const char *refname,\n+static int string_list_add_one_ref(const char *refname, const char *referent UNUSED,\n \t\t\t\t   const struct object_id *oid UNUSED,\n \t\t\t\t   int flag UNUSED, void *cb)\n {\ndiff --git a/object-name.c b/object-name.c\nindex 527b853ac45..7cca43020f2 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -1365,7 +1365,7 @@ struct handle_one_ref_cb {\n \tstruct commit_list **list;\n };\n \n-static int handle_one_ref(const char *path, const struct object_id *oid,\n+static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flag UNUSED,\n \t\t\t  void *cb_data)\n {\ndiff --git a/pseudo-merge.c b/pseudo-merge.c\nindex f0fde13c471..77a83b9c5ce 100644\n--- a/pseudo-merge.c\n+++ b/pseudo-merge.c\n@@ -201,6 +201,7 @@ void load_pseudo_merges_from_config(struct string_list *list)\n }\n \n static int find_pseudo_merge_group_for_ref(const char *refname,\n+\t\t\t\t\t   const char *referent UNUSED,\n \t\t\t\t\t   const struct object_id *oid,\n \t\t\t\t\t   int flags UNUSED,\n \t\t\t\t\t   void *_data)\ndiff --git a/reachable.c b/reachable.c\nindex 46613a6bb6f..3e9b3dd0a46 100644\n--- a/reachable.c\n+++ b/reachable.c\n@@ -79,7 +79,7 @@ static void add_rebase_files(struct rev_info *revs)\n \tfree_worktrees(worktrees);\n }\n \n-static int add_one_ref(const char *path, const struct object_id *oid,\n+static int add_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag, void *cb_data)\n {\n \tstruct rev_info *revs = (struct rev_info *)cb_data;\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 8c5e673fc0a..39997890feb 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -2865,7 +2865,7 @@ struct ref_filter_cbdata {\n  * A call-back given to for_each_ref().  Filter refs and keep them for\n  * later object processing.\n  */\n-static int filter_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_one(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\n@@ -2900,7 +2900,7 @@ struct ref_filter_and_format_cbdata {\n \t} internal;\n };\n \n-static int filter_and_format_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_and_format_one(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_and_format_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\ndiff --git a/reflog.c b/reflog.c\nindex 5ca944529b3..d592c1cfb56 100644\n--- a/reflog.c\n+++ b/reflog.c\n@@ -300,6 +300,7 @@ int should_expire_reflog_ent_verbose(struct object_id *ooid,\n }\n \n static int push_tip_to_list(const char *refname UNUSED,\n+\t\t\t    const char *referent UNUSED,\n \t\t\t    const struct object_id *oid,\n \t\t\t    int flags, void *cb_data)\n {\ndiff --git a/refs.c b/refs.c\nindex bb90a188758..0a5c12cf16b 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -414,7 +414,7 @@ int refs_ref_exists(struct ref_store *refs, const char *refname)\n \t\t\t\t\t NULL, NULL);\n }\n \n-static int for_each_filter_refs(const char *refname,\n+static int for_each_filter_refs(const char *refname, const char *referent,\n \t\t\t\tconst struct object_id *oid,\n \t\t\t\tint flags, void *data)\n {\n@@ -424,7 +424,7 @@ static int for_each_filter_refs(const char *refname,\n \t\treturn 0;\n \tif (filter->prefix)\n \t\tskip_prefix(refname, filter->prefix, &refname);\n-\treturn filter->fn(refname, oid, flags, filter->cb_data);\n+\treturn filter->fn(refname, referent, oid, flags, filter->cb_data);\n }\n \n struct warn_if_dangling_data {\n@@ -435,7 +435,7 @@ struct warn_if_dangling_data {\n \tconst char *msg_fmt;\n };\n \n-static int warn_if_dangling_symref(const char *refname,\n+static int warn_if_dangling_symref(const char *refname, const char *referent UNUSED,\n \t\t\t\t   const struct object_id *oid UNUSED,\n \t\t\t\t   int flags, void *cb_data)\n {\n@@ -506,7 +506,7 @@ int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_da\n \n \tstrbuf_addf(&buf, \"%sHEAD\", get_git_namespace());\n \tif (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag))\n-\t\tret = fn(buf.buf, &oid, flag, cb_data);\n+\t\tret = fn(buf.buf, NULL, &oid, flag, cb_data);\n \tstrbuf_release(&buf);\n \n \treturn ret;\n@@ -1547,7 +1547,7 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n \n \tif (refs_resolve_ref_unsafe(refs, \"HEAD\", RESOLVE_REF_READING,\n \t\t\t\t    &oid, &flag))\n-\t\treturn fn(\"HEAD\", &oid, flag, cb_data);\n+\t\treturn fn(\"HEAD\", NULL, &oid, flag, cb_data);\n \n \treturn 0;\n }\n@@ -2387,7 +2387,7 @@ struct do_for_each_reflog_help {\n \tvoid *cb_data;\n };\n \n-static int do_for_each_reflog_helper(const char *refname,\n+static int do_for_each_reflog_helper(const char *refname, 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)\n@@ -2593,7 +2593,7 @@ struct migration_data {\n \tstruct strbuf *errbuf;\n };\n \n-static int migrate_one_ref(const char *refname, const struct object_id *oid,\n+static int migrate_one_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t   int flags, void *cb_data)\n {\n \tstruct migration_data *data = cb_data;\ndiff --git a/refs.h b/refs.h\nindex 0ecba21b4a7..158e0a12197 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -298,7 +298,7 @@ struct ref_transaction;\n  * arguments is only guaranteed to be valid for the duration of a\n  * single callback invocation.\n  */\n-typedef int each_ref_fn(const char *refname,\n+typedef int each_ref_fn(const char *refname, const char *referent,\n \t\t\tconst struct object_id *oid, int flags, void *cb_data);\n \n /*\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 5ed69c23f74..ad5f0c21771 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -3046,7 +3046,7 @@ static int files_transaction_abort(struct ref_store *ref_store,\n \treturn 0;\n }\n \n-static int ref_present(const char *refname,\n+static int ref_present(const char *refname, const char *referent UNUSED,\n \t\t       const struct object_id *oid UNUSED,\n \t\t       int flags UNUSED,\n \t\t       void *cb_data)\ndiff --git a/refs/iterator.c b/refs/iterator.c\nindex 75fbe5d72ab..8e999d81fc6 100644\n--- a/refs/iterator.c\n+++ b/refs/iterator.c\n@@ -450,7 +450,7 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,\n \n \tcurrent_ref_iter = iter;\n \twhile ((ok = ref_iterator_advance(iter)) == ITER_OK) {\n-\t\tretval = fn(iter->refname, iter->oid, iter->flags, cb_data);\n+\t\tretval = fn(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/remote.c b/remote.c\nindex f43cf5e7a4d..4a1269a47ab 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -2354,7 +2354,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,\n \treturn 1;\n }\n \n-static int one_local_ref(const char *refname, const struct object_id *oid,\n+static int one_local_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t int flag UNUSED,\n \t\t\t void *cb_data)\n {\n@@ -2439,7 +2439,7 @@ struct stale_heads_info {\n \tstruct refspec *rs;\n };\n \n-static int get_stale_heads_cb(const char *refname, const struct object_id *oid,\n+static int get_stale_heads_cb(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t      int flags, void *cb_data)\n {\n \tstruct stale_heads_info *info = cb_data;\ndiff --git a/replace-object.c b/replace-object.c\nindex 59252d565ef..9a3cdd809a9 100644\n--- a/replace-object.c\n+++ b/replace-object.c\n@@ -9,6 +9,7 @@\n #include \"commit.h\"\n \n static int register_replace_ref(const 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)\ndiff --git a/revision.c b/revision.c\nindex 1c0192f5225..6b33bd814f7 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1648,7 +1648,7 @@ struct all_refs_cb {\n \tstruct worktree *wt;\n };\n \n-static int handle_one_ref(const char *path, const struct object_id *oid,\n+static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flag UNUSED,\n \t\t\t  void *cb_data)\n {\ndiff --git a/server-info.c b/server-info.c\nindex f61296a60d0..1508fa6f825 100644\n--- a/server-info.c\n+++ b/server-info.c\n@@ -147,7 +147,7 @@ out:\n \treturn ret;\n }\n \n-static int add_info_ref(const char *path, const struct object_id *oid,\n+static int add_info_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t\tint flag UNUSED,\n \t\t\tvoid *cb_data)\n {\ndiff --git a/shallow.c b/shallow.c\nindex 31a6ca40fe2..b8cd051e3b6 100644\n--- a/shallow.c\n+++ b/shallow.c\n@@ -612,6 +612,7 @@ static void paint_down(struct paint_info *info, const struct object_id *oid,\n }\n \n static int mark_uninteresting(const char *refname UNUSED,\n+\t\t\t      const char *referent UNUSED,\n \t\t\t      const struct object_id *oid,\n \t\t\t      int flags UNUSED,\n \t\t\t      void *cb_data UNUSED)\n@@ -727,6 +728,7 @@ struct commit_array {\n };\n \n static int add_ref(const char *refname UNUSED,\n+\t\t  const char *referent UNUSED,\n \t\t   const struct object_id *oid,\n \t\t   int flags UNUSED,\n \t\t   void *cb_data)\ndiff --git a/submodule.c b/submodule.c\nindex ab99a302530..7c0f5db861d 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -953,6 +953,7 @@ static void free_submodules_data(struct string_list *submodules)\n }\n \n static int has_remote(const char *refname UNUSED,\n+\t\t      const char *referent UNUSED,\n \t\t      const struct object_id *oid UNUSED,\n \t\t      int flags UNUSED, void *cb_data UNUSED)\n {\n@@ -1273,6 +1274,7 @@ int push_unpushed_submodules(struct repository *r,\n }\n \n static int append_oid_to_array(const char *ref UNUSED,\n+\t\t\t       const char *referent UNUSED,\n \t\t\t       const struct object_id *oid,\n \t\t\t       int flags UNUSED, void *data)\n {\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex 637b8b294e9..65346dee551 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -156,7 +156,7 @@ static int cmd_rename_ref(struct ref_store *refs, const char **argv)\n \treturn refs_rename_ref(refs, oldref, newref, logmsg);\n }\n \n-static int each_ref(const char *refname, const struct object_id *oid,\n+static int each_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flags, void *cb_data UNUSED)\n {\n \tprintf(\"%s %s 0x%x\\n\", oid_to_hex(oid), refname, flags);\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 0052c6a4dce..f03ba3e98be 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -857,7 +857,7 @@ static void send_unshallow(struct upload_pack_data *data)\n \t}\n }\n \n-static int check_ref(const char *refname_full, const struct object_id *oid,\n+static int check_ref(const char *refname_full, const char *referent UNUSED, const struct object_id *oid,\n \t\t     int flag, void *cb_data);\n static void deepen(struct upload_pack_data *data, int depth)\n {\n@@ -1208,7 +1208,7 @@ static int mark_our_ref(const char *refname, const char *refname_full,\n \treturn 0;\n }\n \n-static int check_ref(const char *refname_full, const struct object_id *oid,\n+static int check_ref(const char *refname_full, const char *referent UNUSED,const struct object_id *oid,\n \t\t     int flag UNUSED, void *cb_data)\n {\n \tconst char *refname = strip_namespace(refname_full);\n@@ -1276,14 +1276,14 @@ static void write_v0_ref(struct upload_pack_data *data,\n \treturn;\n }\n \n-static int send_ref(const char *refname, const struct object_id *oid,\n+static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flag UNUSED, void *cb_data)\n {\n \twrite_v0_ref(cb_data, refname, strip_namespace(refname), oid);\n \treturn 0;\n }\n \n-static int find_symref(const char *refname,\n+static int find_symref(const char *refname, const char *referent UNUSED,\n \t\t       const struct object_id *oid UNUSED,\n \t\t       int flag, void *cb_data)\n {\ndiff --git a/walker.c b/walker.c\nindex 0fafdc97cf1..807a7a38813 100644\n--- a/walker.c\n+++ b/walker.c\n@@ -221,6 +221,7 @@ static int interpret_target(struct walker *walker, char *target, struct object_i\n }\n \n static int mark_complete(const char *path UNUSED,\n+\t\t\tconst char *referent UNUSED,\n \t\t\t const struct object_id *oid,\n \t\t\t int flag UNUSED,\n \t\t\t void *cb_data UNUSED)\ndiff --git a/worktree.c b/worktree.c\nindex f3c4c8ec54d..fd05f3741c6 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -546,7 +546,7 @@ int other_head_refs(each_ref_fn fn, void *cb_data)\n \t\t\t\t\t    refname.buf,\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+\t\t\tret = fn(refname.buf, NULL, &oid, flag, cb_data);\n \t\tif (ret)\n \t\t\tbreak;\n \t}\n-- \ngitgitgadget\n\n"},{"id":"500349","messageId":"83b70ab8287ae2c929fbbdce24770b61ef64c602.1723059769.git.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":"pull.1712.v3.git.git.1723059768.gitgitgadget@gmail.com","subject":"[PATCH v3 3/3] ref-filter: populate symref from iterator","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-08-07T19:42:48Z","receivedAt":"2024-08-07T19:42:56Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"From: John Cai <johncai86@gmail.com>\n\nWith a previous commit, the reference the symbolic ref points to is saved\nin the ref iterator records. Instead of making a separate call to\nresolve_refdup() each time, we can just populate the ref_array_item with\nthe value from the iterator.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n ref-filter.c | 17 ++++++++++++-----\n 1 file changed, 12 insertions(+), 5 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 39997890feb..4d1f3ff3d23 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -2343,6 +2343,12 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)\n \n \tCALLOC_ARRAY(ref->value, used_atom_cnt);\n \n+\t/**\n+\t * NEEDSWORK: The following code might be unncessary if all codepaths\n+\t * that call populate_value() populates the symref member of ref_array_item\n+\t * like in apply_ref_filter(). Currently pretty_print_ref() is the only codepath\n+\t * that calls populate_value() without first populating symref.\n+\t */\n \tif (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {\n \t\tref->symref = refs_resolve_refdup(get_main_ref_store(the_repository),\n \t\t\t\t\t\t  ref->refname,\n@@ -2783,7 +2789,7 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)\n \treturn ref_kind_from_refname(refname);\n }\n \n-static struct ref_array_item *apply_ref_filter(const char *refname, const struct object_id *oid,\n+static struct ref_array_item *apply_ref_filter(const char *refname, const char *referent, const struct object_id *oid,\n \t\t\t    int flag, struct ref_filter *filter)\n {\n \tstruct ref_array_item *ref;\n@@ -2852,6 +2858,7 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const struct\n \tref->commit = commit;\n \tref->flag = flag;\n \tref->kind = kind;\n+\tref->symref = xstrdup_or_null(referent);\n \n \treturn ref;\n }\n@@ -2865,12 +2872,12 @@ struct ref_filter_cbdata {\n  * A call-back given to for_each_ref().  Filter refs and keep them for\n  * later object processing.\n  */\n-static int filter_one(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\n \n-\tref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);\n+\tref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);\n \tif (ref)\n \t\tref_array_append(ref_cbdata->array, ref);\n \n@@ -2900,13 +2907,13 @@ struct ref_filter_and_format_cbdata {\n \t} internal;\n };\n \n-static int filter_and_format_one(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_and_format_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_and_format_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\n \tstruct strbuf output = STRBUF_INIT, err = STRBUF_INIT;\n \n-\tref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);\n+\tref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);\n \tif (!ref)\n \t\treturn 0;\n \n-- \ngitgitgadget\n"},{"id":"500355","messageId":"xmqqbk24nhad.fsf@gitster.g","threadId":"61598","inReplyTo":"fc3defd9c47e32bb23ba0fcb5c885274f3706b23.1723059769.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 1/3] refs: keep track of unresolved reference value in iterators","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-07T21:40:10Z","receivedAt":"2024-08-07T21:40:19Z","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> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index aa52d9be7c7..5ed69c23f74 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> @@ -245,9 +245,11 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n>  {\n>  \tstruct object_id oid;\n>  \tint flag;\n> -\n> -\tif (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,\n\nHere, we had a nice blank line that separated the decls and the\nfirst statement.\n\n> -\t\t\t\t     &oid, &flag)) {\n> +\tconst char *referent = refs_resolve_ref_unsafe(&refs->base,\n> +\t\t\t\t\t\t       refname,\n> +\t\t\t\t\t\t       RESOLVE_REF_READING,\n> +\t\t\t\t\t\t       &oid, &flag);\n> +\tif (!referent) {\n\nWe lost it here, though.\n\n>  \t\toidclr(&oid, the_repository->hash_algo);\n>  \t\tflag |= REF_ISBROKEN;\n>  \t} else if (is_null_oid(&oid)) {\n> @@ -268,7 +270,11 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n>  \t\toidclr(&oid, the_repository->hash_algo);\n>  \t\tflag |= REF_BAD_NAME | REF_ISBROKEN;\n>  \t}\n> -\tadd_entry_to_dir(dir, create_ref_entry(refname, &oid, flag));\n> +\n> +\tif (!(flag & REF_ISSYMREF))\n> +\t\treferent = NULL;\n\nOK, this is new in this round.  The idea is that everybody else can\nrely on the invariant that the referent being NULL is equivalent to\nREF_ISSYMREF bit in flag word being off from here on.\n\n> +\tadd_entry_to_dir(dir, create_ref_entry(refname, referent, &oid, flag));\n>  }\n>  \n>  /*\n> @@ -886,6 +892,11 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)\n>  \t\titer->base.refname = iter->iter0->refname;\n>  \t\titer->base.oid = iter->iter0->oid;\n>  \t\titer->base.flags = iter->iter0->flags;\n> +\t\tif (iter->iter0->flags & REF_ISSYMREF)\n> +\t\t\titer->base.referent = iter->iter0->referent;\n> +\t\telse\n> +\t\t\titer->base.referent = NULL;\n>  \t\treturn ITER_OK;\n>  \t}\n\nHmph, why not an unconditional\n\n\t\titer->base.referent = iter->iter0->referent;\n\ninstead?  This code is making sure (iter->base.flags & REF_ISSYMREF)\nis directly linked to non-NULL-ness or iter->base.referent, and we\nwant to make everybody take it as invariant.  Shouldn't this code\nalso rely on the same invariant?  If iter-iter0->referent is NULL,\niter->iter0->flag has REF_ISSYMREF bit off, and vice versa.\n"},{"id":"500513","messageId":"7935D449-F5AB-42CA-951A-E2A07053CC07@gmail.com","threadId":"61598","inReplyTo":"xmqqbk24nhad.fsf@gitster.g","subject":"Re: [PATCH v3 1/3] refs: keep track of unresolved reference value in iterators","fromName":"John Cai","fromEmail":"johncai86@gmail.com","sentAt":"2024-08-08T18:09:43Z","receivedAt":"2024-08-08T18:09:45Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"Hi Junio,\n\nOn 7 Aug 2024, at 17:40, Junio C Hamano wrote:\n\n> \"John Cai via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>> diff --git a/refs/files-backend.c b/refs/files-backend.c\n>> index aa52d9be7c7..5ed69c23f74 100644\n>> --- a/refs/files-backend.c\n>> +++ b/refs/files-backend.c\n>> @@ -245,9 +245,11 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n>>  {\n>>  \tstruct object_id oid;\n>>  \tint flag;\n>> -\n>> -\tif (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,\n>\n> Here, we had a nice blank line that separated the decls and the\n> first statement.\n>\n>> -\t\t\t\t     &oid, &flag)) {\n>> +\tconst char *referent = refs_resolve_ref_unsafe(&refs->base,\n>> +\t\t\t\t\t\t       refname,\n>> +\t\t\t\t\t\t       RESOLVE_REF_READING,\n>> +\t\t\t\t\t\t       &oid, &flag);\n>> +\tif (!referent) {\n>\n> We lost it here, though.\n\nAh will restore the blank line.\n\n>\n>>  \t\toidclr(&oid, the_repository->hash_algo);\n>>  \t\tflag |= REF_ISBROKEN;\n>>  \t} else if (is_null_oid(&oid)) {\n>> @@ -268,7 +270,11 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n>>  \t\toidclr(&oid, the_repository->hash_algo);\n>>  \t\tflag |= REF_BAD_NAME | REF_ISBROKEN;\n>>  \t}\n>> -\tadd_entry_to_dir(dir, create_ref_entry(refname, &oid, flag));\n>> +\n>> +\tif (!(flag & REF_ISSYMREF))\n>> +\t\treferent = NULL;\n>\n> OK, this is new in this round.  The idea is that everybody else can\n> rely on the invariant that the referent being NULL is equivalent to\n> REF_ISSYMREF bit in flag word being off from here on.\n>\n>> +\tadd_entry_to_dir(dir, create_ref_entry(refname, referent, &oid, flag));\n>>  }\n>>\n>>  /*\n>> @@ -886,6 +892,11 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)\n>>  \t\titer->base.refname = iter->iter0->refname;\n>>  \t\titer->base.oid = iter->iter0->oid;\n>>  \t\titer->base.flags = iter->iter0->flags;\n>> +\t\tif (iter->iter0->flags & REF_ISSYMREF)\n>> +\t\t\titer->base.referent = iter->iter0->referent;\n>> +\t\telse\n>> +\t\t\titer->base.referent = NULL;\n>>  \t\treturn ITER_OK;\n>>  \t}\n>\n> Hmph, why not an unconditional\n>\n> \t\titer->base.referent = iter->iter0->referent;\n>\n> instead?  This code is making sure (iter->base.flags & REF_ISSYMREF)\n> is directly linked to non-NULL-ness or iter->base.referent, and we\n> want to make everybody take it as invariant.  Shouldn't this code\n> also rely on the same invariant?  If iter-iter0->referent is NULL,\n> iter->iter0->flag has REF_ISSYMREF bit off, and vice versa.\n\nThat's a good point. Even though this code will be used in a loop, if we just\nset it every time unconditionally then we won't end up with leftover values. Wil\nl adjust in the next version.\n\n"},{"id":"500542","messageId":"pull.1712.v4.git.git.1723217871.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":"pull.1712.v3.git.git.1723059768.gitgitgadget@gmail.com","subject":"[PATCH v4 0/3] keep track of unresolved value of symbolic-ref in ref iterators","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-08-09T15:37:48Z","receivedAt":"2024-08-09T15:37:55Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"For reftable development, it's useful to be able to print out the direct\nvalue of a symbolic reference before resolution. This is currently possible\nwith git-for-each-ref, but since the iterators do not keep track of the\nvalue of the symbolic ref, a separate call needs to be made each time making\nit inefficient.\n\nAddress this inefficiency by keeping track of the value of the symbolic\nreference in the ref iterator. This patch series also ends with a commit to\nuse this value in the iterator through callbacks in ref-filter.c.\n\nThis series started with [1] but I decided to send a separate patch series\nsince it is substantially different.\n\nBenchmarking shows that with these changes, we experience a speedup in\ngit-for-each-ref(1) on a repository with many symbolic refs:\n\n$ hyperfine --warmup 5 \"git for-each-ref --format='%(refname) %(objectname)\n%(symref)'\" \"~/Projects/git/git for-each-ref --format='%(refname)\n%(objectname) %(symref)'\" Benchmark 1: git for-each-ref --format='%(refname)\n%(objectname) %(symref)' Time (mean ± σ): 905.1 ms ± 13.2 ms [User: 56.3 ms,\nSystem: 628.6 ms] Range (min … max): 893.4 ms … 936.9 ms 10 runs\n\nBenchmark 2: ~/Projects/git/git for-each-ref --format='%(refname)\n%(objectname) %(symref)' Time (mean ± σ): 482.2 ms ± 26.4 ms [User: 34.7 ms,\nSystem: 410.6 ms] Range (min … max): 459.4 ms … 541.8 ms 10 runs\n\nSummary ~/Projects/git/git for-each-ref --format='%(refname) %(objectname)\n%(symref)' ran 1.88 ± 0.11 times faster than git for-each-ref\n--format='%(refname) %(objectname) %(symref)'\n\nChanges since V3:\n\n * Added blank line to separate declarations from statements\n * unconditionally set referent member in base iterator in files backend\n\nChanges since V2:\n\n * Style tweaks\n * Added a NEEDSWORK comment around fallback code\n * Stay consistent in the meaning of a non-NULL referent member in the\n   iterator\n\nChanges since V1\n\n * Use the return value from refs_resolve_ref_unsafe instead of using an out\n   parameter\n\n 1. https://lore.kernel.org/git/pull.1684.git.git.1709592718743.gitgitgadget@gmail.com/\n\nJohn Cai (3):\n  refs: keep track of unresolved reference value in iterators\n  refs: add referent to each_ref_fn\n  ref-filter: populate symref from iterator\n\n bisect.c                    |  3 ++-\n builtin/bisect.c            |  4 +++-\n builtin/checkout.c          |  2 +-\n builtin/describe.c          |  2 +-\n builtin/fetch.c             |  3 ++-\n builtin/fsck.c              |  4 ++--\n builtin/gc.c                |  1 +\n builtin/name-rev.c          |  2 +-\n builtin/pack-objects.c      |  5 +++--\n builtin/receive-pack.c      |  2 +-\n builtin/remote.c            |  4 +++-\n builtin/repack.c            |  1 +\n builtin/replace.c           |  1 +\n builtin/rev-parse.c         |  4 ++--\n builtin/show-branch.c       |  8 ++++----\n builtin/show-ref.c          |  3 ++-\n builtin/submodule--helper.c |  1 +\n builtin/worktree.c          |  1 +\n commit-graph.c              |  1 +\n delta-islands.c             |  2 +-\n fetch-pack.c                |  2 ++\n help.c                      |  2 +-\n http-backend.c              |  4 ++--\n log-tree.c                  |  2 +-\n ls-refs.c                   |  4 ++--\n midx-write.c                |  2 +-\n negotiator/default.c        |  2 +-\n negotiator/skipping.c       |  2 +-\n notes.c                     |  2 +-\n object-name.c               |  2 +-\n pseudo-merge.c              |  1 +\n reachable.c                 |  2 +-\n ref-filter.c                | 17 ++++++++++++-----\n reflog.c                    |  1 +\n refs.c                      | 14 +++++++-------\n refs.h                      |  2 +-\n refs/files-backend.c        | 17 +++++++++++++----\n refs/iterator.c             |  4 +++-\n refs/ref-cache.c            |  5 +++++\n refs/ref-cache.h            |  2 ++\n refs/refs-internal.h        |  1 +\n refs/reftable-backend.c     | 10 ++++++++--\n remote.c                    |  4 ++--\n replace-object.c            |  1 +\n revision.c                  |  2 +-\n server-info.c               |  2 +-\n shallow.c                   |  2 ++\n submodule.c                 |  2 ++\n t/helper/test-ref-store.c   |  2 +-\n upload-pack.c               |  8 ++++----\n walker.c                    |  1 +\n worktree.c                  |  2 +-\n 52 files changed, 118 insertions(+), 62 deletions(-)\n\n\nbase-commit: 557ae147e6cdc9db121269b058c757ac5092f9c9\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1712%2Fjohn-cai%2Fjc%2Fsymbolic-ref-in-iterator-v4\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1712/john-cai/jc/symbolic-ref-in-iterator-v4\nPull-Request: https://github.com/git/git/pull/1712\n\nRange-diff vs v3:\n\n 1:  fc3defd9c47 ! 1:  c4f5f5b7dd8 refs: keep track of unresolved reference value in iterators\n     @@ refs/files-backend.c: static void loose_fill_ref_dir_regular_file(struct files_r\n       {\n       \tstruct object_id oid;\n       \tint flag;\n     --\n     --\tif (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,\n     --\t\t\t\t     &oid, &flag)) {\n      +\tconst char *referent = refs_resolve_ref_unsafe(&refs->base,\n      +\t\t\t\t\t\t       refname,\n      +\t\t\t\t\t\t       RESOLVE_REF_READING,\n      +\t\t\t\t\t\t       &oid, &flag);\n     + \n     +-\tif (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,\n     +-\t\t\t\t     &oid, &flag)) {\n      +\tif (!referent) {\n       \t\toidclr(&oid, the_repository->hash_algo);\n       \t\tflag |= REF_ISBROKEN;\n     @@ refs/files-backend.c: static int files_ref_iterator_advance(struct ref_iterator\n       \t\titer->base.refname = iter->iter0->refname;\n       \t\titer->base.oid = iter->iter0->oid;\n       \t\titer->base.flags = iter->iter0->flags;\n     -+\t\tif (iter->iter0->flags & REF_ISSYMREF)\n     -+\t\t\titer->base.referent = iter->iter0->referent;\n     -+\t\telse\n     -+\t\t\titer->base.referent = NULL;\n     ++\t\titer->base.referent = iter->iter0->referent;\n      +\n       \t\treturn ITER_OK;\n       \t}\n     @@ refs/iterator.c: static int merge_ref_iterator_advance(struct ref_iterator *ref_\n       \t\t\titer->base.flags = (*iter->current)->flags;\n      \n       ## refs/ref-cache.c ##\n     -@@\n     - #include \"refs-internal.h\"\n     - #include \"ref-cache.h\"\n     - #include \"../iterator.h\"\n     -+#include \"../strbuf.h\"\n     - \n     - void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry)\n     - {\n      @@ refs/ref-cache.c: struct ref_dir *get_ref_dir(struct ref_entry *entry)\n       }\n       \n 2:  0b6e732ad7e = 2:  6cfac5e51f5 refs: add referent to each_ref_fn\n 3:  83b70ab8287 = 3:  9f609f4d0f5 ref-filter: populate symref from iterator\n\n-- \ngitgitgadget\n"},{"id":"500543","messageId":"c4f5f5b7dd8dad4f17201611faee14dd1b882bff.1723217871.git.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":"pull.1712.v4.git.git.1723217871.gitgitgadget@gmail.com","subject":"[PATCH v4 1/3] refs: keep track of unresolved reference value in iterators","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-08-09T15:37:49Z","receivedAt":"2024-08-09T15:37:56Z","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 iterators for both the\nfiles backend and the reftable backend.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n refs/files-backend.c    | 15 ++++++++++++---\n refs/iterator.c         |  2 ++\n refs/ref-cache.c        |  5 +++++\n refs/ref-cache.h        |  2 ++\n refs/refs-internal.h    |  1 +\n refs/reftable-backend.c | 10 ++++++++--\n 6 files changed, 30 insertions(+), 5 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex aa52d9be7c7..210adf644fa 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -245,9 +245,12 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n {\n \tstruct object_id oid;\n \tint flag;\n+\tconst char *referent = refs_resolve_ref_unsafe(&refs->base,\n+\t\t\t\t\t\t       refname,\n+\t\t\t\t\t\t       RESOLVE_REF_READING,\n+\t\t\t\t\t\t       &oid, &flag);\n \n-\tif (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,\n-\t\t\t\t     &oid, &flag)) {\n+\tif (!referent) {\n \t\toidclr(&oid, the_repository->hash_algo);\n \t\tflag |= REF_ISBROKEN;\n \t} else if (is_null_oid(&oid)) {\n@@ -268,7 +271,11 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,\n \t\toidclr(&oid, the_repository->hash_algo);\n \t\tflag |= REF_BAD_NAME | REF_ISBROKEN;\n \t}\n-\tadd_entry_to_dir(dir, create_ref_entry(refname, &oid, flag));\n+\n+\tif (!(flag & REF_ISSYMREF))\n+\t\treferent = NULL;\n+\n+\tadd_entry_to_dir(dir, create_ref_entry(refname, referent, &oid, flag));\n }\n \n /*\n@@ -886,6 +893,8 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \t\titer->base.refname = iter->iter0->refname;\n \t\titer->base.oid = iter->iter0->oid;\n \t\titer->base.flags = iter->iter0->flags;\n+\t\titer->base.referent = iter->iter0->referent;\n+\n \t\treturn ITER_OK;\n \t}\n \ndiff --git a/refs/iterator.c b/refs/iterator.c\nindex d355ebf0d59..75fbe5d72ab 100644\n--- a/refs/iterator.c\n+++ b/refs/iterator.c\n@@ -29,6 +29,7 @@ void base_ref_iterator_init(struct ref_iterator *iter,\n {\n \titer->vtable = vtable;\n \titer->refname = NULL;\n+\titer->referent = NULL;\n \titer->oid = NULL;\n \titer->flags = 0;\n }\n@@ -199,6 +200,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 4ce519bbc85..35bae7e05de 100644\n--- a/refs/ref-cache.c\n+++ b/refs/ref-cache.c\n@@ -34,6 +34,7 @@ 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@@ -41,6 +42,8 @@ struct ref_entry *create_ref_entry(const char *refname,\n \tFLEX_ALLOC_STR(ref, name, refname);\n \toidcpy(&ref->u.value.oid, oid);\n \tref->flag = flag;\n+\tref->u.value.referent = xstrdup_or_null(referent);\n+\n \treturn ref;\n }\n \n@@ -66,6 +69,7 @@ static void free_ref_entry(struct ref_entry *entry)\n \t\t */\n \t\tclear_ref_dir(&entry->u.subdir);\n \t}\n+\tfree(entry->u.value.referent);\n \tfree(entry);\n }\n \n@@ -431,6 +435,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \t\t\tlevel->index = -1;\n \t\t} else {\n \t\t\titer->base.refname = entry->name;\n+\t\t\titer->base.referent = entry->u.value.referent;\n \t\t\titer->base.oid = &entry->u.value.oid;\n \t\t\titer->base.flags = entry->flag;\n \t\t\treturn ITER_OK;\ndiff --git a/refs/ref-cache.h b/refs/ref-cache.h\nindex 31ebe24f6cf..5f04e518c37 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+\tchar *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 fa975d69aaa..117ec233848 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -299,6 +299,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 fbe74c239d3..50a072b97b0 100644\n--- a/refs/reftable-backend.c\n+++ b/refs/reftable-backend.c\n@@ -455,6 +455,7 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \tstruct reftable_ref_iterator *iter =\n \t\t(struct reftable_ref_iterator *)ref_iterator;\n \tstruct reftable_ref_store *refs = iter->refs;\n+\tconst char *referent = NULL;\n \n \twhile (!iter->err) {\n \t\tint flags = 0;\n@@ -494,8 +495,12 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)\n \t\t\t\tthe_repository->hash_algo);\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\t\t\t\t     RESOLVE_REF_READING, &iter->oid, &flags))\n+\t\t\treferent = refs_resolve_ref_unsafe(&iter->refs->base,\n+\t\t\t\t\t\t\t\t      iter->ref.refname,\n+\t\t\t\t\t\t\t\t      RESOLVE_REF_READING,\n+\t\t\t\t\t\t\t\t      &iter->oid,\n+\t\t\t\t\t\t\t\t      &flags);\n+\t\t\tif (!referent)\n \t\t\t\toidclr(&iter->oid, the_repository->hash_algo);\n \t\t\tbreak;\n \t\tdefault:\n@@ -523,6 +528,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 = referent;\n \t\titer->base.oid = &iter->oid;\n \t\titer->base.flags = flags;\n \n-- \ngitgitgadget\n\n"},{"id":"500545","messageId":"6cfac5e51f5172d4ee4b610e2e734847038c8fd3.1723217871.git.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":"pull.1712.v4.git.git.1723217871.gitgitgadget@gmail.com","subject":"[PATCH v4 2/3] refs: add referent to each_ref_fn","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-08-09T15:37:50Z","receivedAt":"2024-08-09T15:37:58Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"From: John Cai <johncai86@gmail.com>\n\nAdd a parameter to each_ref_fn so that callers to the ref APIs\nthat use this function as a callback can have acess to the\nunresolved value of a symbolic ref.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n bisect.c                    |  3 ++-\n builtin/bisect.c            |  4 +++-\n builtin/checkout.c          |  2 +-\n builtin/describe.c          |  2 +-\n builtin/fetch.c             |  3 ++-\n builtin/fsck.c              |  4 ++--\n builtin/gc.c                |  1 +\n builtin/name-rev.c          |  2 +-\n builtin/pack-objects.c      |  5 +++--\n builtin/receive-pack.c      |  2 +-\n builtin/remote.c            |  4 +++-\n builtin/repack.c            |  1 +\n builtin/replace.c           |  1 +\n builtin/rev-parse.c         |  4 ++--\n builtin/show-branch.c       |  8 ++++----\n builtin/show-ref.c          |  3 ++-\n builtin/submodule--helper.c |  1 +\n builtin/worktree.c          |  1 +\n commit-graph.c              |  1 +\n delta-islands.c             |  2 +-\n fetch-pack.c                |  2 ++\n help.c                      |  2 +-\n http-backend.c              |  4 ++--\n log-tree.c                  |  2 +-\n ls-refs.c                   |  4 ++--\n midx-write.c                |  2 +-\n negotiator/default.c        |  2 +-\n negotiator/skipping.c       |  2 +-\n notes.c                     |  2 +-\n object-name.c               |  2 +-\n pseudo-merge.c              |  1 +\n reachable.c                 |  2 +-\n ref-filter.c                |  4 ++--\n reflog.c                    |  1 +\n refs.c                      | 14 +++++++-------\n refs.h                      |  2 +-\n refs/files-backend.c        |  2 +-\n refs/iterator.c             |  2 +-\n remote.c                    |  4 ++--\n replace-object.c            |  1 +\n revision.c                  |  2 +-\n server-info.c               |  2 +-\n shallow.c                   |  2 ++\n submodule.c                 |  2 ++\n t/helper/test-ref-store.c   |  2 +-\n upload-pack.c               |  8 ++++----\n walker.c                    |  1 +\n worktree.c                  |  2 +-\n 48 files changed, 78 insertions(+), 54 deletions(-)\n\ndiff --git a/bisect.c b/bisect.c\nindex 135f94ba097..4406fc44cf9 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -448,7 +448,7 @@ void find_bisection(struct commit_list **commit_list, int *reaches,\n \tclear_commit_weight(&commit_weight);\n }\n \n-static int register_ref(const char *refname, const struct object_id *oid,\n+static int register_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\tint flags UNUSED, void *cb_data UNUSED)\n {\n \tstruct strbuf good_prefix = STRBUF_INIT;\n@@ -1170,6 +1170,7 @@ int estimate_bisect_steps(int all)\n }\n \n static int mark_for_removal(const char *refname,\n+\t\t\t    const char *referent UNUSED,\n \t\t\t    const struct object_id *oid UNUSED,\n \t\t\t    int flag UNUSED, void *cb_data)\n {\ndiff --git a/builtin/bisect.c b/builtin/bisect.c\nindex dabce9b542b..453a6cccd70 100644\n--- a/builtin/bisect.c\n+++ b/builtin/bisect.c\n@@ -356,6 +356,7 @@ static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)\n }\n \n static int inc_nr(const char *refname UNUSED,\n+\t\t  const char *referent UNUSED,\n \t\t  const struct object_id *oid UNUSED,\n \t\t  int flag UNUSED, void *cb_data)\n {\n@@ -545,7 +546,7 @@ finish:\n \treturn res;\n }\n \n-static int add_bisect_ref(const char *refname, const struct object_id *oid,\n+static int add_bisect_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flags UNUSED, void *cb)\n {\n \tstruct add_bisect_ref_data *data = cb;\n@@ -1162,6 +1163,7 @@ static int bisect_visualize(struct bisect_terms *terms, int argc,\n }\n \n static int get_first_good(const char *refname UNUSED,\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/builtin/checkout.c b/builtin/checkout.c\nindex 3cf44b4683a..562778885e1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1045,7 +1045,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,\n \t\treport_tracking(new_branch_info);\n }\n \n-static int add_pending_uninteresting_ref(const char *refname,\n+static int add_pending_uninteresting_ref(const char *refname, const char *referent UNUSED,\n \t\t\t\t\t const struct object_id *oid,\n \t\t\t\t\t int flags UNUSED, void *cb_data)\n {\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex e5287eddf2d..d95a9109eed 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -145,7 +145,7 @@ static void add_to_known_names(const char *path,\n \t}\n }\n \n-static int get_name(const char *path, const struct object_id *oid,\n+static int get_name(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flag UNUSED, void *cb_data UNUSED)\n {\n \tint is_tag = 0;\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 693f02b9580..c297569a473 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -286,7 +286,7 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,\n \treturn ent;\n }\n \n-static int add_one_refname(const char *refname,\n+static int add_one_refname(const char *refname, const char *referent UNUSED,\n \t\t\t   const struct object_id *oid,\n \t\t\t   int flag UNUSED, void *cbdata)\n {\n@@ -1464,6 +1464,7 @@ static void set_option(struct transport *transport, const char *name, const char\n \n \n static int add_oid(const char *refname UNUSED,\n+\t\t   const char *referent UNUSED,\n \t\t   const struct object_id *oid,\n \t\t   int flags UNUSED, void *cb_data)\n {\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex d13a226c2ed..ef6ee1268bd 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -521,7 +521,7 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)\n \treturn 0;\n }\n \n-static int fsck_handle_ref(const char *refname, const struct object_id *oid,\n+static int fsck_handle_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t   int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object *obj;\n@@ -576,7 +576,7 @@ static void get_default_heads(void)\n \t\tstrbuf_worktree_ref(wt, &ref, \"HEAD\");\n \t\tfsck_head_link(ref.buf, &head_points_at, &head_oid);\n \t\tif (head_points_at && !is_null_oid(&head_oid))\n-\t\t\tfsck_handle_ref(ref.buf, &head_oid, 0, NULL);\n+\t\t\tfsck_handle_ref(ref.buf, NULL, &head_oid, 0, NULL);\n \t\tstrbuf_release(&ref);\n \n \t\tif (include_reflogs)\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex 72bac2554fc..65be7c31b33 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -836,6 +836,7 @@ struct cg_auto_data {\n };\n \n static int dfs_on_ref(const char *refname UNUSED,\n+\t\t      const char *referent UNUSED,\n \t\t      const struct object_id *oid,\n \t\t      int flags UNUSED,\n \t\t      void *cb_data)\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex 70e9ec4e477..52016f1cf7d 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -337,7 +337,7 @@ static int cmp_by_tag_and_age(const void *a_, const void *b_)\n \treturn a->taggerdate != b->taggerdate;\n }\n \n-static int name_ref(const char *path, const struct object_id *oid,\n+static int name_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flags UNUSED, void *cb_data)\n {\n \tstruct object *o = parse_object(the_repository, oid);\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex f395488971f..c481feadbfa 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -771,7 +771,7 @@ static enum write_one_status write_one(struct hashfile *f,\n \treturn WRITE_ONE_WRITTEN;\n }\n \n-static int mark_tagged(const char *path UNUSED, const struct object_id *oid,\n+static int mark_tagged(const char *path UNUSED, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object_id peeled;\n@@ -3129,7 +3129,7 @@ static void add_tag_chain(const struct object_id *oid)\n \t}\n }\n \n-static int add_ref_tag(const char *tag UNUSED, const struct object_id *oid,\n+static int add_ref_tag(const char *tag UNUSED, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object_id peeled;\n@@ -4076,6 +4076,7 @@ static void record_recent_commit(struct commit *commit, void *data UNUSED)\n }\n \n static int mark_bitmap_preferred_tip(const char *refname,\n+\t\t\t\t     const char *referent UNUSED,\n \t\t\t\t     const struct object_id *oid,\n \t\t\t\t     int flags UNUSED,\n \t\t\t\t     void *data UNUSED)\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 339524ae2a8..6e0f462efbb 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -300,7 +300,7 @@ static void show_ref(const char *path, const struct object_id *oid)\n \t}\n }\n \n-static int show_ref_cb(const char *path_full, const struct object_id *oid,\n+static int show_ref_cb(const char *path_full, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED, void *data)\n {\n \tstruct oidset *seen = data;\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 08292498bde..9a3db1b0f13 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -543,6 +543,7 @@ struct branches_for_remote {\n };\n \n static int add_branch_for_removal(const char *refname,\n+\t\t\t\t  const char *referent UNUSED,\n \t\t\t\t  const struct object_id *oid UNUSED,\n \t\t\t\t  int flags UNUSED, void *cb_data)\n {\n@@ -585,7 +586,7 @@ struct rename_info {\n \tuint32_t symrefs_nr;\n };\n \n-static int read_remote_branches(const char *refname,\n+static int read_remote_branches(const char *refname, const char *referent UNUSED,\n \t\t\t\tconst struct object_id *oid UNUSED,\n \t\t\t\tint flags UNUSED, void *cb_data)\n {\n@@ -971,6 +972,7 @@ static void free_remote_ref_states(struct ref_states *states)\n }\n \n static int append_ref_to_tracked_list(const char *refname,\n+\t\t\t\t      const char *referent UNUSED,\n \t\t\t\t      const struct object_id *oid UNUSED,\n \t\t\t\t      int flags, void *cb_data)\n {\ndiff --git a/builtin/repack.c b/builtin/repack.c\nindex f0317fa94ad..d9ea6cecbf9 100644\n--- a/builtin/repack.c\n+++ b/builtin/repack.c\n@@ -667,6 +667,7 @@ struct midx_snapshot_ref_data {\n };\n \n static int midx_snapshot_ref_one(const char *refname UNUSED,\n+\t\t\t\t const char *referent UNUSED,\n \t\t\t\t const struct object_id *oid,\n \t\t\t\t int flag UNUSED, void *_data)\n {\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex 1ef833c07f5..34cc4672bc1 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -49,6 +49,7 @@ struct show_data {\n };\n \n static int show_reference(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/builtin/rev-parse.c b/builtin/rev-parse.c\nindex 2e64f5bda70..a7084ca157f 100644\n--- a/builtin/rev-parse.c\n+++ b/builtin/rev-parse.c\n@@ -211,7 +211,7 @@ static int show_default(void)\n \treturn 0;\n }\n \n-static int show_reference(const char *refname, const struct object_id *oid,\n+static int show_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flag UNUSED, void *cb_data UNUSED)\n {\n \tif (ref_excluded(&ref_excludes, refname))\n@@ -220,7 +220,7 @@ static int show_reference(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n-static int anti_reference(const char *refname, const struct object_id *oid,\n+static int anti_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flag UNUSED, void *cb_data UNUSED)\n {\n \tshow_rev(REVERSED, oid, refname);\ndiff --git a/builtin/show-branch.c b/builtin/show-branch.c\nindex d72f4cb98de..3427c1572e5 100644\n--- a/builtin/show-branch.c\n+++ b/builtin/show-branch.c\n@@ -410,7 +410,7 @@ static int append_ref(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n-static int append_head_ref(const char *refname, const struct object_id *oid,\n+static int append_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t   int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object_id tmp;\n@@ -425,7 +425,7 @@ static int append_head_ref(const char *refname, const struct object_id *oid,\n \treturn append_ref(refname + ofs, oid, 0);\n }\n \n-static int append_remote_ref(const char *refname, const struct object_id *oid,\n+static int append_remote_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t     int flag UNUSED, void *cb_data UNUSED)\n {\n \tstruct object_id tmp;\n@@ -451,7 +451,7 @@ static int append_tag_ref(const char *refname, const struct object_id *oid,\n static const char *match_ref_pattern = NULL;\n static int match_ref_slash = 0;\n \n-static int append_matching_ref(const char *refname, const struct object_id *oid,\n+static int append_matching_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t       int flag, void *cb_data)\n {\n \t/* we want to allow pattern hold/<asterisk> to show all\n@@ -468,7 +468,7 @@ static int append_matching_ref(const char *refname, const struct object_id *oid,\n \tif (wildmatch(match_ref_pattern, tail, 0))\n \t\treturn 0;\n \tif (starts_with(refname, \"refs/heads/\"))\n-\t\treturn append_head_ref(refname, oid, flag, cb_data);\n+\t\treturn append_head_ref(refname, NULL, oid, flag, cb_data);\n \tif (starts_with(refname, \"refs/tags/\"))\n \t\treturn append_tag_ref(refname, oid, flag, cb_data);\n \treturn append_ref(refname, oid, 0);\ndiff --git a/builtin/show-ref.c b/builtin/show-ref.c\nindex 839a5c29f31..04e0db707c6 100644\n--- a/builtin/show-ref.c\n+++ b/builtin/show-ref.c\n@@ -63,7 +63,7 @@ struct show_ref_data {\n \tint show_head;\n };\n \n-static int show_ref(const char *refname, const struct object_id *oid,\n+static int show_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flag UNUSED, void *cbdata)\n {\n \tstruct show_ref_data *data = cbdata;\n@@ -97,6 +97,7 @@ match:\n }\n \n static int add_existing(const char *refname,\n+\t\t\tconst char *referent UNUSED,\n \t\t\tconst struct object_id *oid UNUSED,\n \t\t\tint flag UNUSED, void *cbdata)\n {\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 880ab4456ee..1cdcc465b1c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -609,6 +609,7 @@ static void print_status(unsigned int flags, char state, const char *path,\n }\n \n static int handle_submodule_head_ref(const char *refname UNUSED,\n+\t\t\t\t     const char *referent UNUSED,\n \t\t\t\t     const struct object_id *oid,\n \t\t\t\t     int flags UNUSED,\n \t\t\t\t     void *cb_data)\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 1d51e54fcdc..2e16218c655 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -626,6 +626,7 @@ static void print_preparing_worktree_line(int detach,\n  * Returns 0 on failure and non-zero on success.\n  */\n static int first_valid_ref(const char *refname UNUSED,\n+\t\t\t   const char *referent UNUSED,\n \t\t\t   const struct object_id *oid UNUSED,\n \t\t\t   int flags UNUSED,\n \t\t\t   void *cb_data UNUSED)\ndiff --git a/commit-graph.c b/commit-graph.c\nindex 79b0e72cc42..aa6e08eb39c 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -1847,6 +1847,7 @@ struct refs_cb_data {\n };\n \n static int add_ref_to_set(const char *refname UNUSED,\n+\t\t\t  const char *referent UNUSED,\n \t\t\t  const struct object_id *oid,\n \t\t\t  int flags UNUSED, void *cb_data)\n {\ndiff --git a/delta-islands.c b/delta-islands.c\nindex ffe1ca28144..84435512593 100644\n--- a/delta-islands.c\n+++ b/delta-islands.c\n@@ -390,7 +390,7 @@ static void add_ref_to_island(kh_str_t *remote_islands, const char *island_name,\n \trl->hash += sha_core;\n }\n \n-static int find_island_for_ref(const char *refname, const struct object_id *oid,\n+static int find_island_for_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t       int flags UNUSED, void *cb)\n {\n \tstruct island_load_data *ild = cb;\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 732511604b1..58b4581ad80 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -183,6 +183,7 @@ static int rev_list_insert_ref(struct fetch_negotiator *negotiator,\n }\n \n static int rev_list_insert_ref_oid(const char *refname UNUSED,\n+\t\t\t\t   const char *referent UNUSED,\n \t\t\t\t   const struct object_id *oid,\n \t\t\t\t   int flag UNUSED,\n \t\t\t\t   void *cb_data)\n@@ -610,6 +611,7 @@ static int mark_complete(const struct object_id *oid)\n }\n \n static int mark_complete_oid(const char *refname UNUSED,\n+\t\t\t     const char *referent UNUSED,\n \t\t\t     const struct object_id *oid,\n \t\t\t     int flag UNUSED,\n \t\t\t     void *cb_data UNUSED)\ndiff --git a/help.c b/help.c\nindex a6b4d3b1ebd..c03863f2265 100644\n--- a/help.c\n+++ b/help.c\n@@ -804,7 +804,7 @@ struct similar_ref_cb {\n \tstruct string_list *similar_refs;\n };\n \n-static int append_similar_ref(const char *refname,\n+static int append_similar_ref(const char *refname, const char *referent UNUSED,\n \t\t\t      const struct object_id *oid UNUSED,\n \t\t\t      int flags UNUSED, void *cb_data)\n {\ndiff --git a/http-backend.c b/http-backend.c\nindex 461424972b6..79ce097359b 100644\n--- a/http-backend.c\n+++ b/http-backend.c\n@@ -512,7 +512,7 @@ static void run_service(const char **argv, int buffer_input)\n \t\texit(1);\n }\n \n-static int show_text_ref(const char *name, const struct object_id *oid,\n+static int show_text_ref(const char *name, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t int flag UNUSED, void *cb_data)\n {\n \tconst char *name_nons = strip_namespace(name);\n@@ -568,7 +568,7 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)\n \tstrbuf_release(&buf);\n }\n \n-static int show_head_ref(const char *refname, const struct object_id *oid,\n+static int show_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t int flag, void *cb_data)\n {\n \tstruct strbuf *buf = cb_data;\ndiff --git a/log-tree.c b/log-tree.c\nindex 52feec4356a..40306434556 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -145,7 +145,7 @@ static int ref_filter_match(const char *refname,\n \treturn 1;\n }\n \n-static int add_ref_decoration(const char *refname, const struct object_id *oid,\n+static int add_ref_decoration(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t      int flags UNUSED,\n \t\t\t      void *cb_data)\n {\ndiff --git a/ls-refs.c b/ls-refs.c\nindex 2dd925b43d0..c824aea7146 100644\n--- a/ls-refs.c\n+++ b/ls-refs.c\n@@ -77,7 +77,7 @@ struct ls_refs_data {\n \tunsigned unborn : 1;\n };\n \n-static int send_ref(const char *refname, const struct object_id *oid,\n+static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flag, void *cb_data)\n {\n \tstruct ls_refs_data *data = cb_data;\n@@ -135,7 +135,7 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)\n \toid_is_null = is_null_oid(&oid);\n \tif (!oid_is_null ||\n \t    (data->unborn && data->symrefs && (flag & REF_ISSYMREF)))\n-\t\tsend_ref(namespaced.buf, oid_is_null ? NULL : &oid, flag, data);\n+\t\tsend_ref(namespaced.buf, NULL, oid_is_null ? NULL : &oid, flag, data);\n \tstrbuf_release(&namespaced);\n }\n \ndiff --git a/midx-write.c b/midx-write.c\nindex 478b42e7209..56d1717f9f1 100644\n--- a/midx-write.c\n+++ b/midx-write.c\n@@ -655,7 +655,7 @@ static void prepare_midx_packing_data(struct packing_data *pdata,\n \ttrace2_region_leave(\"midx\", \"prepare_midx_packing_data\", the_repository);\n }\n \n-static int add_ref_to_pending(const char *refname,\n+static int add_ref_to_pending(const char *refname, const char *referent UNUSED,\n \t\t\t      const struct object_id *oid,\n \t\t\t      int flag, void *cb_data)\n {\ndiff --git a/negotiator/default.c b/negotiator/default.c\nindex e3fa5c33249..c479da9b091 100644\n--- a/negotiator/default.c\n+++ b/negotiator/default.c\n@@ -38,7 +38,7 @@ static void rev_list_push(struct negotiation_state *ns,\n \t}\n }\n \n-static int clear_marks(const char *refname, const struct object_id *oid,\n+static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED,\n \t\t       void *cb_data UNUSED)\n {\ndiff --git a/negotiator/skipping.c b/negotiator/skipping.c\nindex f109928ad0b..f65d47858b4 100644\n--- a/negotiator/skipping.c\n+++ b/negotiator/skipping.c\n@@ -75,7 +75,7 @@ static struct entry *rev_list_push(struct data *data, struct commit *commit, int\n \treturn entry;\n }\n \n-static int clear_marks(const char *refname, const struct object_id *oid,\n+static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag UNUSED,\n \t\t       void *cb_data UNUSED)\n {\ndiff --git a/notes.c b/notes.c\nindex 1ba6412aae8..da42df282d5 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -932,7 +932,7 @@ out:\n \treturn ret;\n }\n \n-static int string_list_add_one_ref(const char *refname,\n+static int string_list_add_one_ref(const char *refname, const char *referent UNUSED,\n \t\t\t\t   const struct object_id *oid UNUSED,\n \t\t\t\t   int flag UNUSED, void *cb)\n {\ndiff --git a/object-name.c b/object-name.c\nindex 527b853ac45..7cca43020f2 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -1365,7 +1365,7 @@ struct handle_one_ref_cb {\n \tstruct commit_list **list;\n };\n \n-static int handle_one_ref(const char *path, const struct object_id *oid,\n+static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flag UNUSED,\n \t\t\t  void *cb_data)\n {\ndiff --git a/pseudo-merge.c b/pseudo-merge.c\nindex f0fde13c471..77a83b9c5ce 100644\n--- a/pseudo-merge.c\n+++ b/pseudo-merge.c\n@@ -201,6 +201,7 @@ void load_pseudo_merges_from_config(struct string_list *list)\n }\n \n static int find_pseudo_merge_group_for_ref(const char *refname,\n+\t\t\t\t\t   const char *referent UNUSED,\n \t\t\t\t\t   const struct object_id *oid,\n \t\t\t\t\t   int flags UNUSED,\n \t\t\t\t\t   void *_data)\ndiff --git a/reachable.c b/reachable.c\nindex 46613a6bb6f..3e9b3dd0a46 100644\n--- a/reachable.c\n+++ b/reachable.c\n@@ -79,7 +79,7 @@ static void add_rebase_files(struct rev_info *revs)\n \tfree_worktrees(worktrees);\n }\n \n-static int add_one_ref(const char *path, const struct object_id *oid,\n+static int add_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t       int flag, void *cb_data)\n {\n \tstruct rev_info *revs = (struct rev_info *)cb_data;\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 8c5e673fc0a..39997890feb 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -2865,7 +2865,7 @@ struct ref_filter_cbdata {\n  * A call-back given to for_each_ref().  Filter refs and keep them for\n  * later object processing.\n  */\n-static int filter_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_one(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\n@@ -2900,7 +2900,7 @@ struct ref_filter_and_format_cbdata {\n \t} internal;\n };\n \n-static int filter_and_format_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_and_format_one(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_and_format_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\ndiff --git a/reflog.c b/reflog.c\nindex 5ca944529b3..d592c1cfb56 100644\n--- a/reflog.c\n+++ b/reflog.c\n@@ -300,6 +300,7 @@ int should_expire_reflog_ent_verbose(struct object_id *ooid,\n }\n \n static int push_tip_to_list(const char *refname UNUSED,\n+\t\t\t    const char *referent UNUSED,\n \t\t\t    const struct object_id *oid,\n \t\t\t    int flags, void *cb_data)\n {\ndiff --git a/refs.c b/refs.c\nindex bb90a188758..0a5c12cf16b 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -414,7 +414,7 @@ int refs_ref_exists(struct ref_store *refs, const char *refname)\n \t\t\t\t\t NULL, NULL);\n }\n \n-static int for_each_filter_refs(const char *refname,\n+static int for_each_filter_refs(const char *refname, const char *referent,\n \t\t\t\tconst struct object_id *oid,\n \t\t\t\tint flags, void *data)\n {\n@@ -424,7 +424,7 @@ static int for_each_filter_refs(const char *refname,\n \t\treturn 0;\n \tif (filter->prefix)\n \t\tskip_prefix(refname, filter->prefix, &refname);\n-\treturn filter->fn(refname, oid, flags, filter->cb_data);\n+\treturn filter->fn(refname, referent, oid, flags, filter->cb_data);\n }\n \n struct warn_if_dangling_data {\n@@ -435,7 +435,7 @@ struct warn_if_dangling_data {\n \tconst char *msg_fmt;\n };\n \n-static int warn_if_dangling_symref(const char *refname,\n+static int warn_if_dangling_symref(const char *refname, const char *referent UNUSED,\n \t\t\t\t   const struct object_id *oid UNUSED,\n \t\t\t\t   int flags, void *cb_data)\n {\n@@ -506,7 +506,7 @@ int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_da\n \n \tstrbuf_addf(&buf, \"%sHEAD\", get_git_namespace());\n \tif (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag))\n-\t\tret = fn(buf.buf, &oid, flag, cb_data);\n+\t\tret = fn(buf.buf, NULL, &oid, flag, cb_data);\n \tstrbuf_release(&buf);\n \n \treturn ret;\n@@ -1547,7 +1547,7 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n \n \tif (refs_resolve_ref_unsafe(refs, \"HEAD\", RESOLVE_REF_READING,\n \t\t\t\t    &oid, &flag))\n-\t\treturn fn(\"HEAD\", &oid, flag, cb_data);\n+\t\treturn fn(\"HEAD\", NULL, &oid, flag, cb_data);\n \n \treturn 0;\n }\n@@ -2387,7 +2387,7 @@ struct do_for_each_reflog_help {\n \tvoid *cb_data;\n };\n \n-static int do_for_each_reflog_helper(const char *refname,\n+static int do_for_each_reflog_helper(const char *refname, 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)\n@@ -2593,7 +2593,7 @@ struct migration_data {\n \tstruct strbuf *errbuf;\n };\n \n-static int migrate_one_ref(const char *refname, const struct object_id *oid,\n+static int migrate_one_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t   int flags, void *cb_data)\n {\n \tstruct migration_data *data = cb_data;\ndiff --git a/refs.h b/refs.h\nindex 0ecba21b4a7..158e0a12197 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -298,7 +298,7 @@ struct ref_transaction;\n  * arguments is only guaranteed to be valid for the duration of a\n  * single callback invocation.\n  */\n-typedef int each_ref_fn(const char *refname,\n+typedef int each_ref_fn(const char *refname, const char *referent,\n \t\t\tconst struct object_id *oid, int flags, void *cb_data);\n \n /*\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 210adf644fa..94d1ed0e1c7 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -3044,7 +3044,7 @@ static int files_transaction_abort(struct ref_store *ref_store,\n \treturn 0;\n }\n \n-static int ref_present(const char *refname,\n+static int ref_present(const char *refname, const char *referent UNUSED,\n \t\t       const struct object_id *oid UNUSED,\n \t\t       int flags UNUSED,\n \t\t       void *cb_data)\ndiff --git a/refs/iterator.c b/refs/iterator.c\nindex 75fbe5d72ab..8e999d81fc6 100644\n--- a/refs/iterator.c\n+++ b/refs/iterator.c\n@@ -450,7 +450,7 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,\n \n \tcurrent_ref_iter = iter;\n \twhile ((ok = ref_iterator_advance(iter)) == ITER_OK) {\n-\t\tretval = fn(iter->refname, iter->oid, iter->flags, cb_data);\n+\t\tretval = fn(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/remote.c b/remote.c\nindex f43cf5e7a4d..4a1269a47ab 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -2354,7 +2354,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,\n \treturn 1;\n }\n \n-static int one_local_ref(const char *refname, const struct object_id *oid,\n+static int one_local_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t int flag UNUSED,\n \t\t\t void *cb_data)\n {\n@@ -2439,7 +2439,7 @@ struct stale_heads_info {\n \tstruct refspec *rs;\n };\n \n-static int get_stale_heads_cb(const char *refname, const struct object_id *oid,\n+static int get_stale_heads_cb(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t      int flags, void *cb_data)\n {\n \tstruct stale_heads_info *info = cb_data;\ndiff --git a/replace-object.c b/replace-object.c\nindex 59252d565ef..9a3cdd809a9 100644\n--- a/replace-object.c\n+++ b/replace-object.c\n@@ -9,6 +9,7 @@\n #include \"commit.h\"\n \n static int register_replace_ref(const 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)\ndiff --git a/revision.c b/revision.c\nindex 1c0192f5225..6b33bd814f7 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1648,7 +1648,7 @@ struct all_refs_cb {\n \tstruct worktree *wt;\n };\n \n-static int handle_one_ref(const char *path, const struct object_id *oid,\n+static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t\t  int flag UNUSED,\n \t\t\t  void *cb_data)\n {\ndiff --git a/server-info.c b/server-info.c\nindex f61296a60d0..1508fa6f825 100644\n--- a/server-info.c\n+++ b/server-info.c\n@@ -147,7 +147,7 @@ out:\n \treturn ret;\n }\n \n-static int add_info_ref(const char *path, const struct object_id *oid,\n+static int add_info_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,\n \t\t\tint flag UNUSED,\n \t\t\tvoid *cb_data)\n {\ndiff --git a/shallow.c b/shallow.c\nindex 31a6ca40fe2..b8cd051e3b6 100644\n--- a/shallow.c\n+++ b/shallow.c\n@@ -612,6 +612,7 @@ static void paint_down(struct paint_info *info, const struct object_id *oid,\n }\n \n static int mark_uninteresting(const char *refname UNUSED,\n+\t\t\t      const char *referent UNUSED,\n \t\t\t      const struct object_id *oid,\n \t\t\t      int flags UNUSED,\n \t\t\t      void *cb_data UNUSED)\n@@ -727,6 +728,7 @@ struct commit_array {\n };\n \n static int add_ref(const char *refname UNUSED,\n+\t\t  const char *referent UNUSED,\n \t\t   const struct object_id *oid,\n \t\t   int flags UNUSED,\n \t\t   void *cb_data)\ndiff --git a/submodule.c b/submodule.c\nindex ab99a302530..7c0f5db861d 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -953,6 +953,7 @@ static void free_submodules_data(struct string_list *submodules)\n }\n \n static int has_remote(const char *refname UNUSED,\n+\t\t      const char *referent UNUSED,\n \t\t      const struct object_id *oid UNUSED,\n \t\t      int flags UNUSED, void *cb_data UNUSED)\n {\n@@ -1273,6 +1274,7 @@ int push_unpushed_submodules(struct repository *r,\n }\n \n static int append_oid_to_array(const char *ref UNUSED,\n+\t\t\t       const char *referent UNUSED,\n \t\t\t       const struct object_id *oid,\n \t\t\t       int flags UNUSED, void *data)\n {\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex 637b8b294e9..65346dee551 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -156,7 +156,7 @@ static int cmd_rename_ref(struct ref_store *refs, const char **argv)\n \treturn refs_rename_ref(refs, oldref, newref, logmsg);\n }\n \n-static int each_ref(const char *refname, const struct object_id *oid,\n+static int each_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flags, void *cb_data UNUSED)\n {\n \tprintf(\"%s %s 0x%x\\n\", oid_to_hex(oid), refname, flags);\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 0052c6a4dce..f03ba3e98be 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -857,7 +857,7 @@ static void send_unshallow(struct upload_pack_data *data)\n \t}\n }\n \n-static int check_ref(const char *refname_full, const struct object_id *oid,\n+static int check_ref(const char *refname_full, const char *referent UNUSED, const struct object_id *oid,\n \t\t     int flag, void *cb_data);\n static void deepen(struct upload_pack_data *data, int depth)\n {\n@@ -1208,7 +1208,7 @@ static int mark_our_ref(const char *refname, const char *refname_full,\n \treturn 0;\n }\n \n-static int check_ref(const char *refname_full, const struct object_id *oid,\n+static int check_ref(const char *refname_full, const char *referent UNUSED,const struct object_id *oid,\n \t\t     int flag UNUSED, void *cb_data)\n {\n \tconst char *refname = strip_namespace(refname_full);\n@@ -1276,14 +1276,14 @@ static void write_v0_ref(struct upload_pack_data *data,\n \treturn;\n }\n \n-static int send_ref(const char *refname, const struct object_id *oid,\n+static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,\n \t\t    int flag UNUSED, void *cb_data)\n {\n \twrite_v0_ref(cb_data, refname, strip_namespace(refname), oid);\n \treturn 0;\n }\n \n-static int find_symref(const char *refname,\n+static int find_symref(const char *refname, const char *referent UNUSED,\n \t\t       const struct object_id *oid UNUSED,\n \t\t       int flag, void *cb_data)\n {\ndiff --git a/walker.c b/walker.c\nindex 0fafdc97cf1..807a7a38813 100644\n--- a/walker.c\n+++ b/walker.c\n@@ -221,6 +221,7 @@ static int interpret_target(struct walker *walker, char *target, struct object_i\n }\n \n static int mark_complete(const char *path UNUSED,\n+\t\t\tconst char *referent UNUSED,\n \t\t\t const struct object_id *oid,\n \t\t\t int flag UNUSED,\n \t\t\t void *cb_data UNUSED)\ndiff --git a/worktree.c b/worktree.c\nindex f3c4c8ec54d..fd05f3741c6 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -546,7 +546,7 @@ int other_head_refs(each_ref_fn fn, void *cb_data)\n \t\t\t\t\t    refname.buf,\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+\t\t\tret = fn(refname.buf, NULL, &oid, flag, cb_data);\n \t\tif (ret)\n \t\t\tbreak;\n \t}\n-- \ngitgitgadget\n\n"},{"id":"500544","messageId":"9f609f4d0f50758cd32b172e6e2ed7b3e71cf29f.1723217872.git.gitgitgadget@gmail.com","threadId":"61598","inReplyTo":"pull.1712.v4.git.git.1723217871.gitgitgadget@gmail.com","subject":"[PATCH v4 3/3] ref-filter: populate symref from iterator","fromName":"John Cai via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2024-08-09T15:37:51Z","receivedAt":"2024-08-09T15:37:59Z","isPatch":true,"sender":{"key":"johncai86@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2354211?v=4"},"body":"From: John Cai <johncai86@gmail.com>\n\nWith a previous commit, the reference the symbolic ref points to is saved\nin the ref iterator records. Instead of making a separate call to\nresolve_refdup() each time, we can just populate the ref_array_item with\nthe value from the iterator.\n\nSigned-off-by: John Cai <johncai86@gmail.com>\n---\n ref-filter.c | 17 ++++++++++++-----\n 1 file changed, 12 insertions(+), 5 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 39997890feb..4d1f3ff3d23 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -2343,6 +2343,12 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)\n \n \tCALLOC_ARRAY(ref->value, used_atom_cnt);\n \n+\t/**\n+\t * NEEDSWORK: The following code might be unncessary if all codepaths\n+\t * that call populate_value() populates the symref member of ref_array_item\n+\t * like in apply_ref_filter(). Currently pretty_print_ref() is the only codepath\n+\t * that calls populate_value() without first populating symref.\n+\t */\n \tif (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {\n \t\tref->symref = refs_resolve_refdup(get_main_ref_store(the_repository),\n \t\t\t\t\t\t  ref->refname,\n@@ -2783,7 +2789,7 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)\n \treturn ref_kind_from_refname(refname);\n }\n \n-static struct ref_array_item *apply_ref_filter(const char *refname, const struct object_id *oid,\n+static struct ref_array_item *apply_ref_filter(const char *refname, const char *referent, const struct object_id *oid,\n \t\t\t    int flag, struct ref_filter *filter)\n {\n \tstruct ref_array_item *ref;\n@@ -2852,6 +2858,7 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const struct\n \tref->commit = commit;\n \tref->flag = flag;\n \tref->kind = kind;\n+\tref->symref = xstrdup_or_null(referent);\n \n \treturn ref;\n }\n@@ -2865,12 +2872,12 @@ struct ref_filter_cbdata {\n  * A call-back given to for_each_ref().  Filter refs and keep them for\n  * later object processing.\n  */\n-static int filter_one(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\n \n-\tref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);\n+\tref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);\n \tif (ref)\n \t\tref_array_append(ref_cbdata->array, ref);\n \n@@ -2900,13 +2907,13 @@ struct ref_filter_and_format_cbdata {\n \t} internal;\n };\n \n-static int filter_and_format_one(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flag, void *cb_data)\n+static int filter_and_format_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)\n {\n \tstruct ref_filter_and_format_cbdata *ref_cbdata = cb_data;\n \tstruct ref_array_item *ref;\n \tstruct strbuf output = STRBUF_INIT, err = STRBUF_INIT;\n \n-\tref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);\n+\tref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);\n \tif (!ref)\n \t\treturn 0;\n \n-- \ngitgitgadget\n"},{"id":"500547","messageId":"xmqqttftej29.fsf@gitster.g","threadId":"61598","inReplyTo":"pull.1712.v4.git.git.1723217871.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 0/3] keep track of unresolved value of symbolic-ref in ref iterators","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-09T16:51:10Z","receivedAt":"2024-08-09T16:51:19Z","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> John Cai (3):\n>   refs: keep track of unresolved reference value in iterators\n>   refs: add referent to each_ref_fn\n>   ref-filter: populate symref from iterator\n\nLooks good.  Let me mark the topic for 'next'.\n"},{"id":"507974","messageId":"Z0GRNGQEeoOQHKz3@ArchLinux","threadId":"61598","inReplyTo":"c4f5f5b7dd8dad4f17201611faee14dd1b882bff.1723217871.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 1/3] refs: keep track of unresolved reference value in iterators","fromName":"shejialuo","fromEmail":"shejialuo@gmail.com","sentAt":"2024-11-23T08:24:20Z","receivedAt":"2024-11-23T08:24:08Z","isPatch":true,"sender":{"key":"shejialuo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/56911263?v=4"},"body":"On Fri, Aug 09, 2024 at 03:37:49PM +0000, John Cai via GitGitGadget wrote:\n\n[snip]\n\n> @@ -66,6 +69,7 @@ static void free_ref_entry(struct ref_entry *entry)\n>  \t\t */\n>  \t\tclear_ref_dir(&entry->u.subdir);\n>  \t}\n> +\tfree(entry->u.value.referent);\n>  \tfree(entry);\n>  }\n>  \n\nToday, I am learning the source code of the \"ref-cache.[ch]\". I feel\nrather confused here. And I think this usage is wrong.\n\n\"free_ref_entry\" will do the following things:\n\n1. If \"entry\" is a directory, it will call \"clear_ref_dir\" which will\ncall \"free_ref_entry\" for every loose ref.\n2. If \"entry\" is a loose ref, it will call `free(entry->u.value.referent)`\nand `free(entry)`.\n\nThe problem is if \"entry\" is a directory, we will also execute the\nfollowing statement:\n\n    free(entry->u.value.referent);\n\nThis does not make sense. We should never access the \"entry->u.value\" if\n\"entry\" is a directory. So, I think the correct usage should be:\n\n    if (entry->flag & REF_DIR) {\n        ...\n        clear_ref_dir(...);\n    } else {\n        free(entry->u.value.referent);\n    }\n\nThanks,\nJialuo\n"}]}