* [PATCH 00/13] refs: improvements and fixes for peeling tags
@ 2025-10-07 10:58 Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 01/13] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
` (17 more replies)
0 siblings, 18 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-07 10:58 UTC (permalink / raw)
To: git
Hi,
originally, all I wanted to do was the last patch: a small performance
optimization that stops parsing objects in git-for-each-ref(1) unless we
really need to parse them. But that fix cause one specific test to fail,
and only with the reftable backend. So this led me down the rabbit hole
of tag peeling, ending up with this patch series.
The series is structured like follows:
- Patches 1 to 7 refactor our codebase so that we don't have the
`peel_iterated_object()` hack anymore. I just found it hard to
follow and thought it shouldn't be too hard to get rid of it.
- Patches 8 and 9 remove infrastructure that we don't need anymore
after the first couple of patches.
- Patches 10 to 12 fix a couple of issues with peeled tags that I
found. The underlying issue is that tags store both the tagged
object and their type, but this information may not match. We never
verify the actual object type though when allocating the tagged
object, so this only blows up much later.
- Patch 13 was my original motivation, a small performance
optimization.
I'm not particularly fond of the patches 10 to 12. It feels more like
playing whack-a-mole, and I very much assume that there still are edge
cases where we should properly verify the tagged object type. But
changing it in `parse_tag_buffer()` itself causes a bunch of tests to
fail where we intentionally create such corrupted tags. So I didn't
really dare to touch that part, to be honest.
If anybody has suggestions for an alternative approach I'd be very open
to it.
The topic is built on top of 45547b60ac (Merge branch 'master' of
https://github.com/j6t/gitk, 2025-10-05). There is a merge conflict with
tb/incremental-midx-part-3.1, which moves code from "builtin/repack.c"
into "repack-*.c".
The conflict can be solved by accepting "builtin/repack.c" from
tb/incremental-midx-part-3.1 and adding the below patch to
"repack-midx.c". I can also rebase on top of that series, but given that
it is rather huge it may take a while before it lands.
Thanks!
Patrick
diff --cc builtin/repack.c
index 873e21c35d,ad60c4290d..0000000000
--- a/builtin/repack.c
+++ b/builtin/repack.c
diff --git a/repack-midx.c b/repack-midx.c
index 6f6202c5bc..74bdfa3a6e 100644
--- a/repack-midx.c
+++ b/repack-midx.c
@@ -16,25 +16,23 @@ struct midx_snapshot_ref_data {
int preferred;
};
-static int midx_snapshot_ref_one(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *_data)
+static int midx_snapshot_ref_one(const struct reference *ref, void *_data)
{
struct midx_snapshot_ref_data *data = _data;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
- if (!peel_iterated_oid(data->repo, oid, &peeled))
- oid = &peeled;
+ if (!reference_get_peeled_oid(data->repo, ref, &peeled))
+ maybe_peeled = &peeled;
- if (oidset_insert(&data->seen, oid))
+ if (oidset_insert(&data->seen, maybe_peeled))
return 0; /* already seen */
- if (odb_read_object_info(data->repo->objects, oid, NULL) != OBJ_COMMIT)
+ if (odb_read_object_info(data->repo->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
fprintf(data->f->fp, "%s%s\n", data->preferred ? "+" : "",
- oid_to_hex(oid));
+ oid_to_hex(maybe_peeled));
return 0;
}
---
Patrick Steinhardt (13):
refs: introduce wrapper struct for `each_ref_fn`
refs: introduce `.ref` field for the base iterator
refs: refactor reference status flags
refs: expose peeled object ID via the iterator
upload-pack: convert to use `reference_get_peeled_oid()`
ref-filter: propagate peeled object ID
builtin/show-ref: convert to use `reference_get_peeled_oid()`
refs: drop `current_ref_iter` hack
refs: drop infrastructure to peel via iterators
object: add flag to `peel_object()` to verify object type
refs: don't store peeled object IDs for invalid tags
ref-filter: detect broken tags when dereferencing them
ref-filter: parse objects on demand
bisect.c | 24 ++---
builtin/bisect.c | 17 +---
builtin/checkout.c | 6 +-
builtin/describe.c | 18 ++--
builtin/fetch.c | 13 +--
builtin/fsck.c | 33 +++---
builtin/gc.c | 15 ++-
builtin/ls-remote.c | 2 +-
builtin/name-rev.c | 17 ++--
builtin/pack-objects.c | 28 +++---
builtin/receive-pack.c | 13 ++-
builtin/remote.c | 44 ++++----
builtin/repack.c | 16 ++-
builtin/replace.c | 21 ++--
builtin/rev-parse.c | 12 +--
builtin/show-branch.c | 35 +++----
builtin/show-ref.c | 50 ++++-----
builtin/submodule--helper.c | 10 +-
builtin/tag.c | 2 +-
builtin/verify-tag.c | 2 +-
builtin/worktree.c | 6 +-
commit-graph.c | 14 ++-
delta-islands.c | 9 +-
fetch-pack.c | 16 +--
help.c | 10 +-
http-backend.c | 20 ++--
log-tree.c | 24 ++---
ls-refs.c | 36 ++++---
midx-write.c | 17 ++--
negotiator/default.c | 7 +-
negotiator/skipping.c | 7 +-
notes.c | 8 +-
object-name.c | 10 +-
object.c | 20 +++-
object.h | 15 ++-
pseudo-merge.c | 21 ++--
reachable.c | 9 +-
ref-filter.c | 239 ++++++++++++++++++++++++++++++--------------
ref-filter.h | 5 +-
reflog.c | 9 +-
refs.c | 85 +++++++++-------
refs.h | 84 ++++++++++------
refs/debug.c | 17 +---
refs/files-backend.c | 71 +++++--------
refs/iterator.c | 73 +++-----------
refs/packed-backend.c | 72 +++++--------
refs/ref-cache.c | 18 +---
refs/refs-internal.h | 25 +----
refs/reftable-backend.c | 48 +++------
remote.c | 27 +++--
replace-object.c | 16 ++-
revision.c | 12 +--
server-info.c | 12 +--
shallow.c | 16 +--
submodule.c | 12 +--
t/for-each-ref-tests.sh | 4 +-
t/helper/test-reach.c | 2 +-
t/helper/test-ref-store.c | 5 +-
t/pack-refs-tests.sh | 32 ++++++
t/t0610-reftable-basics.sh | 28 ++++++
tag.c | 12 ---
tag.h | 1 -
upload-pack.c | 49 ++++-----
walker.c | 8 +-
worktree.c | 11 +-
65 files changed, 791 insertions(+), 829 deletions(-)
---
base-commit: 45547b60aca32b45d2f1ef93462cf9df28637c13
change-id: 20250918-b4-pks-ref-filter-skip-parsing-objects-f0d1f6af4a9f
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH 01/13] refs: introduce wrapper struct for `each_ref_fn`
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
@ 2025-10-07 10:58 ` Patrick Steinhardt
2025-10-07 18:05 ` Justin Tobler
` (2 more replies)
2025-10-07 10:58 ` [PATCH 02/13] refs: introduce `.ref` field for the base iterator Patrick Steinhardt
` (16 subsequent siblings)
17 siblings, 3 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-07 10:58 UTC (permalink / raw)
To: git
The `each_ref_fn` callback function type is used across our code base
for several different functions that iterate through reference. There's
a bunch of callbacks implementing this type, which makes any changes to
the callback signature extremely noisy. An example of the required churn
is e8207717f1 (refs: add referent to each_ref_fn, 2024-08-09): adding a
single argument required us to change 48 files.
It was already proposed back then [1] that we might want to introduce a
wrapper structure to alleviate the pain going forward. While this of
course requires the same kind of global refactoring as just introducing
a new parameter, it at least allows us to more change the callback type
afterwards by just extending the wrapper structure.
One counterargument to this refactoring is that it makes the structure
more opaque. While it is obvious which callsites need to be fixed up
when we change the function type, it's not obvious anymore once we use
a structure. That being said, we only have a handful of sites that
actually need to populate this wrapper structure: our ref backends and
"refs/iterator.c".
Introduce this wrapper structure so that we can adapt the iterator
interfaces more readily.
[1]: <ZmarVcF5JjsZx0dl@tanuki>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
bisect.c | 24 +++++++---------
builtin/bisect.c | 17 ++++--------
builtin/checkout.c | 6 ++--
builtin/describe.c | 18 ++++++------
builtin/fetch.c | 13 +++------
builtin/fsck.c | 33 ++++++++++++----------
builtin/gc.c | 15 ++++------
builtin/name-rev.c | 17 ++++++------
builtin/pack-objects.c | 27 ++++++++----------
builtin/receive-pack.c | 13 ++++-----
builtin/remote.c | 44 +++++++++++++----------------
builtin/repack.c | 16 +++++------
builtin/replace.c | 21 ++++++--------
builtin/rev-parse.c | 12 ++++----
builtin/show-branch.c | 35 +++++++++++------------
builtin/show-ref.c | 20 ++++++--------
builtin/submodule--helper.c | 10 ++-----
builtin/worktree.c | 6 +---
commit-graph.c | 14 ++++------
delta-islands.c | 9 +++---
fetch-pack.c | 16 +++--------
help.c | 10 +++----
http-backend.c | 20 ++++++--------
log-tree.c | 24 ++++++++--------
ls-refs.c | 36 ++++++++++++++----------
midx-write.c | 17 ++++++------
negotiator/default.c | 7 ++---
negotiator/skipping.c | 7 ++---
notes.c | 8 ++----
object-name.c | 10 +++----
pseudo-merge.c | 21 ++++++--------
reachable.c | 9 +++---
ref-filter.c | 24 ++++++++--------
reflog.c | 9 ++----
refs.c | 67 +++++++++++++++++++++++++--------------------
refs.h | 22 +++++++++++++--
refs/files-backend.c | 7 ++---
refs/iterator.c | 9 +++++-
remote.c | 27 ++++++++----------
replace-object.c | 16 ++++-------
revision.c | 12 ++++----
server-info.c | 12 ++++----
shallow.c | 16 +++--------
submodule.c | 12 ++------
t/helper/test-ref-store.c | 5 ++--
upload-pack.c | 29 +++++++++-----------
walker.c | 8 ++----
worktree.c | 11 ++++++--
48 files changed, 387 insertions(+), 454 deletions(-)
diff --git a/bisect.c b/bisect.c
index a6dc76b15c9..326b59c0dc7 100644
--- a/bisect.c
+++ b/bisect.c
@@ -450,21 +450,20 @@ void find_bisection(struct commit_list **commit_list, int *reaches,
clear_commit_weight(&commit_weight);
}
-static int register_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb_data UNUSED)
+static int register_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct strbuf good_prefix = STRBUF_INIT;
strbuf_addstr(&good_prefix, term_good);
strbuf_addstr(&good_prefix, "-");
- if (!strcmp(refname, term_bad)) {
+ if (!strcmp(ref->name, term_bad)) {
free(current_bad_oid);
current_bad_oid = xmalloc(sizeof(*current_bad_oid));
- oidcpy(current_bad_oid, oid);
- } else if (starts_with(refname, good_prefix.buf)) {
- oid_array_append(&good_revs, oid);
- } else if (starts_with(refname, "skip-")) {
- oid_array_append(&skipped_revs, oid);
+ oidcpy(current_bad_oid, ref->oid);
+ } else if (starts_with(ref->name, good_prefix.buf)) {
+ oid_array_append(&good_revs, ref->oid);
+ } else if (starts_with(ref->name, "skip-")) {
+ oid_array_append(&skipped_revs, ref->oid);
}
strbuf_release(&good_prefix);
@@ -1178,14 +1177,11 @@ int estimate_bisect_steps(int all)
return (e < 3 * x) ? n : n - 1;
}
-static int mark_for_removal(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cb_data)
+static int mark_for_removal(const struct reference *ref, void *cb_data)
{
struct string_list *refs = cb_data;
- char *ref = xstrfmt("refs/bisect%s", refname);
- string_list_append(refs, ref);
+ char *bisect_ref = xstrfmt("refs/bisect%s", ref->name);
+ string_list_append(refs, bisect_ref);
return 0;
}
diff --git a/builtin/bisect.c b/builtin/bisect.c
index 8b8d870cd1e..5b2024be62d 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -358,10 +358,7 @@ static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)
return 0;
}
-static int inc_nr(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cb_data)
+static int inc_nr(const struct reference *ref UNUSED, void *cb_data)
{
unsigned int *nr = (unsigned int *)cb_data;
(*nr)++;
@@ -549,12 +546,11 @@ static int bisect_append_log_quoted(const char **argv)
return res;
}
-static int add_bisect_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb)
+static int add_bisect_ref(const struct reference *ref, void *cb)
{
struct add_bisect_ref_data *data = cb;
- add_pending_oid(data->revs, refname, oid, data->object_flags);
+ add_pending_oid(data->revs, ref->name, ref->oid, data->object_flags);
return 0;
}
@@ -1165,12 +1161,9 @@ static int bisect_visualize(struct bisect_terms *terms, int argc,
return run_command(&cmd);
}
-static int get_first_good(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int get_first_good(const struct reference *ref, void *cb_data)
{
- oidcpy(cb_data, oid);
+ oidcpy(cb_data, ref->oid);
return 1;
}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f9453473fe2..66b69df6e67 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1063,11 +1063,9 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
report_tracking(new_branch_info);
}
-static int add_pending_uninteresting_ref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int add_pending_uninteresting_ref(const struct reference *ref, void *cb_data)
{
- add_pending_oid(cb_data, refname, oid, UNINTERESTING);
+ add_pending_oid(cb_data, ref->name, ref->oid, UNINTERESTING);
return 0;
}
diff --git a/builtin/describe.c b/builtin/describe.c
index ffaf8d9f0aa..79545350443 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -154,20 +154,19 @@ static void add_to_known_names(const char *path,
}
}
-static int get_name(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int get_name(const struct reference *ref, void *cb_data UNUSED)
{
int is_tag = 0;
struct object_id peeled;
int is_annotated, prio;
const char *path_to_match = NULL;
- if (skip_prefix(path, "refs/tags/", &path_to_match)) {
+ if (skip_prefix(ref->name, "refs/tags/", &path_to_match)) {
is_tag = 1;
} else if (all) {
if ((exclude_patterns.nr || patterns.nr) &&
- !skip_prefix(path, "refs/heads/", &path_to_match) &&
- !skip_prefix(path, "refs/remotes/", &path_to_match)) {
+ !skip_prefix(ref->name, "refs/heads/", &path_to_match) &&
+ !skip_prefix(ref->name, "refs/remotes/", &path_to_match)) {
/* Only accept reference of known type if there are match/exclude patterns */
return 0;
}
@@ -209,10 +208,10 @@ static int get_name(const char *path, const char *referent UNUSED, const struct
}
/* Is it annotated? */
- if (!peel_iterated_oid(the_repository, oid, &peeled)) {
- is_annotated = !oideq(oid, &peeled);
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled)) {
+ is_annotated = !oideq(ref->oid, &peeled);
} else {
- oidcpy(&peeled, oid);
+ oidcpy(&peeled, ref->oid);
is_annotated = 0;
}
@@ -229,7 +228,8 @@ static int get_name(const char *path, const char *referent UNUSED, const struct
else
prio = 0;
- add_to_known_names(all ? path + 5 : path + 10, &peeled, prio, oid);
+ add_to_known_names(all ? ref->name + 5 : ref->name + 10,
+ &peeled, prio, ref->oid);
return 0;
}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index c7ff3480fb1..7052e6ff215 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -289,13 +289,11 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,
return ent;
}
-static int add_one_refname(const char *refname, const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *cbdata)
+static int add_one_refname(const struct reference *ref, void *cbdata)
{
struct hashmap *refname_map = cbdata;
- (void) refname_hash_add(refname_map, refname, oid);
+ (void) refname_hash_add(refname_map, ref->name, ref->oid);
return 0;
}
@@ -1416,14 +1414,11 @@ static void set_option(struct transport *transport, const char *name, const char
}
-static int add_oid(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int add_oid(const struct reference *ref, void *cb_data)
{
struct oid_array *oids = cb_data;
- oid_array_append(oids, oid);
+ oid_array_append(oids, ref->oid);
return 0;
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index d2eb9d4fbe9..2b8fa22cccb 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -530,14 +530,13 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)
return 0;
}
-static int fsck_handle_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object *obj;
- obj = parse_object(the_repository, oid);
+ obj = parse_object(the_repository, ref->oid);
if (!obj) {
- if (is_promisor_object(the_repository, oid)) {
+ if (is_promisor_object(the_repository, ref->oid)) {
/*
* Increment default_refs anyway, because this is a
* valid ref.
@@ -546,19 +545,19 @@ static int fsck_handle_ref(const char *refname, const char *referent UNUSED, con
return 0;
}
error(_("%s: invalid sha1 pointer %s"),
- refname, oid_to_hex(oid));
+ ref->name, oid_to_hex(ref->oid));
errors_found |= ERROR_REACHABLE;
/* We'll continue with the rest despite the error.. */
return 0;
}
- if (obj->type != OBJ_COMMIT && is_branch(refname)) {
- error(_("%s: not a commit"), refname);
+ if (obj->type != OBJ_COMMIT && is_branch(ref->name)) {
+ error(_("%s: not a commit"), ref->name);
errors_found |= ERROR_REFS;
}
default_refs++;
obj->flags |= USED;
fsck_put_object_name(&fsck_walk_options,
- oid, "%s", refname);
+ ref->oid, "%s", ref->name);
mark_object_reachable(obj);
return 0;
@@ -580,13 +579,19 @@ static void get_default_heads(void)
worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
- struct strbuf ref = STRBUF_INIT;
+ struct strbuf refname = STRBUF_INIT;
- strbuf_worktree_ref(wt, &ref, "HEAD");
- fsck_head_link(ref.buf, &head_points_at, &head_oid);
- if (head_points_at && !is_null_oid(&head_oid))
- fsck_handle_ref(ref.buf, NULL, &head_oid, 0, NULL);
- strbuf_release(&ref);
+ strbuf_worktree_ref(wt, &refname, "HEAD");
+ fsck_head_link(refname.buf, &head_points_at, &head_oid);
+ if (head_points_at && !is_null_oid(&head_oid)) {
+ struct reference ref = {
+ .name = refname.buf,
+ .oid = &head_oid,
+ };
+
+ fsck_handle_ref(&ref, NULL);
+ }
+ strbuf_release(&refname);
if (include_reflogs)
refs_for_each_reflog(get_worktree_ref_store(wt),
diff --git a/builtin/gc.c b/builtin/gc.c
index 03ae4926b20..a104c1cb782 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -1098,24 +1098,21 @@ struct cg_auto_data {
int limit;
};
-static int dfs_on_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int dfs_on_ref(const struct reference *ref, void *cb_data)
{
struct cg_auto_data *data = (struct cg_auto_data *)cb_data;
int result = 0;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct commit_list *stack = NULL;
struct commit *commit;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
- if (odb_read_object_info(the_repository->objects, oid, NULL) != OBJ_COMMIT)
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ maybe_peeled = &peeled;
+ if (odb_read_object_info(the_repository->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
- commit = lookup_commit(the_repository, oid);
+ commit = lookup_commit(the_repository, maybe_peeled);
if (!commit)
return 0;
if (repo_parse_commit(the_repository, commit) ||
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 74512e54a38..615f7d1aae4 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -339,10 +339,9 @@ static int cmp_by_tag_and_age(const void *a_, const void *b_)
return a->taggerdate != b->taggerdate;
}
-static int name_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int name_ref(const struct reference *ref, void *cb_data)
{
- struct object *o = parse_object(the_repository, oid);
+ struct object *o = parse_object(the_repository, ref->oid);
struct name_ref_data *data = cb_data;
int can_abbreviate_output = data->tags_only && data->name_only;
int deref = 0;
@@ -350,14 +349,14 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
struct commit *commit = NULL;
timestamp_t taggerdate = TIME_MAX;
- if (data->tags_only && !starts_with(path, "refs/tags/"))
+ if (data->tags_only && !starts_with(ref->name, "refs/tags/"))
return 0;
if (data->exclude_filters.nr) {
struct string_list_item *item;
for_each_string_list_item(item, &data->exclude_filters) {
- if (subpath_matches(path, item->string) >= 0)
+ if (subpath_matches(ref->name, item->string) >= 0)
return 0;
}
}
@@ -378,7 +377,7 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
* shouldn't stop when seeing 'refs/tags/v1.4' matches
* 'refs/tags/v*'. We should show it as 'v1.4'.
*/
- switch (subpath_matches(path, item->string)) {
+ switch (subpath_matches(ref->name, item->string)) {
case -1: /* did not match */
break;
case 0: /* matched fully */
@@ -406,13 +405,13 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
}
if (o && o->type == OBJ_COMMIT) {
commit = (struct commit *)o;
- from_tag = starts_with(path, "refs/tags/");
+ from_tag = starts_with(ref->name, "refs/tags/");
if (taggerdate == TIME_MAX)
taggerdate = commit->date;
}
- add_to_tip_table(oid, path, can_abbreviate_output, commit, taggerdate,
- from_tag, deref);
+ add_to_tip_table(ref->oid, ref->name, can_abbreviate_output,
+ commit, taggerdate, from_tag, deref);
return 0;
}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 5856b5f6bfb..288ba199811 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -831,15 +831,14 @@ static enum write_one_status write_one(struct hashfile *f,
return WRITE_ONE_WRITTEN;
}
-static int mark_tagged(const char *path UNUSED, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int mark_tagged(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
- struct object_entry *entry = packlist_find(&to_pack, oid);
+ struct object_entry *entry = packlist_find(&to_pack, ref->oid);
if (entry)
entry->tagged = 1;
- if (!peel_iterated_oid(the_repository, oid, &peeled)) {
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled)) {
entry = packlist_find(&to_pack, &peeled);
if (entry)
entry->tagged = 1;
@@ -3306,13 +3305,12 @@ static void add_tag_chain(const struct object_id *oid)
}
}
-static int add_ref_tag(const char *tag UNUSED, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int add_ref_tag(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, oid, &peeled) && obj_is_packed(&peeled))
- add_tag_chain(oid);
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled) && obj_is_packed(&peeled))
+ add_tag_chain(ref->oid);
return 0;
}
@@ -4528,19 +4526,16 @@ static void record_recent_commit(struct commit *commit, void *data UNUSED)
oid_array_append(&recent_objects, &commit->object.oid);
}
-static int mark_bitmap_preferred_tip(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *data UNUSED)
+static int mark_bitmap_preferred_tip(const struct reference *ref, void *data UNUSED)
{
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct object *object;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ maybe_peeled = &peeled;
- object = parse_object_or_die(the_repository, oid, refname);
+ object = parse_object_or_die(the_repository, maybe_peeled, ref->name);
if (object->type == OBJ_COMMIT)
object->flags |= NEEDS_BITMAP;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 1113137a6f0..2a45c0867e3 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -305,13 +305,12 @@ static void show_ref(const char *path, const struct object_id *oid)
}
}
-static int show_ref_cb(const char *path_full, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *data)
+static int show_ref_cb(const struct reference *ref, void *data)
{
struct oidset *seen = data;
- const char *path = strip_namespace(path_full);
+ const char *path = strip_namespace(ref->name);
- if (ref_is_hidden(path, path_full, &hidden_refs))
+ if (ref_is_hidden(path, ref->name, &hidden_refs))
return 0;
/*
@@ -320,13 +319,13 @@ static int show_ref_cb(const char *path_full, const char *referent UNUSED, const
* transfer but will otherwise ignore them.
*/
if (!path) {
- if (oidset_insert(seen, oid))
+ if (oidset_insert(seen, ref->oid))
return 0;
path = ".have";
} else {
- oidset_insert(seen, oid);
+ oidset_insert(seen, ref->oid);
}
- show_ref(path, oid);
+ show_ref(path, ref->oid);
return 0;
}
diff --git a/builtin/remote.c b/builtin/remote.c
index 8a7ed4299a4..7ffc14ba157 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -570,17 +570,14 @@ struct branches_for_remote {
struct known_remotes *keep;
};
-static int add_branch_for_removal(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED, void *cb_data)
+static int add_branch_for_removal(const struct reference *ref, void *cb_data)
{
struct branches_for_remote *branches = cb_data;
struct refspec_item refspec;
struct known_remote *kr;
memset(&refspec, 0, sizeof(refspec));
- refspec.dst = (char *)refname;
+ refspec.dst = (char *)ref->name;
if (remote_find_tracking(branches->remote, &refspec))
return 0;
free(refspec.src);
@@ -588,7 +585,7 @@ static int add_branch_for_removal(const char *refname,
/* don't delete a branch if another remote also uses it */
for (kr = branches->keep->list; kr; kr = kr->next) {
memset(&refspec, 0, sizeof(refspec));
- refspec.dst = (char *)refname;
+ refspec.dst = (char *)ref->name;
if (!remote_find_tracking(kr->remote, &refspec)) {
free(refspec.src);
return 0;
@@ -596,16 +593,16 @@ static int add_branch_for_removal(const char *refname,
}
/* don't delete non-remote-tracking refs */
- if (!starts_with(refname, "refs/remotes/")) {
+ if (!starts_with(ref->name, "refs/remotes/")) {
/* advise user how to delete local branches */
- if (starts_with(refname, "refs/heads/"))
+ if (starts_with(ref->name, "refs/heads/"))
string_list_append(branches->skipped,
- abbrev_branch(refname));
+ abbrev_branch(ref->name));
/* silently skip over other non-remote refs */
return 0;
}
- string_list_append(branches->branches, refname);
+ string_list_append(branches->branches, ref->name);
return 0;
}
@@ -713,18 +710,18 @@ static int rename_one_reflog(const char *old_refname,
return error;
}
-static int rename_one_ref(const char *old_refname, const char *referent,
- const struct object_id *oid,
- int flags, void *cb_data)
+static int rename_one_ref(const struct reference *ref, void *cb_data)
{
struct strbuf new_referent = STRBUF_INIT;
struct strbuf new_refname = STRBUF_INIT;
struct rename_info *rename = cb_data;
+ const struct object_id *oid = ref->oid;
+ const char *referent = ref->target;
int error;
- compute_renamed_ref(rename, old_refname, &new_refname);
+ compute_renamed_ref(rename, ref->name, &new_refname);
- if (flags & REF_ISSYMREF) {
+ if (ref->flags & REF_ISSYMREF) {
/*
* Stupidly enough `referent` is not pointing to the immediate
* target of a symref, but it's the recursively resolved value.
@@ -732,25 +729,25 @@ static int rename_one_ref(const char *old_refname, const char *referent,
* unborn symrefs don't have any value for the `referent` at all.
*/
referent = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- old_refname, RESOLVE_REF_NO_RECURSE,
+ ref->name, RESOLVE_REF_NO_RECURSE,
NULL, NULL);
compute_renamed_ref(rename, referent, &new_referent);
oid = NULL;
}
- error = ref_transaction_delete(rename->transaction, old_refname,
+ error = ref_transaction_delete(rename->transaction, ref->name,
oid, referent, REF_NO_DEREF, NULL, rename->err);
if (error < 0)
goto out;
error = ref_transaction_update(rename->transaction, new_refname.buf, oid, null_oid(the_hash_algo),
- (flags & REF_ISSYMREF) ? new_referent.buf : NULL, NULL,
+ (ref->flags & REF_ISSYMREF) ? new_referent.buf : NULL, NULL,
REF_SKIP_CREATE_REFLOG | REF_NO_DEREF | REF_SKIP_OID_VERIFICATION,
NULL, rename->err);
if (error < 0)
goto out;
- error = rename_one_reflog(old_refname, oid, rename);
+ error = rename_one_reflog(ref->name, oid, rename);
if (error < 0)
goto out;
@@ -1125,19 +1122,16 @@ static void free_remote_ref_states(struct ref_states *states)
string_list_clear_func(&states->push, clear_push_info);
}
-static int append_ref_to_tracked_list(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags, void *cb_data)
+static int append_ref_to_tracked_list(const struct reference *ref, void *cb_data)
{
struct ref_states *states = cb_data;
struct refspec_item refspec;
- if (flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
return 0;
memset(&refspec, 0, sizeof(refspec));
- refspec.dst = (char *)refname;
+ refspec.dst = (char *)ref->name;
if (!remote_find_tracking(states->remote, &refspec)) {
string_list_append(&states->tracked, abbrev_branch(refspec.src));
free(refspec.src);
diff --git a/builtin/repack.c b/builtin/repack.c
index c490a51e919..893c105c81f 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -773,25 +773,23 @@ struct midx_snapshot_ref_data {
int preferred;
};
-static int midx_snapshot_ref_one(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *_data)
+static int midx_snapshot_ref_one(const struct reference *ref, void *_data)
{
struct midx_snapshot_ref_data *data = _data;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ maybe_peeled = &peeled;
- if (oidset_insert(&data->seen, oid))
+ if (oidset_insert(&data->seen, maybe_peeled))
return 0; /* already seen */
- if (odb_read_object_info(the_repository->objects, oid, NULL) != OBJ_COMMIT)
+ if (odb_read_object_info(the_repository->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
fprintf(data->f->fp, "%s%s\n", data->preferred ? "+" : "",
- oid_to_hex(oid));
+ oid_to_hex(maybe_peeled));
return 0;
}
diff --git a/builtin/replace.c b/builtin/replace.c
index 900b560a77d..4c62c5ab58b 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -47,30 +47,27 @@ struct show_data {
enum replace_format format;
};
-static int show_reference(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int show_reference(const struct reference *ref, void *cb_data)
{
struct show_data *data = cb_data;
- if (!wildmatch(data->pattern, refname, 0)) {
+ if (!wildmatch(data->pattern, ref->name, 0)) {
if (data->format == REPLACE_FORMAT_SHORT)
- printf("%s\n", refname);
+ printf("%s\n", ref->name);
else if (data->format == REPLACE_FORMAT_MEDIUM)
- printf("%s -> %s\n", refname, oid_to_hex(oid));
+ printf("%s -> %s\n", ref->name, oid_to_hex(ref->oid));
else { /* data->format == REPLACE_FORMAT_LONG */
struct object_id object;
enum object_type obj_type, repl_type;
- if (repo_get_oid(data->repo, refname, &object))
- return error(_("failed to resolve '%s' as a valid ref"), refname);
+ if (repo_get_oid(data->repo, ref->name, &object))
+ return error(_("failed to resolve '%s' as a valid ref"), ref->name);
obj_type = odb_read_object_info(data->repo->objects, &object, NULL);
- repl_type = odb_read_object_info(data->repo->objects, oid, NULL);
+ repl_type = odb_read_object_info(data->repo->objects, ref->oid, NULL);
- printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
- oid_to_hex(oid), type_name(repl_type));
+ printf("%s (%s) -> %s (%s)\n", ref->name, type_name(obj_type),
+ oid_to_hex(ref->oid), type_name(repl_type));
}
}
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 9da92b990d0..3578591b4f2 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -217,19 +217,17 @@ static int show_default(void)
return 0;
}
-static int show_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int show_reference(const struct reference *ref, void *cb_data UNUSED)
{
- if (ref_excluded(&ref_excludes, refname))
+ if (ref_excluded(&ref_excludes, ref->name))
return 0;
- show_rev(NORMAL, oid, refname);
+ show_rev(NORMAL, ref->oid, ref->name);
return 0;
}
-static int anti_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int anti_reference(const struct reference *ref, void *cb_data UNUSED)
{
- show_rev(REVERSED, oid, refname);
+ show_rev(REVERSED, ref->oid, ref->name);
return 0;
}
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 441babf2e35..10475a6b5ed 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -413,34 +413,32 @@ static int append_ref(const char *refname, const struct object_id *oid,
return 0;
}
-static int append_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int append_head_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id tmp;
int ofs = 11;
- if (!starts_with(refname, "refs/heads/"))
+ if (!starts_with(ref->name, "refs/heads/"))
return 0;
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
+ if (repo_get_oid(the_repository, ref->name + ofs, &tmp) || !oideq(&tmp, ref->oid))
ofs = 5;
- return append_ref(refname + ofs, oid, 0);
+ return append_ref(ref->name + ofs, ref->oid, 0);
}
-static int append_remote_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int append_remote_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id tmp;
int ofs = 13;
- if (!starts_with(refname, "refs/remotes/"))
+ if (!starts_with(ref->name, "refs/remotes/"))
return 0;
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
+ if (repo_get_oid(the_repository, ref->name + ofs, &tmp) || !oideq(&tmp, ref->oid))
ofs = 5;
- return append_ref(refname + ofs, oid, 0);
+ return append_ref(ref->name + ofs, ref->oid, 0);
}
static int append_tag_ref(const char *refname, const struct object_id *oid,
@@ -454,27 +452,26 @@ static int append_tag_ref(const char *refname, const struct object_id *oid,
static const char *match_ref_pattern = NULL;
static int match_ref_slash = 0;
-static int append_matching_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int append_matching_ref(const struct reference *ref, void *cb_data)
{
/* we want to allow pattern hold/<asterisk> to show all
* branches under refs/heads/hold/, and v0.99.9? to show
* refs/tags/v0.99.9a and friends.
*/
const char *tail;
- int slash = count_slashes(refname);
- for (tail = refname; *tail && match_ref_slash < slash; )
+ int slash = count_slashes(ref->name);
+ for (tail = ref->name; *tail && match_ref_slash < slash; )
if (*tail++ == '/')
slash--;
if (!*tail)
return 0;
if (wildmatch(match_ref_pattern, tail, 0))
return 0;
- if (starts_with(refname, "refs/heads/"))
- return append_head_ref(refname, NULL, oid, flag, cb_data);
- if (starts_with(refname, "refs/tags/"))
- return append_tag_ref(refname, oid, flag, cb_data);
- return append_ref(refname, oid, 0);
+ if (starts_with(ref->name, "refs/heads/"))
+ return append_head_ref(ref, cb_data);
+ if (starts_with(ref->name, "refs/tags/"))
+ return append_tag_ref(ref->name, ref->oid, ref->flags, cb_data);
+ return append_ref(ref->name, ref->oid, 0);
}
static void snarf_refs(int head, int remotes)
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 0b6f9edf86c..4803b5e5986 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -66,26 +66,25 @@ struct show_ref_data {
int show_head;
};
-static int show_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cbdata)
+static int show_ref(const struct reference *ref, void *cbdata)
{
struct show_ref_data *data = cbdata;
- if (data->show_head && !strcmp(refname, "HEAD"))
+ if (data->show_head && !strcmp(ref->name, "HEAD"))
goto match;
if (data->patterns) {
- int reflen = strlen(refname);
+ int reflen = strlen(ref->name);
const char **p = data->patterns, *m;
while ((m = *p++) != NULL) {
int len = strlen(m);
if (len > reflen)
continue;
- if (memcmp(m, refname + reflen - len, len))
+ if (memcmp(m, ref->name + reflen - len, len))
continue;
if (len == reflen)
goto match;
- if (refname[reflen - len - 1] == '/')
+ if (ref->name[reflen - len - 1] == '/')
goto match;
}
return 0;
@@ -94,18 +93,15 @@ static int show_ref(const char *refname, const char *referent UNUSED, const stru
match:
data->found_match++;
- show_one(data->show_one_opts, refname, oid);
+ show_one(data->show_one_opts, ref->name, ref->oid);
return 0;
}
-static int add_existing(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cbdata)
+static int add_existing(const struct reference *ref, void *cbdata)
{
struct string_list *list = (struct string_list *)cbdata;
- string_list_insert(list, refname);
+ string_list_insert(list, ref->name);
return 0;
}
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index fcd73abe533..35f6cf735e5 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -593,16 +593,12 @@ static void print_status(unsigned int flags, char state, const char *path,
printf("\n");
}
-static int handle_submodule_head_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int handle_submodule_head_ref(const struct reference *ref, void *cb_data)
{
struct object_id *output = cb_data;
- if (oid)
- oidcpy(output, oid);
+ if (ref->oid)
+ oidcpy(output, ref->oid);
return 0;
}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 812774a5ca9..b7f323b5e4d 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -635,11 +635,7 @@ static void print_preparing_worktree_line(int detach,
*
* Returns 0 on failure and non-zero on success.
*/
-static int first_valid_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED,
- void *cb_data UNUSED)
+static int first_valid_ref(const struct reference *ref UNUSED, void *cb_data UNUSED)
{
return 1;
}
diff --git a/commit-graph.c b/commit-graph.c
index 2f20f66cfdd..0cfe16ad082 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1846,18 +1846,16 @@ struct refs_cb_data {
struct progress *progress;
};
-static int add_ref_to_set(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int add_ref_to_set(const struct reference *ref, void *cb_data)
{
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct refs_cb_data *data = (struct refs_cb_data *)cb_data;
- if (!peel_iterated_oid(data->repo, oid, &peeled))
- oid = &peeled;
- if (odb_read_object_info(data->repo->objects, oid, NULL) == OBJ_COMMIT)
- oidset_insert(data->commits, oid);
+ if (!peel_iterated_oid(data->repo, ref->oid, &peeled))
+ maybe_peeled = &peeled;
+ if (odb_read_object_info(data->repo->objects, maybe_peeled, NULL) == OBJ_COMMIT)
+ oidset_insert(data->commits, maybe_peeled);
display_progress(data->progress, oidset_size(data->commits));
diff --git a/delta-islands.c b/delta-islands.c
index 36c94799d69..7cfebc4162b 100644
--- a/delta-islands.c
+++ b/delta-islands.c
@@ -390,8 +390,7 @@ static void add_ref_to_island(kh_str_t *remote_islands, const char *island_name,
rl->hash += sha_core;
}
-static int find_island_for_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb)
+static int find_island_for_ref(const struct reference *ref, void *cb)
{
struct island_load_data *ild = cb;
@@ -406,7 +405,7 @@ static int find_island_for_ref(const char *refname, const char *referent UNUSED,
/* walk backwards to get last-one-wins ordering */
for (i = ild->nr - 1; i >= 0; i--) {
- if (!regexec(&ild->rx[i], refname,
+ if (!regexec(&ild->rx[i], ref->name,
ARRAY_SIZE(matches), matches, 0))
break;
}
@@ -428,10 +427,10 @@ static int find_island_for_ref(const char *refname, const char *referent UNUSED,
if (island_name.len)
strbuf_addch(&island_name, '-');
- strbuf_add(&island_name, refname + match->rm_so, match->rm_eo - match->rm_so);
+ strbuf_add(&island_name, ref->name + match->rm_so, match->rm_eo - match->rm_so);
}
- add_ref_to_island(ild->remote_islands, island_name.buf, oid);
+ add_ref_to_island(ild->remote_islands, island_name.buf, ref->oid);
strbuf_release(&island_name);
return 0;
}
diff --git a/fetch-pack.c b/fetch-pack.c
index 6ed56629518..f576e593b9f 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -188,13 +188,9 @@ static int rev_list_insert_ref(struct fetch_negotiator *negotiator,
return 0;
}
-static int rev_list_insert_ref_oid(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int rev_list_insert_ref_oid(const struct reference *ref, void *cb_data)
{
- return rev_list_insert_ref(cb_data, oid);
+ return rev_list_insert_ref(cb_data, ref->oid);
}
enum ack_type {
@@ -616,13 +612,9 @@ static int mark_complete(const struct object_id *oid)
return 0;
}
-static int mark_complete_oid(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED,
- void *cb_data UNUSED)
+static int mark_complete_oid(const struct reference *ref, void *cb_data UNUSED)
{
- return mark_complete(oid);
+ return mark_complete(ref->oid);
}
static void mark_recent_complete_commits(struct fetch_pack_args *args,
diff --git a/help.c b/help.c
index bb20498cfd0..d8c056fc5d1 100644
--- a/help.c
+++ b/help.c
@@ -845,18 +845,16 @@ struct similar_ref_cb {
struct string_list *similar_refs;
};
-static int append_similar_ref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED, void *cb_data)
+static int append_similar_ref(const struct reference *ref, void *cb_data)
{
struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
- char *branch = strrchr(refname, '/') + 1;
+ char *branch = strrchr(ref->name, '/') + 1;
/* A remote branch of the same name is deemed similar */
- if (starts_with(refname, "refs/remotes/") &&
+ if (starts_with(ref->name, "refs/remotes/") &&
!strcmp(branch, cb->base_ref))
string_list_append_nodup(cb->similar_refs,
- refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), refname, 1));
+ refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), ref->name, 1));
return 0;
}
diff --git a/http-backend.c b/http-backend.c
index d5dfe762bb5..992be9dc70c 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -513,18 +513,17 @@ static void run_service(const char **argv, int buffer_input)
exit(1);
}
-static int show_text_ref(const char *name, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int show_text_ref(const struct reference *ref, void *cb_data)
{
- const char *name_nons = strip_namespace(name);
+ const char *name_nons = strip_namespace(ref->name);
struct strbuf *buf = cb_data;
- struct object *o = parse_object(the_repository, oid);
+ struct object *o = parse_object(the_repository, ref->oid);
if (!o)
return 0;
- strbuf_addf(buf, "%s\t%s\n", oid_to_hex(oid), name_nons);
+ strbuf_addf(buf, "%s\t%s\n", oid_to_hex(ref->oid), name_nons);
if (o->type == OBJ_TAG) {
- o = deref_tag(the_repository, o, name, 0);
+ o = deref_tag(the_repository, o, ref->name, 0);
if (!o)
return 0;
strbuf_addf(buf, "%s\t%s^{}\n", oid_to_hex(&o->oid),
@@ -569,21 +568,20 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)
strbuf_release(&buf);
}
-static int show_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int show_head_ref(const struct reference *ref, void *cb_data)
{
struct strbuf *buf = cb_data;
- if (flag & REF_ISSYMREF) {
+ if (ref->flags & REF_ISSYMREF) {
const char *target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- refname,
+ ref->name,
RESOLVE_REF_READING,
NULL, NULL);
if (target)
strbuf_addf(buf, "ref: %s\n", strip_namespace(target));
} else {
- strbuf_addf(buf, "%s\n", oid_to_hex(oid));
+ strbuf_addf(buf, "%s\n", oid_to_hex(ref->oid));
}
return 0;
diff --git a/log-tree.c b/log-tree.c
index 67d9ace5964..95f1d12c722 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -147,9 +147,7 @@ static int ref_filter_match(const char *refname,
return 1;
}
-static int add_ref_decoration(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int add_ref_decoration(const struct reference *ref, void *cb_data)
{
int i;
struct object *obj;
@@ -158,16 +156,16 @@ static int add_ref_decoration(const char *refname, const char *referent UNUSED,
struct decoration_filter *filter = (struct decoration_filter *)cb_data;
const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref;
- if (filter && !ref_filter_match(refname, filter))
+ if (filter && !ref_filter_match(ref->name, filter))
return 0;
- if (starts_with(refname, git_replace_ref_base)) {
+ if (starts_with(ref->name, git_replace_ref_base)) {
struct object_id original_oid;
if (!replace_refs_enabled(the_repository))
return 0;
- if (get_oid_hex(refname + strlen(git_replace_ref_base),
+ if (get_oid_hex(ref->name + strlen(git_replace_ref_base),
&original_oid)) {
- warning("invalid replace ref %s", refname);
+ warning("invalid replace ref %s", ref->name);
return 0;
}
obj = parse_object(the_repository, &original_oid);
@@ -176,10 +174,10 @@ static int add_ref_decoration(const char *refname, const char *referent UNUSED,
return 0;
}
- objtype = odb_read_object_info(the_repository->objects, oid, NULL);
+ objtype = odb_read_object_info(the_repository->objects, ref->oid, NULL);
if (objtype < 0)
return 0;
- obj = lookup_object_by_type(the_repository, oid, objtype);
+ obj = lookup_object_by_type(the_repository, ref->oid, objtype);
for (i = 0; i < ARRAY_SIZE(ref_namespace); i++) {
struct ref_namespace_info *info = &ref_namespace[i];
@@ -187,24 +185,24 @@ static int add_ref_decoration(const char *refname, const char *referent UNUSED,
if (!info->decoration)
continue;
if (info->exact) {
- if (!strcmp(refname, info->ref)) {
+ if (!strcmp(ref->name, info->ref)) {
deco_type = info->decoration;
break;
}
- } else if (starts_with(refname, info->ref)) {
+ } else if (starts_with(ref->name, info->ref)) {
deco_type = info->decoration;
break;
}
}
- add_name_decoration(deco_type, refname, obj);
+ add_name_decoration(deco_type, ref->name, obj);
while (obj->type == OBJ_TAG) {
if (!obj->parsed)
parse_object(the_repository, &obj->oid);
obj = ((struct tag *)obj)->tagged;
if (!obj)
break;
- add_name_decoration(DECORATION_REF_TAG, refname, obj);
+ add_name_decoration(DECORATION_REF_TAG, ref->name, obj);
}
return 0;
}
diff --git a/ls-refs.c b/ls-refs.c
index c47acde07f3..64d02723691 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -75,42 +75,42 @@ struct ls_refs_data {
unsigned unborn : 1;
};
-static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int send_ref(const struct reference *ref, void *cb_data)
{
struct ls_refs_data *data = cb_data;
- const char *refname_nons = strip_namespace(refname);
+ const char *refname_nons = strip_namespace(ref->name);
strbuf_reset(&data->buf);
- if (ref_is_hidden(refname_nons, refname, &data->hidden_refs))
+ if (ref_is_hidden(refname_nons, ref->name, &data->hidden_refs))
return 0;
if (!ref_match(&data->prefixes, refname_nons))
return 0;
- if (oid)
- strbuf_addf(&data->buf, "%s %s", oid_to_hex(oid), refname_nons);
+ if (ref->oid)
+ strbuf_addf(&data->buf, "%s %s", oid_to_hex(ref->oid), refname_nons);
else
strbuf_addf(&data->buf, "unborn %s", refname_nons);
- if (data->symrefs && flag & REF_ISSYMREF) {
+ if (data->symrefs && ref->flags & REF_ISSYMREF) {
+ int unused_flag;
struct object_id unused;
const char *symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- refname,
+ ref->name,
0,
&unused,
- &flag);
+ &unused_flag);
if (!symref_target)
- die("'%s' is a symref but it is not?", refname);
+ die("'%s' is a symref but it is not?", ref->name);
strbuf_addf(&data->buf, " symref-target:%s",
strip_namespace(symref_target));
}
- if (data->peel && oid) {
+ if (data->peel && ref->oid) {
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
strbuf_addf(&data->buf, " peeled:%s", oid_to_hex(&peeled));
}
@@ -131,9 +131,17 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)
if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), namespaced.buf, 0, &oid, &flag))
return; /* bad ref */
oid_is_null = is_null_oid(&oid);
+
if (!oid_is_null ||
- (data->unborn && data->symrefs && (flag & REF_ISSYMREF)))
- send_ref(namespaced.buf, NULL, oid_is_null ? NULL : &oid, flag, data);
+ (data->unborn && data->symrefs && (flag & REF_ISSYMREF))) {
+ struct reference ref = {
+ .name = namespaced.buf,
+ .oid = oid_is_null ? NULL : &oid,
+ .flags = flag,
+ };
+
+ send_ref(&ref, data);
+ }
strbuf_release(&namespaced);
}
diff --git a/midx-write.c b/midx-write.c
index c73010df6d3..f4dd875747a 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -697,28 +697,27 @@ static void prepare_midx_packing_data(struct packing_data *pdata,
trace2_region_leave("midx", "prepare_midx_packing_data", ctx->repo);
}
-static int add_ref_to_pending(const char *refname, const char *referent UNUSED,
- const struct object_id *oid,
- int flag, void *cb_data)
+static int add_ref_to_pending(const struct reference *ref, void *cb_data)
{
struct rev_info *revs = (struct rev_info*)cb_data;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct object *object;
- if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
- warning("symbolic ref is dangling: %s", refname);
+ if ((ref->flags & REF_ISSYMREF) && (ref->flags & REF_ISBROKEN)) {
+ warning("symbolic ref is dangling: %s", ref->name);
return 0;
}
- if (!peel_iterated_oid(revs->repo, oid, &peeled))
- oid = &peeled;
+ if (!peel_iterated_oid(revs->repo, ref->oid, &peeled))
+ maybe_peeled = &peeled;
- object = parse_object_or_die(revs->repo, oid, refname);
+ object = parse_object_or_die(revs->repo, maybe_peeled, ref->name);
if (object->type != OBJ_COMMIT)
return 0;
add_pending_object(revs, object, "");
- if (bitmap_is_preferred_refname(revs->repo, refname))
+ if (bitmap_is_preferred_refname(revs->repo, ref->name))
object->flags |= NEEDS_BITMAP;
return 0;
}
diff --git a/negotiator/default.c b/negotiator/default.c
index c479da9b091..116dedcf830 100644
--- a/negotiator/default.c
+++ b/negotiator/default.c
@@ -38,11 +38,10 @@ static void rev_list_push(struct negotiation_state *ns,
}
}
-static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data UNUSED)
+static int clear_marks(const struct reference *ref, void *cb_data UNUSED)
{
- struct object *o = deref_tag(the_repository, parse_object(the_repository, oid), refname, 0);
+ struct object *o = deref_tag(the_repository, parse_object(the_repository, ref->oid),
+ ref->name, 0);
if (o && o->type == OBJ_COMMIT)
clear_commit_marks((struct commit *)o,
diff --git a/negotiator/skipping.c b/negotiator/skipping.c
index 616df6bf3af..0a272130fb1 100644
--- a/negotiator/skipping.c
+++ b/negotiator/skipping.c
@@ -75,11 +75,10 @@ static struct entry *rev_list_push(struct data *data, struct commit *commit, int
return entry;
}
-static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data UNUSED)
+static int clear_marks(const struct reference *ref, void *cb_data UNUSED)
{
- struct object *o = deref_tag(the_repository, parse_object(the_repository, oid), refname, 0);
+ struct object *o = deref_tag(the_repository, parse_object(the_repository, ref->oid),
+ ref->name, 0);
if (o && o->type == OBJ_COMMIT)
clear_commit_marks((struct commit *)o,
diff --git a/notes.c b/notes.c
index 9a2e9181fe6..8e00fd8c470 100644
--- a/notes.c
+++ b/notes.c
@@ -938,13 +938,11 @@ int combine_notes_cat_sort_uniq(struct object_id *cur_oid,
return ret;
}
-static int string_list_add_one_ref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cb)
+static int string_list_add_one_ref(const struct reference *ref, void *cb)
{
struct string_list *refs = cb;
- if (!unsorted_string_list_has_string(refs, refname))
- string_list_append(refs, refname);
+ if (!unsorted_string_list_has_string(refs, ref->name))
+ string_list_append(refs, ref->name);
return 0;
}
diff --git a/object-name.c b/object-name.c
index 7774991d281..43c7518f995 100644
--- a/object-name.c
+++ b/object-name.c
@@ -1444,18 +1444,16 @@ struct handle_one_ref_cb {
struct commit_list **list;
};
-static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int handle_one_ref(const struct reference *ref, void *cb_data)
{
struct handle_one_ref_cb *cb = cb_data;
struct commit_list **list = cb->list;
- struct object *object = parse_object(cb->repo, oid);
+ struct object *object = parse_object(cb->repo, ref->oid);
if (!object)
return 0;
if (object->type == OBJ_TAG) {
- object = deref_tag(cb->repo, object, path,
- strlen(path));
+ object = deref_tag(cb->repo, object, ref->name,
+ strlen(ref->name));
if (!object)
return 0;
}
diff --git a/pseudo-merge.c b/pseudo-merge.c
index 893b763fe45..0abd51b42c1 100644
--- a/pseudo-merge.c
+++ b/pseudo-merge.c
@@ -221,28 +221,25 @@ void load_pseudo_merges_from_config(struct repository *r,
}
}
-static int find_pseudo_merge_group_for_ref(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *_data)
+static int find_pseudo_merge_group_for_ref(const struct reference *ref, void *_data)
{
struct bitmap_writer *writer = _data;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct commit *c;
uint32_t i;
int has_bitmap;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ maybe_peeled = &peeled;
- c = lookup_commit(the_repository, oid);
+ c = lookup_commit(the_repository, maybe_peeled);
if (!c)
return 0;
- if (!packlist_find(writer->to_pack, oid))
+ if (!packlist_find(writer->to_pack, maybe_peeled))
return 0;
- has_bitmap = bitmap_writer_has_bitmapped_object_id(writer, oid);
+ has_bitmap = bitmap_writer_has_bitmapped_object_id(writer, maybe_peeled);
for (i = 0; i < writer->pseudo_merge_groups.nr; i++) {
struct pseudo_merge_group *group;
@@ -252,7 +249,7 @@ static int find_pseudo_merge_group_for_ref(const char *refname,
size_t j;
group = writer->pseudo_merge_groups.items[i].util;
- if (regexec(group->pattern, refname, ARRAY_SIZE(captures),
+ if (regexec(group->pattern, ref->name, ARRAY_SIZE(captures),
captures, 0))
continue;
@@ -269,7 +266,7 @@ static int find_pseudo_merge_group_for_ref(const char *refname,
if (group_name.len)
strbuf_addch(&group_name, '-');
- strbuf_add(&group_name, refname + match->rm_so,
+ strbuf_add(&group_name, ref->name + match->rm_so,
match->rm_eo - match->rm_so);
}
diff --git a/reachable.c b/reachable.c
index 22266db5233..b753c395530 100644
--- a/reachable.c
+++ b/reachable.c
@@ -83,18 +83,17 @@ static void add_rebase_files(struct rev_info *revs)
free_worktrees(worktrees);
}
-static int add_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int add_one_ref(const struct reference *ref, void *cb_data)
{
struct rev_info *revs = (struct rev_info *)cb_data;
struct object *object;
- if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
- warning("symbolic ref is dangling: %s", path);
+ if ((ref->flags & REF_ISSYMREF) && (ref->flags & REF_ISBROKEN)) {
+ warning("symbolic ref is dangling: %s", ref->name);
return 0;
}
- object = parse_object_or_die(the_repository, oid, path);
+ object = parse_object_or_die(the_repository, ref->oid, ref->name);
add_pending_object(revs, object, "");
return 0;
diff --git a/ref-filter.c b/ref-filter.c
index 520d2539c9e..7740f35e937 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2954,14 +2954,15 @@ struct ref_filter_cbdata {
* A call-back given to for_each_ref(). Filter refs and keep them for
* later object processing.
*/
-static int filter_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)
+static int filter_one(const struct reference *ref, void *cb_data)
{
struct ref_filter_cbdata *ref_cbdata = cb_data;
- struct ref_array_item *ref;
+ struct ref_array_item *item;
- ref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);
- if (ref)
- ref_array_append(ref_cbdata->array, ref);
+ item = apply_ref_filter(ref->name, ref->target, ref->oid,
+ ref->flags, ref_cbdata->filter);
+ if (item)
+ ref_array_append(ref_cbdata->array, item);
return 0;
}
@@ -2990,17 +2991,18 @@ struct ref_filter_and_format_cbdata {
} internal;
};
-static int filter_and_format_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)
+static int filter_and_format_one(const struct reference *ref, void *cb_data)
{
struct ref_filter_and_format_cbdata *ref_cbdata = cb_data;
- struct ref_array_item *ref;
+ struct ref_array_item *item;
struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
- ref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);
- if (!ref)
+ item = apply_ref_filter(ref->name, ref->target, ref->oid,
+ ref->flags, ref_cbdata->filter);
+ if (!item)
return 0;
- if (format_ref_array_item(ref, ref_cbdata->format, &output, &err))
+ if (format_ref_array_item(item, ref_cbdata->format, &output, &err))
die("%s", err.buf);
if (output.len || !ref_cbdata->format->array_opts.omit_empty) {
@@ -3010,7 +3012,7 @@ static int filter_and_format_one(const char *refname, const char *referent, cons
strbuf_release(&output);
strbuf_release(&err);
- free_array_item(ref);
+ free_array_item(item);
/*
* Increment the running count of refs that match the filter. If
diff --git a/reflog.c b/reflog.c
index 65ef259b4f5..ac87e20c4f9 100644
--- a/reflog.c
+++ b/reflog.c
@@ -423,16 +423,13 @@ int should_expire_reflog_ent_verbose(struct object_id *ooid,
return expire;
}
-static int push_tip_to_list(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags, void *cb_data)
+static int push_tip_to_list(const struct reference *ref, void *cb_data)
{
struct commit_list **list = cb_data;
struct commit *tip_commit;
- if (flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
return 0;
- tip_commit = lookup_commit_reference_gently(the_repository, oid, 1);
+ tip_commit = lookup_commit_reference_gently(the_repository, ref->oid, 1);
if (!tip_commit)
return 0;
commit_list_insert(tip_commit, list);
diff --git a/refs.c b/refs.c
index 750e5db0771..262849301d0 100644
--- a/refs.c
+++ b/refs.c
@@ -423,17 +423,19 @@ int refs_ref_exists(struct ref_store *refs, const char *refname)
NULL, NULL);
}
-static int for_each_filter_refs(const char *refname, const char *referent,
- const struct object_id *oid,
- int flags, void *data)
+static int for_each_filter_refs(const struct reference *ref, void *data)
{
struct for_each_ref_filter *filter = data;
- if (wildmatch(filter->pattern, refname, 0))
+ if (wildmatch(filter->pattern, ref->name, 0))
return 0;
- if (filter->prefix)
- skip_prefix(refname, filter->prefix, &refname);
- return filter->fn(refname, referent, oid, flags, filter->cb_data);
+ if (filter->prefix) {
+ struct reference skipped = *ref;
+ skip_prefix(skipped.name, filter->prefix, &skipped.name);
+ return filter->fn(&skipped, filter->cb_data);
+ } else {
+ return filter->fn(ref, filter->cb_data);
+ }
}
struct warn_if_dangling_data {
@@ -444,17 +446,15 @@ struct warn_if_dangling_data {
int dry_run;
};
-static int warn_if_dangling_symref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags, void *cb_data)
+static int warn_if_dangling_symref(const struct reference *ref, void *cb_data)
{
struct warn_if_dangling_data *d = cb_data;
const char *resolves_to, *msg;
- if (!(flags & REF_ISSYMREF))
+ if (!(ref->flags & REF_ISSYMREF))
return 0;
- resolves_to = refs_resolve_ref_unsafe(d->refs, refname, 0, NULL, NULL);
+ resolves_to = refs_resolve_ref_unsafe(d->refs, ref->name, 0, NULL, NULL);
if (!resolves_to
|| !string_list_has_string(d->refnames, resolves_to)) {
return 0;
@@ -463,7 +463,7 @@ static int warn_if_dangling_symref(const char *refname, const char *referent UNU
msg = d->dry_run
? _("%s%s will become dangling after %s is deleted\n")
: _("%s%s has become dangling after %s was deleted\n");
- fprintf(d->fp, msg, d->indent, refname, resolves_to);
+ fprintf(d->fp, msg, d->indent, ref->name, resolves_to);
return 0;
}
@@ -504,8 +504,15 @@ int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_da
int flag;
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
- if (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag))
- ret = fn(buf.buf, NULL, &oid, flag, cb_data);
+ if (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag)) {
+ struct reference ref = {
+ .name = buf.buf,
+ .oid = &oid,
+ .flags = flag,
+ };
+
+ ret = fn(&ref, cb_data);
+ }
strbuf_release(&buf);
return ret;
@@ -1740,8 +1747,15 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
int flag;
if (refs_resolve_ref_unsafe(refs, "HEAD", RESOLVE_REF_READING,
- &oid, &flag))
- return fn("HEAD", NULL, &oid, flag, cb_data);
+ &oid, &flag)) {
+ struct reference ref = {
+ .name = "HEAD",
+ .oid = &oid,
+ .flags = flag,
+ };
+
+ return fn(&ref, cb_data);
+ }
return 0;
}
@@ -2752,14 +2766,10 @@ struct do_for_each_reflog_help {
void *cb_data;
};
-static int do_for_each_reflog_helper(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED,
- void *cb_data)
+static int do_for_each_reflog_helper(const struct reference *ref, void *cb_data)
{
struct do_for_each_reflog_help *hp = cb_data;
- return hp->fn(refname, hp->cb_data);
+ return hp->fn(ref->name, hp->cb_data);
}
int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data)
@@ -2977,25 +2987,24 @@ struct migration_data {
uint64_t index;
};
-static int migrate_one_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags, void *cb_data)
+static int migrate_one_ref(const struct reference *ref, void *cb_data)
{
struct migration_data *data = cb_data;
struct strbuf symref_target = STRBUF_INIT;
int ret;
- if (flags & REF_ISSYMREF) {
- ret = refs_read_symbolic_ref(data->old_refs, refname, &symref_target);
+ if (ref->flags & REF_ISSYMREF) {
+ ret = refs_read_symbolic_ref(data->old_refs, ref->name, &symref_target);
if (ret < 0)
goto done;
- ret = ref_transaction_update(data->transaction, refname, NULL, null_oid(the_hash_algo),
+ ret = ref_transaction_update(data->transaction, ref->name, NULL, null_oid(the_hash_algo),
symref_target.buf, NULL,
REF_SKIP_CREATE_REFLOG | REF_NO_DEREF, NULL, data->errbuf);
if (ret < 0)
goto done;
} else {
- ret = ref_transaction_create(data->transaction, refname, oid, NULL,
+ ret = ref_transaction_create(data->transaction, ref->name, ref->oid, NULL,
REF_SKIP_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION,
NULL, data->errbuf);
if (ret < 0)
diff --git a/refs.h b/refs.h
index 4e6bd63aa86..2b24a3d9974 100644
--- a/refs.h
+++ b/refs.h
@@ -355,14 +355,32 @@ struct ref_transaction;
*/
#define REF_BAD_NAME 0x08
+/* A reference passed to `for_each_ref()`-style callbacks. */
+struct reference {
+ /* The fully-qualified name of the reference. */
+ const char *name;
+
+ /* The target of a symbolic ref. `NULL` for direct references. */
+ const char *target;
+
+ /*
+ * The object ID of a reference. Either the direct object ID or the
+ * resolved object ID in the case of a symbolic ref. May be the zero
+ * object ID in case the symbolic ref cannot be resolved.
+ */
+ const struct object_id *oid;
+
+ /* A bitfield of `REF_` flags. */
+ int flags;
+};
+
/*
* The signature for the callback function for the for_each_*()
* functions below. The memory pointed to by the refname and oid
* arguments is only guaranteed to be valid for the duration of a
* single callback invocation.
*/
-typedef int each_ref_fn(const char *refname, const char *referent,
- const struct object_id *oid, int flags, void *cb_data);
+typedef int each_ref_fn(const struct reference *ref, void *cb_data);
/*
* The following functions invoke the specified callback function for
diff --git a/refs/files-backend.c b/refs/files-backend.c
index bb2bec38076..0ddcf22aed7 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3151,14 +3151,11 @@ static int parse_and_write_reflog(struct files_ref_store *refs,
return 0;
}
-static int ref_present(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED,
- void *cb_data)
+static int ref_present(const struct reference *ref, void *cb_data)
{
struct string_list *affected_refnames = cb_data;
- return string_list_has_string(affected_refnames, refname);
+ return string_list_has_string(affected_refnames, ref->name);
}
static int files_transaction_finish_initial(struct files_ref_store *refs,
diff --git a/refs/iterator.c b/refs/iterator.c
index 17ef841d8a3..7f2e718f1c9 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -476,7 +476,14 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
- retval = fn(iter->refname, iter->referent, iter->oid, iter->flags, cb_data);
+ struct reference ref = {
+ .name = iter->refname,
+ .target = iter->referent,
+ .oid = iter->oid,
+ .flags = iter->flags,
+ };
+
+ retval = fn(&ref, cb_data);
if (retval)
goto out;
}
diff --git a/remote.c b/remote.c
index df9675cd330..59b37151208 100644
--- a/remote.c
+++ b/remote.c
@@ -2315,21 +2315,19 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
return 1;
}
-static int one_local_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int one_local_ref(const struct reference *ref, void *cb_data)
{
struct ref ***local_tail = cb_data;
- struct ref *ref;
+ struct ref *local_ref;
/* we already know it starts with refs/ to get here */
- if (check_refname_format(refname + 5, 0))
+ if (check_refname_format(ref->name + 5, 0))
return 0;
- ref = alloc_ref(refname);
- oidcpy(&ref->new_oid, oid);
- **local_tail = ref;
- *local_tail = &ref->next;
+ local_ref = alloc_ref(ref->name);
+ oidcpy(&local_ref->new_oid, ref->oid);
+ **local_tail = local_ref;
+ *local_tail = &local_ref->next;
return 0;
}
@@ -2402,15 +2400,14 @@ struct stale_heads_info {
struct refspec *rs;
};
-static int get_stale_heads_cb(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags, void *cb_data)
+static int get_stale_heads_cb(const struct reference *ref, void *cb_data)
{
struct stale_heads_info *info = cb_data;
struct string_list matches = STRING_LIST_INIT_DUP;
struct refspec_item query;
int i, stale = 1;
memset(&query, 0, sizeof(struct refspec_item));
- query.dst = (char *)refname;
+ query.dst = (char *)ref->name;
refspec_find_all_matches(info->rs, &query, &matches);
if (matches.nr == 0)
@@ -2423,7 +2420,7 @@ static int get_stale_heads_cb(const char *refname, const char *referent UNUSED,
* overlapping refspecs, we need to go over all of the
* matching refs.
*/
- if (flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
goto clean_exit;
for (i = 0; stale && i < matches.nr; i++)
@@ -2431,8 +2428,8 @@ static int get_stale_heads_cb(const char *refname, const char *referent UNUSED,
stale = 0;
if (stale) {
- struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
- oidcpy(&ref->new_oid, oid);
+ struct ref *linked_ref = make_linked_ref(ref->name, &info->stale_refs_tail);
+ oidcpy(&linked_ref->new_oid, ref->oid);
}
clean_exit:
diff --git a/replace-object.c b/replace-object.c
index 3eae0510745..03d0f1f083b 100644
--- a/replace-object.c
+++ b/replace-object.c
@@ -8,31 +8,27 @@
#include "repository.h"
#include "commit.h"
-static int register_replace_ref(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int register_replace_ref(const struct reference *ref, void *cb_data)
{
struct repository *r = cb_data;
/* Get sha1 from refname */
- const char *slash = strrchr(refname, '/');
- const char *hash = slash ? slash + 1 : refname;
+ const char *slash = strrchr(ref->name, '/');
+ const char *hash = slash ? slash + 1 : ref->name;
struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj));
if (get_oid_hex_algop(hash, &repl_obj->original.oid, r->hash_algo)) {
free(repl_obj);
- warning(_("bad replace ref name: %s"), refname);
+ warning(_("bad replace ref name: %s"), ref->name);
return 0;
}
/* Copy sha1 from the read ref */
- oidcpy(&repl_obj->replacement, oid);
+ oidcpy(&repl_obj->replacement, ref->oid);
/* Register new object */
if (oidmap_put(&r->objects->replace_map, repl_obj))
- die(_("duplicate replace ref: %s"), refname);
+ die(_("duplicate replace ref: %s"), ref->name);
return 0;
}
diff --git a/revision.c b/revision.c
index 806a1c4c24d..a195c0ded45 100644
--- a/revision.c
+++ b/revision.c
@@ -1647,19 +1647,17 @@ struct all_refs_cb {
struct worktree *wt;
};
-static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int handle_one_ref(const struct reference *ref, void *cb_data)
{
struct all_refs_cb *cb = cb_data;
struct object *object;
- if (ref_excluded(&cb->all_revs->ref_excludes, path))
+ if (ref_excluded(&cb->all_revs->ref_excludes, ref->name))
return 0;
- object = get_reference(cb->all_revs, path, oid, cb->all_flags);
- add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
- add_pending_object(cb->all_revs, object, path);
+ object = get_reference(cb->all_revs, ref->name, ref->oid, cb->all_flags);
+ add_rev_cmdline(cb->all_revs, object, ref->name, REV_CMD_REF, cb->all_flags);
+ add_pending_object(cb->all_revs, object, ref->name);
return 0;
}
diff --git a/server-info.c b/server-info.c
index 9bb30d9ab71..aea4df80416 100644
--- a/server-info.c
+++ b/server-info.c
@@ -148,23 +148,21 @@ static int update_info_file(struct repository *r, char *path,
return ret;
}
-static int add_info_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int add_info_ref(const struct reference *ref, void *cb_data)
{
struct update_info_ctx *uic = cb_data;
- struct object *o = parse_object(uic->repo, oid);
+ struct object *o = parse_object(uic->repo, ref->oid);
if (!o)
return -1;
- if (uic_printf(uic, "%s %s\n", oid_to_hex(oid), path) < 0)
+ if (uic_printf(uic, "%s %s\n", oid_to_hex(ref->oid), ref->name) < 0)
return -1;
if (o->type == OBJ_TAG) {
- o = deref_tag(uic->repo, o, path, 0);
+ o = deref_tag(uic->repo, o, ref->name, 0);
if (o)
if (uic_printf(uic, "%s %s^{}\n",
- oid_to_hex(&o->oid), path) < 0)
+ oid_to_hex(&o->oid), ref->name) < 0)
return -1;
}
return 0;
diff --git a/shallow.c b/shallow.c
index d9cd4e219cb..55b9cd9d3f2 100644
--- a/shallow.c
+++ b/shallow.c
@@ -626,14 +626,10 @@ static void paint_down(struct paint_info *info, const struct object_id *oid,
free(tmp);
}
-static int mark_uninteresting(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data UNUSED)
+static int mark_uninteresting(const struct reference *ref, void *cb_data UNUSED)
{
struct commit *commit = lookup_commit_reference_gently(the_repository,
- oid, 1);
+ ref->oid, 1);
if (!commit)
return 0;
commit->object.flags |= UNINTERESTING;
@@ -742,16 +738,12 @@ struct commit_array {
size_t nr, alloc;
};
-static int add_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int add_ref(const struct reference *ref, void *cb_data)
{
struct commit_array *ca = cb_data;
ALLOC_GROW(ca->commits, ca->nr + 1, ca->alloc);
ca->commits[ca->nr] = lookup_commit_reference_gently(the_repository,
- oid, 1);
+ ref->oid, 1);
if (ca->commits[ca->nr])
ca->nr++;
return 0;
diff --git a/submodule.c b/submodule.c
index 35c55155f7b..40a5c6fb9d1 100644
--- a/submodule.c
+++ b/submodule.c
@@ -934,10 +934,7 @@ static void free_submodules_data(struct string_list *submodules)
string_list_clear(submodules, 1);
}
-static int has_remote(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED, void *cb_data UNUSED)
+static int has_remote(const struct reference *ref UNUSED, void *cb_data UNUSED)
{
return 1;
}
@@ -1255,13 +1252,10 @@ int push_unpushed_submodules(struct repository *r,
return ret;
}
-static int append_oid_to_array(const char *ref UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *data)
+static int append_oid_to_array(const struct reference *ref, void *data)
{
struct oid_array *array = data;
- oid_array_append(array, oid);
+ oid_array_append(array, ref->oid);
return 0;
}
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 83b06d39a36..b1215947c5e 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -154,10 +154,9 @@ static int cmd_rename_ref(struct ref_store *refs, const char **argv)
return refs_rename_ref(refs, oldref, newref, logmsg);
}
-static int each_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags, void *cb_data UNUSED)
+static int each_ref(const struct reference *ref, void *cb_data UNUSED)
{
- printf("%s %s 0x%x\n", oid_to_hex(oid), refname, flags);
+ printf("%s %s 0x%x\n", oid_to_hex(ref->oid), ref->name, ref->flags);
return 0;
}
diff --git a/upload-pack.c b/upload-pack.c
index 1e87ae95593..0d563ae74e9 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -870,8 +870,8 @@ static void send_unshallow(struct upload_pack_data *data)
}
}
-static int check_ref(const char *refname_full, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data);
+static int check_ref(const struct reference *ref, void *cb_data);
+
static void deepen(struct upload_pack_data *data, int depth)
{
if (depth == INFINITE_DEPTH && !is_repository_shallow(the_repository)) {
@@ -1224,13 +1224,12 @@ static int mark_our_ref(const char *refname, const char *refname_full,
return 0;
}
-static int check_ref(const char *refname_full, const char *referent UNUSED,const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int check_ref(const struct reference *ref, void *cb_data)
{
- const char *refname = strip_namespace(refname_full);
+ const char *refname = strip_namespace(ref->name);
struct upload_pack_data *data = cb_data;
- mark_our_ref(refname, refname_full, oid, &data->hidden_refs);
+ mark_our_ref(refname, ref->name, ref->oid, &data->hidden_refs);
return 0;
}
@@ -1292,27 +1291,25 @@ static void write_v0_ref(struct upload_pack_data *data,
return;
}
-static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int send_ref(const struct reference *ref, void *cb_data)
{
- write_v0_ref(cb_data, refname, strip_namespace(refname), oid);
+ write_v0_ref(cb_data, ref->name, strip_namespace(ref->name), ref->oid);
return 0;
}
-static int find_symref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag, void *cb_data)
+static int find_symref(const struct reference *ref, void *cb_data)
{
const char *symref_target;
struct string_list_item *item;
+ int flag;
- if ((flag & REF_ISSYMREF) == 0)
+ if ((ref->flags & REF_ISSYMREF) == 0)
return 0;
symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- refname, 0, NULL, &flag);
+ ref->name, 0, NULL, &flag);
if (!symref_target || (flag & REF_ISSYMREF) == 0)
- die("'%s' is a symref but it is not?", refname);
- item = string_list_append(cb_data, strip_namespace(refname));
+ die("'%s' is a symref but it is not?", ref->name);
+ item = string_list_append(cb_data, strip_namespace(ref->name));
item->util = xstrdup(strip_namespace(symref_target));
return 0;
}
diff --git a/walker.c b/walker.c
index 80737545172..409b646578a 100644
--- a/walker.c
+++ b/walker.c
@@ -226,14 +226,10 @@ static int interpret_target(struct walker *walker, char *target, struct object_i
return -1;
}
-static int mark_complete(const char *path UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED,
- void *cb_data UNUSED)
+static int mark_complete(const struct reference *ref, void *cb_data UNUSED)
{
struct commit *commit = lookup_commit_reference_gently(the_repository,
- oid, 1);
+ ref->oid, 1);
if (commit) {
commit->object.flags |= COMPLETE;
diff --git a/worktree.c b/worktree.c
index a2a5f51f29f..9308389cb6f 100644
--- a/worktree.c
+++ b/worktree.c
@@ -595,8 +595,15 @@ int other_head_refs(each_ref_fn fn, void *cb_data)
if (refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
refname.buf,
RESOLVE_REF_READING,
- &oid, &flag))
- ret = fn(refname.buf, NULL, &oid, flag, cb_data);
+ &oid, &flag)) {
+ struct reference ref = {
+ .name = refname.buf,
+ .oid = &oid,
+ .flags = flag,
+ };
+
+ ret = fn(&ref, cb_data);
+ }
if (ret)
break;
}
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH 02/13] refs: introduce `.ref` field for the base iterator
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 01/13] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
@ 2025-10-07 10:58 ` Patrick Steinhardt
2025-10-07 14:24 ` Karthik Nayak
` (2 more replies)
2025-10-07 10:58 ` [PATCH 03/13] refs: refactor reference status flags Patrick Steinhardt
` (15 subsequent siblings)
17 siblings, 3 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-07 10:58 UTC (permalink / raw)
To: git
The base iterator has a couple of fields that tracks the name, target,
object ID and flags for the current reference. Due do this design we
have to create a new `struct reference` whenever we want to hand over
that reference to the callback function, which is tedious and not very
efficient.
Convert the structure to instead contain a `stuct reference` as member.
This member is expected to be populated by the implementations of the
iterator and is handed over to the callback directly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs.c | 8 ++++----
refs/debug.c | 8 +++-----
refs/files-backend.c | 47 +++++++++++++++++++++--------------------------
refs/iterator.c | 39 ++++++++++++---------------------------
refs/packed-backend.c | 46 +++++++++++++++++++++++-----------------------
refs/ref-cache.c | 10 +++++-----
refs/refs-internal.h | 5 +----
refs/reftable-backend.c | 12 ++++++------
8 files changed, 75 insertions(+), 100 deletions(-)
diff --git a/refs.c b/refs.c
index 262849301d..15ad0ef7a8 100644
--- a/refs.c
+++ b/refs.c
@@ -2326,8 +2326,8 @@ int refs_optimize(struct ref_store *refs, struct pack_refs_opts *opts)
int peel_iterated_oid(struct repository *r, const struct object_id *base, struct object_id *peeled)
{
if (current_ref_iter &&
- (current_ref_iter->oid == base ||
- oideq(current_ref_iter->oid, base)))
+ (current_ref_iter->ref.oid == base ||
+ oideq(current_ref_iter->ref.oid, base)))
return ref_iterator_peel(current_ref_iter, peeled);
return peel_object(r, base, peeled) ? -1 : 0;
@@ -2702,7 +2702,7 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
if (skip &&
- string_list_has_string(skip, iter->refname))
+ string_list_has_string(skip, iter->ref.name))
continue;
if (transaction && ref_transaction_maybe_set_rejected(
@@ -2711,7 +2711,7 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
continue;
strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
- iter->refname, refname);
+ iter->ref.name, refname);
goto cleanup;
}
diff --git a/refs/debug.c b/refs/debug.c
index 1cb955961e..7a26035617 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -161,11 +161,9 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
trace_printf_key(&trace_refs, "iterator_advance: (%d)\n", res);
else
trace_printf_key(&trace_refs, "iterator_advance: %s (0)\n",
- diter->iter->refname);
+ diter->iter->ref.name);
- diter->base.refname = diter->iter->refname;
- diter->base.oid = diter->iter->oid;
- diter->base.flags = diter->iter->flags;
+ diter->base.ref = diter->iter->ref;
return res;
}
@@ -186,7 +184,7 @@ static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct debug_ref_iterator *diter =
(struct debug_ref_iterator *)ref_iterator;
int res = diter->iter->vtable->peel(diter->iter, peeled);
- trace_printf_key(&trace_refs, "iterator_peel: %s: %d\n", diter->iter->refname, res);
+ trace_printf_key(&trace_refs, "iterator_peel: %s: %d\n", diter->iter->ref.name, res);
return res;
}
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 0ddcf22aed..d34fbe55d6 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -962,26 +962,23 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
- parse_worktree_ref(iter->iter0->refname, NULL, NULL,
+ parse_worktree_ref(iter->iter0->ref.name, NULL, NULL,
NULL) != REF_WORKTREE_CURRENT)
continue;
if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) &&
- (iter->iter0->flags & REF_ISSYMREF) &&
- (iter->iter0->flags & REF_ISBROKEN))
+ (iter->iter0->ref.flags & REF_ISSYMREF) &&
+ (iter->iter0->ref.flags & REF_ISBROKEN))
continue;
if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
- !ref_resolves_to_object(iter->iter0->refname,
+ !ref_resolves_to_object(iter->iter0->ref.name,
iter->repo,
- iter->iter0->oid,
- iter->iter0->flags))
+ iter->iter0->ref.oid,
+ iter->iter0->ref.flags))
continue;
- iter->base.refname = iter->iter0->refname;
- iter->base.oid = iter->iter0->oid;
- iter->base.flags = iter->iter0->flags;
- iter->base.referent = iter->iter0->referent;
+ iter->base.ref = iter->iter0->ref;
return ITER_OK;
}
@@ -1368,30 +1365,29 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune **refs_
* Return true if the specified reference should be packed.
*/
static int should_pack_ref(struct files_ref_store *refs,
- const char *refname,
- const struct object_id *oid, unsigned int ref_flags,
+ const struct reference *ref,
struct pack_refs_opts *opts)
{
struct string_list_item *item;
/* Do not pack per-worktree refs: */
- if (parse_worktree_ref(refname, NULL, NULL, NULL) !=
+ if (parse_worktree_ref(ref->name, NULL, NULL, NULL) !=
REF_WORKTREE_SHARED)
return 0;
/* Do not pack symbolic refs: */
- if (ref_flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
return 0;
/* Do not pack broken refs: */
- if (!ref_resolves_to_object(refname, refs->base.repo, oid, ref_flags))
+ if (!ref_resolves_to_object(ref->name, refs->base.repo, ref->oid, ref->flags))
return 0;
- if (ref_excluded(opts->exclusions, refname))
+ if (ref_excluded(opts->exclusions, ref->name))
return 0;
for_each_string_list_item(item, opts->includes)
- if (!wildmatch(item->string, refname, 0))
+ if (!wildmatch(item->string, ref->name, 0))
return 1;
return 0;
@@ -1444,8 +1440,7 @@ static int should_pack_refs(struct files_ref_store *refs,
iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, 0), NULL,
refs->base.repo, 0);
while ((ret = ref_iterator_advance(iter)) == ITER_OK) {
- if (should_pack_ref(refs, iter->refname, iter->oid,
- iter->flags, opts))
+ if (should_pack_ref(refs, &iter->ref, opts))
refcount++;
if (refcount >= limit) {
ref_iterator_free(iter);
@@ -1490,24 +1485,24 @@ static int files_pack_refs(struct ref_store *ref_store,
* in the packed ref cache. If the reference should be
* pruned, also add it to refs_to_prune.
*/
- if (!should_pack_ref(refs, iter->refname, iter->oid, iter->flags, opts))
+ if (!should_pack_ref(refs, &iter->ref, opts))
continue;
/*
* Add a reference creation for this reference to the
* packed-refs transaction:
*/
- if (ref_transaction_update(transaction, iter->refname,
- iter->oid, NULL, NULL, NULL,
+ if (ref_transaction_update(transaction, iter->ref.name,
+ iter->ref.oid, NULL, NULL, NULL,
REF_NO_DEREF, NULL, &err))
die("failure preparing to create packed reference %s: %s",
- iter->refname, err.buf);
+ iter->ref.name, err.buf);
/* Schedule the loose reference for pruning if requested. */
if ((opts->flags & PACK_REFS_PRUNE)) {
struct ref_to_prune *n;
- FLEX_ALLOC_STR(n, name, iter->refname);
- oidcpy(&n->oid, iter->oid);
+ FLEX_ALLOC_STR(n, name, iter->ref.name);
+ oidcpy(&n->oid, iter->ref.oid);
n->next = refs_to_prune;
refs_to_prune = n;
}
@@ -2380,7 +2375,7 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
REFNAME_ALLOW_ONELEVEL))
continue;
- iter->base.refname = diter->relative_path;
+ iter->base.ref.name = diter->relative_path;
return ITER_OK;
}
diff --git a/refs/iterator.c b/refs/iterator.c
index 7f2e718f1c..fe5980e1b6 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -41,10 +41,7 @@ void base_ref_iterator_init(struct ref_iterator *iter,
struct ref_iterator_vtable *vtable)
{
iter->vtable = vtable;
- iter->refname = NULL;
- iter->referent = NULL;
- iter->oid = NULL;
- iter->flags = 0;
+ memset(&iter->ref, 0, sizeof(iter->ref));
}
struct empty_ref_iterator {
@@ -127,8 +124,8 @@ enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
* latter.
*/
if (iter_worktree) {
- int cmp = strcmp(iter_worktree->refname,
- iter_common->refname);
+ int cmp = strcmp(iter_worktree->ref.name,
+ iter_common->ref.name);
if (cmp < 0)
return ITER_SELECT_0;
else if (!cmp)
@@ -139,7 +136,7 @@ enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
* We now know that the lexicographically-next ref is a common
* ref. When the common ref is a shared one we return it.
*/
- if (parse_worktree_ref(iter_common->refname, NULL, NULL,
+ if (parse_worktree_ref(iter_common->ref.name, NULL, NULL,
NULL) == REF_WORKTREE_SHARED)
return ITER_SELECT_1;
@@ -212,10 +209,7 @@ static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
}
if (selection & ITER_YIELD_CURRENT) {
- iter->base.referent = (*iter->current)->referent;
- iter->base.refname = (*iter->current)->refname;
- iter->base.oid = (*iter->current)->oid;
- iter->base.flags = (*iter->current)->flags;
+ iter->base.ref = (*iter->current)->ref;
return ITER_OK;
}
}
@@ -313,7 +307,7 @@ static enum iterator_selection overlay_iterator_select(
else if (!front)
return ITER_SELECT_1;
- cmp = strcmp(front->refname, back->refname);
+ cmp = strcmp(front->ref.name, back->ref.name);
if (cmp < 0)
return ITER_SELECT_0;
@@ -371,7 +365,7 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
int ok;
while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
- int cmp = compare_prefix(iter->iter0->refname, iter->prefix);
+ int cmp = compare_prefix(iter->iter0->ref.name, iter->prefix);
if (cmp < 0)
continue;
/*
@@ -382,6 +376,8 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
if (cmp > 0)
return ITER_DONE;
+ iter->base.ref = iter->iter0->ref;
+
if (iter->trim) {
/*
* It is nonsense to trim off characters that
@@ -392,15 +388,11 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
* one character left in the refname after
* trimming, report it as a bug:
*/
- if (strlen(iter->iter0->refname) <= iter->trim)
+ if (strlen(iter->base.ref.name) <= iter->trim)
BUG("attempt to trim too many characters");
- iter->base.refname = iter->iter0->refname + iter->trim;
- } else {
- iter->base.refname = iter->iter0->refname;
+ iter->base.ref.name += iter->trim;
}
- iter->base.oid = iter->iter0->oid;
- iter->base.flags = iter->iter0->flags;
return ITER_OK;
}
@@ -476,14 +468,7 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
- struct reference ref = {
- .name = iter->refname,
- .target = iter->referent,
- .oid = iter->oid,
- .flags = iter->flags,
- };
-
- retval = fn(&ref, cb_data);
+ retval = fn(&iter->ref, cb_data);
if (retval)
goto out;
}
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index a8c22a0a7f..7987acdc96 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -908,7 +908,7 @@ static int next_record(struct packed_ref_iterator *iter)
if (iter->pos == iter->eof)
return ITER_DONE;
- iter->base.flags = REF_ISPACKED;
+ iter->base.ref.flags = REF_ISPACKED;
p = iter->pos;
if (iter->eof - p < snapshot_hexsz(iter->snapshot) + 2 ||
@@ -923,22 +923,22 @@ static int next_record(struct packed_ref_iterator *iter)
iter->pos, iter->eof - iter->pos);
strbuf_add(&iter->refname_buf, p, eol - p);
- iter->base.refname = iter->refname_buf.buf;
+ iter->base.ref.name = iter->refname_buf.buf;
if (refname_contains_nul(&iter->refname_buf))
- die("packed refname contains embedded NULL: %s", iter->base.refname);
+ die("packed refname contains embedded NULL: %s", iter->base.ref.name);
- if (check_refname_format(iter->base.refname, REFNAME_ALLOW_ONELEVEL)) {
- if (!refname_is_safe(iter->base.refname))
+ if (check_refname_format(iter->base.ref.name, REFNAME_ALLOW_ONELEVEL)) {
+ if (!refname_is_safe(iter->base.ref.name))
die("packed refname is dangerous: %s",
- iter->base.refname);
+ iter->base.ref.name);
oidclr(&iter->oid, iter->repo->hash_algo);
- iter->base.flags |= REF_BAD_NAME | REF_ISBROKEN;
+ iter->base.ref.flags |= REF_BAD_NAME | REF_ISBROKEN;
}
if (iter->snapshot->peeled == PEELED_FULLY ||
(iter->snapshot->peeled == PEELED_TAGS &&
- starts_with(iter->base.refname, "refs/tags/")))
- iter->base.flags |= REF_KNOWS_PEELED;
+ starts_with(iter->base.ref.name, "refs/tags/")))
+ iter->base.ref.flags |= REF_KNOWS_PEELED;
iter->pos = eol + 1;
@@ -956,11 +956,11 @@ static int next_record(struct packed_ref_iterator *iter)
* definitely know the value of *this* reference. But
* we suppress it if the reference is broken:
*/
- if ((iter->base.flags & REF_ISBROKEN)) {
+ if ((iter->base.ref.flags & REF_ISBROKEN)) {
oidclr(&iter->peeled, iter->repo->hash_algo);
- iter->base.flags &= ~REF_KNOWS_PEELED;
+ iter->base.ref.flags &= ~REF_KNOWS_PEELED;
} else {
- iter->base.flags |= REF_KNOWS_PEELED;
+ iter->base.ref.flags |= REF_KNOWS_PEELED;
}
} else {
oidclr(&iter->peeled, iter->repo->hash_algo);
@@ -976,15 +976,15 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
int ok;
while ((ok = next_record(iter)) == ITER_OK) {
- const char *refname = iter->base.refname;
+ const char *refname = iter->base.ref.name;
const char *prefix = iter->prefix;
if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
- !is_per_worktree_ref(iter->base.refname))
+ !is_per_worktree_ref(iter->base.ref.name))
continue;
if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
- !ref_resolves_to_object(iter->base.refname, iter->repo,
+ !ref_resolves_to_object(iter->base.ref.name, iter->repo,
&iter->oid, iter->flags))
continue;
@@ -1033,10 +1033,10 @@ static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct packed_ref_iterator *iter =
(struct packed_ref_iterator *)ref_iterator;
- if ((iter->base.flags & REF_KNOWS_PEELED)) {
+ if ((iter->base.ref.flags & REF_KNOWS_PEELED)) {
oidcpy(peeled, &iter->peeled);
return is_null_oid(&iter->peeled) ? -1 : 0;
- } else if ((iter->base.flags & (REF_ISBROKEN | REF_ISSYMREF))) {
+ } else if ((iter->base.ref.flags & (REF_ISBROKEN | REF_ISSYMREF))) {
return -1;
} else {
return peel_object(iter->repo, &iter->oid, peeled) ? -1 : 0;
@@ -1194,7 +1194,7 @@ static struct ref_iterator *packed_ref_iterator_begin(
iter->snapshot = snapshot;
acquire_snapshot(snapshot);
strbuf_init(&iter->refname_buf, 0);
- iter->base.oid = &iter->oid;
+ iter->base.ref.oid = &iter->oid;
iter->repo = ref_store->repo;
iter->flags = flags;
@@ -1436,7 +1436,7 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
if (!iter)
cmp = +1;
else
- cmp = strcmp(iter->refname, update->refname);
+ cmp = strcmp(iter->ref.name, update->refname);
}
if (!cmp) {
@@ -1459,11 +1459,11 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
}
goto error;
- } else if (!oideq(&update->old_oid, iter->oid)) {
+ } else if (!oideq(&update->old_oid, iter->ref.oid)) {
strbuf_addf(err, "cannot update ref '%s': "
"is at %s but expected %s",
update->refname,
- oid_to_hex(iter->oid),
+ oid_to_hex(iter->ref.oid),
oid_to_hex(&update->old_oid));
ret = REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE;
@@ -1527,8 +1527,8 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
struct object_id peeled;
int peel_error = ref_iterator_peel(iter, &peeled);
- if (write_packed_entry(out, iter->refname,
- iter->oid,
+ if (write_packed_entry(out, iter->ref.name,
+ iter->ref.oid,
peel_error ? NULL : &peeled))
goto write_error;
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index c180e0aad7..97555fa118 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -425,10 +425,10 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
level->prefix_state = entry_prefix_state;
level->index = -1;
} else {
- iter->base.refname = entry->name;
- iter->base.referent = entry->u.value.referent;
- iter->base.oid = &entry->u.value.oid;
- iter->base.flags = entry->flag;
+ iter->base.ref.name = entry->name;
+ iter->base.ref.target = entry->u.value.referent;
+ iter->base.ref.oid = &entry->u.value.oid;
+ iter->base.ref.flags = entry->flag;
return ITER_OK;
}
}
@@ -550,7 +550,7 @@ static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
{
struct cache_ref_iterator *iter =
(struct cache_ref_iterator *)ref_iterator;
- return peel_object(iter->repo, ref_iterator->oid, peeled) ? -1 : 0;
+ return peel_object(iter->repo, ref_iterator->ref.oid, peeled) ? -1 : 0;
}
static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 4ef3bd75c6..ed749d1657 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -249,10 +249,7 @@ const char *find_descendant_ref(const char *dirname,
*/
struct ref_iterator {
struct ref_iterator_vtable *vtable;
- const char *refname;
- const char *referent;
- const struct object_id *oid;
- unsigned int flags;
+ struct reference ref;
};
/*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 9884b876c1..7fbc77492e 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -703,10 +703,10 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
&iter->oid, flags))
continue;
- iter->base.refname = iter->ref.refname;
- iter->base.referent = referent;
- iter->base.oid = &iter->oid;
- iter->base.flags = flags;
+ iter->base.ref.name = iter->ref.refname;
+ iter->base.ref.target = referent;
+ iter->base.ref.oid = &iter->oid;
+ iter->base.ref.flags = flags;
break;
}
@@ -827,7 +827,7 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
iter = xcalloc(1, sizeof(*iter));
base_ref_iterator_init(&iter->base, &reftable_ref_iterator_vtable);
- iter->base.oid = &iter->oid;
+ iter->base.ref.oid = &iter->oid;
iter->flags = flags;
iter->refs = refs;
iter->exclude_patterns = filter_exclude_patterns(exclude_patterns);
@@ -2071,7 +2071,7 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
strbuf_reset(&iter->last_name);
strbuf_addstr(&iter->last_name, iter->log.refname);
- iter->base.refname = iter->log.refname;
+ iter->base.ref.name = iter->log.refname;
break;
}
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH 03/13] refs: refactor reference status flags
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 01/13] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 02/13] refs: introduce `.ref` field for the base iterator Patrick Steinhardt
@ 2025-10-07 10:58 ` Patrick Steinhardt
2025-10-07 14:27 ` Karthik Nayak
2025-10-07 10:58 ` [PATCH 04/13] refs: expose peeled object ID via the iterator Patrick Steinhardt
` (14 subsequent siblings)
17 siblings, 1 reply; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-07 10:58 UTC (permalink / raw)
To: git
The reference flags encode information like whether or not a reference
is a symbolic reference or whether it may be broken. This information is
stored in a `int flags` bitfield, which is in conflict with our modern
best practices; we tend to use an unsigned integer to store flags.
Change the type of the field to be `unsigned`. While at it, refactor the
individual flags to be part of an `enum` instead of using preprocessor
defines.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs.h | 41 +++++++++++++++++++++--------------------
1 file changed, 21 insertions(+), 20 deletions(-)
diff --git a/refs.h b/refs.h
index 2b24a3d997..8f484e2ffc 100644
--- a/refs.h
+++ b/refs.h
@@ -333,27 +333,28 @@ struct ref_transaction;
* stored in ref_iterator::flags. Other bits are for internal use
* only:
*/
+enum reference_status {
+ /* Reference is a symbolic reference. */
+ REF_ISSYMREF = (1 << 0),
-/* Reference is a symbolic reference. */
-#define REF_ISSYMREF 0x01
+ /* Reference is a packed reference. */
+ REF_ISPACKED = (1 << 1),
-/* Reference is a packed reference. */
-#define REF_ISPACKED 0x02
-
-/*
- * Reference cannot be resolved to an object name: dangling symbolic
- * reference (directly or indirectly), corrupt reference file,
- * reference exists but name is bad, or symbolic reference refers to
- * ill-formatted reference name.
- */
-#define REF_ISBROKEN 0x04
+ /*
+ * Reference cannot be resolved to an object name: dangling symbolic
+ * reference (directly or indirectly), corrupt reference file,
+ * reference exists but name is bad, or symbolic reference refers to
+ * ill-formatted reference name.
+ */
+ REF_ISBROKEN = (1 << 2),
-/*
- * Reference name is not well formed.
- *
- * See git-check-ref-format(1) for the definition of well formed ref names.
- */
-#define REF_BAD_NAME 0x08
+ /*
+ * Reference name is not well formed.
+ *
+ * See git-check-ref-format(1) for the definition of well formed ref names.
+ */
+ REF_BAD_NAME = (1 << 3),
+};
/* A reference passed to `for_each_ref()`-style callbacks. */
struct reference {
@@ -370,8 +371,8 @@ struct reference {
*/
const struct object_id *oid;
- /* A bitfield of `REF_` flags. */
- int flags;
+ /* A bitfield of `enum reference_status` flags. */
+ unsigned flags;
};
/*
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH 04/13] refs: expose peeled object ID via the iterator
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
` (2 preceding siblings ...)
2025-10-07 10:58 ` [PATCH 03/13] refs: refactor reference status flags Patrick Steinhardt
@ 2025-10-07 10:58 ` Patrick Steinhardt
2025-10-07 14:52 ` Karthik Nayak
2025-10-07 10:58 ` [PATCH 05/13] upload-pack: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
` (13 subsequent siblings)
17 siblings, 1 reply; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-07 10:58 UTC (permalink / raw)
To: git
Both the "files" and "reftable" backend are able to store peeled values
for tags in the respective formats. This allows for a more efficient
lookup of the target object of such a tag without having to manually
peel via the object database.
The infrastructure to access these peeled object IDs is somewhat funky
though. When iterating through objects, we store a pointer reference to
the current iterator in a global variable. The callbacks invoked by that
iterator are then expected to call `peel_iterated_oid()`, which checks
whether the globally-stored iterator's current reference refers to the
one handed into that function. If so, we ask the iterator to peel the
object, otherwise we manually peel the object via the object database.
Depending on global state like this is somewhat weird and also quite
fragile.
Introduce a new `struct reference::peeled_oid` field that can be
populated by the reference backends. This field can be accessed via a
new function `reference_get_peeled_oid()` that either uses that value,
if set, or alternatively peels via the ODB. With this change we don't
have to rely on global state anymore, but make the peeled object ID
available to the callback functions directly.
Adjust trivial callers that already have a `struct reference` available.
Remaining callers will be adjusted in subsequent commits.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/describe.c | 2 +-
builtin/gc.c | 2 +-
builtin/pack-objects.c | 7 ++++---
builtin/repack.c | 2 +-
commit-graph.c | 2 +-
ls-refs.c | 2 +-
midx-write.c | 2 +-
pseudo-merge.c | 2 +-
refs.c | 12 ++++++++++++
refs.h | 19 +++++++++++++++++++
refs/packed-backend.c | 3 +++
refs/ref-cache.c | 1 +
refs/reftable-backend.c | 7 +++++++
13 files changed, 53 insertions(+), 10 deletions(-)
diff --git a/builtin/describe.c b/builtin/describe.c
index 7954535044..443546aaac 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -208,7 +208,7 @@ static int get_name(const struct reference *ref, void *cb_data UNUSED)
}
/* Is it annotated? */
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled)) {
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
is_annotated = !oideq(ref->oid, &peeled);
} else {
oidcpy(&peeled, ref->oid);
diff --git a/builtin/gc.c b/builtin/gc.c
index a104c1cb78..814be8d1a9 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -1107,7 +1107,7 @@ static int dfs_on_ref(const struct reference *ref, void *cb_data)
struct commit_list *stack = NULL;
struct commit *commit;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
if (odb_read_object_info(the_repository->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 288ba19981..7fd457779d 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -838,7 +838,7 @@ static int mark_tagged(const struct reference *ref, void *cb_data UNUSED)
if (entry)
entry->tagged = 1;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled)) {
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
entry = packlist_find(&to_pack, &peeled);
if (entry)
entry->tagged = 1;
@@ -3309,7 +3309,8 @@ static int add_ref_tag(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled) && obj_is_packed(&peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled) &&
+ obj_is_packed(&peeled))
add_tag_chain(ref->oid);
return 0;
}
@@ -4532,7 +4533,7 @@ static int mark_bitmap_preferred_tip(const struct reference *ref, void *data UNU
struct object_id peeled;
struct object *object;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
object = parse_object_or_die(the_repository, maybe_peeled, ref->name);
diff --git a/builtin/repack.c b/builtin/repack.c
index 893c105c81..873e21c35d 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -779,7 +779,7 @@ static int midx_snapshot_ref_one(const struct reference *ref, void *_data)
const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
if (oidset_insert(&data->seen, maybe_peeled))
diff --git a/commit-graph.c b/commit-graph.c
index 0cfe16ad08..bfd8b187f0 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1852,7 +1852,7 @@ static int add_ref_to_set(const struct reference *ref, void *cb_data)
struct object_id peeled;
struct refs_cb_data *data = (struct refs_cb_data *)cb_data;
- if (!peel_iterated_oid(data->repo, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(data->repo, ref, &peeled))
maybe_peeled = &peeled;
if (odb_read_object_info(data->repo->objects, maybe_peeled, NULL) == OBJ_COMMIT)
oidset_insert(data->commits, maybe_peeled);
diff --git a/ls-refs.c b/ls-refs.c
index 64d0272369..8641281b86 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -110,7 +110,7 @@ static int send_ref(const struct reference *ref, void *cb_data)
if (data->peel && ref->oid) {
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
strbuf_addf(&data->buf, " peeled:%s", oid_to_hex(&peeled));
}
diff --git a/midx-write.c b/midx-write.c
index f4dd875747..23e61cb000 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -709,7 +709,7 @@ static int add_ref_to_pending(const struct reference *ref, void *cb_data)
return 0;
}
- if (!peel_iterated_oid(revs->repo, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(revs->repo, ref, &peeled))
maybe_peeled = &peeled;
object = parse_object_or_die(revs->repo, maybe_peeled, ref->name);
diff --git a/pseudo-merge.c b/pseudo-merge.c
index 0abd51b42c..a2d5bd85f9 100644
--- a/pseudo-merge.c
+++ b/pseudo-merge.c
@@ -230,7 +230,7 @@ static int find_pseudo_merge_group_for_ref(const struct reference *ref, void *_d
uint32_t i;
int has_bitmap;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
c = lookup_commit(the_repository, maybe_peeled);
diff --git a/refs.c b/refs.c
index 15ad0ef7a8..5002e56435 100644
--- a/refs.c
+++ b/refs.c
@@ -2333,6 +2333,18 @@ int peel_iterated_oid(struct repository *r, const struct object_id *base, struct
return peel_object(r, base, peeled) ? -1 : 0;
}
+int reference_get_peeled_oid(struct repository *repo,
+ const struct reference *ref,
+ struct object_id *peeled_oid)
+{
+ if (ref->peeled_oid) {
+ oidcpy(peeled_oid, ref->peeled_oid);
+ return 0;
+ }
+
+ return peel_object(repo, ref->oid, peeled_oid) ? -1 : 0;
+}
+
int refs_update_symref(struct ref_store *refs, const char *ref,
const char *target, const char *logmsg)
{
diff --git a/refs.h b/refs.h
index 8f484e2ffc..f1e023df35 100644
--- a/refs.h
+++ b/refs.h
@@ -371,10 +371,29 @@ struct reference {
*/
const struct object_id *oid;
+ /*
+ * An optional peeled object ID. This field _may_ be set for tags in
+ * case the peeled value is present in the backend. Please refer to
+ * `reference_get_peeled_oid()`.
+ */
+ const struct object_id *peeled_oid;
+
/* A bitfield of `enum reference_status` flags. */
unsigned flags;
};
+/*
+ * Peel the tag to a non-tag commit. If present, this uses the peeled object ID
+ * exposed by the reference backend. Otherwise, the object is peeled via the
+ * object database, which is less efficient.
+ *
+ * Return `0` if the reference could be peeled, a negative error code
+ * otherwise.
+ */
+int reference_get_peeled_oid(struct repository *repo,
+ const struct reference *ref,
+ struct object_id *peeled_oid);
+
/*
* The signature for the callback function for the for_each_*()
* functions below. The memory pointed to by the refname and oid
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 7987acdc96..7922d63acc 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -959,11 +959,14 @@ static int next_record(struct packed_ref_iterator *iter)
if ((iter->base.ref.flags & REF_ISBROKEN)) {
oidclr(&iter->peeled, iter->repo->hash_algo);
iter->base.ref.flags &= ~REF_KNOWS_PEELED;
+ iter->base.ref.peeled_oid = NULL;
} else {
iter->base.ref.flags |= REF_KNOWS_PEELED;
+ iter->base.ref.peeled_oid = &iter->peeled;
}
} else {
oidclr(&iter->peeled, iter->repo->hash_algo);
+ iter->base.ref.peeled_oid = NULL;
}
return ITER_OK;
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index 97555fa118..2f46f650a6 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -428,6 +428,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
iter->base.ref.name = entry->name;
iter->base.ref.target = entry->u.value.referent;
iter->base.ref.oid = &entry->u.value.oid;
+ iter->base.ref.peeled_oid = NULL;
iter->base.ref.flags = entry->flag;
return ITER_OK;
}
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 7fbc77492e..f93ab96358 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -546,6 +546,7 @@ struct reftable_ref_iterator {
struct reftable_iterator iter;
struct reftable_ref_record ref;
struct object_id oid;
+ struct object_id peeled_oid;
char *prefix;
size_t prefix_len;
@@ -670,6 +671,8 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
case REFTABLE_REF_VAL2:
oidread(&iter->oid, iter->ref.value.val2.value,
refs->base.repo->hash_algo);
+ oidread(&iter->peeled_oid, iter->ref.value.val2.target_value,
+ refs->base.repo->hash_algo);
break;
case REFTABLE_REF_SYMREF:
referent = refs_resolve_ref_unsafe(&iter->refs->base,
@@ -706,6 +709,10 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
iter->base.ref.name = iter->ref.refname;
iter->base.ref.target = referent;
iter->base.ref.oid = &iter->oid;
+ if (iter->ref.value_type == REFTABLE_REF_VAL2)
+ iter->base.ref.peeled_oid = &iter->peeled_oid;
+ else
+ iter->base.ref.peeled_oid = NULL;
iter->base.ref.flags = flags;
break;
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH 05/13] upload-pack: convert to use `reference_get_peeled_oid()`
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
` (3 preceding siblings ...)
2025-10-07 10:58 ` [PATCH 04/13] refs: expose peeled object ID via the iterator Patrick Steinhardt
@ 2025-10-07 10:58 ` Patrick Steinhardt
2025-10-07 16:18 ` Karthik Nayak
2025-10-07 10:58 ` [PATCH 06/13] ref-filter: propagate peeled object ID Patrick Steinhardt
` (12 subsequent siblings)
17 siblings, 1 reply; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-07 10:58 UTC (permalink / raw)
To: git
The `write_v0_ref()` callback is invoked from two callsites:
- Once via `send_ref()` which is a callback passed to
`for_each_namespaced_ref_1()`.
- Once manually to announce capabilities.
When sending references to the client we also send the peeled value of
tags. As we don't have a `struct reference` available in the second
case, we cannot easily peel by calling `reference_get_peeled_oid()`, but
we instead have to depend on on global state via `peel_iterated_oid()`.
We do have a reference available though in the first case, it's only the
second case that keeps us from using `reference_get_peeled_oid()`. But
that second case only announces capabilities anyway, so we're not really
handling a reference at all here.
Adapt that case to construct a reference manually and pass that to
`write_v0_ref()`. Start to use `reference_get_peeled_oid()` now that we
always have a `struct reference` available.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
upload-pack.c | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/upload-pack.c b/upload-pack.c
index 0d563ae74e..2d2b70cbf2 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -1249,15 +1249,15 @@ static void format_session_id(struct strbuf *buf, struct upload_pack_data *d) {
}
static void write_v0_ref(struct upload_pack_data *data,
- const char *refname, const char *refname_nons,
- const struct object_id *oid)
+ const struct reference *ref,
+ const char *refname_nons)
{
static const char *capabilities = "multi_ack thin-pack side-band"
" side-band-64k ofs-delta shallow deepen-since deepen-not"
" deepen-relative no-progress include-tag multi_ack_detailed";
struct object_id peeled;
- if (mark_our_ref(refname_nons, refname, oid, &data->hidden_refs))
+ if (mark_our_ref(refname_nons, ref->name, ref->oid, &data->hidden_refs))
return;
if (capabilities) {
@@ -1267,7 +1267,7 @@ static void write_v0_ref(struct upload_pack_data *data,
format_symref_info(&symref_info, &data->symref);
format_session_id(&session_id, data);
packet_fwrite_fmt(stdout, "%s %s%c%s%s%s%s%s%s%s object-format=%s agent=%s\n",
- oid_to_hex(oid), refname_nons,
+ oid_to_hex(ref->oid), refname_nons,
0, capabilities,
(data->allow_uor & ALLOW_TIP_SHA1) ?
" allow-tip-sha1-in-want" : "",
@@ -1283,17 +1283,17 @@ static void write_v0_ref(struct upload_pack_data *data,
strbuf_release(&session_id);
data->sent_capabilities = 1;
} else {
- packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(oid), refname_nons);
+ packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(ref->oid), refname_nons);
}
capabilities = NULL;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
packet_fwrite_fmt(stdout, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
return;
}
static int send_ref(const struct reference *ref, void *cb_data)
{
- write_v0_ref(cb_data, ref->name, strip_namespace(ref->name), ref->oid);
+ write_v0_ref(cb_data, ref, strip_namespace(ref->name));
return 0;
}
@@ -1442,8 +1442,12 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
send_ref, &data);
for_each_namespaced_ref_1(send_ref, &data);
if (!data.sent_capabilities) {
- const char *refname = "capabilities^{}";
- write_v0_ref(&data, refname, refname, null_oid(the_hash_algo));
+ struct reference ref = {
+ .name = "capabilities^{}",
+ .oid = null_oid(the_hash_algo),
+ };
+
+ write_v0_ref(&data, &ref, ref.name);
}
/*
* fflush stdout before calling advertise_shallow_grafts because send_ref
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH 06/13] ref-filter: propagate peeled object ID
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
` (4 preceding siblings ...)
2025-10-07 10:58 ` [PATCH 05/13] upload-pack: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
@ 2025-10-07 10:58 ` Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 07/13] builtin/show-ref: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
` (11 subsequent siblings)
17 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-07 10:58 UTC (permalink / raw)
To: git
When queueing a reference in the "ref-filter" subsystem we end up
creating a new ref array item that contains the reference's info. One
bit of info that we always discard though is the peeled object ID, and
because of that we are forced to use `peel_iterated_oid()`.
Refactor the code to propagate the peeled object ID via the ref array,
if available. This allows us to manually peel tags without having to go
through the object database.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/ls-remote.c | 2 +-
builtin/tag.c | 2 +-
builtin/verify-tag.c | 2 +-
ref-filter.c | 66 ++++++++++++++++++++++++++++++----------------------
ref-filter.h | 5 +++-
5 files changed, 45 insertions(+), 32 deletions(-)
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index df09000b30..fe77829557 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -156,7 +156,7 @@ int cmd_ls_remote(int argc,
continue;
if (!tail_match(&pattern, ref->name))
continue;
- item = ref_array_push(&ref_array, ref->name, &ref->old_oid);
+ item = ref_array_push(&ref_array, ref->name, &ref->old_oid, NULL);
item->symref = xstrdup_or_null(ref->symref);
}
diff --git a/builtin/tag.c b/builtin/tag.c
index f0665af3ac..01eba90c5c 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -153,7 +153,7 @@ static int verify_tag(const char *name, const char *ref UNUSED,
return -1;
if (format->format)
- pretty_print_ref(name, oid, format);
+ pretty_print_ref(name, oid, NULL, format);
return 0;
}
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index cd6bc11095..558121eaa1 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -67,7 +67,7 @@ int cmd_verify_tag(int argc,
}
if (format.format)
- pretty_print_ref(name, &oid, &format);
+ pretty_print_ref(name, &oid, NULL, &format);
}
return had_error;
}
diff --git a/ref-filter.c b/ref-filter.c
index 7740f35e93..b18a032e57 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2578,8 +2578,15 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
* If it is a tag object, see if we use the peeled value. If we do,
* grab the peeled OID.
*/
- if (need_tagged && peel_iterated_oid(the_repository, &obj->oid, &oi_deref.oid))
- die("bad tag");
+ if (need_tagged) {
+ if (!is_null_oid(&ref->peeled_oid)) {
+ oidcpy(&oi_deref.oid, &ref->peeled_oid);
+ } else if (!peel_object(the_repository, &obj->oid, &oi_deref.oid)) {
+ /* We managed to peel the object ourselves. */
+ } else {
+ die("bad tag");
+ }
+ }
return get_object(ref, 1, &obj, &oi_deref, err);
}
@@ -2807,12 +2814,15 @@ static int match_points_at(struct oid_array *points_at,
* Callers can then fill in other struct members at their leisure.
*/
static struct ref_array_item *new_ref_array_item(const char *refname,
- const struct object_id *oid)
+ const struct object_id *oid,
+ const struct object_id *peeled_oid)
{
struct ref_array_item *ref;
FLEX_ALLOC_STR(ref, refname, refname);
oidcpy(&ref->objectname, oid);
+ if (peeled_oid)
+ oidcpy(&ref->peeled_oid, peeled_oid);
ref->rest = NULL;
return ref;
@@ -2826,9 +2836,10 @@ static void ref_array_append(struct ref_array *array, struct ref_array_item *ref
struct ref_array_item *ref_array_push(struct ref_array *array,
const char *refname,
- const struct object_id *oid)
+ const struct object_id *oid,
+ const struct object_id *peeled_oid)
{
- struct ref_array_item *ref = new_ref_array_item(refname, oid);
+ struct ref_array_item *ref = new_ref_array_item(refname, oid, peeled_oid);
ref_array_append(array, ref);
return ref;
}
@@ -2871,25 +2882,25 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)
return ref_kind_from_refname(refname);
}
-static struct ref_array_item *apply_ref_filter(const char *refname, const char *referent, const struct object_id *oid,
- int flag, struct ref_filter *filter)
+static struct ref_array_item *apply_ref_filter(const struct reference *ref,
+ struct ref_filter *filter)
{
- struct ref_array_item *ref;
+ struct ref_array_item *item;
struct commit *commit = NULL;
unsigned int kind;
- if (flag & REF_BAD_NAME) {
- warning(_("ignoring ref with broken name %s"), refname);
+ if (ref->flags & REF_BAD_NAME) {
+ warning(_("ignoring ref with broken name %s"), ref->name);
return NULL;
}
- if (flag & REF_ISBROKEN) {
- warning(_("ignoring broken ref %s"), refname);
+ if (ref->flags & REF_ISBROKEN) {
+ warning(_("ignoring broken ref %s"), ref->name);
return NULL;
}
/* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */
- kind = filter_ref_kind(filter, refname);
+ kind = filter_ref_kind(filter, ref->name);
/*
* Generally HEAD refs are printed with special description denoting a rebase,
@@ -2902,13 +2913,13 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const char *
else if (!(kind & filter->kind))
return NULL;
- if (!filter_pattern_match(filter, refname))
+ if (!filter_pattern_match(filter, ref->name))
return NULL;
- if (filter_exclude_match(filter, refname))
+ if (filter_exclude_match(filter, ref->name))
return NULL;
- if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname))
+ if (filter->points_at.nr && !match_points_at(&filter->points_at, ref->oid, ref->name))
return NULL;
/*
@@ -2918,7 +2929,7 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const char *
*/
if (filter->reachable_from || filter->unreachable_from ||
filter->with_commit || filter->no_commit || filter->verbose) {
- commit = lookup_commit_reference_gently(the_repository, oid, 1);
+ commit = lookup_commit_reference_gently(the_repository, ref->oid, 1);
if (!commit)
return NULL;
/* We perform the filtering for the '--contains' option... */
@@ -2936,13 +2947,13 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const char *
* to do its job and the resulting list may yet to be pruned
* by maxcount logic.
*/
- ref = new_ref_array_item(refname, oid);
- ref->commit = commit;
- ref->flag = flag;
- ref->kind = kind;
- ref->symref = xstrdup_or_null(referent);
+ item = new_ref_array_item(ref->name, ref->oid, ref->peeled_oid);
+ item->commit = commit;
+ item->flag = ref->flags;
+ item->kind = kind;
+ item->symref = xstrdup_or_null(ref->target);
- return ref;
+ return item;
}
struct ref_filter_cbdata {
@@ -2959,8 +2970,7 @@ static int filter_one(const struct reference *ref, void *cb_data)
struct ref_filter_cbdata *ref_cbdata = cb_data;
struct ref_array_item *item;
- item = apply_ref_filter(ref->name, ref->target, ref->oid,
- ref->flags, ref_cbdata->filter);
+ item = apply_ref_filter(ref, ref_cbdata->filter);
if (item)
ref_array_append(ref_cbdata->array, item);
@@ -2997,8 +3007,7 @@ static int filter_and_format_one(const struct reference *ref, void *cb_data)
struct ref_array_item *item;
struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
- item = apply_ref_filter(ref->name, ref->target, ref->oid,
- ref->flags, ref_cbdata->filter);
+ item = apply_ref_filter(ref, ref_cbdata->filter);
if (!item)
return 0;
@@ -3585,13 +3594,14 @@ void print_formatted_ref_array(struct ref_array *array, struct ref_format *forma
}
void pretty_print_ref(const char *name, const struct object_id *oid,
+ const struct object_id *peeled_oid,
struct ref_format *format)
{
struct ref_array_item *ref_item;
struct strbuf output = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
- ref_item = new_ref_array_item(name, oid);
+ ref_item = new_ref_array_item(name, oid, peeled_oid);
ref_item->kind = ref_kind_from_refname(name);
if (format_ref_array_item(ref_item, format, &output, &err))
die("%s", err.buf);
diff --git a/ref-filter.h b/ref-filter.h
index 81f2c229a9..1126826201 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -41,6 +41,7 @@ enum ref_sorting_order {
struct ref_array_item {
struct object_id objectname;
+ struct object_id peeled_oid;
const char *rest;
int flag;
unsigned int kind;
@@ -185,6 +186,7 @@ void print_formatted_ref_array(struct ref_array *array, struct ref_format *forma
* name must be a fully qualified refname.
*/
void pretty_print_ref(const char *name, const struct object_id *oid,
+ const struct object_id *peeled_oid,
struct ref_format *format);
/*
@@ -193,7 +195,8 @@ void pretty_print_ref(const char *name, const struct object_id *oid,
*/
struct ref_array_item *ref_array_push(struct ref_array *array,
const char *refname,
- const struct object_id *oid);
+ const struct object_id *oid,
+ const struct object_id *peeled_oid);
/*
* If the provided format includes ahead-behind atoms, then compute the
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH 07/13] builtin/show-ref: convert to use `reference_get_peeled_oid()`
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
` (5 preceding siblings ...)
2025-10-07 10:58 ` [PATCH 06/13] ref-filter: propagate peeled object ID Patrick Steinhardt
@ 2025-10-07 10:58 ` Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 08/13] refs: drop `current_ref_iter` hack Patrick Steinhardt
` (10 subsequent siblings)
17 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-07 10:58 UTC (permalink / raw)
To: git
The git-show-ref(1) command has multiple different modes:
- It knows to show all references matching a pattern.
- It knows to list all references that are an exact match to whatever
the user has provided.
- It knows to check for reference existence.
The first two commands use mostly the same infrastructure to print the
references via `show_one()`. But while the former mode uses a proper
iterator and thus has a `struct reference` available in its context, the
latter calls `refs_read_ref()` and thus doesn't. Consequently, we cannot
easily use `reference_get_peeled_oid()` to print the peeled value.
Adapt the code so that we manually construct a `struct reference` when
verifying refs. We wouldn't ever have the peeled value available anyway
as we're not using an iterator here, so we can simply plug in the values
we _do_ have.
With this change we now have a `struct reference` available at both
callsites of `show_one()` and can thus pass it, which allows us to use
`reference_get_peeled_oid()` instead of `peel_iterated_oid()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/show-ref.c | 32 +++++++++++++++++++-------------
1 file changed, 19 insertions(+), 13 deletions(-)
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 4803b5e598..4d4984e4e0 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -31,31 +31,31 @@ struct show_one_options {
};
static void show_one(const struct show_one_options *opts,
- const char *refname, const struct object_id *oid)
+ const struct reference *ref)
{
const char *hex;
struct object_id peeled;
- if (!odb_has_object(the_repository->objects, oid,
+ if (!odb_has_object(the_repository->objects, ref->oid,
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
- die("git show-ref: bad ref %s (%s)", refname,
- oid_to_hex(oid));
+ die("git show-ref: bad ref %s (%s)", ref->name,
+ oid_to_hex(ref->oid));
if (opts->quiet)
return;
- hex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev);
+ hex = repo_find_unique_abbrev(the_repository, ref->oid, opts->abbrev);
if (opts->hash_only)
printf("%s\n", hex);
else
- printf("%s %s\n", hex, refname);
+ printf("%s %s\n", hex, ref->name);
if (!opts->deref_tags)
return;
- if (!peel_iterated_oid(the_repository, oid, &peeled)) {
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
hex = repo_find_unique_abbrev(the_repository, &peeled, opts->abbrev);
- printf("%s %s^{}\n", hex, refname);
+ printf("%s %s^{}\n", hex, ref->name);
}
}
@@ -93,7 +93,7 @@ static int show_ref(const struct reference *ref, void *cbdata)
match:
data->found_match++;
- show_one(data->show_one_opts, ref->name, ref->oid);
+ show_one(data->show_one_opts, ref);
return 0;
}
@@ -175,12 +175,18 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) &&
!refs_read_ref(get_main_ref_store(the_repository), *refs, &oid)) {
- show_one(show_one_opts, *refs, &oid);
- }
- else if (!show_one_opts->quiet)
+ struct reference ref = {
+ .name = *refs,
+ .oid = &oid,
+ };
+
+ show_one(show_one_opts, &ref);
+ } else if (!show_one_opts->quiet) {
die("'%s' - not a valid ref", *refs);
- else
+ } else {
return 1;
+ }
+
refs++;
}
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH 08/13] refs: drop `current_ref_iter` hack
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
` (6 preceding siblings ...)
2025-10-07 10:58 ` [PATCH 07/13] builtin/show-ref: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
@ 2025-10-07 10:58 ` Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 09/13] refs: drop infrastructure to peel via iterators Patrick Steinhardt
` (9 subsequent siblings)
17 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-07 10:58 UTC (permalink / raw)
To: git
In preceding commits we have refactored all callers of
`peel_iterated_oid()` to instead use `reference_get_peeled_oid()`. This
allows us to thus get rid of the former function.
Getting rid of that function is nice, but even nicer is that this also
allows us to get rid of the `current_ref_iter` hack. This global
variable tracked the currently-active ref iterator so that we can use it
to peel an object ID. Now that the peeled object ID is propagated via
`struct reference` though we don't have to depend on this hack anymore,
which makes for a more robust and easier-to-understand infrastructure.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs.c | 10 ----------
refs/iterator.c | 5 -----
refs/refs-internal.h | 13 -------------
3 files changed, 28 deletions(-)
diff --git a/refs.c b/refs.c
index 5002e56435..b0ceba8bc3 100644
--- a/refs.c
+++ b/refs.c
@@ -2323,16 +2323,6 @@ int refs_optimize(struct ref_store *refs, struct pack_refs_opts *opts)
return refs->be->optimize(refs, opts);
}
-int peel_iterated_oid(struct repository *r, const struct object_id *base, struct object_id *peeled)
-{
- if (current_ref_iter &&
- (current_ref_iter->ref.oid == base ||
- oideq(current_ref_iter->ref.oid, base)))
- return ref_iterator_peel(current_ref_iter, peeled);
-
- return peel_object(r, base, peeled) ? -1 : 0;
-}
-
int reference_get_peeled_oid(struct repository *repo,
const struct reference *ref,
struct object_id *peeled_oid)
diff --git a/refs/iterator.c b/refs/iterator.c
index fe5980e1b6..072c6aacdb 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -458,15 +458,11 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
return ref_iterator;
}
-struct ref_iterator *current_ref_iter = NULL;
-
int do_for_each_ref_iterator(struct ref_iterator *iter,
each_ref_fn fn, void *cb_data)
{
int retval = 0, ok;
- struct ref_iterator *old_ref_iter = current_ref_iter;
- current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
retval = fn(&iter->ref, cb_data);
if (retval)
@@ -474,7 +470,6 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
}
out:
- current_ref_iter = old_ref_iter;
if (ok == ITER_ERROR)
retval = -1;
ref_iterator_free(iter);
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index ed749d1657..f4f845bbea 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -376,19 +376,6 @@ struct ref_iterator_vtable {
ref_iterator_release_fn *release;
};
-/*
- * current_ref_iter is a performance hack: when iterating over
- * references using the for_each_ref*() functions, current_ref_iter is
- * set to the reference iterator before calling the callback function.
- * If the callback function calls peel_ref(), then peel_ref() first
- * checks whether the reference to be peeled is the one referred to by
- * the iterator (it usually is) and if so, asks the iterator for the
- * peeled version of the reference if it is available. This avoids a
- * refname lookup in a common case. current_ref_iter is set to NULL
- * when the iteration is over.
- */
-extern struct ref_iterator *current_ref_iter;
-
struct ref_store;
/* refs backends */
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH 09/13] refs: drop infrastructure to peel via iterators
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
` (7 preceding siblings ...)
2025-10-07 10:58 ` [PATCH 08/13] refs: drop `current_ref_iter` hack Patrick Steinhardt
@ 2025-10-07 10:58 ` Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 10/13] object: add flag to `peel_object()` to verify object type Patrick Steinhardt
` (8 subsequent siblings)
17 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-07 10:58 UTC (permalink / raw)
To: git
Now that the peeled object ID gets propagated via the `struct reference`
there is no need anymore to call into the reference iterator itself to
dereference an object. Remove this infrastructure.
Most of the changes are straight-forward deletions of code. There is one
exception though in `refs/packed-backend.c::write_with_updates()`. Here
we stop peeling the iterator and instead just pass the peeled object ID
of that iterator directly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs.h | 14 --------------
refs/debug.c | 11 -----------
refs/files-backend.c | 17 -----------------
refs/iterator.c | 36 ------------------------------------
refs/packed-backend.c | 24 +-----------------------
refs/ref-cache.c | 9 ---------
refs/refs-internal.h | 7 -------
refs/reftable-backend.c | 24 ------------------------
8 files changed, 1 insertion(+), 141 deletions(-)
diff --git a/refs.h b/refs.h
index f1e023df35..1f76000055 100644
--- a/refs.h
+++ b/refs.h
@@ -1289,10 +1289,6 @@ int repo_migrate_ref_storage_format(struct repository *repo,
* to the next entry, ref_iterator_advance() aborts the iteration,
* frees the ref_iterator, and returns ITER_ERROR.
*
- * The reference currently being looked at can be peeled by calling
- * ref_iterator_peel(). This function is often faster than peel_ref(),
- * so it should be preferred when iterating over references.
- *
* Putting it all together, a typical iteration looks like this:
*
* int ok;
@@ -1307,9 +1303,6 @@ int repo_migrate_ref_storage_format(struct repository *repo,
* // Access information about the current reference:
* if (!(iter->flags & REF_ISSYMREF))
* printf("%s is %s\n", iter->refname, oid_to_hex(iter->oid));
- *
- * // If you need to peel the reference:
- * ref_iterator_peel(iter, &oid);
* }
*
* if (ok != ITER_DONE)
@@ -1400,13 +1393,6 @@ enum ref_iterator_seek_flag {
int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname,
unsigned int flags);
-/*
- * If possible, peel the reference currently being viewed by the
- * iterator. Return 0 on success.
- */
-int ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled);
-
/* Free the reference iterator and any associated resources. */
void ref_iterator_free(struct ref_iterator *ref_iterator);
diff --git a/refs/debug.c b/refs/debug.c
index 7a26035617..162c24e5cc 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -178,16 +178,6 @@ static int debug_ref_iterator_seek(struct ref_iterator *ref_iterator,
return res;
}
-static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct debug_ref_iterator *diter =
- (struct debug_ref_iterator *)ref_iterator;
- int res = diter->iter->vtable->peel(diter->iter, peeled);
- trace_printf_key(&trace_refs, "iterator_peel: %s: %d\n", diter->iter->ref.name, res);
- return res;
-}
-
static void debug_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct debug_ref_iterator *diter =
@@ -199,7 +189,6 @@ static void debug_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable debug_ref_iterator_vtable = {
.advance = debug_ref_iterator_advance,
.seek = debug_ref_iterator_seek,
- .peel = debug_ref_iterator_peel,
.release = debug_ref_iterator_release,
};
diff --git a/refs/files-backend.c b/refs/files-backend.c
index d34fbe55d6..a4cda57981 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -994,15 +994,6 @@ static int files_ref_iterator_seek(struct ref_iterator *ref_iterator,
return ref_iterator_seek(iter->iter0, refname, flags);
}
-static int files_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct files_ref_iterator *iter =
- (struct files_ref_iterator *)ref_iterator;
-
- return ref_iterator_peel(iter->iter0, peeled);
-}
-
static void files_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct files_ref_iterator *iter =
@@ -1013,7 +1004,6 @@ static void files_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable files_ref_iterator_vtable = {
.advance = files_ref_iterator_advance,
.seek = files_ref_iterator_seek,
- .peel = files_ref_iterator_peel,
.release = files_ref_iterator_release,
};
@@ -2389,12 +2379,6 @@ static int files_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
BUG("ref_iterator_seek() called for reflog_iterator");
}
-static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
- struct object_id *peeled UNUSED)
-{
- BUG("ref_iterator_peel() called for reflog_iterator");
-}
-
static void files_reflog_iterator_release(struct ref_iterator *ref_iterator)
{
struct files_reflog_iterator *iter =
@@ -2405,7 +2389,6 @@ static void files_reflog_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable files_reflog_iterator_vtable = {
.advance = files_reflog_iterator_advance,
.seek = files_reflog_iterator_seek,
- .peel = files_reflog_iterator_peel,
.release = files_reflog_iterator_release,
};
diff --git a/refs/iterator.c b/refs/iterator.c
index 072c6aacdb..d79aa5ec82 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -21,12 +21,6 @@ int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname,
return ref_iterator->vtable->seek(ref_iterator, refname, flags);
}
-int ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- return ref_iterator->vtable->peel(ref_iterator, peeled);
-}
-
void ref_iterator_free(struct ref_iterator *ref_iterator)
{
if (ref_iterator) {
@@ -60,12 +54,6 @@ static int empty_ref_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
return 0;
}
-static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
- struct object_id *peeled UNUSED)
-{
- BUG("peel called for empty iterator");
-}
-
static void empty_ref_iterator_release(struct ref_iterator *ref_iterator UNUSED)
{
}
@@ -73,7 +61,6 @@ static void empty_ref_iterator_release(struct ref_iterator *ref_iterator UNUSED)
static struct ref_iterator_vtable empty_ref_iterator_vtable = {
.advance = empty_ref_iterator_advance,
.seek = empty_ref_iterator_seek,
- .peel = empty_ref_iterator_peel,
.release = empty_ref_iterator_release,
};
@@ -240,18 +227,6 @@ static int merge_ref_iterator_seek(struct ref_iterator *ref_iterator,
return 0;
}
-static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct merge_ref_iterator *iter =
- (struct merge_ref_iterator *)ref_iterator;
-
- if (!iter->current) {
- BUG("peel called before advance for merge iterator");
- }
- return ref_iterator_peel(*iter->current, peeled);
-}
-
static void merge_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct merge_ref_iterator *iter =
@@ -263,7 +238,6 @@ static void merge_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable merge_ref_iterator_vtable = {
.advance = merge_ref_iterator_advance,
.seek = merge_ref_iterator_seek,
- .peel = merge_ref_iterator_peel,
.release = merge_ref_iterator_release,
};
@@ -412,15 +386,6 @@ static int prefix_ref_iterator_seek(struct ref_iterator *ref_iterator,
return ref_iterator_seek(iter->iter0, refname, flags);
}
-static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct prefix_ref_iterator *iter =
- (struct prefix_ref_iterator *)ref_iterator;
-
- return ref_iterator_peel(iter->iter0, peeled);
-}
-
static void prefix_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct prefix_ref_iterator *iter =
@@ -432,7 +397,6 @@ static void prefix_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable prefix_ref_iterator_vtable = {
.advance = prefix_ref_iterator_advance,
.seek = prefix_ref_iterator_seek,
- .peel = prefix_ref_iterator_peel,
.release = prefix_ref_iterator_release,
};
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 7922d63acc..274c2f23aa 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1030,22 +1030,6 @@ static int packed_ref_iterator_seek(struct ref_iterator *ref_iterator,
return 0;
}
-static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct packed_ref_iterator *iter =
- (struct packed_ref_iterator *)ref_iterator;
-
- if ((iter->base.ref.flags & REF_KNOWS_PEELED)) {
- oidcpy(peeled, &iter->peeled);
- return is_null_oid(&iter->peeled) ? -1 : 0;
- } else if ((iter->base.ref.flags & (REF_ISBROKEN | REF_ISSYMREF))) {
- return -1;
- } else {
- return peel_object(iter->repo, &iter->oid, peeled) ? -1 : 0;
- }
-}
-
static void packed_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct packed_ref_iterator *iter =
@@ -1059,7 +1043,6 @@ static void packed_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable packed_ref_iterator_vtable = {
.advance = packed_ref_iterator_advance,
.seek = packed_ref_iterator_seek,
- .peel = packed_ref_iterator_peel,
.release = packed_ref_iterator_release,
};
@@ -1526,13 +1509,8 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
if (cmp < 0) {
/* Pass the old reference through. */
-
- struct object_id peeled;
- int peel_error = ref_iterator_peel(iter, &peeled);
-
if (write_packed_entry(out, iter->ref.name,
- iter->ref.oid,
- peel_error ? NULL : &peeled))
+ iter->ref.oid, iter->ref.peeled_oid))
goto write_error;
if ((ok = ref_iterator_advance(iter)) != ITER_OK) {
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index 2f46f650a6..7d515fc527 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -546,14 +546,6 @@ static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator,
return 0;
}
-static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct cache_ref_iterator *iter =
- (struct cache_ref_iterator *)ref_iterator;
- return peel_object(iter->repo, ref_iterator->ref.oid, peeled) ? -1 : 0;
-}
-
static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct cache_ref_iterator *iter =
@@ -565,7 +557,6 @@ static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable cache_ref_iterator_vtable = {
.advance = cache_ref_iterator_advance,
.seek = cache_ref_iterator_seek,
- .peel = cache_ref_iterator_peel,
.release = cache_ref_iterator_release,
};
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index f4f845bbea..4671517dad 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -357,12 +357,6 @@ typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator);
typedef int ref_iterator_seek_fn(struct ref_iterator *ref_iterator,
const char *refname, unsigned int flags);
-/*
- * Peels the current ref, returning 0 for success or -1 for failure.
- */
-typedef int ref_iterator_peel_fn(struct ref_iterator *ref_iterator,
- struct object_id *peeled);
-
/*
* Implementations of this function should free any resources specific
* to the derived class.
@@ -372,7 +366,6 @@ typedef void ref_iterator_release_fn(struct ref_iterator *ref_iterator);
struct ref_iterator_vtable {
ref_iterator_advance_fn *advance;
ref_iterator_seek_fn *seek;
- ref_iterator_peel_fn *peel;
ref_iterator_release_fn *release;
};
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index f93ab96358..14160023e5 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -744,21 +744,6 @@ static int reftable_ref_iterator_seek(struct ref_iterator *ref_iterator,
return iter->err;
}
-static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct reftable_ref_iterator *iter =
- (struct reftable_ref_iterator *)ref_iterator;
-
- if (iter->ref.value_type == REFTABLE_REF_VAL2) {
- oidread(peeled, iter->ref.value.val2.target_value,
- iter->refs->base.repo->hash_algo);
- return 0;
- }
-
- return -1;
-}
-
static void reftable_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct reftable_ref_iterator *iter =
@@ -776,7 +761,6 @@ static void reftable_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable reftable_ref_iterator_vtable = {
.advance = reftable_ref_iterator_advance,
.seek = reftable_ref_iterator_seek,
- .peel = reftable_ref_iterator_peel,
.release = reftable_ref_iterator_release,
};
@@ -2098,13 +2082,6 @@ static int reftable_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSE
return -1;
}
-static int reftable_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
- struct object_id *peeled UNUSED)
-{
- BUG("reftable reflog iterator cannot be peeled");
- return -1;
-}
-
static void reftable_reflog_iterator_release(struct ref_iterator *ref_iterator)
{
struct reftable_reflog_iterator *iter =
@@ -2117,7 +2094,6 @@ static void reftable_reflog_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable reftable_reflog_iterator_vtable = {
.advance = reftable_reflog_iterator_advance,
.seek = reftable_reflog_iterator_seek,
- .peel = reftable_reflog_iterator_peel,
.release = reftable_reflog_iterator_release,
};
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH 10/13] object: add flag to `peel_object()` to verify object type
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
` (8 preceding siblings ...)
2025-10-07 10:58 ` [PATCH 09/13] refs: drop infrastructure to peel via iterators Patrick Steinhardt
@ 2025-10-07 10:58 ` Patrick Steinhardt
2025-10-08 11:04 ` Kristoffer Haugsbakk
2025-10-07 10:58 ` [PATCH 11/13] refs: don't store peeled object IDs for invalid tags Patrick Steinhardt
` (7 subsequent siblings)
17 siblings, 1 reply; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-07 10:58 UTC (permalink / raw)
To: git
When peeling a tag to a non-tag object we repeatedly call
`parse_object()` on the tagged object until we find the first object
that isn't a tag. While this feels sensible at first, there is a big
catch here: `parse_object()` doesn't actually verify the type of the
tagged object.
The relevant code path here eventually ends up in `parse_tag_buffer()`.
Here, we parset he various fields of the tag, including the "type". Once
we've figured out the type and the tagged object ID, we call one of the
`lookup_${type}()` functions for whatever type we have found. There is
two possible outcomes in the successful case:
1. The object is already part of our cached objects. In that case we
double-check whether the type we're trying to look up matches the
type that was cached.
2. The object is _not_ part of our cached objects. In that case, we
simply create a new object with the expected type, but we don't
parse that object.
In the first case we might notice type mismatches, but only in the case
where our cache has the object with the correct type. In the second
case, we'll blindly assume that the type is correct and then go with it.
We'll only notice that the type might be wrong when we try to parse the
object at a later point.
Now arguably, we could change `parse_tag_buffer()` to verify the tagged
object's type for us. But that would have the effect that such a tag
cannot be parsed at all anymore, and we have a small bunch of tests for
exactly this case that assert we still can open such tags. So this
change does not feel like something we can retroactively tighten, even
though one shouldn't ever hit such corrupted tags.
Instead, add a new `flags` field to `peel_object()` that allows the
caller to opt in to strict object verification. This will be wired up at
a subset of callsites over the next few commits.
Note that this change also inlines `deref_tag_noverify()`. There's only
been two callsites of that function, the one we're changing and one in
our test helpers. The latter callsite can trivially use `deref_tag()`
instead, so by inlining the function we avoid having to pass down the
flag.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
object.c | 20 +++++++++++++++++---
object.h | 15 ++++++++++++++-
ref-filter.c | 2 +-
refs.c | 2 +-
refs/packed-backend.c | 5 ++---
refs/reftable-backend.c | 4 ++--
t/helper/test-reach.c | 2 +-
tag.c | 12 ------------
tag.h | 1 -
9 files changed, 38 insertions(+), 25 deletions(-)
diff --git a/object.c b/object.c
index 986114a6dba..e72b0ed4360 100644
--- a/object.c
+++ b/object.c
@@ -209,11 +209,12 @@ struct object *lookup_object_by_type(struct repository *r,
enum peel_status peel_object(struct repository *r,
const struct object_id *name,
- struct object_id *oid)
+ struct object_id *oid,
+ unsigned flags)
{
struct object *o = lookup_unknown_object(r, name);
- if (o->type == OBJ_NONE) {
+ if (o->type == OBJ_NONE || flags & PEEL_OBJECT_VERIFY_OBJECT_TYPE) {
int type = odb_read_object_info(r->objects, name, NULL);
if (type < 0 || !object_as_type(o, type, 0))
return PEEL_INVALID;
@@ -222,7 +223,20 @@ enum peel_status peel_object(struct repository *r,
if (o->type != OBJ_TAG)
return PEEL_NON_TAG;
- o = deref_tag_noverify(r, o);
+ while (o && o->type == OBJ_TAG) {
+ o = parse_object(r, &o->oid);
+ if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged) {
+ o = ((struct tag *)o)->tagged;
+
+ if (flags & PEEL_OBJECT_VERIFY_OBJECT_TYPE) {
+ int type = odb_read_object_info(r->objects, &o->oid, NULL);
+ if (type < 0 || !object_as_type(o, type, 0))
+ return PEEL_INVALID;
+ }
+ } else {
+ o = NULL;
+ }
+ }
if (!o)
return PEEL_INVALID;
diff --git a/object.h b/object.h
index 8c3c1c46e1b..1499f63d507 100644
--- a/object.h
+++ b/object.h
@@ -287,6 +287,17 @@ enum peel_status {
PEEL_BROKEN = -4
};
+enum peel_object_flags {
+ /*
+ * Always verify the object type, even in the case where the looked-up
+ * object already has an object type. This can be useful when the
+ * stored object type may be invalid. One such case is when looking up
+ * objects via tags, where we blindly trust the object type declared by
+ * the tag.
+ */
+ PEEL_OBJECT_VERIFY_OBJECT_TYPE = (1 << 0),
+};
+
/*
* Peel the named object; i.e., if the object is a tag, resolve the
* tag recursively until a non-tag is found. If successful, store the
@@ -295,7 +306,9 @@ enum peel_status {
* and leave oid unchanged.
*/
enum peel_status peel_object(struct repository *r,
- const struct object_id *name, struct object_id *oid);
+ const struct object_id *name,
+ struct object_id *oid,
+ unsigned flags);
struct object_list *object_list_insert(struct object *item,
struct object_list **list_p);
diff --git a/ref-filter.c b/ref-filter.c
index b18a032e575..72e5a221ff3 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2581,7 +2581,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
if (need_tagged) {
if (!is_null_oid(&ref->peeled_oid)) {
oidcpy(&oi_deref.oid, &ref->peeled_oid);
- } else if (!peel_object(the_repository, &obj->oid, &oi_deref.oid)) {
+ } else if (!peel_object(the_repository, &oi.oid, &oi_deref.oid, 0)) {
/* We managed to peel the object ourselves. */
} else {
die("bad tag");
diff --git a/refs.c b/refs.c
index b0ceba8bc38..40acaa3f42f 100644
--- a/refs.c
+++ b/refs.c
@@ -2332,7 +2332,7 @@ int reference_get_peeled_oid(struct repository *repo,
return 0;
}
- return peel_object(repo, ref->oid, peeled_oid) ? -1 : 0;
+ return peel_object(repo, ref->oid, peeled_oid, 0) ? -1 : 0;
}
int refs_update_symref(struct ref_store *refs, const char *ref,
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 274c2f23aa4..6b8e5247a93 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1528,9 +1528,8 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
i++;
} else {
struct object_id peeled;
- int peel_error = peel_object(refs->base.repo,
- &update->new_oid,
- &peeled);
+ int peel_error = peel_object(refs->base.repo, &update->new_oid,
+ &peeled, 0);
if (write_packed_entry(out, update->refname,
&update->new_oid,
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 14160023e54..fff17f9ef4e 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1632,7 +1632,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
ref.refname = (char *)u->refname;
ref.update_index = ts;
- peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled);
+ peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled, 0);
if (!peel_error) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
@@ -2497,7 +2497,7 @@ static int write_reflog_expiry_table(struct reftable_writer *writer, void *cb_da
ref.refname = (char *)arg->refname;
ref.update_index = ts;
- if (!peel_object(arg->refs->base.repo, &arg->update_oid, &peeled)) {
+ if (!peel_object(arg->refs->base.repo, &arg->update_oid, &peeled, 0)) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
memcpy(ref.value.val2.value, arg->update_oid.hash, GIT_MAX_RAWSZ);
diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c
index 028ec003067..c58c93800f3 100644
--- a/t/helper/test-reach.c
+++ b/t/helper/test-reach.c
@@ -63,7 +63,7 @@ int cmd__reach(int ac, const char **av)
die("failed to resolve %s", buf.buf + 2);
orig = parse_object(r, &oid);
- peeled = deref_tag_noverify(the_repository, orig);
+ peeled = deref_tag(the_repository, orig, NULL, 0);
if (!peeled)
die("failed to load commit for input %s resulting in oid %s",
diff --git a/tag.c b/tag.c
index 1d52686ee10..f5c232d2f1f 100644
--- a/tag.c
+++ b/tag.c
@@ -94,18 +94,6 @@ struct object *deref_tag(struct repository *r, struct object *o, const char *war
return o;
}
-struct object *deref_tag_noverify(struct repository *r, struct object *o)
-{
- while (o && o->type == OBJ_TAG) {
- o = parse_object(r, &o->oid);
- if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged)
- o = ((struct tag *)o)->tagged;
- else
- o = NULL;
- }
- return o;
-}
-
struct tag *lookup_tag(struct repository *r, const struct object_id *oid)
{
struct object *obj = lookup_object(r, oid);
diff --git a/tag.h b/tag.h
index c49d7c19ad3..ef12a610372 100644
--- a/tag.h
+++ b/tag.h
@@ -16,7 +16,6 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
int parse_tag(struct tag *item);
void release_tag_memory(struct tag *t);
struct object *deref_tag(struct repository *r, struct object *, const char *, int);
-struct object *deref_tag_noverify(struct repository *r, struct object *);
int gpg_verify_tag(const struct object_id *oid,
const char *name_to_report, unsigned flags);
struct object_id *get_tagged_oid(struct tag *tag);
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH 11/13] refs: don't store peeled object IDs for invalid tags
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
` (9 preceding siblings ...)
2025-10-07 10:58 ` [PATCH 10/13] object: add flag to `peel_object()` to verify object type Patrick Steinhardt
@ 2025-10-07 10:58 ` Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 12/13] ref-filter: detect broken tags when dereferencing them Patrick Steinhardt
` (6 subsequent siblings)
17 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-07 10:58 UTC (permalink / raw)
To: git
Both the "files" and "reftable" backend store peeled object IDs for
references that point to tags:
- The "files" backend stores the value when packing refs, where each
peeled object ID is prefixed with "^".
- The "reftable" backend stores the value whenever writing a new
reference that points to a tag via a special ref record type.
Both of these backends use `peel_object()` to find the peeled object ID.
But as explained in the preceding commit, that function does not detect
the case where the tag's tagged object and its claimed type mismatch.
The consequence of storing these bogus peeled object IDs is that we're
less likely to detect such corruption in other parts of Git.
git-for-each-ref(1) for example does not notice anymore that the tag is
broken when using "--format=%(*objectname)" to dereference tags.
One could claim that this is good, because it still allows us to mostly
use the tag as intended. But the biggest problem here is that we now
have different behaviour for such a broken tag depending on whether or
not we have its peeled value in the refdb.
Fix the issue by verifying the object type when peeling the object. If
that verification fails we simply skip storing the peeled value in
either of the reference formats.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs/packed-backend.c | 2 +-
refs/reftable-backend.c | 3 ++-
t/pack-refs-tests.sh | 32 ++++++++++++++++++++++++++++++++
t/t0610-reftable-basics.sh | 28 ++++++++++++++++++++++++++++
4 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 6b8e5247a9..acf87a61b1 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1529,7 +1529,7 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
} else {
struct object_id peeled;
int peel_error = peel_object(refs->base.repo, &update->new_oid,
- &peeled, 0);
+ &peeled, PEEL_OBJECT_VERIFY_OBJECT_TYPE);
if (write_packed_entry(out, update->refname,
&update->new_oid,
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index fff17f9ef4..5f4c903b3f 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1632,7 +1632,8 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
ref.refname = (char *)u->refname;
ref.update_index = ts;
- peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled, 0);
+ peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled,
+ PEEL_OBJECT_VERIFY_OBJECT_TYPE);
if (!peel_error) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
diff --git a/t/pack-refs-tests.sh b/t/pack-refs-tests.sh
index 3dbcc01718..095823d915 100644
--- a/t/pack-refs-tests.sh
+++ b/t/pack-refs-tests.sh
@@ -428,4 +428,36 @@ do
'
done
+test_expect_success 'pack-refs does not store invalid peeled tag value' '
+ test_when_finished rm -rf repo &&
+ git init repo &&
+ (
+ cd repo &&
+ git commit --allow-empty --message initial &&
+
+ echo garbage >blob-content &&
+ blob_id=$(git hash-object -w -t blob blob-content) &&
+
+ # Write an invalid tag into the object database. The tag itself
+ # is well-formed, but the tagged object is a blob while we
+ # claim that it is a commit.
+ cat >tag-content <<-EOF &&
+ object $blob_id
+ type commit
+ tag bad-tag
+ tagger C O Mitter <committer@example.com> 1112354055 +0200
+
+ annotated
+ EOF
+ tag_id=$(git hash-object -w -t tag tag-content) &&
+ git update-ref refs/tags/bad-tag "$tag_id" &&
+
+ # The packed-refs file should not contain the peeled object ID.
+ # If it did this would cause commands that use the peeled value
+ # to not notice this corrupted tag.
+ git pack-refs --all &&
+ test_grep ! "^\^" .git/packed-refs
+ )
+'
+
test_done
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
index 3ea5d51532..6575528f21 100755
--- a/t/t0610-reftable-basics.sh
+++ b/t/t0610-reftable-basics.sh
@@ -1135,4 +1135,32 @@ test_expect_success 'fetch: accessing FETCH_HEAD special ref works' '
test_cmp expect actual
'
+test_expect_success 'writes do not persist peeled value for invalid tags' '
+ test_when_finished rm -rf repo &&
+ git init repo &&
+ (
+ cd repo &&
+ git commit --allow-empty --message initial &&
+
+ # We cannot easily verify that the peeled value is not stored
+ # in the tables. Instead, we test this indirectly: we create
+ # two tags that both point to the same object, but they claim
+ # different object types. If we parse both tags we notice that
+ # the parsed tagged object has a mismatch between the two tags
+ # and bail out.
+ #
+ # If we instead use the persisted peeled value we would not
+ # even parse the tags. As such, we would not notice the
+ # discrepancy either and thus listing these tags would succeed.
+ git tag tag-1 -m "tag 1" &&
+ git cat-file tag tag-1 >raw-tag &&
+ sed "s/^type commit$/type blob/" <raw-tag >broken-tag &&
+ broken_tag_id=$(git hash-object -w -t tag broken-tag) &&
+ git update-ref refs/tags/tag-2 $broken_tag_id &&
+
+ test_must_fail git for-each-ref --format="%(*objectname)" refs/tags/ 2>err &&
+ test_grep "bad tag pointer" err
+ )
+'
+
test_done
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH 12/13] ref-filter: detect broken tags when dereferencing them
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
` (10 preceding siblings ...)
2025-10-07 10:58 ` [PATCH 11/13] refs: don't store peeled object IDs for invalid tags Patrick Steinhardt
@ 2025-10-07 10:58 ` Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 13/13] ref-filter: parse objects on demand Patrick Steinhardt
` (5 subsequent siblings)
17 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-07 10:58 UTC (permalink / raw)
To: git
Users can ask git-for-each-ref(1) to peel tags and return information of
the tagged object by adding an asterisk to the format, like for example
"%(*$objectname)". If so, git-for-each-ref(1) peels that object to the
first non-tag object and then returns its values.
As mentioned in preceding commits, it can happen that the tagged object
type and the claimed object type differ, effectively resulting in a
corrupt tag. git-for-each-ref(1) would notice this mismatch, print an
error and then bail out when trying to peel the tag.
But we only notice this corruption in some very specific edge cases!
While we have a test in "t/for-each-ref-tests.sh" that verifies the
above scenario, this test is specifically crafted to detect the issue at
hand. Namely, we create two tags:
- One tag points to a specific object with the correct type.
- The other tag points to the *same* object with a different type.
The fact that both tags point to the same object is important here:
`peel_object()` wouldn't notice the corruption if the tagged objects
were different.
The root cause is that `peel_object()` calls `lookup_${type}()`
eventually, where the type is the same type declared in the tag object.
Consequently, when we have two tags pointing to the same object but with
different declared types we'll call two different lookup functions. The
first lookup will store the object with an unverified type A, whereas
the second lookup will try to look up the object with a different
unverified type B. And it is only now that we notice the discrepancy in
object types, even though type A could've already been the wrong type.
Fix the issue by verifying the object type in `populate_value()`. With
this change we'll also notice type mismatches when only dereferencing a
tag once.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
ref-filter.c | 3 ++-
t/for-each-ref-tests.sh | 4 +++-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/ref-filter.c b/ref-filter.c
index 72e5a221ff..72cf85c8c6 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2581,7 +2581,8 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
if (need_tagged) {
if (!is_null_oid(&ref->peeled_oid)) {
oidcpy(&oi_deref.oid, &ref->peeled_oid);
- } else if (!peel_object(the_repository, &oi.oid, &oi_deref.oid, 0)) {
+ } else if (!peel_object(the_repository, &oi.oid, &oi_deref.oid,
+ PEEL_OBJECT_VERIFY_OBJECT_TYPE)) {
/* We managed to peel the object ourselves. */
} else {
die("bad tag");
diff --git a/t/for-each-ref-tests.sh b/t/for-each-ref-tests.sh
index e3ad19298a..4593be5fd5 100644
--- a/t/for-each-ref-tests.sh
+++ b/t/for-each-ref-tests.sh
@@ -1809,7 +1809,9 @@ test_expect_success "${git_for_each_ref} reports broken tags" '
bad=$(git hash-object -w -t tag bad) &&
git update-ref refs/tags/broken-tag-bad $bad &&
test_must_fail ${git_for_each_ref} --format="%(*objectname)" \
- refs/tags/broken-tag-*
+ refs/tags/broken-tag-* &&
+ test_must_fail ${git_for_each_ref} --format="%(*objectname)" \
+ refs/tags/broken-tag-bad
'
test_expect_success 'set up tag with signature and no blank lines' '
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH 13/13] ref-filter: parse objects on demand
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
` (11 preceding siblings ...)
2025-10-07 10:58 ` [PATCH 12/13] ref-filter: detect broken tags when dereferencing them Patrick Steinhardt
@ 2025-10-07 10:58 ` Patrick Steinhardt
2025-10-08 11:05 ` Kristoffer Haugsbakk
2025-10-07 21:00 ` [PATCH 00/13] refs: improvements and fixes for peeling tags Junio C Hamano
` (4 subsequent siblings)
17 siblings, 1 reply; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-07 10:58 UTC (permalink / raw)
To: git
When formatting an arbitray object we parse that object regardless of
whether or not we actually need any parsed data. In fact, many of the
atoms we have don't require any.
Refactor the code so that we parse the data on demand when we see an
atom that wants to access the objects. This leads to a small speedup,
for example in the Chromium repository with around 40000 refs:
Benchmark 1: for-each-ref --format='%(raw)' (HEAD~)
Time (mean ± σ): 388.7 ms ± 1.1 ms [User: 322.2 ms, System: 65.0 ms]
Range (min … max): 387.3 ms … 390.8 ms 10 runs
Benchmark 2: for-each-ref --format='%(raw)' (HEAD)
Time (mean ± σ): 344.7 ms ± 0.7 ms [User: 287.8 ms, System: 55.1 ms]
Range (min … max): 343.9 ms … 345.7 ms 10 runs
Summary
for-each-ref --format='%(raw)' (HEAD) ran
1.13 ± 0.00 times faster than for-each-ref --format='%(raw)' (HEAD~)
With this change, we now spend ~90% of the time decompressing objects,
which is almost as good as it gets regarding git-for-each-ref(1)'s own
infrastructure.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
ref-filter.c | 156 ++++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 117 insertions(+), 39 deletions(-)
diff --git a/ref-filter.c b/ref-filter.c
index 72cf85c8c6..537c7babac 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -91,6 +91,7 @@ static struct expand_data {
struct object_id delta_base_oid;
void *content;
+ struct object *maybe_object;
struct object_info info;
} oi, oi_deref;
@@ -1475,11 +1476,28 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_
}
}
+static int get_or_parse_object(struct expand_data *data, const char *refname,
+ struct object **object, struct strbuf *err, int *eaten)
+{
+ if (!data->maybe_object) {
+ data->maybe_object = parse_object_buffer(the_repository, &data->oid, data->type,
+ data->size, data->content, eaten);
+ if (!data->maybe_object)
+ return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
+ oid_to_hex(&data->oid), refname);
+ }
+
+ *object = data->maybe_object;
+ return 0;
+}
+
/* See grab_values */
-static void grab_tag_values(struct atom_value *val, int deref, struct object *obj)
+static int grab_tag_values(struct atom_value *val, int deref,
+ struct expand_data *data, const char *refname,
+ struct strbuf *err, int *eaten)
{
- int i;
- struct tag *tag = (struct tag *) obj;
+ struct tag *tag = NULL;
+ int i, ret;
for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i].name;
@@ -1487,6 +1505,17 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
struct atom_value *v = &val[i];
if (!!deref != (*name == '*'))
continue;
+
+ if (!tag) {
+ struct object *object;
+
+ ret = get_or_parse_object(data, refname, &object, err, eaten);
+ if (ret < 0)
+ return ret;
+
+ tag = (struct tag *) object;
+ }
+
if (deref)
name++;
if (atom_type == ATOM_TAG)
@@ -1496,22 +1525,38 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
else if (atom_type == ATOM_OBJECT && tag->tagged)
v->s = xstrdup(oid_to_hex(&tag->tagged->oid));
}
+
+ return 0;
}
/* See grab_values */
-static void grab_commit_values(struct atom_value *val, int deref, struct object *obj)
+static int grab_commit_values(struct atom_value *val, int deref,
+ struct expand_data *data, const char *refname,
+ struct strbuf *err, int *eaten)
{
- int i;
- struct commit *commit = (struct commit *) obj;
+ int i, ret;
+ struct commit *commit = NULL;
for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i].name;
enum atom_type atom_type = used_atom[i].atom_type;
struct atom_value *v = &val[i];
+
if (!!deref != (*name == '*'))
continue;
if (deref)
name++;
+
+ if (!commit) {
+ struct object *object;
+
+ ret = get_or_parse_object(data, refname, &object, err, eaten);
+ if (ret < 0)
+ return ret;
+
+ commit = (struct commit *) object;
+ }
+
if (atom_type == ATOM_TREE &&
grab_oid(name, "tree", get_commit_tree_oid(commit), v, &used_atom[i]))
continue;
@@ -1531,6 +1576,8 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
v->s = strbuf_detach(&s, NULL);
}
}
+
+ return 0;
}
static const char *find_wholine(const char *who, int wholen, const char *buf)
@@ -1759,10 +1806,12 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void
}
}
-static void grab_signature(struct atom_value *val, int deref, struct object *obj)
+static int grab_signature(struct atom_value *val, int deref,
+ struct expand_data *data, const char *refname,
+ struct strbuf *err, int *eaten)
{
- int i;
- struct commit *commit = (struct commit *) obj;
+ int i, ret;
+ struct commit *commit = NULL;
struct signature_check sigc = { 0 };
int signature_checked = 0;
@@ -1790,6 +1839,16 @@ static void grab_signature(struct atom_value *val, int deref, struct object *obj
continue;
if (!signature_checked) {
+ if (!commit) {
+ struct object *object;
+
+ ret = get_or_parse_object(data, refname, &object, err, eaten);
+ if (ret < 0)
+ return ret;
+
+ commit = (struct commit *) object;
+ }
+
check_commit_signature(commit, &sigc);
signature_checked = 1;
}
@@ -1843,6 +1902,8 @@ static void grab_signature(struct atom_value *val, int deref, struct object *obj
if (signature_checked)
signature_check_clear(&sigc);
+
+ return 0;
}
static void find_subpos(const char *buf,
@@ -1920,9 +1981,8 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size
}
static void grab_describe_values(struct atom_value *val, int deref,
- struct object *obj)
+ struct expand_data *data)
{
- struct commit *commit = (struct commit *)obj;
int i;
for (i = 0; i < used_atom_cnt; i++) {
@@ -1944,7 +2004,7 @@ static void grab_describe_values(struct atom_value *val, int deref,
cmd.git_cmd = 1;
strvec_push(&cmd.args, "describe");
strvec_pushv(&cmd.args, atom->u.describe_args.v);
- strvec_push(&cmd.args, oid_to_hex(&commit->object.oid));
+ strvec_push(&cmd.args, oid_to_hex(&data->oid));
if (pipe_command(&cmd, NULL, 0, &out, 0, &err, 0) < 0) {
error(_("failed to run 'describe'"));
v->s = xstrdup("");
@@ -2066,24 +2126,36 @@ static void fill_missing_values(struct atom_value *val)
* pointed at by the ref itself; otherwise it is the object the
* ref (which is a tag) refers to.
*/
-static void grab_values(struct atom_value *val, int deref, struct object *obj, struct expand_data *data)
+static int grab_values(struct atom_value *val, int deref, struct expand_data *data,
+ const char *refname, struct strbuf *err, int *eaten)
{
void *buf = data->content;
+ int ret;
- switch (obj->type) {
+ switch (data->type) {
case OBJ_TAG:
- grab_tag_values(val, deref, obj);
+ ret = grab_tag_values(val, deref, data, refname, err, eaten);
+ if (ret < 0)
+ goto out;
+
grab_sub_body_contents(val, deref, data);
grab_person("tagger", val, deref, buf);
- grab_describe_values(val, deref, obj);
+ grab_describe_values(val, deref, data);
break;
case OBJ_COMMIT:
- grab_commit_values(val, deref, obj);
+ ret = grab_commit_values(val, deref, data, refname, err, eaten);
+ if (ret < 0)
+ goto out;
+
grab_sub_body_contents(val, deref, data);
grab_person("author", val, deref, buf);
grab_person("committer", val, deref, buf);
- grab_signature(val, deref, obj);
- grab_describe_values(val, deref, obj);
+
+ ret = grab_signature(val, deref, data, refname, err, eaten);
+ if (ret < 0)
+ goto out;
+
+ grab_describe_values(val, deref, data);
break;
case OBJ_TREE:
/* grab_tree_values(val, deref, obj, buf, sz); */
@@ -2094,8 +2166,12 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s
grab_sub_body_contents(val, deref, data);
break;
default:
- die("Eh? Object of type %d?", obj->type);
+ die("Eh? Object of type %d?", data->type);
}
+
+ ret = 0;
+out:
+ return ret;
}
static inline char *copy_advance(char *dst, const char *src)
@@ -2292,38 +2368,41 @@ static const char *get_refname(struct used_atom *atom, struct ref_array_item *re
return show_ref(&atom->u.refname, ref->refname);
}
-static int get_object(struct ref_array_item *ref, int deref, struct object **obj,
+static int get_object(struct ref_array_item *ref, int deref,
struct expand_data *oi, struct strbuf *err)
{
- /* parse_object_buffer() will set eaten to 0 if free() will be needed */
- int eaten = 1;
+ /* parse_object_buffer() will set eaten to 1 if free() will be needed */
+ int eaten = 0;
+ int ret;
+
if (oi->info.contentp) {
/* We need to know that to use parse_object_buffer properly */
oi->info.sizep = &oi->size;
oi->info.typep = &oi->type;
}
+
if (odb_read_object_info_extended(the_repository->objects, &oi->oid, &oi->info,
- OBJECT_INFO_LOOKUP_REPLACE))
- return strbuf_addf_ret(err, -1, _("missing object %s for %s"),
- oid_to_hex(&oi->oid), ref->refname);
+ OBJECT_INFO_LOOKUP_REPLACE)) {
+ ret = strbuf_addf_ret(err, -1, _("missing object %s for %s"),
+ oid_to_hex(&oi->oid), ref->refname);
+ goto out;
+ }
if (oi->info.disk_sizep && oi->disk_size < 0)
BUG("Object size is less than zero.");
if (oi->info.contentp) {
- *obj = parse_object_buffer(the_repository, &oi->oid, oi->type, oi->size, oi->content, &eaten);
- if (!*obj) {
- if (!eaten)
- free(oi->content);
- return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
- oid_to_hex(&oi->oid), ref->refname);
- }
- grab_values(ref->value, deref, *obj, oi);
+ ret = grab_values(ref->value, deref, oi, ref->refname, err, &eaten);
+ if (ret < 0)
+ goto out;
}
grab_common_values(ref->value, deref, oi);
+ ret = 0;
+
+out:
if (!eaten)
free(oi->content);
- return 0;
+ return ret;
}
static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees)
@@ -2376,7 +2455,6 @@ static char *get_worktree_path(const struct ref_array_item *ref)
*/
static int populate_value(struct ref_array_item *ref, struct strbuf *err)
{
- struct object *obj;
int i;
struct object_info empty = OBJECT_INFO_INIT;
int ahead_behind_atoms = 0;
@@ -2564,14 +2642,14 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
oi.oid = ref->objectname;
- if (get_object(ref, 0, &obj, &oi, err))
+ if (get_object(ref, 0, &oi, err))
return -1;
/*
* If there is no atom that wants to know about tagged
* object, we are done.
*/
- if (!need_tagged || (obj->type != OBJ_TAG))
+ if (!need_tagged || (oi.type != OBJ_TAG))
return 0;
/*
@@ -2589,7 +2667,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
}
}
- return get_object(ref, 1, &obj, &oi_deref, err);
+ return get_object(ref, 1, &oi_deref, err);
}
/*
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* Re: [PATCH 02/13] refs: introduce `.ref` field for the base iterator
2025-10-07 10:58 ` [PATCH 02/13] refs: introduce `.ref` field for the base iterator Patrick Steinhardt
@ 2025-10-07 14:24 ` Karthik Nayak
2025-10-08 13:44 ` Patrick Steinhardt
2025-10-07 20:19 ` Justin Tobler
2025-10-07 21:57 ` Taylor Blau
2 siblings, 1 reply; 106+ messages in thread
From: Karthik Nayak @ 2025-10-07 14:24 UTC (permalink / raw)
To: Patrick Steinhardt, git
[-- Attachment #1: Type: text/plain, Size: 758 bytes --]
Patrick Steinhardt <ps@pks.im> writes:
> The base iterator has a couple of fields that tracks the name, target,
> object ID and flags for the current reference. Due do this design we
> have to create a new `struct reference` whenever we want to hand over
> that reference to the callback function, which is tedious and not very
> efficient.
>
> Convert the structure to instead contain a `stuct reference` as member.
s/stuct/struct
> This member is expected to be populated by the implementations of the
> iterator and is handed over to the callback directly.
>
Wouldn't this also add the burden on each backend to ensure we don't
serve stale data for each '_advance()' call?
Would it make sense to reset this data in `ref_iterator_advance()`?
Karthik
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 03/13] refs: refactor reference status flags
2025-10-07 10:58 ` [PATCH 03/13] refs: refactor reference status flags Patrick Steinhardt
@ 2025-10-07 14:27 ` Karthik Nayak
2025-10-08 13:44 ` Patrick Steinhardt
0 siblings, 1 reply; 106+ messages in thread
From: Karthik Nayak @ 2025-10-07 14:27 UTC (permalink / raw)
To: Patrick Steinhardt, git
[-- Attachment #1: Type: text/plain, Size: 1221 bytes --]
Patrick Steinhardt <ps@pks.im> writes:
> The reference flags encode information like whether or not a reference
> is a symbolic reference or whether it may be broken. This information is
> stored in a `int flags` bitfield, which is in conflict with our modern
> best practices; we tend to use an unsigned integer to store flags.
>
> Change the type of the field to be `unsigned`. While at it, refactor the
> individual flags to be part of an `enum` instead of using preprocessor
> defines.
>
Using an enum is much nicer.
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> refs.h | 41 +++++++++++++++++++++--------------------
> 1 file changed, 21 insertions(+), 20 deletions(-)
>
> diff --git a/refs.h b/refs.h
> index 2b24a3d997..8f484e2ffc 100644
> --- a/refs.h
> +++ b/refs.h
> @@ -333,27 +333,28 @@ struct ref_transaction;
> * stored in ref_iterator::flags. Other bits are for internal use
> * only:
> */
> +enum reference_status {
> + /* Reference is a symbolic reference. */
> + REF_ISSYMREF = (1 << 0),
>
Nit: It would also be nice to rename these flags to
'REFERENCE_STATUS_*', since that seems to be the pattern we generally
follow. Doesn't have to be in your series though :)
Rest looks good.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 04/13] refs: expose peeled object ID via the iterator
2025-10-07 10:58 ` [PATCH 04/13] refs: expose peeled object ID via the iterator Patrick Steinhardt
@ 2025-10-07 14:52 ` Karthik Nayak
2025-10-08 13:45 ` Patrick Steinhardt
0 siblings, 1 reply; 106+ messages in thread
From: Karthik Nayak @ 2025-10-07 14:52 UTC (permalink / raw)
To: Patrick Steinhardt, git
[-- Attachment #1: Type: text/plain, Size: 4994 bytes --]
Patrick Steinhardt <ps@pks.im> writes:
> Both the "files" and "reftable" backend are able to store peeled values
> for tags in the respective formats. This allows for a more efficient
> lookup of the target object of such a tag without having to manually
> peel via the object database.
>
In the 'files' backend, I thought only packed-refs store peeled values?
> The infrastructure to access these peeled object IDs is somewhat funky
> though. When iterating through objects, we store a pointer reference to
> the current iterator in a global variable. The callbacks invoked by that
> iterator are then expected to call `peel_iterated_oid()`, which checks
> whether the globally-stored iterator's current reference refers to the
> one handed into that function. If so, we ask the iterator to peel the
> object, otherwise we manually peel the object via the object database.
> Depending on global state like this is somewhat weird and also quite
> fragile.
>
> Introduce a new `struct reference::peeled_oid` field that can be
> populated by the reference backends. This field can be accessed via a
> new function `reference_get_peeled_oid()` that either uses that value,
> if set, or alternatively peels via the ODB. With this change we don't
> have to rely on global state anymore, but make the peeled object ID
> available to the callback functions directly.
>
> Adjust trivial callers that already have a `struct reference` available.
> Remaining callers will be adjusted in subsequent commits.
[snip]
> diff --git a/refs.c b/refs.c
> index 15ad0ef7a8..5002e56435 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -2333,6 +2333,18 @@ int peel_iterated_oid(struct repository *r, const struct object_id *base, struct
> return peel_object(r, base, peeled) ? -1 : 0;
> }
>
> +int reference_get_peeled_oid(struct repository *repo,
> + const struct reference *ref,
> + struct object_id *peeled_oid)
> +{
> + if (ref->peeled_oid) {
> + oidcpy(peeled_oid, ref->peeled_oid);
> + return 0;
> + }
> +
> + return peel_object(repo, ref->oid, peeled_oid) ? -1 : 0;
> +}
> +
>
So similar to `peel_iterated_oid()` but instead of relying on the
reference backend to actually provide us the information, we simply rely
on the value if present. This avoids the round-trip. Makes sense.
The last resource is to look into the object database. Which should only
happen with loose refs in the files backend.
> diff --git a/refs/packed-backend.c b/refs/packed-backend.c
> index 7987acdc96..7922d63acc 100644
> --- a/refs/packed-backend.c
> +++ b/refs/packed-backend.c
> @@ -959,11 +959,14 @@ static int next_record(struct packed_ref_iterator *iter)
> if ((iter->base.ref.flags & REF_ISBROKEN)) {
> oidclr(&iter->peeled, iter->repo->hash_algo);
> iter->base.ref.flags &= ~REF_KNOWS_PEELED;
> + iter->base.ref.peeled_oid = NULL;
> } else {
> iter->base.ref.flags |= REF_KNOWS_PEELED;
> + iter->base.ref.peeled_oid = &iter->peeled;
> }
> } else {
> oidclr(&iter->peeled, iter->repo->hash_algo);
> + iter->base.ref.peeled_oid = NULL;
>
So my comment on the previous commit holds. We have to manually ensure
we reset the fields.
> }
>
> return ITER_OK;
> diff --git a/refs/ref-cache.c b/refs/ref-cache.c
> index 97555fa118..2f46f650a6 100644
> --- a/refs/ref-cache.c
> +++ b/refs/ref-cache.c
> @@ -428,6 +428,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
> iter->base.ref.name = entry->name;
> iter->base.ref.target = entry->u.value.referent;
> iter->base.ref.oid = &entry->u.value.oid;
> + iter->base.ref.peeled_oid = NULL;
> iter->base.ref.flags = entry->flag;
> return ITER_OK;
> }
> diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
> index 7fbc77492e..f93ab96358 100644
> --- a/refs/reftable-backend.c
> +++ b/refs/reftable-backend.c
> @@ -546,6 +546,7 @@ struct reftable_ref_iterator {
> struct reftable_iterator iter;
> struct reftable_ref_record ref;
> struct object_id oid;
> + struct object_id peeled_oid;
>
> char *prefix;
> size_t prefix_len;
> @@ -670,6 +671,8 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
> case REFTABLE_REF_VAL2:
> oidread(&iter->oid, iter->ref.value.val2.value,
> refs->base.repo->hash_algo);
> + oidread(&iter->peeled_oid, iter->ref.value.val2.target_value,
> + refs->base.repo->hash_algo);
> break;
> case REFTABLE_REF_SYMREF:
> referent = refs_resolve_ref_unsafe(&iter->refs->base,
> @@ -706,6 +709,10 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
> iter->base.ref.name = iter->ref.refname;
> iter->base.ref.target = referent;
> iter->base.ref.oid = &iter->oid;
> + if (iter->ref.value_type == REFTABLE_REF_VAL2)
> + iter->base.ref.peeled_oid = &iter->peeled_oid;
> + else
> + iter->base.ref.peeled_oid = NULL;
> iter->base.ref.flags = flags;
>
> break;
>
> --
> 2.51.0.764.g787ff6f08a.dirty
All of this looks good and as expected!
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 05/13] upload-pack: convert to use `reference_get_peeled_oid()`
2025-10-07 10:58 ` [PATCH 05/13] upload-pack: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
@ 2025-10-07 16:18 ` Karthik Nayak
2025-10-08 13:45 ` Patrick Steinhardt
0 siblings, 1 reply; 106+ messages in thread
From: Karthik Nayak @ 2025-10-07 16:18 UTC (permalink / raw)
To: Patrick Steinhardt, git
[-- Attachment #1: Type: text/plain, Size: 1816 bytes --]
Patrick Steinhardt <ps@pks.im> writes:
> The `write_v0_ref()` callback is invoked from two callsites:
>
Okay so this function does multiple things based on whether the
capabilities are already advertised or not.
- If not, we propagate the capabilities and set the static variable
`capabilities` to NULL and also set `data->sent_capabilities = 1;`.
- We receive `ref->oid` as a zero oid for the hash algorithm being
used, we convert it to the hex format with `oid_to_hex()`.
- If already advertised, we simply propagate the reference and if it can
be peeled, also propagate the peeled reference.
Not for your series: but this feels like the capabilities should be an
independent function.
> - Once via `send_ref()` which is a callback passed to
> `for_each_namespaced_ref_1()`.
>
and passed to `refs_head_ref_namespaced()`
> - Once manually to announce capabilities.
>
> When sending references to the client we also send the peeled value of
> tags. As we don't have a `struct reference` available in the second
> case, we cannot easily peel by calling `reference_get_peeled_oid()`, but
> we instead have to depend on on global state via `peel_iterated_oid()`.
>
> We do have a reference available though in the first case, it's only the
> second case that keeps us from using `reference_get_peeled_oid()`. But
> that second case only announces capabilities anyway, so we're not really
> handling a reference at all here.
>
Yup, this was my inference above as well.
> Adapt that case to construct a reference manually and pass that to
> `write_v0_ref()`. Start to use `reference_get_peeled_oid()` now that we
> always have a `struct reference` available.
>
This is a fair solution for now. I think this also shows that these two
operation modes should definitely be separated out.
[snip]
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 01/13] refs: introduce wrapper struct for `each_ref_fn`
2025-10-07 10:58 ` [PATCH 01/13] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
@ 2025-10-07 18:05 ` Justin Tobler
2025-10-08 13:42 ` Patrick Steinhardt
2025-10-07 21:56 ` Taylor Blau
2025-10-08 15:52 ` shejialuo
2 siblings, 1 reply; 106+ messages in thread
From: Justin Tobler @ 2025-10-07 18:05 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
On 25/10/07 12:58PM, Patrick Steinhardt wrote:
> The `each_ref_fn` callback function type is used across our code base
> for several different functions that iterate through reference. There's
> a bunch of callbacks implementing this type, which makes any changes to
> the callback signature extremely noisy. An example of the required churn
> is e8207717f1 (refs: add referent to each_ref_fn, 2024-08-09): adding a
> single argument required us to change 48 files.
>
> It was already proposed back then [1] that we might want to introduce a
> wrapper structure to alleviate the pain going forward. While this of
> course requires the same kind of global refactoring as just introducing
> a new parameter, it at least allows us to more change the callback type
> afterwards by just extending the wrapper structure.
>
> One counterargument to this refactoring is that it makes the structure
> more opaque. While it is obvious which callsites need to be fixed up
> when we change the function type, it's not obvious anymore once we use
> a structure. That being said, we only have a handful of sites that
> actually need to populate this wrapper structure: our ref backends and
> "refs/iterator.c".
It looks like we also populate `stuct reference` in a couple other spots
where we invoke the callback explicitly.
> Introduce this wrapper structure so that we can adapt the iterator
> interfaces more readily.
>
> [1]: <ZmarVcF5JjsZx0dl@tanuki>
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
[snip]
> diff --git a/refs.h b/refs.h
> index 4e6bd63aa86..2b24a3d9974 100644
> --- a/refs.h
> +++ b/refs.h
> @@ -355,14 +355,32 @@ struct ref_transaction;
> */
> #define REF_BAD_NAME 0x08
>
> +/* A reference passed to `for_each_ref()`-style callbacks. */
> +struct reference {
> + /* The fully-qualified name of the reference. */
> + const char *name;
> +
> + /* The target of a symbolic ref. `NULL` for direct references. */
> + const char *target;
> +
> + /*
> + * The object ID of a reference. Either the direct object ID or the
> + * resolved object ID in the case of a symbolic ref. May be the zero
> + * object ID in case the symbolic ref cannot be resolved.
> + */
> + const struct object_id *oid;
> +
> + /* A bitfield of `REF_` flags. */
> + int flags;
I was considering for a little while whether it would make sense for all
the arguments to be moved here, or if ones such as flags should remain.
Since all these fields directly relate to the reference though, I think
it does make sense to relocate all of them.
> +};
Ok, so now all the explicit callback arguments are contained in `struct
reference` here. Going forward this certainly would reduce churn if need
need to add additional fields here. Overall, this seems sensible to me.
> +
> /*
> * The signature for the callback function for the for_each_*()
> * functions below. The memory pointed to by the refname and oid
> * arguments is only guaranteed to be valid for the duration of a
> * single callback invocation.
> */
Should we update this comment now that these fields are contained the
wrapper struct?
[snip]
> diff --git a/refs/iterator.c b/refs/iterator.c
> index 17ef841d8a3..7f2e718f1c9 100644
> --- a/refs/iterator.c
> +++ b/refs/iterator.c
> @@ -476,7 +476,14 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
>
> current_ref_iter = iter;
> while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
> - retval = fn(iter->refname, iter->referent, iter->oid, iter->flags, cb_data);
> + struct reference ref = {
> + .name = iter->refname,
> + .target = iter->referent,
> + .oid = iter->oid,
> + .flags = iter->flags,
> + };
Now we wire up the new wrapper struct instead of passing explicit
arguments. Looks good.
> +
> + retval = fn(&ref, cb_data);
> if (retval)
> goto out;
> }
-Justin
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 02/13] refs: introduce `.ref` field for the base iterator
2025-10-07 10:58 ` [PATCH 02/13] refs: introduce `.ref` field for the base iterator Patrick Steinhardt
2025-10-07 14:24 ` Karthik Nayak
@ 2025-10-07 20:19 ` Justin Tobler
2025-10-07 21:57 ` Taylor Blau
2 siblings, 0 replies; 106+ messages in thread
From: Justin Tobler @ 2025-10-07 20:19 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
On 25/10/07 12:58PM, Patrick Steinhardt wrote:
> The base iterator has a couple of fields that tracks the name, target,
> object ID and flags for the current reference. Due do this design we
s/Due do/Due to/
> have to create a new `struct reference` whenever we want to hand over
> that reference to the callback function, which is tedious and not very
> efficient.
>
> Convert the structure to instead contain a `stuct reference` as member.
> This member is expected to be populated by the implementations of the
> iterator and is handed over to the callback directly.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
[snip]
> diff --git a/refs/files-backend.c b/refs/files-backend.c
> index 0ddcf22aed..d34fbe55d6 100644
> --- a/refs/files-backend.c
> +++ b/refs/files-backend.c
> @@ -962,26 +962,23 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
>
> while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
> if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
> - parse_worktree_ref(iter->iter0->refname, NULL, NULL,
> + parse_worktree_ref(iter->iter0->ref.name, NULL, NULL,
> NULL) != REF_WORKTREE_CURRENT)
> continue;
>
> if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) &&
> - (iter->iter0->flags & REF_ISSYMREF) &&
> - (iter->iter0->flags & REF_ISBROKEN))
> + (iter->iter0->ref.flags & REF_ISSYMREF) &&
> + (iter->iter0->ref.flags & REF_ISBROKEN))
> continue;
>
> if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
> - !ref_resolves_to_object(iter->iter0->refname,
> + !ref_resolves_to_object(iter->iter0->ref.name,
> iter->repo,
> - iter->iter0->oid,
> - iter->iter0->flags))
> + iter->iter0->ref.oid,
> + iter->iter0->ref.flags))
> continue;
>
> - iter->base.refname = iter->iter0->refname;
> - iter->base.oid = iter->iter0->oid;
> - iter->base.flags = iter->iter0->flags;
> - iter->base.referent = iter->iter0->referent;
> + iter->base.ref = iter->iter0->ref;
Ok, so here we already have a `struct reference` setup and thus we
directly propagate to the base when advacing the iterator. Makes sense.
>
> return ITER_OK;
> }
> @@ -1368,30 +1365,29 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune **refs_
> * Return true if the specified reference should be packed.
> */
> static int should_pack_ref(struct files_ref_store *refs,
> - const char *refname,
> - const struct object_id *oid, unsigned int ref_flags,
> + const struct reference *ref,
> struct pack_refs_opts *opts)
This hunk is simplifies the arguments required by should_pack_ref() by
using `struct reference`. The change seems sensible, it might be worth
mentioning in the commit message though.
> {
> struct string_list_item *item;
>
> /* Do not pack per-worktree refs: */
> - if (parse_worktree_ref(refname, NULL, NULL, NULL) !=
> + if (parse_worktree_ref(ref->name, NULL, NULL, NULL) !=
> REF_WORKTREE_SHARED)
> return 0;
>
> /* Do not pack symbolic refs: */
> - if (ref_flags & REF_ISSYMREF)
> + if (ref->flags & REF_ISSYMREF)
> return 0;
>
> /* Do not pack broken refs: */
> - if (!ref_resolves_to_object(refname, refs->base.repo, oid, ref_flags))
> + if (!ref_resolves_to_object(ref->name, refs->base.repo, ref->oid, ref->flags))
> return 0;
>
> - if (ref_excluded(opts->exclusions, refname))
> + if (ref_excluded(opts->exclusions, ref->name))
> return 0;
>
> for_each_string_list_item(item, opts->includes)
> - if (!wildmatch(item->string, refname, 0))
> + if (!wildmatch(item->string, ref->name, 0))
> return 1;
>
> return 0;
[snip]
> @@ -476,14 +468,7 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
>
> current_ref_iter = iter;
> while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
> - struct reference ref = {
> - .name = iter->refname,
> - .target = iter->referent,
> - .oid = iter->oid,
> - .flags = iter->flags,
> - };
> -
> - retval = fn(&ref, cb_data);
> + retval = fn(&iter->ref, cb_data);
Now we propagate `struct reference` directly when invoking the for each
callback. Makes sense.
> if (retval)
> goto out;
> }
-Justin
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 00/13] refs: improvements and fixes for peeling tags
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
` (12 preceding siblings ...)
2025-10-07 10:58 ` [PATCH 13/13] ref-filter: parse objects on demand Patrick Steinhardt
@ 2025-10-07 21:00 ` Junio C Hamano
2025-10-07 21:49 ` Taylor Blau
2025-10-07 23:01 ` Junio C Hamano
` (3 subsequent siblings)
17 siblings, 1 reply; 106+ messages in thread
From: Junio C Hamano @ 2025-10-07 21:00 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Patrick Steinhardt <ps@pks.im> writes:
> diff --cc builtin/repack.c
> index 873e21c35d,ad60c4290d..0000000000
> --- a/builtin/repack.c
> +++ b/builtin/repack.c
> diff --git a/repack-midx.c b/repack-midx.c
> index 6f6202c5bc..74bdfa3a6e 100644
> --- a/repack-midx.c
> +++ b/repack-midx.c
> @@ -16,25 +16,23 @@ struct midx_snapshot_ref_data {
> int preferred;
> };
>
> -static int midx_snapshot_ref_one(const char *refname UNUSED,
> - const char *referent UNUSED,
> - const struct object_id *oid,
> - int flag UNUSED, void *_data)
> +static int midx_snapshot_ref_one(const struct reference *ref, void *_data)
> {
> struct midx_snapshot_ref_data *data = _data;
> + const struct object_id *maybe_peeled = ref->oid;
> struct object_id peeled;
>
> - if (!peel_iterated_oid(data->repo, oid, &peeled))
> - oid = &peeled;
> + if (!reference_get_peeled_oid(data->repo, ref, &peeled))
> + maybe_peeled = &peeled;
>
> - if (oidset_insert(&data->seen, oid))
> + if (oidset_insert(&data->seen, maybe_peeled))
> return 0; /* already seen */
>
> - if (odb_read_object_info(data->repo->objects, oid, NULL) != OBJ_COMMIT)
> + if (odb_read_object_info(data->repo->objects, maybe_peeled, NULL) != OBJ_COMMIT)
> return 0;
>
> fprintf(data->f->fp, "%s%s\n", data->preferred ? "+" : "",
> - oid_to_hex(oid));
> + oid_to_hex(maybe_peeled));
>
> return 0;
> }
This seems to match the manual resolution I just made, except that I
line wrapped overly wide line ;-).
Queued.
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 00/13] refs: improvements and fixes for peeling tags
2025-10-07 21:00 ` [PATCH 00/13] refs: improvements and fixes for peeling tags Junio C Hamano
@ 2025-10-07 21:49 ` Taylor Blau
0 siblings, 0 replies; 106+ messages in thread
From: Taylor Blau @ 2025-10-07 21:49 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Patrick Steinhardt, git
On Tue, Oct 07, 2025 at 02:00:24PM -0700, Junio C Hamano wrote:
> This seems to match the manual resolution I just made, except that I
> line wrapped overly wide line ;-).
The resolution looks good to me as well. Thanks, both.
Thanks,
Taylor
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 01/13] refs: introduce wrapper struct for `each_ref_fn`
2025-10-07 10:58 ` [PATCH 01/13] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
2025-10-07 18:05 ` Justin Tobler
@ 2025-10-07 21:56 ` Taylor Blau
2025-10-08 15:52 ` shejialuo
2 siblings, 0 replies; 106+ messages in thread
From: Taylor Blau @ 2025-10-07 21:56 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
On Tue, Oct 07, 2025 at 12:58:38PM +0200, Patrick Steinhardt wrote:
> [1]: <ZmarVcF5JjsZx0dl@tanuki>
I agree with Junio's follow-up[2] there that the benefit of dropping
peel_iterated_oid() is worth the one-time blast radius of this change.
I reviewed it as closely as I could, and everything here looks good to
me. I'm excited about the direction that this patch is taking us in :-).
Thanks,
Taylor
[2]: https://lore.kernel.org/git/xmqqy17c3c1v.fsf@gitster.g/
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 02/13] refs: introduce `.ref` field for the base iterator
2025-10-07 10:58 ` [PATCH 02/13] refs: introduce `.ref` field for the base iterator Patrick Steinhardt
2025-10-07 14:24 ` Karthik Nayak
2025-10-07 20:19 ` Justin Tobler
@ 2025-10-07 21:57 ` Taylor Blau
2 siblings, 0 replies; 106+ messages in thread
From: Taylor Blau @ 2025-10-07 21:57 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
On Tue, Oct 07, 2025 at 12:58:39PM +0200, Patrick Steinhardt wrote:
> ---
> refs.c | 8 ++++----
> refs/debug.c | 8 +++-----
> refs/files-backend.c | 47 +++++++++++++++++++++--------------------------
> refs/iterator.c | 39 ++++++++++++---------------------------
> refs/packed-backend.c | 46 +++++++++++++++++++++++-----------------------
> refs/ref-cache.c | 10 +++++-----
> refs/refs-internal.h | 5 +----
> refs/reftable-backend.c | 12 ++++++------
> 8 files changed, 75 insertions(+), 100 deletions(-)
Besides the couple of typos that Justin and Karthik mentioned in their
respective replies to this message, this change makes sense and looks
good to me.
Thanks,
Taylor
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 00/13] refs: improvements and fixes for peeling tags
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
` (13 preceding siblings ...)
2025-10-07 21:00 ` [PATCH 00/13] refs: improvements and fixes for peeling tags Junio C Hamano
@ 2025-10-07 23:01 ` Junio C Hamano
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
` (2 subsequent siblings)
17 siblings, 0 replies; 106+ messages in thread
From: Junio C Hamano @ 2025-10-07 23:01 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Patrick Steinhardt <ps@pks.im> writes:
> Hi,
>
> originally, all I wanted to do was the last patch: a small performance
> optimization that stops parsing objects in git-for-each-ref(1) unless we
> really need to parse them. But that fix cause one specific test to fail,
> and only with the reftable backend. So this led me down the rabbit hole
> of tag peeling, ending up with this patch series.
>
> The series is structured like follows:
>
> - Patches 1 to 7 refactor our codebase so that we don't have the
> `peel_iterated_object()` hack anymore. I just found it hard to
> follow and thought it shouldn't be too hard to get rid of it.
I've only read up to here, and found all of them welcome
improvements. Will continue reading later.
> - Patches 8 and 9 remove infrastructure that we don't need anymore
> after the first couple of patches.
> - Patches 10 to 12 fix a couple of issues with peeled tags that I
> found. The underlying issue is that tags store both the tagged
> object and their type, but this information may not match. We never
> verify the actual object type though when allocating the tagged
> object, so this only blows up much later.
>
> - Patch 13 was my original motivation, a small performance
> optimization.
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 10/13] object: add flag to `peel_object()` to verify object type
2025-10-07 10:58 ` [PATCH 10/13] object: add flag to `peel_object()` to verify object type Patrick Steinhardt
@ 2025-10-08 11:04 ` Kristoffer Haugsbakk
0 siblings, 0 replies; 106+ messages in thread
From: Kristoffer Haugsbakk @ 2025-10-08 11:04 UTC (permalink / raw)
To: Patrick Steinhardt, git
On Tue, Oct 7, 2025, at 12:58, Patrick Steinhardt wrote:
> When peeling a tag to a non-tag object we repeatedly call
> `parse_object()` on the tagged object until we find the first object
> that isn't a tag. While this feels sensible at first, there is a big
> catch here: `parse_object()` doesn't actually verify the type of the
> tagged object.
>
> The relevant code path here eventually ends up in `parse_tag_buffer()`.
> Here, we parset he various fields of the tag, including the "type". Once
s/parset he/parse the/
> we've figured out the type and the tagged object ID, we call one of the
> `lookup_${type}()` functions for whatever type we have found. There is
> two possible outcomes in the successful case:
>
>[snip]
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 13/13] ref-filter: parse objects on demand
2025-10-07 10:58 ` [PATCH 13/13] ref-filter: parse objects on demand Patrick Steinhardt
@ 2025-10-08 11:05 ` Kristoffer Haugsbakk
2025-10-08 13:45 ` Patrick Steinhardt
0 siblings, 1 reply; 106+ messages in thread
From: Kristoffer Haugsbakk @ 2025-10-08 11:05 UTC (permalink / raw)
To: Patrick Steinhardt, git
On Tue, Oct 7, 2025, at 12:58, Patrick Steinhardt wrote:
> When formatting an arbitray object we parse that object regardless of
s/arbitray/arbitrary/
> whether or not we actually need any parsed data. In fact, many of the
> atoms we have don't require any.
>
>[snip]
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 01/13] refs: introduce wrapper struct for `each_ref_fn`
2025-10-07 18:05 ` Justin Tobler
@ 2025-10-08 13:42 ` Patrick Steinhardt
0 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 13:42 UTC (permalink / raw)
To: Justin Tobler; +Cc: git
On Tue, Oct 07, 2025 at 01:05:15PM -0500, Justin Tobler wrote:
> On 25/10/07 12:58PM, Patrick Steinhardt wrote:
> > The `each_ref_fn` callback function type is used across our code base
> > for several different functions that iterate through reference. There's
> > a bunch of callbacks implementing this type, which makes any changes to
> > the callback signature extremely noisy. An example of the required churn
> > is e8207717f1 (refs: add referent to each_ref_fn, 2024-08-09): adding a
> > single argument required us to change 48 files.
> >
> > It was already proposed back then [1] that we might want to introduce a
> > wrapper structure to alleviate the pain going forward. While this of
> > course requires the same kind of global refactoring as just introducing
> > a new parameter, it at least allows us to more change the callback type
> > afterwards by just extending the wrapper structure.
> >
> > One counterargument to this refactoring is that it makes the structure
> > more opaque. While it is obvious which callsites need to be fixed up
> > when we change the function type, it's not obvious anymore once we use
> > a structure. That being said, we only have a handful of sites that
> > actually need to populate this wrapper structure: our ref backends and
> > "refs/iterator.c".
>
> It looks like we also populate `stuct reference` in a couple other spots
> where we invoke the callback explicitly.
Fair, let me mention that.
[snip]
> > /*
> > * The signature for the callback function for the for_each_*()
> > * functions below. The memory pointed to by the refname and oid
> > * arguments is only guaranteed to be valid for the duration of a
> > * single callback invocation.
> > */
>
> Should we update this comment now that these fields are contained the
> wrapper struct?
Yeah, will do.
Patrick
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 02/13] refs: introduce `.ref` field for the base iterator
2025-10-07 14:24 ` Karthik Nayak
@ 2025-10-08 13:44 ` Patrick Steinhardt
2025-10-08 15:03 ` Patrick Steinhardt
0 siblings, 1 reply; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 13:44 UTC (permalink / raw)
To: Karthik Nayak; +Cc: git
On Tue, Oct 07, 2025 at 07:24:01AM -0700, Karthik Nayak wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > The base iterator has a couple of fields that tracks the name, target,
> > object ID and flags for the current reference. Due do this design we
> > have to create a new `struct reference` whenever we want to hand over
> > that reference to the callback function, which is tedious and not very
> > efficient.
> >
> > Convert the structure to instead contain a `stuct reference` as member.
>
> s/stuct/struct
>
> > This member is expected to be populated by the implementations of the
> > iterator and is handed over to the callback directly.
> >
>
> Wouldn't this also add the burden on each backend to ensure we don't
> serve stale data for each '_advance()' call?
>
> Would it make sense to reset this data in `ref_iterator_advance()`?
Yeah, that's true indeed. I was a bit hesitant to do such a change
though because the ref iterator is part of a bunch of hot loops. I'll do
some benchmarking here to figure out whether a memset(3p) would have a
negative impact on performance.
Patrick
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 03/13] refs: refactor reference status flags
2025-10-07 14:27 ` Karthik Nayak
@ 2025-10-08 13:44 ` Patrick Steinhardt
0 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 13:44 UTC (permalink / raw)
To: Karthik Nayak; +Cc: git
On Tue, Oct 07, 2025 at 07:27:55AM -0700, Karthik Nayak wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> > diff --git a/refs.h b/refs.h
> > index 2b24a3d997..8f484e2ffc 100644
> > --- a/refs.h
> > +++ b/refs.h
> > @@ -333,27 +333,28 @@ struct ref_transaction;
> > * stored in ref_iterator::flags. Other bits are for internal use
> > * only:
> > */
> > +enum reference_status {
> > + /* Reference is a symbolic reference. */
> > + REF_ISSYMREF = (1 << 0),
> >
>
> Nit: It would also be nice to rename these flags to
> 'REFERENCE_STATUS_*', since that seems to be the pattern we generally
> follow. Doesn't have to be in your series though :)
Yeah, let's rather punt this to another series. There's hundreds of uses
of these, and I feel like the series is already quite big.
Patrick
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 04/13] refs: expose peeled object ID via the iterator
2025-10-07 14:52 ` Karthik Nayak
@ 2025-10-08 13:45 ` Patrick Steinhardt
2025-10-15 8:28 ` Karthik Nayak
0 siblings, 1 reply; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 13:45 UTC (permalink / raw)
To: Karthik Nayak; +Cc: git
On Tue, Oct 07, 2025 at 07:52:43AM -0700, Karthik Nayak wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > Both the "files" and "reftable" backend are able to store peeled values
> > for tags in the respective formats. This allows for a more efficient
> > lookup of the target object of such a tag without having to manually
> > peel via the object database.
> >
>
> In the 'files' backend, I thought only packed-refs store peeled values?
Yup, peeled values may or may not be available with the "files" backend,
depending on whether or not a reference is packed. But the paragraph
doesn't really contradict this, does it?
Patrick
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 05/13] upload-pack: convert to use `reference_get_peeled_oid()`
2025-10-07 16:18 ` Karthik Nayak
@ 2025-10-08 13:45 ` Patrick Steinhardt
0 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 13:45 UTC (permalink / raw)
To: Karthik Nayak; +Cc: git
On Tue, Oct 07, 2025 at 09:18:52AM -0700, Karthik Nayak wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > The `write_v0_ref()` callback is invoked from two callsites:
> >
>
> Okay so this function does multiple things based on whether the
> capabilities are already advertised or not.
>
> - If not, we propagate the capabilities and set the static variable
> `capabilities` to NULL and also set `data->sent_capabilities = 1;`.
> - We receive `ref->oid` as a zero oid for the hash algorithm being
> used, we convert it to the hex format with `oid_to_hex()`.
> - If already advertised, we simply propagate the reference and if it can
> be peeled, also propagate the peeled reference.
>
> Not for your series: but this feels like the capabilities should be an
> independent function.
Yeah, agreed, it's a bit of a weird design choice that should be cleaned
up eventually.
Patrick
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 13/13] ref-filter: parse objects on demand
2025-10-08 11:05 ` Kristoffer Haugsbakk
@ 2025-10-08 13:45 ` Patrick Steinhardt
0 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 13:45 UTC (permalink / raw)
To: Kristoffer Haugsbakk; +Cc: git
On Wed, Oct 08, 2025 at 01:05:13PM +0200, Kristoffer Haugsbakk wrote:
> On Tue, Oct 7, 2025, at 12:58, Patrick Steinhardt wrote:
> > When formatting an arbitray object we parse that object regardless of
>
> s/arbitray/arbitrary/
Thanks, I've addressed both of your typo fixes locally.
Patrick
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 02/13] refs: introduce `.ref` field for the base iterator
2025-10-08 13:44 ` Patrick Steinhardt
@ 2025-10-08 15:03 ` Patrick Steinhardt
0 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 15:03 UTC (permalink / raw)
To: Karthik Nayak; +Cc: git
On Wed, Oct 08, 2025 at 03:44:16PM +0200, Patrick Steinhardt wrote:
> On Tue, Oct 07, 2025 at 07:24:01AM -0700, Karthik Nayak wrote:
> > Patrick Steinhardt <ps@pks.im> writes:
> >
> > > The base iterator has a couple of fields that tracks the name, target,
> > > object ID and flags for the current reference. Due do this design we
> > > have to create a new `struct reference` whenever we want to hand over
> > > that reference to the callback function, which is tedious and not very
> > > efficient.
> > >
> > > Convert the structure to instead contain a `stuct reference` as member.
> >
> > s/stuct/struct
> >
> > > This member is expected to be populated by the implementations of the
> > > iterator and is handed over to the callback directly.
> > >
> >
> > Wouldn't this also add the burden on each backend to ensure we don't
> > serve stale data for each '_advance()' call?
> >
> > Would it make sense to reset this data in `ref_iterator_advance()`?
>
> Yeah, that's true indeed. I was a bit hesitant to do such a change
> though because the ref iterator is part of a bunch of hot loops. I'll do
> some benchmarking here to figure out whether a memset(3p) would have a
> negative impact on performance.
Okay, I've measured this and it doesn't make any difference. I'll add
another patch to do this.
Patrick
^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v2 00/14] refs: improvements and fixes for peeling tags
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
` (14 preceding siblings ...)
2025-10-07 23:01 ` Junio C Hamano
@ 2025-10-08 15:50 ` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 01/14] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
` (14 more replies)
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
17 siblings, 15 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 15:50 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
Hi,
originally, all I wanted to do was the last patch: a small performance
optimization that stops parsing objects in git-for-each-ref(1) unless we
really need to parse them. But that fix cause one specific test to fail,
and only with the reftable backend. So this led me down the rabbit hole
of tag peeling, ending up with this patch series.
The series is structured like follows:
- Patches 1 to 8 refactor our codebase so that we don't have the
`peel_iterated_object()` hack anymore. I just found it hard to
follow and thought it shouldn't be too hard to get rid of it.
- Patches 9 and 10 remove infrastructure that we don't need anymore
after the first couple of patches.
- Patches 11 to 13 fix a couple of issues with peeled tags that I
found. The underlying issue is that tags store both the tagged
object and their type, but this information may not match. We never
verify the actual object type though when allocating the tagged
object, so this only blows up much later.
- Patch 14 was my original motivation, a small performance
optimization.
I'm not particularly fond of the patches 11 to 13. It feels more like
playing whack-a-mole, and I very much assume that there still are edge
cases where we should properly verify the tagged object type. But
changing it in `parse_tag_buffer()` itself causes a bunch of tests to
fail where we intentionally create such corrupted tags. So I didn't
really dare to touch that part, to be honest.
If anybody has suggestions for an alternative approach I'd be very open
to it.
The topic is built on top of 45547b60ac (Merge branch 'master' of
https://github.com/j6t/gitk, 2025-10-05). There is a merge conflict with
tb/incremental-midx-part-3.1, which moves code from "builtin/repack.c"
into "repack-*.c".
Changes in v2:
- A couple of improvements to commit messages.
- A new commit that ensures that `struct ref_iterator::ref` is always
zeroed out to protect against stale state.
- Link to v1: https://lore.kernel.org/r/20251007-b4-pks-ref-filter-skip-parsing-objects-v1-0-916cc7c6886b@pks.im
Thanks!
Patrick
---
Patrick Steinhardt (14):
refs: introduce wrapper struct for `each_ref_fn`
refs: introduce `.ref` field for the base iterator
refs: fully reset `struct ref_iterator::ref` on iteration
refs: refactor reference status flags
refs: expose peeled object ID via the iterator
upload-pack: convert to use `reference_get_peeled_oid()`
ref-filter: propagate peeled object ID
builtin/show-ref: convert to use `reference_get_peeled_oid()`
refs: drop `current_ref_iter` hack
refs: drop infrastructure to peel via iterators
object: add flag to `peel_object()` to verify object type
refs: don't store peeled object IDs for invalid tags
ref-filter: detect broken tags when dereferencing them
ref-filter: parse objects on demand
bisect.c | 24 ++---
builtin/bisect.c | 17 +---
builtin/checkout.c | 6 +-
builtin/describe.c | 18 ++--
builtin/fetch.c | 13 +--
builtin/fsck.c | 33 +++---
builtin/gc.c | 15 ++-
builtin/ls-remote.c | 2 +-
builtin/name-rev.c | 17 ++--
builtin/pack-objects.c | 28 +++---
builtin/receive-pack.c | 13 ++-
builtin/remote.c | 44 ++++----
builtin/repack.c | 16 ++-
builtin/replace.c | 21 ++--
builtin/rev-parse.c | 12 +--
builtin/show-branch.c | 35 +++----
builtin/show-ref.c | 50 ++++-----
builtin/submodule--helper.c | 10 +-
builtin/tag.c | 2 +-
builtin/verify-tag.c | 2 +-
builtin/worktree.c | 6 +-
commit-graph.c | 14 ++-
delta-islands.c | 9 +-
fetch-pack.c | 16 +--
help.c | 10 +-
http-backend.c | 20 ++--
log-tree.c | 24 ++---
ls-refs.c | 36 ++++---
midx-write.c | 17 ++--
negotiator/default.c | 7 +-
negotiator/skipping.c | 7 +-
notes.c | 8 +-
object-name.c | 10 +-
object.c | 20 +++-
object.h | 15 ++-
pseudo-merge.c | 21 ++--
reachable.c | 9 +-
ref-filter.c | 239 ++++++++++++++++++++++++++++++--------------
ref-filter.h | 5 +-
reflog.c | 9 +-
refs.c | 85 +++++++++-------
refs.h | 88 ++++++++++------
refs/debug.c | 17 +---
refs/files-backend.c | 71 +++++--------
refs/iterator.c | 73 +++-----------
refs/packed-backend.c | 71 +++++--------
refs/ref-cache.c | 18 +---
refs/refs-internal.h | 25 +----
refs/reftable-backend.c | 47 +++------
remote.c | 27 +++--
replace-object.c | 16 ++-
revision.c | 12 +--
server-info.c | 12 +--
shallow.c | 16 +--
submodule.c | 12 +--
t/for-each-ref-tests.sh | 4 +-
t/helper/test-reach.c | 2 +-
t/helper/test-ref-store.c | 5 +-
t/pack-refs-tests.sh | 32 ++++++
t/t0610-reftable-basics.sh | 28 ++++++
tag.c | 12 ---
tag.h | 1 -
upload-pack.c | 49 ++++-----
walker.c | 8 +-
worktree.c | 11 +-
65 files changed, 791 insertions(+), 831 deletions(-)
Range-diff versus v1:
1: 4f4de68657 ! 1: 3d0c5110b3 refs: introduce wrapper struct for `each_ref_fn`
@@ Commit message
more opaque. While it is obvious which callsites need to be fixed up
when we change the function type, it's not obvious anymore once we use
a structure. That being said, we only have a handful of sites that
- actually need to populate this wrapper structure: our ref backends and
- "refs/iterator.c".
+ actually need to populate this wrapper structure: our ref backends,
+ "refs/iterator.c" as well as very few sites that invoke the iterator
+ callback functions directly.
Introduce this wrapper structure so that we can adapt the iterator
interfaces more readily.
@@ refs.h: struct ref_transaction;
+
/*
* The signature for the callback function for the for_each_*()
- * functions below. The memory pointed to by the refname and oid
- * arguments is only guaranteed to be valid for the duration of a
+- * functions below. The memory pointed to by the refname and oid
+- * arguments is only guaranteed to be valid for the duration of a
++ * functions below. The memory pointed to by the `struct reference`
++ * argument is only guaranteed to be valid for the duration of a
* single callback invocation.
*/
-typedef int each_ref_fn(const char *refname, const char *referent,
2: e90c26f12c ! 2: 412818b8d1 refs: introduce `.ref` field for the base iterator
@@ Commit message
refs: introduce `.ref` field for the base iterator
The base iterator has a couple of fields that tracks the name, target,
- object ID and flags for the current reference. Due do this design we
+ object ID and flags for the current reference. Due to this design we
have to create a new `struct reference` whenever we want to hand over
that reference to the callback function, which is tedious and not very
efficient.
- Convert the structure to instead contain a `stuct reference` as member.
+ Convert the structure to instead contain a `struct reference` as member.
This member is expected to be populated by the implementations of the
iterator and is handed over to the callback directly.
+ While at it, simplify `should_pack_ref()` to take a `struct reference`
+ directly instead of passing its respective fields.
+
Signed-off-by: Patrick Steinhardt <ps@pks.im>
## refs.c ##
-: ---------- > 3: 231adc4960 refs: fully reset `struct ref_iterator::ref` on iteration
3: bc0432dc35 = 4: 1d2189e7a9 refs: refactor reference status flags
4: aba078d59c ! 5: 1f975f1079 refs: expose peeled object ID via the iterator
@@ refs.h: struct reference {
+
/*
* The signature for the callback function for the for_each_*()
- * functions below. The memory pointed to by the refname and oid
+ * functions below. The memory pointed to by the `struct reference`
## refs/packed-backend.c ##
@@ refs/packed-backend.c: static int next_record(struct packed_ref_iterator *iter)
- if ((iter->base.ref.flags & REF_ISBROKEN)) {
- oidclr(&iter->peeled, iter->repo->hash_algo);
iter->base.ref.flags &= ~REF_KNOWS_PEELED;
-+ iter->base.ref.peeled_oid = NULL;
} else {
iter->base.ref.flags |= REF_KNOWS_PEELED;
+ iter->base.ref.peeled_oid = &iter->peeled;
}
} else {
oidclr(&iter->peeled, iter->repo->hash_algo);
-+ iter->base.ref.peeled_oid = NULL;
- }
-
- return ITER_OK;
-
- ## refs/ref-cache.c ##
-@@ refs/ref-cache.c: static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
- iter->base.ref.name = entry->name;
- iter->base.ref.target = entry->u.value.referent;
- iter->base.ref.oid = &entry->u.value.oid;
-+ iter->base.ref.peeled_oid = NULL;
- iter->base.ref.flags = entry->flag;
- return ITER_OK;
- }
## refs/reftable-backend.c ##
@@ refs/reftable-backend.c: struct reftable_ref_iterator {
@@ refs/reftable-backend.c: static int reftable_ref_iterator_advance(struct ref_ite
iter->base.ref.oid = &iter->oid;
+ if (iter->ref.value_type == REFTABLE_REF_VAL2)
+ iter->base.ref.peeled_oid = &iter->peeled_oid;
-+ else
-+ iter->base.ref.peeled_oid = NULL;
iter->base.ref.flags = flags;
break;
5: 664775be02 ! 6: 8f3b757726 upload-pack: convert to use `reference_get_peeled_oid()`
@@ Commit message
The `write_v0_ref()` callback is invoked from two callsites:
- Once via `send_ref()` which is a callback passed to
- `for_each_namespaced_ref_1()`.
+ `for_each_namespaced_ref_1()` and `refs_head_ref_namespaced()`.
- Once manually to announce capabilities.
6: e0c95a3df2 = 7: 603213919f ref-filter: propagate peeled object ID
7: 9ef21d25d1 = 8: 8d3cae65f8 builtin/show-ref: convert to use `reference_get_peeled_oid()`
8: 2d32a2410d = 9: 35226e7a7d refs: drop `current_ref_iter` hack
9: a9fbbe6b1a = 10: 2e73f3b227 refs: drop infrastructure to peel via iterators
10: 68b3a85ade ! 11: ade4d5875a object: add flag to `peel_object()` to verify object type
@@ Commit message
tagged object.
The relevant code path here eventually ends up in `parse_tag_buffer()`.
- Here, we parset he various fields of the tag, including the "type". Once
+ Here, we parse the various fields of the tag, including the "type". Once
we've figured out the type and the tagged object ID, we call one of the
`lookup_${type}()` functions for whatever type we have found. There is
two possible outcomes in the successful case:
11: 80ec80883e = 12: c1ccb8e6e1 refs: don't store peeled object IDs for invalid tags
12: 8056c68337 = 13: e5db08d3fa ref-filter: detect broken tags when dereferencing them
13: 40cc049093 ! 14: 617d03d8c1 ref-filter: parse objects on demand
@@ Metadata
## Commit message ##
ref-filter: parse objects on demand
- When formatting an arbitray object we parse that object regardless of
+ When formatting an arbitrary object we parse that object regardless of
whether or not we actually need any parsed data. In fact, many of the
atoms we have don't require any.
---
base-commit: 45547b60aca32b45d2f1ef93462cf9df28637c13
change-id: 20250918-b4-pks-ref-filter-skip-parsing-objects-f0d1f6af4a9f
^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v2 01/14] refs: introduce wrapper struct for `each_ref_fn`
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
@ 2025-10-08 15:50 ` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 02/14] refs: introduce `.ref` field for the base iterator Patrick Steinhardt
` (13 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 15:50 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
The `each_ref_fn` callback function type is used across our code base
for several different functions that iterate through reference. There's
a bunch of callbacks implementing this type, which makes any changes to
the callback signature extremely noisy. An example of the required churn
is e8207717f1 (refs: add referent to each_ref_fn, 2024-08-09): adding a
single argument required us to change 48 files.
It was already proposed back then [1] that we might want to introduce a
wrapper structure to alleviate the pain going forward. While this of
course requires the same kind of global refactoring as just introducing
a new parameter, it at least allows us to more change the callback type
afterwards by just extending the wrapper structure.
One counterargument to this refactoring is that it makes the structure
more opaque. While it is obvious which callsites need to be fixed up
when we change the function type, it's not obvious anymore once we use
a structure. That being said, we only have a handful of sites that
actually need to populate this wrapper structure: our ref backends,
"refs/iterator.c" as well as very few sites that invoke the iterator
callback functions directly.
Introduce this wrapper structure so that we can adapt the iterator
interfaces more readily.
[1]: <ZmarVcF5JjsZx0dl@tanuki>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
bisect.c | 24 +++++++---------
builtin/bisect.c | 17 ++++--------
builtin/checkout.c | 6 ++--
builtin/describe.c | 18 ++++++------
builtin/fetch.c | 13 +++------
builtin/fsck.c | 33 ++++++++++++----------
builtin/gc.c | 15 ++++------
builtin/name-rev.c | 17 ++++++------
builtin/pack-objects.c | 27 ++++++++----------
builtin/receive-pack.c | 13 ++++-----
builtin/remote.c | 44 +++++++++++++----------------
builtin/repack.c | 16 +++++------
builtin/replace.c | 21 ++++++--------
builtin/rev-parse.c | 12 ++++----
builtin/show-branch.c | 35 +++++++++++------------
builtin/show-ref.c | 20 ++++++--------
builtin/submodule--helper.c | 10 ++-----
builtin/worktree.c | 6 +---
commit-graph.c | 14 ++++------
delta-islands.c | 9 +++---
fetch-pack.c | 16 +++--------
help.c | 10 +++----
http-backend.c | 20 ++++++--------
log-tree.c | 24 ++++++++--------
ls-refs.c | 36 ++++++++++++++----------
midx-write.c | 17 ++++++------
negotiator/default.c | 7 ++---
negotiator/skipping.c | 7 ++---
notes.c | 8 ++----
object-name.c | 10 +++----
pseudo-merge.c | 21 ++++++--------
reachable.c | 9 +++---
ref-filter.c | 24 ++++++++--------
reflog.c | 9 ++----
refs.c | 67 +++++++++++++++++++++++++--------------------
refs.h | 26 +++++++++++++++---
refs/files-backend.c | 7 ++---
refs/iterator.c | 9 +++++-
remote.c | 27 ++++++++----------
replace-object.c | 16 ++++-------
revision.c | 12 ++++----
server-info.c | 12 ++++----
shallow.c | 16 +++--------
submodule.c | 12 ++------
t/helper/test-ref-store.c | 5 ++--
upload-pack.c | 29 +++++++++-----------
walker.c | 8 ++----
worktree.c | 11 ++++++--
48 files changed, 389 insertions(+), 456 deletions(-)
diff --git a/bisect.c b/bisect.c
index a6dc76b15c9..326b59c0dc7 100644
--- a/bisect.c
+++ b/bisect.c
@@ -450,21 +450,20 @@ void find_bisection(struct commit_list **commit_list, int *reaches,
clear_commit_weight(&commit_weight);
}
-static int register_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb_data UNUSED)
+static int register_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct strbuf good_prefix = STRBUF_INIT;
strbuf_addstr(&good_prefix, term_good);
strbuf_addstr(&good_prefix, "-");
- if (!strcmp(refname, term_bad)) {
+ if (!strcmp(ref->name, term_bad)) {
free(current_bad_oid);
current_bad_oid = xmalloc(sizeof(*current_bad_oid));
- oidcpy(current_bad_oid, oid);
- } else if (starts_with(refname, good_prefix.buf)) {
- oid_array_append(&good_revs, oid);
- } else if (starts_with(refname, "skip-")) {
- oid_array_append(&skipped_revs, oid);
+ oidcpy(current_bad_oid, ref->oid);
+ } else if (starts_with(ref->name, good_prefix.buf)) {
+ oid_array_append(&good_revs, ref->oid);
+ } else if (starts_with(ref->name, "skip-")) {
+ oid_array_append(&skipped_revs, ref->oid);
}
strbuf_release(&good_prefix);
@@ -1178,14 +1177,11 @@ int estimate_bisect_steps(int all)
return (e < 3 * x) ? n : n - 1;
}
-static int mark_for_removal(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cb_data)
+static int mark_for_removal(const struct reference *ref, void *cb_data)
{
struct string_list *refs = cb_data;
- char *ref = xstrfmt("refs/bisect%s", refname);
- string_list_append(refs, ref);
+ char *bisect_ref = xstrfmt("refs/bisect%s", ref->name);
+ string_list_append(refs, bisect_ref);
return 0;
}
diff --git a/builtin/bisect.c b/builtin/bisect.c
index 8b8d870cd1e..5b2024be62d 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -358,10 +358,7 @@ static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)
return 0;
}
-static int inc_nr(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cb_data)
+static int inc_nr(const struct reference *ref UNUSED, void *cb_data)
{
unsigned int *nr = (unsigned int *)cb_data;
(*nr)++;
@@ -549,12 +546,11 @@ static int bisect_append_log_quoted(const char **argv)
return res;
}
-static int add_bisect_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb)
+static int add_bisect_ref(const struct reference *ref, void *cb)
{
struct add_bisect_ref_data *data = cb;
- add_pending_oid(data->revs, refname, oid, data->object_flags);
+ add_pending_oid(data->revs, ref->name, ref->oid, data->object_flags);
return 0;
}
@@ -1165,12 +1161,9 @@ static int bisect_visualize(struct bisect_terms *terms, int argc,
return run_command(&cmd);
}
-static int get_first_good(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int get_first_good(const struct reference *ref, void *cb_data)
{
- oidcpy(cb_data, oid);
+ oidcpy(cb_data, ref->oid);
return 1;
}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f9453473fe2..66b69df6e67 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1063,11 +1063,9 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
report_tracking(new_branch_info);
}
-static int add_pending_uninteresting_ref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int add_pending_uninteresting_ref(const struct reference *ref, void *cb_data)
{
- add_pending_oid(cb_data, refname, oid, UNINTERESTING);
+ add_pending_oid(cb_data, ref->name, ref->oid, UNINTERESTING);
return 0;
}
diff --git a/builtin/describe.c b/builtin/describe.c
index ffaf8d9f0aa..79545350443 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -154,20 +154,19 @@ static void add_to_known_names(const char *path,
}
}
-static int get_name(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int get_name(const struct reference *ref, void *cb_data UNUSED)
{
int is_tag = 0;
struct object_id peeled;
int is_annotated, prio;
const char *path_to_match = NULL;
- if (skip_prefix(path, "refs/tags/", &path_to_match)) {
+ if (skip_prefix(ref->name, "refs/tags/", &path_to_match)) {
is_tag = 1;
} else if (all) {
if ((exclude_patterns.nr || patterns.nr) &&
- !skip_prefix(path, "refs/heads/", &path_to_match) &&
- !skip_prefix(path, "refs/remotes/", &path_to_match)) {
+ !skip_prefix(ref->name, "refs/heads/", &path_to_match) &&
+ !skip_prefix(ref->name, "refs/remotes/", &path_to_match)) {
/* Only accept reference of known type if there are match/exclude patterns */
return 0;
}
@@ -209,10 +208,10 @@ static int get_name(const char *path, const char *referent UNUSED, const struct
}
/* Is it annotated? */
- if (!peel_iterated_oid(the_repository, oid, &peeled)) {
- is_annotated = !oideq(oid, &peeled);
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled)) {
+ is_annotated = !oideq(ref->oid, &peeled);
} else {
- oidcpy(&peeled, oid);
+ oidcpy(&peeled, ref->oid);
is_annotated = 0;
}
@@ -229,7 +228,8 @@ static int get_name(const char *path, const char *referent UNUSED, const struct
else
prio = 0;
- add_to_known_names(all ? path + 5 : path + 10, &peeled, prio, oid);
+ add_to_known_names(all ? ref->name + 5 : ref->name + 10,
+ &peeled, prio, ref->oid);
return 0;
}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index c7ff3480fb1..7052e6ff215 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -289,13 +289,11 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,
return ent;
}
-static int add_one_refname(const char *refname, const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *cbdata)
+static int add_one_refname(const struct reference *ref, void *cbdata)
{
struct hashmap *refname_map = cbdata;
- (void) refname_hash_add(refname_map, refname, oid);
+ (void) refname_hash_add(refname_map, ref->name, ref->oid);
return 0;
}
@@ -1416,14 +1414,11 @@ static void set_option(struct transport *transport, const char *name, const char
}
-static int add_oid(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int add_oid(const struct reference *ref, void *cb_data)
{
struct oid_array *oids = cb_data;
- oid_array_append(oids, oid);
+ oid_array_append(oids, ref->oid);
return 0;
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index d2eb9d4fbe9..2b8fa22cccb 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -530,14 +530,13 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)
return 0;
}
-static int fsck_handle_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object *obj;
- obj = parse_object(the_repository, oid);
+ obj = parse_object(the_repository, ref->oid);
if (!obj) {
- if (is_promisor_object(the_repository, oid)) {
+ if (is_promisor_object(the_repository, ref->oid)) {
/*
* Increment default_refs anyway, because this is a
* valid ref.
@@ -546,19 +545,19 @@ static int fsck_handle_ref(const char *refname, const char *referent UNUSED, con
return 0;
}
error(_("%s: invalid sha1 pointer %s"),
- refname, oid_to_hex(oid));
+ ref->name, oid_to_hex(ref->oid));
errors_found |= ERROR_REACHABLE;
/* We'll continue with the rest despite the error.. */
return 0;
}
- if (obj->type != OBJ_COMMIT && is_branch(refname)) {
- error(_("%s: not a commit"), refname);
+ if (obj->type != OBJ_COMMIT && is_branch(ref->name)) {
+ error(_("%s: not a commit"), ref->name);
errors_found |= ERROR_REFS;
}
default_refs++;
obj->flags |= USED;
fsck_put_object_name(&fsck_walk_options,
- oid, "%s", refname);
+ ref->oid, "%s", ref->name);
mark_object_reachable(obj);
return 0;
@@ -580,13 +579,19 @@ static void get_default_heads(void)
worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
- struct strbuf ref = STRBUF_INIT;
+ struct strbuf refname = STRBUF_INIT;
- strbuf_worktree_ref(wt, &ref, "HEAD");
- fsck_head_link(ref.buf, &head_points_at, &head_oid);
- if (head_points_at && !is_null_oid(&head_oid))
- fsck_handle_ref(ref.buf, NULL, &head_oid, 0, NULL);
- strbuf_release(&ref);
+ strbuf_worktree_ref(wt, &refname, "HEAD");
+ fsck_head_link(refname.buf, &head_points_at, &head_oid);
+ if (head_points_at && !is_null_oid(&head_oid)) {
+ struct reference ref = {
+ .name = refname.buf,
+ .oid = &head_oid,
+ };
+
+ fsck_handle_ref(&ref, NULL);
+ }
+ strbuf_release(&refname);
if (include_reflogs)
refs_for_each_reflog(get_worktree_ref_store(wt),
diff --git a/builtin/gc.c b/builtin/gc.c
index 03ae4926b20..a104c1cb782 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -1098,24 +1098,21 @@ struct cg_auto_data {
int limit;
};
-static int dfs_on_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int dfs_on_ref(const struct reference *ref, void *cb_data)
{
struct cg_auto_data *data = (struct cg_auto_data *)cb_data;
int result = 0;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct commit_list *stack = NULL;
struct commit *commit;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
- if (odb_read_object_info(the_repository->objects, oid, NULL) != OBJ_COMMIT)
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ maybe_peeled = &peeled;
+ if (odb_read_object_info(the_repository->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
- commit = lookup_commit(the_repository, oid);
+ commit = lookup_commit(the_repository, maybe_peeled);
if (!commit)
return 0;
if (repo_parse_commit(the_repository, commit) ||
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 74512e54a38..615f7d1aae4 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -339,10 +339,9 @@ static int cmp_by_tag_and_age(const void *a_, const void *b_)
return a->taggerdate != b->taggerdate;
}
-static int name_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int name_ref(const struct reference *ref, void *cb_data)
{
- struct object *o = parse_object(the_repository, oid);
+ struct object *o = parse_object(the_repository, ref->oid);
struct name_ref_data *data = cb_data;
int can_abbreviate_output = data->tags_only && data->name_only;
int deref = 0;
@@ -350,14 +349,14 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
struct commit *commit = NULL;
timestamp_t taggerdate = TIME_MAX;
- if (data->tags_only && !starts_with(path, "refs/tags/"))
+ if (data->tags_only && !starts_with(ref->name, "refs/tags/"))
return 0;
if (data->exclude_filters.nr) {
struct string_list_item *item;
for_each_string_list_item(item, &data->exclude_filters) {
- if (subpath_matches(path, item->string) >= 0)
+ if (subpath_matches(ref->name, item->string) >= 0)
return 0;
}
}
@@ -378,7 +377,7 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
* shouldn't stop when seeing 'refs/tags/v1.4' matches
* 'refs/tags/v*'. We should show it as 'v1.4'.
*/
- switch (subpath_matches(path, item->string)) {
+ switch (subpath_matches(ref->name, item->string)) {
case -1: /* did not match */
break;
case 0: /* matched fully */
@@ -406,13 +405,13 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
}
if (o && o->type == OBJ_COMMIT) {
commit = (struct commit *)o;
- from_tag = starts_with(path, "refs/tags/");
+ from_tag = starts_with(ref->name, "refs/tags/");
if (taggerdate == TIME_MAX)
taggerdate = commit->date;
}
- add_to_tip_table(oid, path, can_abbreviate_output, commit, taggerdate,
- from_tag, deref);
+ add_to_tip_table(ref->oid, ref->name, can_abbreviate_output,
+ commit, taggerdate, from_tag, deref);
return 0;
}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 5856b5f6bfb..288ba199811 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -831,15 +831,14 @@ static enum write_one_status write_one(struct hashfile *f,
return WRITE_ONE_WRITTEN;
}
-static int mark_tagged(const char *path UNUSED, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int mark_tagged(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
- struct object_entry *entry = packlist_find(&to_pack, oid);
+ struct object_entry *entry = packlist_find(&to_pack, ref->oid);
if (entry)
entry->tagged = 1;
- if (!peel_iterated_oid(the_repository, oid, &peeled)) {
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled)) {
entry = packlist_find(&to_pack, &peeled);
if (entry)
entry->tagged = 1;
@@ -3306,13 +3305,12 @@ static void add_tag_chain(const struct object_id *oid)
}
}
-static int add_ref_tag(const char *tag UNUSED, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int add_ref_tag(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, oid, &peeled) && obj_is_packed(&peeled))
- add_tag_chain(oid);
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled) && obj_is_packed(&peeled))
+ add_tag_chain(ref->oid);
return 0;
}
@@ -4528,19 +4526,16 @@ static void record_recent_commit(struct commit *commit, void *data UNUSED)
oid_array_append(&recent_objects, &commit->object.oid);
}
-static int mark_bitmap_preferred_tip(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *data UNUSED)
+static int mark_bitmap_preferred_tip(const struct reference *ref, void *data UNUSED)
{
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct object *object;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ maybe_peeled = &peeled;
- object = parse_object_or_die(the_repository, oid, refname);
+ object = parse_object_or_die(the_repository, maybe_peeled, ref->name);
if (object->type == OBJ_COMMIT)
object->flags |= NEEDS_BITMAP;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 1113137a6f0..2a45c0867e3 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -305,13 +305,12 @@ static void show_ref(const char *path, const struct object_id *oid)
}
}
-static int show_ref_cb(const char *path_full, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *data)
+static int show_ref_cb(const struct reference *ref, void *data)
{
struct oidset *seen = data;
- const char *path = strip_namespace(path_full);
+ const char *path = strip_namespace(ref->name);
- if (ref_is_hidden(path, path_full, &hidden_refs))
+ if (ref_is_hidden(path, ref->name, &hidden_refs))
return 0;
/*
@@ -320,13 +319,13 @@ static int show_ref_cb(const char *path_full, const char *referent UNUSED, const
* transfer but will otherwise ignore them.
*/
if (!path) {
- if (oidset_insert(seen, oid))
+ if (oidset_insert(seen, ref->oid))
return 0;
path = ".have";
} else {
- oidset_insert(seen, oid);
+ oidset_insert(seen, ref->oid);
}
- show_ref(path, oid);
+ show_ref(path, ref->oid);
return 0;
}
diff --git a/builtin/remote.c b/builtin/remote.c
index 8a7ed4299a4..7ffc14ba157 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -570,17 +570,14 @@ struct branches_for_remote {
struct known_remotes *keep;
};
-static int add_branch_for_removal(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED, void *cb_data)
+static int add_branch_for_removal(const struct reference *ref, void *cb_data)
{
struct branches_for_remote *branches = cb_data;
struct refspec_item refspec;
struct known_remote *kr;
memset(&refspec, 0, sizeof(refspec));
- refspec.dst = (char *)refname;
+ refspec.dst = (char *)ref->name;
if (remote_find_tracking(branches->remote, &refspec))
return 0;
free(refspec.src);
@@ -588,7 +585,7 @@ static int add_branch_for_removal(const char *refname,
/* don't delete a branch if another remote also uses it */
for (kr = branches->keep->list; kr; kr = kr->next) {
memset(&refspec, 0, sizeof(refspec));
- refspec.dst = (char *)refname;
+ refspec.dst = (char *)ref->name;
if (!remote_find_tracking(kr->remote, &refspec)) {
free(refspec.src);
return 0;
@@ -596,16 +593,16 @@ static int add_branch_for_removal(const char *refname,
}
/* don't delete non-remote-tracking refs */
- if (!starts_with(refname, "refs/remotes/")) {
+ if (!starts_with(ref->name, "refs/remotes/")) {
/* advise user how to delete local branches */
- if (starts_with(refname, "refs/heads/"))
+ if (starts_with(ref->name, "refs/heads/"))
string_list_append(branches->skipped,
- abbrev_branch(refname));
+ abbrev_branch(ref->name));
/* silently skip over other non-remote refs */
return 0;
}
- string_list_append(branches->branches, refname);
+ string_list_append(branches->branches, ref->name);
return 0;
}
@@ -713,18 +710,18 @@ static int rename_one_reflog(const char *old_refname,
return error;
}
-static int rename_one_ref(const char *old_refname, const char *referent,
- const struct object_id *oid,
- int flags, void *cb_data)
+static int rename_one_ref(const struct reference *ref, void *cb_data)
{
struct strbuf new_referent = STRBUF_INIT;
struct strbuf new_refname = STRBUF_INIT;
struct rename_info *rename = cb_data;
+ const struct object_id *oid = ref->oid;
+ const char *referent = ref->target;
int error;
- compute_renamed_ref(rename, old_refname, &new_refname);
+ compute_renamed_ref(rename, ref->name, &new_refname);
- if (flags & REF_ISSYMREF) {
+ if (ref->flags & REF_ISSYMREF) {
/*
* Stupidly enough `referent` is not pointing to the immediate
* target of a symref, but it's the recursively resolved value.
@@ -732,25 +729,25 @@ static int rename_one_ref(const char *old_refname, const char *referent,
* unborn symrefs don't have any value for the `referent` at all.
*/
referent = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- old_refname, RESOLVE_REF_NO_RECURSE,
+ ref->name, RESOLVE_REF_NO_RECURSE,
NULL, NULL);
compute_renamed_ref(rename, referent, &new_referent);
oid = NULL;
}
- error = ref_transaction_delete(rename->transaction, old_refname,
+ error = ref_transaction_delete(rename->transaction, ref->name,
oid, referent, REF_NO_DEREF, NULL, rename->err);
if (error < 0)
goto out;
error = ref_transaction_update(rename->transaction, new_refname.buf, oid, null_oid(the_hash_algo),
- (flags & REF_ISSYMREF) ? new_referent.buf : NULL, NULL,
+ (ref->flags & REF_ISSYMREF) ? new_referent.buf : NULL, NULL,
REF_SKIP_CREATE_REFLOG | REF_NO_DEREF | REF_SKIP_OID_VERIFICATION,
NULL, rename->err);
if (error < 0)
goto out;
- error = rename_one_reflog(old_refname, oid, rename);
+ error = rename_one_reflog(ref->name, oid, rename);
if (error < 0)
goto out;
@@ -1125,19 +1122,16 @@ static void free_remote_ref_states(struct ref_states *states)
string_list_clear_func(&states->push, clear_push_info);
}
-static int append_ref_to_tracked_list(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags, void *cb_data)
+static int append_ref_to_tracked_list(const struct reference *ref, void *cb_data)
{
struct ref_states *states = cb_data;
struct refspec_item refspec;
- if (flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
return 0;
memset(&refspec, 0, sizeof(refspec));
- refspec.dst = (char *)refname;
+ refspec.dst = (char *)ref->name;
if (!remote_find_tracking(states->remote, &refspec)) {
string_list_append(&states->tracked, abbrev_branch(refspec.src));
free(refspec.src);
diff --git a/builtin/repack.c b/builtin/repack.c
index c490a51e919..893c105c81f 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -773,25 +773,23 @@ struct midx_snapshot_ref_data {
int preferred;
};
-static int midx_snapshot_ref_one(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *_data)
+static int midx_snapshot_ref_one(const struct reference *ref, void *_data)
{
struct midx_snapshot_ref_data *data = _data;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ maybe_peeled = &peeled;
- if (oidset_insert(&data->seen, oid))
+ if (oidset_insert(&data->seen, maybe_peeled))
return 0; /* already seen */
- if (odb_read_object_info(the_repository->objects, oid, NULL) != OBJ_COMMIT)
+ if (odb_read_object_info(the_repository->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
fprintf(data->f->fp, "%s%s\n", data->preferred ? "+" : "",
- oid_to_hex(oid));
+ oid_to_hex(maybe_peeled));
return 0;
}
diff --git a/builtin/replace.c b/builtin/replace.c
index 900b560a77d..4c62c5ab58b 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -47,30 +47,27 @@ struct show_data {
enum replace_format format;
};
-static int show_reference(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int show_reference(const struct reference *ref, void *cb_data)
{
struct show_data *data = cb_data;
- if (!wildmatch(data->pattern, refname, 0)) {
+ if (!wildmatch(data->pattern, ref->name, 0)) {
if (data->format == REPLACE_FORMAT_SHORT)
- printf("%s\n", refname);
+ printf("%s\n", ref->name);
else if (data->format == REPLACE_FORMAT_MEDIUM)
- printf("%s -> %s\n", refname, oid_to_hex(oid));
+ printf("%s -> %s\n", ref->name, oid_to_hex(ref->oid));
else { /* data->format == REPLACE_FORMAT_LONG */
struct object_id object;
enum object_type obj_type, repl_type;
- if (repo_get_oid(data->repo, refname, &object))
- return error(_("failed to resolve '%s' as a valid ref"), refname);
+ if (repo_get_oid(data->repo, ref->name, &object))
+ return error(_("failed to resolve '%s' as a valid ref"), ref->name);
obj_type = odb_read_object_info(data->repo->objects, &object, NULL);
- repl_type = odb_read_object_info(data->repo->objects, oid, NULL);
+ repl_type = odb_read_object_info(data->repo->objects, ref->oid, NULL);
- printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
- oid_to_hex(oid), type_name(repl_type));
+ printf("%s (%s) -> %s (%s)\n", ref->name, type_name(obj_type),
+ oid_to_hex(ref->oid), type_name(repl_type));
}
}
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 9da92b990d0..3578591b4f2 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -217,19 +217,17 @@ static int show_default(void)
return 0;
}
-static int show_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int show_reference(const struct reference *ref, void *cb_data UNUSED)
{
- if (ref_excluded(&ref_excludes, refname))
+ if (ref_excluded(&ref_excludes, ref->name))
return 0;
- show_rev(NORMAL, oid, refname);
+ show_rev(NORMAL, ref->oid, ref->name);
return 0;
}
-static int anti_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int anti_reference(const struct reference *ref, void *cb_data UNUSED)
{
- show_rev(REVERSED, oid, refname);
+ show_rev(REVERSED, ref->oid, ref->name);
return 0;
}
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 441babf2e35..10475a6b5ed 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -413,34 +413,32 @@ static int append_ref(const char *refname, const struct object_id *oid,
return 0;
}
-static int append_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int append_head_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id tmp;
int ofs = 11;
- if (!starts_with(refname, "refs/heads/"))
+ if (!starts_with(ref->name, "refs/heads/"))
return 0;
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
+ if (repo_get_oid(the_repository, ref->name + ofs, &tmp) || !oideq(&tmp, ref->oid))
ofs = 5;
- return append_ref(refname + ofs, oid, 0);
+ return append_ref(ref->name + ofs, ref->oid, 0);
}
-static int append_remote_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int append_remote_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id tmp;
int ofs = 13;
- if (!starts_with(refname, "refs/remotes/"))
+ if (!starts_with(ref->name, "refs/remotes/"))
return 0;
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
+ if (repo_get_oid(the_repository, ref->name + ofs, &tmp) || !oideq(&tmp, ref->oid))
ofs = 5;
- return append_ref(refname + ofs, oid, 0);
+ return append_ref(ref->name + ofs, ref->oid, 0);
}
static int append_tag_ref(const char *refname, const struct object_id *oid,
@@ -454,27 +452,26 @@ static int append_tag_ref(const char *refname, const struct object_id *oid,
static const char *match_ref_pattern = NULL;
static int match_ref_slash = 0;
-static int append_matching_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int append_matching_ref(const struct reference *ref, void *cb_data)
{
/* we want to allow pattern hold/<asterisk> to show all
* branches under refs/heads/hold/, and v0.99.9? to show
* refs/tags/v0.99.9a and friends.
*/
const char *tail;
- int slash = count_slashes(refname);
- for (tail = refname; *tail && match_ref_slash < slash; )
+ int slash = count_slashes(ref->name);
+ for (tail = ref->name; *tail && match_ref_slash < slash; )
if (*tail++ == '/')
slash--;
if (!*tail)
return 0;
if (wildmatch(match_ref_pattern, tail, 0))
return 0;
- if (starts_with(refname, "refs/heads/"))
- return append_head_ref(refname, NULL, oid, flag, cb_data);
- if (starts_with(refname, "refs/tags/"))
- return append_tag_ref(refname, oid, flag, cb_data);
- return append_ref(refname, oid, 0);
+ if (starts_with(ref->name, "refs/heads/"))
+ return append_head_ref(ref, cb_data);
+ if (starts_with(ref->name, "refs/tags/"))
+ return append_tag_ref(ref->name, ref->oid, ref->flags, cb_data);
+ return append_ref(ref->name, ref->oid, 0);
}
static void snarf_refs(int head, int remotes)
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 0b6f9edf86c..4803b5e5986 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -66,26 +66,25 @@ struct show_ref_data {
int show_head;
};
-static int show_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cbdata)
+static int show_ref(const struct reference *ref, void *cbdata)
{
struct show_ref_data *data = cbdata;
- if (data->show_head && !strcmp(refname, "HEAD"))
+ if (data->show_head && !strcmp(ref->name, "HEAD"))
goto match;
if (data->patterns) {
- int reflen = strlen(refname);
+ int reflen = strlen(ref->name);
const char **p = data->patterns, *m;
while ((m = *p++) != NULL) {
int len = strlen(m);
if (len > reflen)
continue;
- if (memcmp(m, refname + reflen - len, len))
+ if (memcmp(m, ref->name + reflen - len, len))
continue;
if (len == reflen)
goto match;
- if (refname[reflen - len - 1] == '/')
+ if (ref->name[reflen - len - 1] == '/')
goto match;
}
return 0;
@@ -94,18 +93,15 @@ static int show_ref(const char *refname, const char *referent UNUSED, const stru
match:
data->found_match++;
- show_one(data->show_one_opts, refname, oid);
+ show_one(data->show_one_opts, ref->name, ref->oid);
return 0;
}
-static int add_existing(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cbdata)
+static int add_existing(const struct reference *ref, void *cbdata)
{
struct string_list *list = (struct string_list *)cbdata;
- string_list_insert(list, refname);
+ string_list_insert(list, ref->name);
return 0;
}
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index fcd73abe533..35f6cf735e5 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -593,16 +593,12 @@ static void print_status(unsigned int flags, char state, const char *path,
printf("\n");
}
-static int handle_submodule_head_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int handle_submodule_head_ref(const struct reference *ref, void *cb_data)
{
struct object_id *output = cb_data;
- if (oid)
- oidcpy(output, oid);
+ if (ref->oid)
+ oidcpy(output, ref->oid);
return 0;
}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 812774a5ca9..b7f323b5e4d 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -635,11 +635,7 @@ static void print_preparing_worktree_line(int detach,
*
* Returns 0 on failure and non-zero on success.
*/
-static int first_valid_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED,
- void *cb_data UNUSED)
+static int first_valid_ref(const struct reference *ref UNUSED, void *cb_data UNUSED)
{
return 1;
}
diff --git a/commit-graph.c b/commit-graph.c
index 2f20f66cfdd..0cfe16ad082 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1846,18 +1846,16 @@ struct refs_cb_data {
struct progress *progress;
};
-static int add_ref_to_set(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int add_ref_to_set(const struct reference *ref, void *cb_data)
{
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct refs_cb_data *data = (struct refs_cb_data *)cb_data;
- if (!peel_iterated_oid(data->repo, oid, &peeled))
- oid = &peeled;
- if (odb_read_object_info(data->repo->objects, oid, NULL) == OBJ_COMMIT)
- oidset_insert(data->commits, oid);
+ if (!peel_iterated_oid(data->repo, ref->oid, &peeled))
+ maybe_peeled = &peeled;
+ if (odb_read_object_info(data->repo->objects, maybe_peeled, NULL) == OBJ_COMMIT)
+ oidset_insert(data->commits, maybe_peeled);
display_progress(data->progress, oidset_size(data->commits));
diff --git a/delta-islands.c b/delta-islands.c
index 36c94799d69..7cfebc4162b 100644
--- a/delta-islands.c
+++ b/delta-islands.c
@@ -390,8 +390,7 @@ static void add_ref_to_island(kh_str_t *remote_islands, const char *island_name,
rl->hash += sha_core;
}
-static int find_island_for_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb)
+static int find_island_for_ref(const struct reference *ref, void *cb)
{
struct island_load_data *ild = cb;
@@ -406,7 +405,7 @@ static int find_island_for_ref(const char *refname, const char *referent UNUSED,
/* walk backwards to get last-one-wins ordering */
for (i = ild->nr - 1; i >= 0; i--) {
- if (!regexec(&ild->rx[i], refname,
+ if (!regexec(&ild->rx[i], ref->name,
ARRAY_SIZE(matches), matches, 0))
break;
}
@@ -428,10 +427,10 @@ static int find_island_for_ref(const char *refname, const char *referent UNUSED,
if (island_name.len)
strbuf_addch(&island_name, '-');
- strbuf_add(&island_name, refname + match->rm_so, match->rm_eo - match->rm_so);
+ strbuf_add(&island_name, ref->name + match->rm_so, match->rm_eo - match->rm_so);
}
- add_ref_to_island(ild->remote_islands, island_name.buf, oid);
+ add_ref_to_island(ild->remote_islands, island_name.buf, ref->oid);
strbuf_release(&island_name);
return 0;
}
diff --git a/fetch-pack.c b/fetch-pack.c
index 6ed56629518..f576e593b9f 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -188,13 +188,9 @@ static int rev_list_insert_ref(struct fetch_negotiator *negotiator,
return 0;
}
-static int rev_list_insert_ref_oid(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int rev_list_insert_ref_oid(const struct reference *ref, void *cb_data)
{
- return rev_list_insert_ref(cb_data, oid);
+ return rev_list_insert_ref(cb_data, ref->oid);
}
enum ack_type {
@@ -616,13 +612,9 @@ static int mark_complete(const struct object_id *oid)
return 0;
}
-static int mark_complete_oid(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED,
- void *cb_data UNUSED)
+static int mark_complete_oid(const struct reference *ref, void *cb_data UNUSED)
{
- return mark_complete(oid);
+ return mark_complete(ref->oid);
}
static void mark_recent_complete_commits(struct fetch_pack_args *args,
diff --git a/help.c b/help.c
index bb20498cfd0..d8c056fc5d1 100644
--- a/help.c
+++ b/help.c
@@ -845,18 +845,16 @@ struct similar_ref_cb {
struct string_list *similar_refs;
};
-static int append_similar_ref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED, void *cb_data)
+static int append_similar_ref(const struct reference *ref, void *cb_data)
{
struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
- char *branch = strrchr(refname, '/') + 1;
+ char *branch = strrchr(ref->name, '/') + 1;
/* A remote branch of the same name is deemed similar */
- if (starts_with(refname, "refs/remotes/") &&
+ if (starts_with(ref->name, "refs/remotes/") &&
!strcmp(branch, cb->base_ref))
string_list_append_nodup(cb->similar_refs,
- refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), refname, 1));
+ refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), ref->name, 1));
return 0;
}
diff --git a/http-backend.c b/http-backend.c
index d5dfe762bb5..992be9dc70c 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -513,18 +513,17 @@ static void run_service(const char **argv, int buffer_input)
exit(1);
}
-static int show_text_ref(const char *name, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int show_text_ref(const struct reference *ref, void *cb_data)
{
- const char *name_nons = strip_namespace(name);
+ const char *name_nons = strip_namespace(ref->name);
struct strbuf *buf = cb_data;
- struct object *o = parse_object(the_repository, oid);
+ struct object *o = parse_object(the_repository, ref->oid);
if (!o)
return 0;
- strbuf_addf(buf, "%s\t%s\n", oid_to_hex(oid), name_nons);
+ strbuf_addf(buf, "%s\t%s\n", oid_to_hex(ref->oid), name_nons);
if (o->type == OBJ_TAG) {
- o = deref_tag(the_repository, o, name, 0);
+ o = deref_tag(the_repository, o, ref->name, 0);
if (!o)
return 0;
strbuf_addf(buf, "%s\t%s^{}\n", oid_to_hex(&o->oid),
@@ -569,21 +568,20 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)
strbuf_release(&buf);
}
-static int show_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int show_head_ref(const struct reference *ref, void *cb_data)
{
struct strbuf *buf = cb_data;
- if (flag & REF_ISSYMREF) {
+ if (ref->flags & REF_ISSYMREF) {
const char *target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- refname,
+ ref->name,
RESOLVE_REF_READING,
NULL, NULL);
if (target)
strbuf_addf(buf, "ref: %s\n", strip_namespace(target));
} else {
- strbuf_addf(buf, "%s\n", oid_to_hex(oid));
+ strbuf_addf(buf, "%s\n", oid_to_hex(ref->oid));
}
return 0;
diff --git a/log-tree.c b/log-tree.c
index 67d9ace5964..95f1d12c722 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -147,9 +147,7 @@ static int ref_filter_match(const char *refname,
return 1;
}
-static int add_ref_decoration(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int add_ref_decoration(const struct reference *ref, void *cb_data)
{
int i;
struct object *obj;
@@ -158,16 +156,16 @@ static int add_ref_decoration(const char *refname, const char *referent UNUSED,
struct decoration_filter *filter = (struct decoration_filter *)cb_data;
const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref;
- if (filter && !ref_filter_match(refname, filter))
+ if (filter && !ref_filter_match(ref->name, filter))
return 0;
- if (starts_with(refname, git_replace_ref_base)) {
+ if (starts_with(ref->name, git_replace_ref_base)) {
struct object_id original_oid;
if (!replace_refs_enabled(the_repository))
return 0;
- if (get_oid_hex(refname + strlen(git_replace_ref_base),
+ if (get_oid_hex(ref->name + strlen(git_replace_ref_base),
&original_oid)) {
- warning("invalid replace ref %s", refname);
+ warning("invalid replace ref %s", ref->name);
return 0;
}
obj = parse_object(the_repository, &original_oid);
@@ -176,10 +174,10 @@ static int add_ref_decoration(const char *refname, const char *referent UNUSED,
return 0;
}
- objtype = odb_read_object_info(the_repository->objects, oid, NULL);
+ objtype = odb_read_object_info(the_repository->objects, ref->oid, NULL);
if (objtype < 0)
return 0;
- obj = lookup_object_by_type(the_repository, oid, objtype);
+ obj = lookup_object_by_type(the_repository, ref->oid, objtype);
for (i = 0; i < ARRAY_SIZE(ref_namespace); i++) {
struct ref_namespace_info *info = &ref_namespace[i];
@@ -187,24 +185,24 @@ static int add_ref_decoration(const char *refname, const char *referent UNUSED,
if (!info->decoration)
continue;
if (info->exact) {
- if (!strcmp(refname, info->ref)) {
+ if (!strcmp(ref->name, info->ref)) {
deco_type = info->decoration;
break;
}
- } else if (starts_with(refname, info->ref)) {
+ } else if (starts_with(ref->name, info->ref)) {
deco_type = info->decoration;
break;
}
}
- add_name_decoration(deco_type, refname, obj);
+ add_name_decoration(deco_type, ref->name, obj);
while (obj->type == OBJ_TAG) {
if (!obj->parsed)
parse_object(the_repository, &obj->oid);
obj = ((struct tag *)obj)->tagged;
if (!obj)
break;
- add_name_decoration(DECORATION_REF_TAG, refname, obj);
+ add_name_decoration(DECORATION_REF_TAG, ref->name, obj);
}
return 0;
}
diff --git a/ls-refs.c b/ls-refs.c
index c47acde07f3..64d02723691 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -75,42 +75,42 @@ struct ls_refs_data {
unsigned unborn : 1;
};
-static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int send_ref(const struct reference *ref, void *cb_data)
{
struct ls_refs_data *data = cb_data;
- const char *refname_nons = strip_namespace(refname);
+ const char *refname_nons = strip_namespace(ref->name);
strbuf_reset(&data->buf);
- if (ref_is_hidden(refname_nons, refname, &data->hidden_refs))
+ if (ref_is_hidden(refname_nons, ref->name, &data->hidden_refs))
return 0;
if (!ref_match(&data->prefixes, refname_nons))
return 0;
- if (oid)
- strbuf_addf(&data->buf, "%s %s", oid_to_hex(oid), refname_nons);
+ if (ref->oid)
+ strbuf_addf(&data->buf, "%s %s", oid_to_hex(ref->oid), refname_nons);
else
strbuf_addf(&data->buf, "unborn %s", refname_nons);
- if (data->symrefs && flag & REF_ISSYMREF) {
+ if (data->symrefs && ref->flags & REF_ISSYMREF) {
+ int unused_flag;
struct object_id unused;
const char *symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- refname,
+ ref->name,
0,
&unused,
- &flag);
+ &unused_flag);
if (!symref_target)
- die("'%s' is a symref but it is not?", refname);
+ die("'%s' is a symref but it is not?", ref->name);
strbuf_addf(&data->buf, " symref-target:%s",
strip_namespace(symref_target));
}
- if (data->peel && oid) {
+ if (data->peel && ref->oid) {
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
strbuf_addf(&data->buf, " peeled:%s", oid_to_hex(&peeled));
}
@@ -131,9 +131,17 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)
if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), namespaced.buf, 0, &oid, &flag))
return; /* bad ref */
oid_is_null = is_null_oid(&oid);
+
if (!oid_is_null ||
- (data->unborn && data->symrefs && (flag & REF_ISSYMREF)))
- send_ref(namespaced.buf, NULL, oid_is_null ? NULL : &oid, flag, data);
+ (data->unborn && data->symrefs && (flag & REF_ISSYMREF))) {
+ struct reference ref = {
+ .name = namespaced.buf,
+ .oid = oid_is_null ? NULL : &oid,
+ .flags = flag,
+ };
+
+ send_ref(&ref, data);
+ }
strbuf_release(&namespaced);
}
diff --git a/midx-write.c b/midx-write.c
index c73010df6d3..f4dd875747a 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -697,28 +697,27 @@ static void prepare_midx_packing_data(struct packing_data *pdata,
trace2_region_leave("midx", "prepare_midx_packing_data", ctx->repo);
}
-static int add_ref_to_pending(const char *refname, const char *referent UNUSED,
- const struct object_id *oid,
- int flag, void *cb_data)
+static int add_ref_to_pending(const struct reference *ref, void *cb_data)
{
struct rev_info *revs = (struct rev_info*)cb_data;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct object *object;
- if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
- warning("symbolic ref is dangling: %s", refname);
+ if ((ref->flags & REF_ISSYMREF) && (ref->flags & REF_ISBROKEN)) {
+ warning("symbolic ref is dangling: %s", ref->name);
return 0;
}
- if (!peel_iterated_oid(revs->repo, oid, &peeled))
- oid = &peeled;
+ if (!peel_iterated_oid(revs->repo, ref->oid, &peeled))
+ maybe_peeled = &peeled;
- object = parse_object_or_die(revs->repo, oid, refname);
+ object = parse_object_or_die(revs->repo, maybe_peeled, ref->name);
if (object->type != OBJ_COMMIT)
return 0;
add_pending_object(revs, object, "");
- if (bitmap_is_preferred_refname(revs->repo, refname))
+ if (bitmap_is_preferred_refname(revs->repo, ref->name))
object->flags |= NEEDS_BITMAP;
return 0;
}
diff --git a/negotiator/default.c b/negotiator/default.c
index c479da9b091..116dedcf830 100644
--- a/negotiator/default.c
+++ b/negotiator/default.c
@@ -38,11 +38,10 @@ static void rev_list_push(struct negotiation_state *ns,
}
}
-static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data UNUSED)
+static int clear_marks(const struct reference *ref, void *cb_data UNUSED)
{
- struct object *o = deref_tag(the_repository, parse_object(the_repository, oid), refname, 0);
+ struct object *o = deref_tag(the_repository, parse_object(the_repository, ref->oid),
+ ref->name, 0);
if (o && o->type == OBJ_COMMIT)
clear_commit_marks((struct commit *)o,
diff --git a/negotiator/skipping.c b/negotiator/skipping.c
index 616df6bf3af..0a272130fb1 100644
--- a/negotiator/skipping.c
+++ b/negotiator/skipping.c
@@ -75,11 +75,10 @@ static struct entry *rev_list_push(struct data *data, struct commit *commit, int
return entry;
}
-static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data UNUSED)
+static int clear_marks(const struct reference *ref, void *cb_data UNUSED)
{
- struct object *o = deref_tag(the_repository, parse_object(the_repository, oid), refname, 0);
+ struct object *o = deref_tag(the_repository, parse_object(the_repository, ref->oid),
+ ref->name, 0);
if (o && o->type == OBJ_COMMIT)
clear_commit_marks((struct commit *)o,
diff --git a/notes.c b/notes.c
index 9a2e9181fe6..8e00fd8c470 100644
--- a/notes.c
+++ b/notes.c
@@ -938,13 +938,11 @@ int combine_notes_cat_sort_uniq(struct object_id *cur_oid,
return ret;
}
-static int string_list_add_one_ref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cb)
+static int string_list_add_one_ref(const struct reference *ref, void *cb)
{
struct string_list *refs = cb;
- if (!unsorted_string_list_has_string(refs, refname))
- string_list_append(refs, refname);
+ if (!unsorted_string_list_has_string(refs, ref->name))
+ string_list_append(refs, ref->name);
return 0;
}
diff --git a/object-name.c b/object-name.c
index 7774991d281..43c7518f995 100644
--- a/object-name.c
+++ b/object-name.c
@@ -1444,18 +1444,16 @@ struct handle_one_ref_cb {
struct commit_list **list;
};
-static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int handle_one_ref(const struct reference *ref, void *cb_data)
{
struct handle_one_ref_cb *cb = cb_data;
struct commit_list **list = cb->list;
- struct object *object = parse_object(cb->repo, oid);
+ struct object *object = parse_object(cb->repo, ref->oid);
if (!object)
return 0;
if (object->type == OBJ_TAG) {
- object = deref_tag(cb->repo, object, path,
- strlen(path));
+ object = deref_tag(cb->repo, object, ref->name,
+ strlen(ref->name));
if (!object)
return 0;
}
diff --git a/pseudo-merge.c b/pseudo-merge.c
index 893b763fe45..0abd51b42c1 100644
--- a/pseudo-merge.c
+++ b/pseudo-merge.c
@@ -221,28 +221,25 @@ void load_pseudo_merges_from_config(struct repository *r,
}
}
-static int find_pseudo_merge_group_for_ref(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *_data)
+static int find_pseudo_merge_group_for_ref(const struct reference *ref, void *_data)
{
struct bitmap_writer *writer = _data;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct commit *c;
uint32_t i;
int has_bitmap;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ maybe_peeled = &peeled;
- c = lookup_commit(the_repository, oid);
+ c = lookup_commit(the_repository, maybe_peeled);
if (!c)
return 0;
- if (!packlist_find(writer->to_pack, oid))
+ if (!packlist_find(writer->to_pack, maybe_peeled))
return 0;
- has_bitmap = bitmap_writer_has_bitmapped_object_id(writer, oid);
+ has_bitmap = bitmap_writer_has_bitmapped_object_id(writer, maybe_peeled);
for (i = 0; i < writer->pseudo_merge_groups.nr; i++) {
struct pseudo_merge_group *group;
@@ -252,7 +249,7 @@ static int find_pseudo_merge_group_for_ref(const char *refname,
size_t j;
group = writer->pseudo_merge_groups.items[i].util;
- if (regexec(group->pattern, refname, ARRAY_SIZE(captures),
+ if (regexec(group->pattern, ref->name, ARRAY_SIZE(captures),
captures, 0))
continue;
@@ -269,7 +266,7 @@ static int find_pseudo_merge_group_for_ref(const char *refname,
if (group_name.len)
strbuf_addch(&group_name, '-');
- strbuf_add(&group_name, refname + match->rm_so,
+ strbuf_add(&group_name, ref->name + match->rm_so,
match->rm_eo - match->rm_so);
}
diff --git a/reachable.c b/reachable.c
index 22266db5233..b753c395530 100644
--- a/reachable.c
+++ b/reachable.c
@@ -83,18 +83,17 @@ static void add_rebase_files(struct rev_info *revs)
free_worktrees(worktrees);
}
-static int add_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int add_one_ref(const struct reference *ref, void *cb_data)
{
struct rev_info *revs = (struct rev_info *)cb_data;
struct object *object;
- if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
- warning("symbolic ref is dangling: %s", path);
+ if ((ref->flags & REF_ISSYMREF) && (ref->flags & REF_ISBROKEN)) {
+ warning("symbolic ref is dangling: %s", ref->name);
return 0;
}
- object = parse_object_or_die(the_repository, oid, path);
+ object = parse_object_or_die(the_repository, ref->oid, ref->name);
add_pending_object(revs, object, "");
return 0;
diff --git a/ref-filter.c b/ref-filter.c
index 520d2539c9e..7740f35e937 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2954,14 +2954,15 @@ struct ref_filter_cbdata {
* A call-back given to for_each_ref(). Filter refs and keep them for
* later object processing.
*/
-static int filter_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)
+static int filter_one(const struct reference *ref, void *cb_data)
{
struct ref_filter_cbdata *ref_cbdata = cb_data;
- struct ref_array_item *ref;
+ struct ref_array_item *item;
- ref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);
- if (ref)
- ref_array_append(ref_cbdata->array, ref);
+ item = apply_ref_filter(ref->name, ref->target, ref->oid,
+ ref->flags, ref_cbdata->filter);
+ if (item)
+ ref_array_append(ref_cbdata->array, item);
return 0;
}
@@ -2990,17 +2991,18 @@ struct ref_filter_and_format_cbdata {
} internal;
};
-static int filter_and_format_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)
+static int filter_and_format_one(const struct reference *ref, void *cb_data)
{
struct ref_filter_and_format_cbdata *ref_cbdata = cb_data;
- struct ref_array_item *ref;
+ struct ref_array_item *item;
struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
- ref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);
- if (!ref)
+ item = apply_ref_filter(ref->name, ref->target, ref->oid,
+ ref->flags, ref_cbdata->filter);
+ if (!item)
return 0;
- if (format_ref_array_item(ref, ref_cbdata->format, &output, &err))
+ if (format_ref_array_item(item, ref_cbdata->format, &output, &err))
die("%s", err.buf);
if (output.len || !ref_cbdata->format->array_opts.omit_empty) {
@@ -3010,7 +3012,7 @@ static int filter_and_format_one(const char *refname, const char *referent, cons
strbuf_release(&output);
strbuf_release(&err);
- free_array_item(ref);
+ free_array_item(item);
/*
* Increment the running count of refs that match the filter. If
diff --git a/reflog.c b/reflog.c
index 65ef259b4f5..ac87e20c4f9 100644
--- a/reflog.c
+++ b/reflog.c
@@ -423,16 +423,13 @@ int should_expire_reflog_ent_verbose(struct object_id *ooid,
return expire;
}
-static int push_tip_to_list(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags, void *cb_data)
+static int push_tip_to_list(const struct reference *ref, void *cb_data)
{
struct commit_list **list = cb_data;
struct commit *tip_commit;
- if (flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
return 0;
- tip_commit = lookup_commit_reference_gently(the_repository, oid, 1);
+ tip_commit = lookup_commit_reference_gently(the_repository, ref->oid, 1);
if (!tip_commit)
return 0;
commit_list_insert(tip_commit, list);
diff --git a/refs.c b/refs.c
index 750e5db0771..262849301d0 100644
--- a/refs.c
+++ b/refs.c
@@ -423,17 +423,19 @@ int refs_ref_exists(struct ref_store *refs, const char *refname)
NULL, NULL);
}
-static int for_each_filter_refs(const char *refname, const char *referent,
- const struct object_id *oid,
- int flags, void *data)
+static int for_each_filter_refs(const struct reference *ref, void *data)
{
struct for_each_ref_filter *filter = data;
- if (wildmatch(filter->pattern, refname, 0))
+ if (wildmatch(filter->pattern, ref->name, 0))
return 0;
- if (filter->prefix)
- skip_prefix(refname, filter->prefix, &refname);
- return filter->fn(refname, referent, oid, flags, filter->cb_data);
+ if (filter->prefix) {
+ struct reference skipped = *ref;
+ skip_prefix(skipped.name, filter->prefix, &skipped.name);
+ return filter->fn(&skipped, filter->cb_data);
+ } else {
+ return filter->fn(ref, filter->cb_data);
+ }
}
struct warn_if_dangling_data {
@@ -444,17 +446,15 @@ struct warn_if_dangling_data {
int dry_run;
};
-static int warn_if_dangling_symref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags, void *cb_data)
+static int warn_if_dangling_symref(const struct reference *ref, void *cb_data)
{
struct warn_if_dangling_data *d = cb_data;
const char *resolves_to, *msg;
- if (!(flags & REF_ISSYMREF))
+ if (!(ref->flags & REF_ISSYMREF))
return 0;
- resolves_to = refs_resolve_ref_unsafe(d->refs, refname, 0, NULL, NULL);
+ resolves_to = refs_resolve_ref_unsafe(d->refs, ref->name, 0, NULL, NULL);
if (!resolves_to
|| !string_list_has_string(d->refnames, resolves_to)) {
return 0;
@@ -463,7 +463,7 @@ static int warn_if_dangling_symref(const char *refname, const char *referent UNU
msg = d->dry_run
? _("%s%s will become dangling after %s is deleted\n")
: _("%s%s has become dangling after %s was deleted\n");
- fprintf(d->fp, msg, d->indent, refname, resolves_to);
+ fprintf(d->fp, msg, d->indent, ref->name, resolves_to);
return 0;
}
@@ -504,8 +504,15 @@ int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_da
int flag;
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
- if (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag))
- ret = fn(buf.buf, NULL, &oid, flag, cb_data);
+ if (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag)) {
+ struct reference ref = {
+ .name = buf.buf,
+ .oid = &oid,
+ .flags = flag,
+ };
+
+ ret = fn(&ref, cb_data);
+ }
strbuf_release(&buf);
return ret;
@@ -1740,8 +1747,15 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
int flag;
if (refs_resolve_ref_unsafe(refs, "HEAD", RESOLVE_REF_READING,
- &oid, &flag))
- return fn("HEAD", NULL, &oid, flag, cb_data);
+ &oid, &flag)) {
+ struct reference ref = {
+ .name = "HEAD",
+ .oid = &oid,
+ .flags = flag,
+ };
+
+ return fn(&ref, cb_data);
+ }
return 0;
}
@@ -2752,14 +2766,10 @@ struct do_for_each_reflog_help {
void *cb_data;
};
-static int do_for_each_reflog_helper(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED,
- void *cb_data)
+static int do_for_each_reflog_helper(const struct reference *ref, void *cb_data)
{
struct do_for_each_reflog_help *hp = cb_data;
- return hp->fn(refname, hp->cb_data);
+ return hp->fn(ref->name, hp->cb_data);
}
int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data)
@@ -2977,25 +2987,24 @@ struct migration_data {
uint64_t index;
};
-static int migrate_one_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags, void *cb_data)
+static int migrate_one_ref(const struct reference *ref, void *cb_data)
{
struct migration_data *data = cb_data;
struct strbuf symref_target = STRBUF_INIT;
int ret;
- if (flags & REF_ISSYMREF) {
- ret = refs_read_symbolic_ref(data->old_refs, refname, &symref_target);
+ if (ref->flags & REF_ISSYMREF) {
+ ret = refs_read_symbolic_ref(data->old_refs, ref->name, &symref_target);
if (ret < 0)
goto done;
- ret = ref_transaction_update(data->transaction, refname, NULL, null_oid(the_hash_algo),
+ ret = ref_transaction_update(data->transaction, ref->name, NULL, null_oid(the_hash_algo),
symref_target.buf, NULL,
REF_SKIP_CREATE_REFLOG | REF_NO_DEREF, NULL, data->errbuf);
if (ret < 0)
goto done;
} else {
- ret = ref_transaction_create(data->transaction, refname, oid, NULL,
+ ret = ref_transaction_create(data->transaction, ref->name, ref->oid, NULL,
REF_SKIP_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION,
NULL, data->errbuf);
if (ret < 0)
diff --git a/refs.h b/refs.h
index 4e6bd63aa86..68d235438c2 100644
--- a/refs.h
+++ b/refs.h
@@ -355,14 +355,32 @@ struct ref_transaction;
*/
#define REF_BAD_NAME 0x08
+/* A reference passed to `for_each_ref()`-style callbacks. */
+struct reference {
+ /* The fully-qualified name of the reference. */
+ const char *name;
+
+ /* The target of a symbolic ref. `NULL` for direct references. */
+ const char *target;
+
+ /*
+ * The object ID of a reference. Either the direct object ID or the
+ * resolved object ID in the case of a symbolic ref. May be the zero
+ * object ID in case the symbolic ref cannot be resolved.
+ */
+ const struct object_id *oid;
+
+ /* A bitfield of `REF_` flags. */
+ int flags;
+};
+
/*
* The signature for the callback function for the for_each_*()
- * functions below. The memory pointed to by the refname and oid
- * arguments is only guaranteed to be valid for the duration of a
+ * functions below. The memory pointed to by the `struct reference`
+ * argument is only guaranteed to be valid for the duration of a
* single callback invocation.
*/
-typedef int each_ref_fn(const char *refname, const char *referent,
- const struct object_id *oid, int flags, void *cb_data);
+typedef int each_ref_fn(const struct reference *ref, void *cb_data);
/*
* The following functions invoke the specified callback function for
diff --git a/refs/files-backend.c b/refs/files-backend.c
index bb2bec38076..0ddcf22aed7 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3151,14 +3151,11 @@ static int parse_and_write_reflog(struct files_ref_store *refs,
return 0;
}
-static int ref_present(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED,
- void *cb_data)
+static int ref_present(const struct reference *ref, void *cb_data)
{
struct string_list *affected_refnames = cb_data;
- return string_list_has_string(affected_refnames, refname);
+ return string_list_has_string(affected_refnames, ref->name);
}
static int files_transaction_finish_initial(struct files_ref_store *refs,
diff --git a/refs/iterator.c b/refs/iterator.c
index 17ef841d8a3..7f2e718f1c9 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -476,7 +476,14 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
- retval = fn(iter->refname, iter->referent, iter->oid, iter->flags, cb_data);
+ struct reference ref = {
+ .name = iter->refname,
+ .target = iter->referent,
+ .oid = iter->oid,
+ .flags = iter->flags,
+ };
+
+ retval = fn(&ref, cb_data);
if (retval)
goto out;
}
diff --git a/remote.c b/remote.c
index df9675cd330..59b37151208 100644
--- a/remote.c
+++ b/remote.c
@@ -2315,21 +2315,19 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
return 1;
}
-static int one_local_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int one_local_ref(const struct reference *ref, void *cb_data)
{
struct ref ***local_tail = cb_data;
- struct ref *ref;
+ struct ref *local_ref;
/* we already know it starts with refs/ to get here */
- if (check_refname_format(refname + 5, 0))
+ if (check_refname_format(ref->name + 5, 0))
return 0;
- ref = alloc_ref(refname);
- oidcpy(&ref->new_oid, oid);
- **local_tail = ref;
- *local_tail = &ref->next;
+ local_ref = alloc_ref(ref->name);
+ oidcpy(&local_ref->new_oid, ref->oid);
+ **local_tail = local_ref;
+ *local_tail = &local_ref->next;
return 0;
}
@@ -2402,15 +2400,14 @@ struct stale_heads_info {
struct refspec *rs;
};
-static int get_stale_heads_cb(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags, void *cb_data)
+static int get_stale_heads_cb(const struct reference *ref, void *cb_data)
{
struct stale_heads_info *info = cb_data;
struct string_list matches = STRING_LIST_INIT_DUP;
struct refspec_item query;
int i, stale = 1;
memset(&query, 0, sizeof(struct refspec_item));
- query.dst = (char *)refname;
+ query.dst = (char *)ref->name;
refspec_find_all_matches(info->rs, &query, &matches);
if (matches.nr == 0)
@@ -2423,7 +2420,7 @@ static int get_stale_heads_cb(const char *refname, const char *referent UNUSED,
* overlapping refspecs, we need to go over all of the
* matching refs.
*/
- if (flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
goto clean_exit;
for (i = 0; stale && i < matches.nr; i++)
@@ -2431,8 +2428,8 @@ static int get_stale_heads_cb(const char *refname, const char *referent UNUSED,
stale = 0;
if (stale) {
- struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
- oidcpy(&ref->new_oid, oid);
+ struct ref *linked_ref = make_linked_ref(ref->name, &info->stale_refs_tail);
+ oidcpy(&linked_ref->new_oid, ref->oid);
}
clean_exit:
diff --git a/replace-object.c b/replace-object.c
index 3eae0510745..03d0f1f083b 100644
--- a/replace-object.c
+++ b/replace-object.c
@@ -8,31 +8,27 @@
#include "repository.h"
#include "commit.h"
-static int register_replace_ref(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int register_replace_ref(const struct reference *ref, void *cb_data)
{
struct repository *r = cb_data;
/* Get sha1 from refname */
- const char *slash = strrchr(refname, '/');
- const char *hash = slash ? slash + 1 : refname;
+ const char *slash = strrchr(ref->name, '/');
+ const char *hash = slash ? slash + 1 : ref->name;
struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj));
if (get_oid_hex_algop(hash, &repl_obj->original.oid, r->hash_algo)) {
free(repl_obj);
- warning(_("bad replace ref name: %s"), refname);
+ warning(_("bad replace ref name: %s"), ref->name);
return 0;
}
/* Copy sha1 from the read ref */
- oidcpy(&repl_obj->replacement, oid);
+ oidcpy(&repl_obj->replacement, ref->oid);
/* Register new object */
if (oidmap_put(&r->objects->replace_map, repl_obj))
- die(_("duplicate replace ref: %s"), refname);
+ die(_("duplicate replace ref: %s"), ref->name);
return 0;
}
diff --git a/revision.c b/revision.c
index 806a1c4c24d..a195c0ded45 100644
--- a/revision.c
+++ b/revision.c
@@ -1647,19 +1647,17 @@ struct all_refs_cb {
struct worktree *wt;
};
-static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int handle_one_ref(const struct reference *ref, void *cb_data)
{
struct all_refs_cb *cb = cb_data;
struct object *object;
- if (ref_excluded(&cb->all_revs->ref_excludes, path))
+ if (ref_excluded(&cb->all_revs->ref_excludes, ref->name))
return 0;
- object = get_reference(cb->all_revs, path, oid, cb->all_flags);
- add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
- add_pending_object(cb->all_revs, object, path);
+ object = get_reference(cb->all_revs, ref->name, ref->oid, cb->all_flags);
+ add_rev_cmdline(cb->all_revs, object, ref->name, REV_CMD_REF, cb->all_flags);
+ add_pending_object(cb->all_revs, object, ref->name);
return 0;
}
diff --git a/server-info.c b/server-info.c
index 9bb30d9ab71..aea4df80416 100644
--- a/server-info.c
+++ b/server-info.c
@@ -148,23 +148,21 @@ static int update_info_file(struct repository *r, char *path,
return ret;
}
-static int add_info_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int add_info_ref(const struct reference *ref, void *cb_data)
{
struct update_info_ctx *uic = cb_data;
- struct object *o = parse_object(uic->repo, oid);
+ struct object *o = parse_object(uic->repo, ref->oid);
if (!o)
return -1;
- if (uic_printf(uic, "%s %s\n", oid_to_hex(oid), path) < 0)
+ if (uic_printf(uic, "%s %s\n", oid_to_hex(ref->oid), ref->name) < 0)
return -1;
if (o->type == OBJ_TAG) {
- o = deref_tag(uic->repo, o, path, 0);
+ o = deref_tag(uic->repo, o, ref->name, 0);
if (o)
if (uic_printf(uic, "%s %s^{}\n",
- oid_to_hex(&o->oid), path) < 0)
+ oid_to_hex(&o->oid), ref->name) < 0)
return -1;
}
return 0;
diff --git a/shallow.c b/shallow.c
index d9cd4e219cb..55b9cd9d3f2 100644
--- a/shallow.c
+++ b/shallow.c
@@ -626,14 +626,10 @@ static void paint_down(struct paint_info *info, const struct object_id *oid,
free(tmp);
}
-static int mark_uninteresting(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data UNUSED)
+static int mark_uninteresting(const struct reference *ref, void *cb_data UNUSED)
{
struct commit *commit = lookup_commit_reference_gently(the_repository,
- oid, 1);
+ ref->oid, 1);
if (!commit)
return 0;
commit->object.flags |= UNINTERESTING;
@@ -742,16 +738,12 @@ struct commit_array {
size_t nr, alloc;
};
-static int add_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int add_ref(const struct reference *ref, void *cb_data)
{
struct commit_array *ca = cb_data;
ALLOC_GROW(ca->commits, ca->nr + 1, ca->alloc);
ca->commits[ca->nr] = lookup_commit_reference_gently(the_repository,
- oid, 1);
+ ref->oid, 1);
if (ca->commits[ca->nr])
ca->nr++;
return 0;
diff --git a/submodule.c b/submodule.c
index 35c55155f7b..40a5c6fb9d1 100644
--- a/submodule.c
+++ b/submodule.c
@@ -934,10 +934,7 @@ static void free_submodules_data(struct string_list *submodules)
string_list_clear(submodules, 1);
}
-static int has_remote(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED, void *cb_data UNUSED)
+static int has_remote(const struct reference *ref UNUSED, void *cb_data UNUSED)
{
return 1;
}
@@ -1255,13 +1252,10 @@ int push_unpushed_submodules(struct repository *r,
return ret;
}
-static int append_oid_to_array(const char *ref UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *data)
+static int append_oid_to_array(const struct reference *ref, void *data)
{
struct oid_array *array = data;
- oid_array_append(array, oid);
+ oid_array_append(array, ref->oid);
return 0;
}
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 83b06d39a36..b1215947c5e 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -154,10 +154,9 @@ static int cmd_rename_ref(struct ref_store *refs, const char **argv)
return refs_rename_ref(refs, oldref, newref, logmsg);
}
-static int each_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags, void *cb_data UNUSED)
+static int each_ref(const struct reference *ref, void *cb_data UNUSED)
{
- printf("%s %s 0x%x\n", oid_to_hex(oid), refname, flags);
+ printf("%s %s 0x%x\n", oid_to_hex(ref->oid), ref->name, ref->flags);
return 0;
}
diff --git a/upload-pack.c b/upload-pack.c
index 1e87ae95593..0d563ae74e9 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -870,8 +870,8 @@ static void send_unshallow(struct upload_pack_data *data)
}
}
-static int check_ref(const char *refname_full, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data);
+static int check_ref(const struct reference *ref, void *cb_data);
+
static void deepen(struct upload_pack_data *data, int depth)
{
if (depth == INFINITE_DEPTH && !is_repository_shallow(the_repository)) {
@@ -1224,13 +1224,12 @@ static int mark_our_ref(const char *refname, const char *refname_full,
return 0;
}
-static int check_ref(const char *refname_full, const char *referent UNUSED,const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int check_ref(const struct reference *ref, void *cb_data)
{
- const char *refname = strip_namespace(refname_full);
+ const char *refname = strip_namespace(ref->name);
struct upload_pack_data *data = cb_data;
- mark_our_ref(refname, refname_full, oid, &data->hidden_refs);
+ mark_our_ref(refname, ref->name, ref->oid, &data->hidden_refs);
return 0;
}
@@ -1292,27 +1291,25 @@ static void write_v0_ref(struct upload_pack_data *data,
return;
}
-static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int send_ref(const struct reference *ref, void *cb_data)
{
- write_v0_ref(cb_data, refname, strip_namespace(refname), oid);
+ write_v0_ref(cb_data, ref->name, strip_namespace(ref->name), ref->oid);
return 0;
}
-static int find_symref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag, void *cb_data)
+static int find_symref(const struct reference *ref, void *cb_data)
{
const char *symref_target;
struct string_list_item *item;
+ int flag;
- if ((flag & REF_ISSYMREF) == 0)
+ if ((ref->flags & REF_ISSYMREF) == 0)
return 0;
symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- refname, 0, NULL, &flag);
+ ref->name, 0, NULL, &flag);
if (!symref_target || (flag & REF_ISSYMREF) == 0)
- die("'%s' is a symref but it is not?", refname);
- item = string_list_append(cb_data, strip_namespace(refname));
+ die("'%s' is a symref but it is not?", ref->name);
+ item = string_list_append(cb_data, strip_namespace(ref->name));
item->util = xstrdup(strip_namespace(symref_target));
return 0;
}
diff --git a/walker.c b/walker.c
index 80737545172..409b646578a 100644
--- a/walker.c
+++ b/walker.c
@@ -226,14 +226,10 @@ static int interpret_target(struct walker *walker, char *target, struct object_i
return -1;
}
-static int mark_complete(const char *path UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED,
- void *cb_data UNUSED)
+static int mark_complete(const struct reference *ref, void *cb_data UNUSED)
{
struct commit *commit = lookup_commit_reference_gently(the_repository,
- oid, 1);
+ ref->oid, 1);
if (commit) {
commit->object.flags |= COMPLETE;
diff --git a/worktree.c b/worktree.c
index a2a5f51f29f..9308389cb6f 100644
--- a/worktree.c
+++ b/worktree.c
@@ -595,8 +595,15 @@ int other_head_refs(each_ref_fn fn, void *cb_data)
if (refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
refname.buf,
RESOLVE_REF_READING,
- &oid, &flag))
- ret = fn(refname.buf, NULL, &oid, flag, cb_data);
+ &oid, &flag)) {
+ struct reference ref = {
+ .name = refname.buf,
+ .oid = &oid,
+ .flags = flag,
+ };
+
+ ret = fn(&ref, cb_data);
+ }
if (ret)
break;
}
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v2 02/14] refs: introduce `.ref` field for the base iterator
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 01/14] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
@ 2025-10-08 15:50 ` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 03/14] refs: fully reset `struct ref_iterator::ref` on iteration Patrick Steinhardt
` (12 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 15:50 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
The base iterator has a couple of fields that tracks the name, target,
object ID and flags for the current reference. Due to this design we
have to create a new `struct reference` whenever we want to hand over
that reference to the callback function, which is tedious and not very
efficient.
Convert the structure to instead contain a `struct reference` as member.
This member is expected to be populated by the implementations of the
iterator and is handed over to the callback directly.
While at it, simplify `should_pack_ref()` to take a `struct reference`
directly instead of passing its respective fields.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs.c | 8 ++++----
refs/debug.c | 8 +++-----
refs/files-backend.c | 47 +++++++++++++++++++++--------------------------
refs/iterator.c | 39 ++++++++++++---------------------------
refs/packed-backend.c | 46 +++++++++++++++++++++++-----------------------
refs/ref-cache.c | 10 +++++-----
refs/refs-internal.h | 5 +----
refs/reftable-backend.c | 12 ++++++------
8 files changed, 75 insertions(+), 100 deletions(-)
diff --git a/refs.c b/refs.c
index 262849301d..15ad0ef7a8 100644
--- a/refs.c
+++ b/refs.c
@@ -2326,8 +2326,8 @@ int refs_optimize(struct ref_store *refs, struct pack_refs_opts *opts)
int peel_iterated_oid(struct repository *r, const struct object_id *base, struct object_id *peeled)
{
if (current_ref_iter &&
- (current_ref_iter->oid == base ||
- oideq(current_ref_iter->oid, base)))
+ (current_ref_iter->ref.oid == base ||
+ oideq(current_ref_iter->ref.oid, base)))
return ref_iterator_peel(current_ref_iter, peeled);
return peel_object(r, base, peeled) ? -1 : 0;
@@ -2702,7 +2702,7 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
if (skip &&
- string_list_has_string(skip, iter->refname))
+ string_list_has_string(skip, iter->ref.name))
continue;
if (transaction && ref_transaction_maybe_set_rejected(
@@ -2711,7 +2711,7 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
continue;
strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
- iter->refname, refname);
+ iter->ref.name, refname);
goto cleanup;
}
diff --git a/refs/debug.c b/refs/debug.c
index 1cb955961e..7a26035617 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -161,11 +161,9 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
trace_printf_key(&trace_refs, "iterator_advance: (%d)\n", res);
else
trace_printf_key(&trace_refs, "iterator_advance: %s (0)\n",
- diter->iter->refname);
+ diter->iter->ref.name);
- diter->base.refname = diter->iter->refname;
- diter->base.oid = diter->iter->oid;
- diter->base.flags = diter->iter->flags;
+ diter->base.ref = diter->iter->ref;
return res;
}
@@ -186,7 +184,7 @@ static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct debug_ref_iterator *diter =
(struct debug_ref_iterator *)ref_iterator;
int res = diter->iter->vtable->peel(diter->iter, peeled);
- trace_printf_key(&trace_refs, "iterator_peel: %s: %d\n", diter->iter->refname, res);
+ trace_printf_key(&trace_refs, "iterator_peel: %s: %d\n", diter->iter->ref.name, res);
return res;
}
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 0ddcf22aed..d34fbe55d6 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -962,26 +962,23 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
- parse_worktree_ref(iter->iter0->refname, NULL, NULL,
+ parse_worktree_ref(iter->iter0->ref.name, NULL, NULL,
NULL) != REF_WORKTREE_CURRENT)
continue;
if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) &&
- (iter->iter0->flags & REF_ISSYMREF) &&
- (iter->iter0->flags & REF_ISBROKEN))
+ (iter->iter0->ref.flags & REF_ISSYMREF) &&
+ (iter->iter0->ref.flags & REF_ISBROKEN))
continue;
if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
- !ref_resolves_to_object(iter->iter0->refname,
+ !ref_resolves_to_object(iter->iter0->ref.name,
iter->repo,
- iter->iter0->oid,
- iter->iter0->flags))
+ iter->iter0->ref.oid,
+ iter->iter0->ref.flags))
continue;
- iter->base.refname = iter->iter0->refname;
- iter->base.oid = iter->iter0->oid;
- iter->base.flags = iter->iter0->flags;
- iter->base.referent = iter->iter0->referent;
+ iter->base.ref = iter->iter0->ref;
return ITER_OK;
}
@@ -1368,30 +1365,29 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune **refs_
* Return true if the specified reference should be packed.
*/
static int should_pack_ref(struct files_ref_store *refs,
- const char *refname,
- const struct object_id *oid, unsigned int ref_flags,
+ const struct reference *ref,
struct pack_refs_opts *opts)
{
struct string_list_item *item;
/* Do not pack per-worktree refs: */
- if (parse_worktree_ref(refname, NULL, NULL, NULL) !=
+ if (parse_worktree_ref(ref->name, NULL, NULL, NULL) !=
REF_WORKTREE_SHARED)
return 0;
/* Do not pack symbolic refs: */
- if (ref_flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
return 0;
/* Do not pack broken refs: */
- if (!ref_resolves_to_object(refname, refs->base.repo, oid, ref_flags))
+ if (!ref_resolves_to_object(ref->name, refs->base.repo, ref->oid, ref->flags))
return 0;
- if (ref_excluded(opts->exclusions, refname))
+ if (ref_excluded(opts->exclusions, ref->name))
return 0;
for_each_string_list_item(item, opts->includes)
- if (!wildmatch(item->string, refname, 0))
+ if (!wildmatch(item->string, ref->name, 0))
return 1;
return 0;
@@ -1444,8 +1440,7 @@ static int should_pack_refs(struct files_ref_store *refs,
iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, 0), NULL,
refs->base.repo, 0);
while ((ret = ref_iterator_advance(iter)) == ITER_OK) {
- if (should_pack_ref(refs, iter->refname, iter->oid,
- iter->flags, opts))
+ if (should_pack_ref(refs, &iter->ref, opts))
refcount++;
if (refcount >= limit) {
ref_iterator_free(iter);
@@ -1490,24 +1485,24 @@ static int files_pack_refs(struct ref_store *ref_store,
* in the packed ref cache. If the reference should be
* pruned, also add it to refs_to_prune.
*/
- if (!should_pack_ref(refs, iter->refname, iter->oid, iter->flags, opts))
+ if (!should_pack_ref(refs, &iter->ref, opts))
continue;
/*
* Add a reference creation for this reference to the
* packed-refs transaction:
*/
- if (ref_transaction_update(transaction, iter->refname,
- iter->oid, NULL, NULL, NULL,
+ if (ref_transaction_update(transaction, iter->ref.name,
+ iter->ref.oid, NULL, NULL, NULL,
REF_NO_DEREF, NULL, &err))
die("failure preparing to create packed reference %s: %s",
- iter->refname, err.buf);
+ iter->ref.name, err.buf);
/* Schedule the loose reference for pruning if requested. */
if ((opts->flags & PACK_REFS_PRUNE)) {
struct ref_to_prune *n;
- FLEX_ALLOC_STR(n, name, iter->refname);
- oidcpy(&n->oid, iter->oid);
+ FLEX_ALLOC_STR(n, name, iter->ref.name);
+ oidcpy(&n->oid, iter->ref.oid);
n->next = refs_to_prune;
refs_to_prune = n;
}
@@ -2380,7 +2375,7 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
REFNAME_ALLOW_ONELEVEL))
continue;
- iter->base.refname = diter->relative_path;
+ iter->base.ref.name = diter->relative_path;
return ITER_OK;
}
diff --git a/refs/iterator.c b/refs/iterator.c
index 7f2e718f1c..fe5980e1b6 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -41,10 +41,7 @@ void base_ref_iterator_init(struct ref_iterator *iter,
struct ref_iterator_vtable *vtable)
{
iter->vtable = vtable;
- iter->refname = NULL;
- iter->referent = NULL;
- iter->oid = NULL;
- iter->flags = 0;
+ memset(&iter->ref, 0, sizeof(iter->ref));
}
struct empty_ref_iterator {
@@ -127,8 +124,8 @@ enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
* latter.
*/
if (iter_worktree) {
- int cmp = strcmp(iter_worktree->refname,
- iter_common->refname);
+ int cmp = strcmp(iter_worktree->ref.name,
+ iter_common->ref.name);
if (cmp < 0)
return ITER_SELECT_0;
else if (!cmp)
@@ -139,7 +136,7 @@ enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
* We now know that the lexicographically-next ref is a common
* ref. When the common ref is a shared one we return it.
*/
- if (parse_worktree_ref(iter_common->refname, NULL, NULL,
+ if (parse_worktree_ref(iter_common->ref.name, NULL, NULL,
NULL) == REF_WORKTREE_SHARED)
return ITER_SELECT_1;
@@ -212,10 +209,7 @@ static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
}
if (selection & ITER_YIELD_CURRENT) {
- iter->base.referent = (*iter->current)->referent;
- iter->base.refname = (*iter->current)->refname;
- iter->base.oid = (*iter->current)->oid;
- iter->base.flags = (*iter->current)->flags;
+ iter->base.ref = (*iter->current)->ref;
return ITER_OK;
}
}
@@ -313,7 +307,7 @@ static enum iterator_selection overlay_iterator_select(
else if (!front)
return ITER_SELECT_1;
- cmp = strcmp(front->refname, back->refname);
+ cmp = strcmp(front->ref.name, back->ref.name);
if (cmp < 0)
return ITER_SELECT_0;
@@ -371,7 +365,7 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
int ok;
while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
- int cmp = compare_prefix(iter->iter0->refname, iter->prefix);
+ int cmp = compare_prefix(iter->iter0->ref.name, iter->prefix);
if (cmp < 0)
continue;
/*
@@ -382,6 +376,8 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
if (cmp > 0)
return ITER_DONE;
+ iter->base.ref = iter->iter0->ref;
+
if (iter->trim) {
/*
* It is nonsense to trim off characters that
@@ -392,15 +388,11 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
* one character left in the refname after
* trimming, report it as a bug:
*/
- if (strlen(iter->iter0->refname) <= iter->trim)
+ if (strlen(iter->base.ref.name) <= iter->trim)
BUG("attempt to trim too many characters");
- iter->base.refname = iter->iter0->refname + iter->trim;
- } else {
- iter->base.refname = iter->iter0->refname;
+ iter->base.ref.name += iter->trim;
}
- iter->base.oid = iter->iter0->oid;
- iter->base.flags = iter->iter0->flags;
return ITER_OK;
}
@@ -476,14 +468,7 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
- struct reference ref = {
- .name = iter->refname,
- .target = iter->referent,
- .oid = iter->oid,
- .flags = iter->flags,
- };
-
- retval = fn(&ref, cb_data);
+ retval = fn(&iter->ref, cb_data);
if (retval)
goto out;
}
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index a8c22a0a7f..7987acdc96 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -908,7 +908,7 @@ static int next_record(struct packed_ref_iterator *iter)
if (iter->pos == iter->eof)
return ITER_DONE;
- iter->base.flags = REF_ISPACKED;
+ iter->base.ref.flags = REF_ISPACKED;
p = iter->pos;
if (iter->eof - p < snapshot_hexsz(iter->snapshot) + 2 ||
@@ -923,22 +923,22 @@ static int next_record(struct packed_ref_iterator *iter)
iter->pos, iter->eof - iter->pos);
strbuf_add(&iter->refname_buf, p, eol - p);
- iter->base.refname = iter->refname_buf.buf;
+ iter->base.ref.name = iter->refname_buf.buf;
if (refname_contains_nul(&iter->refname_buf))
- die("packed refname contains embedded NULL: %s", iter->base.refname);
+ die("packed refname contains embedded NULL: %s", iter->base.ref.name);
- if (check_refname_format(iter->base.refname, REFNAME_ALLOW_ONELEVEL)) {
- if (!refname_is_safe(iter->base.refname))
+ if (check_refname_format(iter->base.ref.name, REFNAME_ALLOW_ONELEVEL)) {
+ if (!refname_is_safe(iter->base.ref.name))
die("packed refname is dangerous: %s",
- iter->base.refname);
+ iter->base.ref.name);
oidclr(&iter->oid, iter->repo->hash_algo);
- iter->base.flags |= REF_BAD_NAME | REF_ISBROKEN;
+ iter->base.ref.flags |= REF_BAD_NAME | REF_ISBROKEN;
}
if (iter->snapshot->peeled == PEELED_FULLY ||
(iter->snapshot->peeled == PEELED_TAGS &&
- starts_with(iter->base.refname, "refs/tags/")))
- iter->base.flags |= REF_KNOWS_PEELED;
+ starts_with(iter->base.ref.name, "refs/tags/")))
+ iter->base.ref.flags |= REF_KNOWS_PEELED;
iter->pos = eol + 1;
@@ -956,11 +956,11 @@ static int next_record(struct packed_ref_iterator *iter)
* definitely know the value of *this* reference. But
* we suppress it if the reference is broken:
*/
- if ((iter->base.flags & REF_ISBROKEN)) {
+ if ((iter->base.ref.flags & REF_ISBROKEN)) {
oidclr(&iter->peeled, iter->repo->hash_algo);
- iter->base.flags &= ~REF_KNOWS_PEELED;
+ iter->base.ref.flags &= ~REF_KNOWS_PEELED;
} else {
- iter->base.flags |= REF_KNOWS_PEELED;
+ iter->base.ref.flags |= REF_KNOWS_PEELED;
}
} else {
oidclr(&iter->peeled, iter->repo->hash_algo);
@@ -976,15 +976,15 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
int ok;
while ((ok = next_record(iter)) == ITER_OK) {
- const char *refname = iter->base.refname;
+ const char *refname = iter->base.ref.name;
const char *prefix = iter->prefix;
if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
- !is_per_worktree_ref(iter->base.refname))
+ !is_per_worktree_ref(iter->base.ref.name))
continue;
if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
- !ref_resolves_to_object(iter->base.refname, iter->repo,
+ !ref_resolves_to_object(iter->base.ref.name, iter->repo,
&iter->oid, iter->flags))
continue;
@@ -1033,10 +1033,10 @@ static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct packed_ref_iterator *iter =
(struct packed_ref_iterator *)ref_iterator;
- if ((iter->base.flags & REF_KNOWS_PEELED)) {
+ if ((iter->base.ref.flags & REF_KNOWS_PEELED)) {
oidcpy(peeled, &iter->peeled);
return is_null_oid(&iter->peeled) ? -1 : 0;
- } else if ((iter->base.flags & (REF_ISBROKEN | REF_ISSYMREF))) {
+ } else if ((iter->base.ref.flags & (REF_ISBROKEN | REF_ISSYMREF))) {
return -1;
} else {
return peel_object(iter->repo, &iter->oid, peeled) ? -1 : 0;
@@ -1194,7 +1194,7 @@ static struct ref_iterator *packed_ref_iterator_begin(
iter->snapshot = snapshot;
acquire_snapshot(snapshot);
strbuf_init(&iter->refname_buf, 0);
- iter->base.oid = &iter->oid;
+ iter->base.ref.oid = &iter->oid;
iter->repo = ref_store->repo;
iter->flags = flags;
@@ -1436,7 +1436,7 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
if (!iter)
cmp = +1;
else
- cmp = strcmp(iter->refname, update->refname);
+ cmp = strcmp(iter->ref.name, update->refname);
}
if (!cmp) {
@@ -1459,11 +1459,11 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
}
goto error;
- } else if (!oideq(&update->old_oid, iter->oid)) {
+ } else if (!oideq(&update->old_oid, iter->ref.oid)) {
strbuf_addf(err, "cannot update ref '%s': "
"is at %s but expected %s",
update->refname,
- oid_to_hex(iter->oid),
+ oid_to_hex(iter->ref.oid),
oid_to_hex(&update->old_oid));
ret = REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE;
@@ -1527,8 +1527,8 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
struct object_id peeled;
int peel_error = ref_iterator_peel(iter, &peeled);
- if (write_packed_entry(out, iter->refname,
- iter->oid,
+ if (write_packed_entry(out, iter->ref.name,
+ iter->ref.oid,
peel_error ? NULL : &peeled))
goto write_error;
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index c180e0aad7..97555fa118 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -425,10 +425,10 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
level->prefix_state = entry_prefix_state;
level->index = -1;
} else {
- iter->base.refname = entry->name;
- iter->base.referent = entry->u.value.referent;
- iter->base.oid = &entry->u.value.oid;
- iter->base.flags = entry->flag;
+ iter->base.ref.name = entry->name;
+ iter->base.ref.target = entry->u.value.referent;
+ iter->base.ref.oid = &entry->u.value.oid;
+ iter->base.ref.flags = entry->flag;
return ITER_OK;
}
}
@@ -550,7 +550,7 @@ static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
{
struct cache_ref_iterator *iter =
(struct cache_ref_iterator *)ref_iterator;
- return peel_object(iter->repo, ref_iterator->oid, peeled) ? -1 : 0;
+ return peel_object(iter->repo, ref_iterator->ref.oid, peeled) ? -1 : 0;
}
static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 4ef3bd75c6..ed749d1657 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -249,10 +249,7 @@ const char *find_descendant_ref(const char *dirname,
*/
struct ref_iterator {
struct ref_iterator_vtable *vtable;
- const char *refname;
- const char *referent;
- const struct object_id *oid;
- unsigned int flags;
+ struct reference ref;
};
/*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 9884b876c1..7fbc77492e 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -703,10 +703,10 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
&iter->oid, flags))
continue;
- iter->base.refname = iter->ref.refname;
- iter->base.referent = referent;
- iter->base.oid = &iter->oid;
- iter->base.flags = flags;
+ iter->base.ref.name = iter->ref.refname;
+ iter->base.ref.target = referent;
+ iter->base.ref.oid = &iter->oid;
+ iter->base.ref.flags = flags;
break;
}
@@ -827,7 +827,7 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
iter = xcalloc(1, sizeof(*iter));
base_ref_iterator_init(&iter->base, &reftable_ref_iterator_vtable);
- iter->base.oid = &iter->oid;
+ iter->base.ref.oid = &iter->oid;
iter->flags = flags;
iter->refs = refs;
iter->exclude_patterns = filter_exclude_patterns(exclude_patterns);
@@ -2071,7 +2071,7 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
strbuf_reset(&iter->last_name);
strbuf_addstr(&iter->last_name, iter->log.refname);
- iter->base.refname = iter->log.refname;
+ iter->base.ref.name = iter->log.refname;
break;
}
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v2 03/14] refs: fully reset `struct ref_iterator::ref` on iteration
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 01/14] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 02/14] refs: introduce `.ref` field for the base iterator Patrick Steinhardt
@ 2025-10-08 15:50 ` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 04/14] refs: refactor reference status flags Patrick Steinhardt
` (11 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 15:50 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
With the introduction of the `struct ref_iterator::ref` field it now is
a whole lot easier to introduce new fields that become accessible to the
caller without having to adapt every single callsite. But there's a
downside: when a new field is introduced we always have to adapt all
backends to set that field.
This isn't something we can avoid in the general case: when the new
field is expected to be populated by all backends we of course cannot
avoid doing so. But new fields may be entirely optional, in which case
we'd still have such churn. And furthermore, it is very easy right now
to leak state from a previous iteration into the next iteration.
Address this issue by ensuring that the reference backends all fully
reset the field on every single iteration. This ensures that no state
from previous iterations can leak into the next one. And it ensures that
any newly introduced fields will be zeroed out by default.
Note that we don't have to explicitly adapt the "files" backend, as it
uses the `cache_ref_iterator` internally. Furthermore, other "wrapping"
iterators like for example the `prefix_ref_iterator` copy around the
whole reference, so these don't need to be adapted either.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs/packed-backend.c | 3 ++-
refs/ref-cache.c | 1 +
refs/reftable-backend.c | 1 +
3 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 7987acdc96..711e07f832 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -882,6 +882,7 @@ static int next_record(struct packed_ref_iterator *iter)
{
const char *p, *eol;
+ memset(&iter->base.ref, 0, sizeof(iter->base.ref));
strbuf_reset(&iter->refname_buf);
/*
@@ -916,6 +917,7 @@ static int next_record(struct packed_ref_iterator *iter)
!isspace(*p++))
die_invalid_line(iter->snapshot->refs->path,
iter->pos, iter->eof - iter->pos);
+ iter->base.ref.oid = &iter->oid;
eol = memchr(p, '\n', iter->eof - p);
if (!eol)
@@ -1194,7 +1196,6 @@ static struct ref_iterator *packed_ref_iterator_begin(
iter->snapshot = snapshot;
acquire_snapshot(snapshot);
strbuf_init(&iter->refname_buf, 0);
- iter->base.ref.oid = &iter->oid;
iter->repo = ref_store->repo;
iter->flags = flags;
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index 97555fa118..4726de430d 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -425,6 +425,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
level->prefix_state = entry_prefix_state;
level->index = -1;
} else {
+ memset(&iter->base.ref, 0, sizeof(iter->base.ref));
iter->base.ref.name = entry->name;
iter->base.ref.target = entry->u.value.referent;
iter->base.ref.oid = &entry->u.value.oid;
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 7fbc77492e..1e047fddae 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -703,6 +703,7 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
&iter->oid, flags))
continue;
+ memset(&iter->base.ref, 0, sizeof(iter->base.ref));
iter->base.ref.name = iter->ref.refname;
iter->base.ref.target = referent;
iter->base.ref.oid = &iter->oid;
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v2 04/14] refs: refactor reference status flags
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
` (2 preceding siblings ...)
2025-10-08 15:50 ` [PATCH v2 03/14] refs: fully reset `struct ref_iterator::ref` on iteration Patrick Steinhardt
@ 2025-10-08 15:50 ` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 05/14] refs: expose peeled object ID via the iterator Patrick Steinhardt
` (10 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 15:50 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
The reference flags encode information like whether or not a reference
is a symbolic reference or whether it may be broken. This information is
stored in a `int flags` bitfield, which is in conflict with our modern
best practices; we tend to use an unsigned integer to store flags.
Change the type of the field to be `unsigned`. While at it, refactor the
individual flags to be part of an `enum` instead of using preprocessor
defines.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs.h | 41 +++++++++++++++++++++--------------------
1 file changed, 21 insertions(+), 20 deletions(-)
diff --git a/refs.h b/refs.h
index 68d235438c..4f0a685714 100644
--- a/refs.h
+++ b/refs.h
@@ -333,27 +333,28 @@ struct ref_transaction;
* stored in ref_iterator::flags. Other bits are for internal use
* only:
*/
+enum reference_status {
+ /* Reference is a symbolic reference. */
+ REF_ISSYMREF = (1 << 0),
-/* Reference is a symbolic reference. */
-#define REF_ISSYMREF 0x01
+ /* Reference is a packed reference. */
+ REF_ISPACKED = (1 << 1),
-/* Reference is a packed reference. */
-#define REF_ISPACKED 0x02
-
-/*
- * Reference cannot be resolved to an object name: dangling symbolic
- * reference (directly or indirectly), corrupt reference file,
- * reference exists but name is bad, or symbolic reference refers to
- * ill-formatted reference name.
- */
-#define REF_ISBROKEN 0x04
+ /*
+ * Reference cannot be resolved to an object name: dangling symbolic
+ * reference (directly or indirectly), corrupt reference file,
+ * reference exists but name is bad, or symbolic reference refers to
+ * ill-formatted reference name.
+ */
+ REF_ISBROKEN = (1 << 2),
-/*
- * Reference name is not well formed.
- *
- * See git-check-ref-format(1) for the definition of well formed ref names.
- */
-#define REF_BAD_NAME 0x08
+ /*
+ * Reference name is not well formed.
+ *
+ * See git-check-ref-format(1) for the definition of well formed ref names.
+ */
+ REF_BAD_NAME = (1 << 3),
+};
/* A reference passed to `for_each_ref()`-style callbacks. */
struct reference {
@@ -370,8 +371,8 @@ struct reference {
*/
const struct object_id *oid;
- /* A bitfield of `REF_` flags. */
- int flags;
+ /* A bitfield of `enum reference_status` flags. */
+ unsigned flags;
};
/*
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v2 05/14] refs: expose peeled object ID via the iterator
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
` (3 preceding siblings ...)
2025-10-08 15:50 ` [PATCH v2 04/14] refs: refactor reference status flags Patrick Steinhardt
@ 2025-10-08 15:50 ` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 06/14] upload-pack: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
` (9 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 15:50 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
Both the "files" and "reftable" backend are able to store peeled values
for tags in the respective formats. This allows for a more efficient
lookup of the target object of such a tag without having to manually
peel via the object database.
The infrastructure to access these peeled object IDs is somewhat funky
though. When iterating through objects, we store a pointer reference to
the current iterator in a global variable. The callbacks invoked by that
iterator are then expected to call `peel_iterated_oid()`, which checks
whether the globally-stored iterator's current reference refers to the
one handed into that function. If so, we ask the iterator to peel the
object, otherwise we manually peel the object via the object database.
Depending on global state like this is somewhat weird and also quite
fragile.
Introduce a new `struct reference::peeled_oid` field that can be
populated by the reference backends. This field can be accessed via a
new function `reference_get_peeled_oid()` that either uses that value,
if set, or alternatively peels via the ODB. With this change we don't
have to rely on global state anymore, but make the peeled object ID
available to the callback functions directly.
Adjust trivial callers that already have a `struct reference` available.
Remaining callers will be adjusted in subsequent commits.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/describe.c | 2 +-
builtin/gc.c | 2 +-
builtin/pack-objects.c | 7 ++++---
builtin/repack.c | 2 +-
commit-graph.c | 2 +-
ls-refs.c | 2 +-
midx-write.c | 2 +-
pseudo-merge.c | 2 +-
refs.c | 12 ++++++++++++
refs.h | 19 +++++++++++++++++++
refs/packed-backend.c | 1 +
refs/reftable-backend.c | 5 +++++
12 files changed, 48 insertions(+), 10 deletions(-)
diff --git a/builtin/describe.c b/builtin/describe.c
index 7954535044..443546aaac 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -208,7 +208,7 @@ static int get_name(const struct reference *ref, void *cb_data UNUSED)
}
/* Is it annotated? */
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled)) {
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
is_annotated = !oideq(ref->oid, &peeled);
} else {
oidcpy(&peeled, ref->oid);
diff --git a/builtin/gc.c b/builtin/gc.c
index a104c1cb78..814be8d1a9 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -1107,7 +1107,7 @@ static int dfs_on_ref(const struct reference *ref, void *cb_data)
struct commit_list *stack = NULL;
struct commit *commit;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
if (odb_read_object_info(the_repository->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 288ba19981..7fd457779d 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -838,7 +838,7 @@ static int mark_tagged(const struct reference *ref, void *cb_data UNUSED)
if (entry)
entry->tagged = 1;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled)) {
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
entry = packlist_find(&to_pack, &peeled);
if (entry)
entry->tagged = 1;
@@ -3309,7 +3309,8 @@ static int add_ref_tag(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled) && obj_is_packed(&peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled) &&
+ obj_is_packed(&peeled))
add_tag_chain(ref->oid);
return 0;
}
@@ -4532,7 +4533,7 @@ static int mark_bitmap_preferred_tip(const struct reference *ref, void *data UNU
struct object_id peeled;
struct object *object;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
object = parse_object_or_die(the_repository, maybe_peeled, ref->name);
diff --git a/builtin/repack.c b/builtin/repack.c
index 893c105c81..873e21c35d 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -779,7 +779,7 @@ static int midx_snapshot_ref_one(const struct reference *ref, void *_data)
const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
if (oidset_insert(&data->seen, maybe_peeled))
diff --git a/commit-graph.c b/commit-graph.c
index 0cfe16ad08..bfd8b187f0 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1852,7 +1852,7 @@ static int add_ref_to_set(const struct reference *ref, void *cb_data)
struct object_id peeled;
struct refs_cb_data *data = (struct refs_cb_data *)cb_data;
- if (!peel_iterated_oid(data->repo, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(data->repo, ref, &peeled))
maybe_peeled = &peeled;
if (odb_read_object_info(data->repo->objects, maybe_peeled, NULL) == OBJ_COMMIT)
oidset_insert(data->commits, maybe_peeled);
diff --git a/ls-refs.c b/ls-refs.c
index 64d0272369..8641281b86 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -110,7 +110,7 @@ static int send_ref(const struct reference *ref, void *cb_data)
if (data->peel && ref->oid) {
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
strbuf_addf(&data->buf, " peeled:%s", oid_to_hex(&peeled));
}
diff --git a/midx-write.c b/midx-write.c
index f4dd875747..23e61cb000 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -709,7 +709,7 @@ static int add_ref_to_pending(const struct reference *ref, void *cb_data)
return 0;
}
- if (!peel_iterated_oid(revs->repo, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(revs->repo, ref, &peeled))
maybe_peeled = &peeled;
object = parse_object_or_die(revs->repo, maybe_peeled, ref->name);
diff --git a/pseudo-merge.c b/pseudo-merge.c
index 0abd51b42c..a2d5bd85f9 100644
--- a/pseudo-merge.c
+++ b/pseudo-merge.c
@@ -230,7 +230,7 @@ static int find_pseudo_merge_group_for_ref(const struct reference *ref, void *_d
uint32_t i;
int has_bitmap;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
c = lookup_commit(the_repository, maybe_peeled);
diff --git a/refs.c b/refs.c
index 15ad0ef7a8..5002e56435 100644
--- a/refs.c
+++ b/refs.c
@@ -2333,6 +2333,18 @@ int peel_iterated_oid(struct repository *r, const struct object_id *base, struct
return peel_object(r, base, peeled) ? -1 : 0;
}
+int reference_get_peeled_oid(struct repository *repo,
+ const struct reference *ref,
+ struct object_id *peeled_oid)
+{
+ if (ref->peeled_oid) {
+ oidcpy(peeled_oid, ref->peeled_oid);
+ return 0;
+ }
+
+ return peel_object(repo, ref->oid, peeled_oid) ? -1 : 0;
+}
+
int refs_update_symref(struct ref_store *refs, const char *ref,
const char *target, const char *logmsg)
{
diff --git a/refs.h b/refs.h
index 4f0a685714..886ed2c0f4 100644
--- a/refs.h
+++ b/refs.h
@@ -371,10 +371,29 @@ struct reference {
*/
const struct object_id *oid;
+ /*
+ * An optional peeled object ID. This field _may_ be set for tags in
+ * case the peeled value is present in the backend. Please refer to
+ * `reference_get_peeled_oid()`.
+ */
+ const struct object_id *peeled_oid;
+
/* A bitfield of `enum reference_status` flags. */
unsigned flags;
};
+/*
+ * Peel the tag to a non-tag commit. If present, this uses the peeled object ID
+ * exposed by the reference backend. Otherwise, the object is peeled via the
+ * object database, which is less efficient.
+ *
+ * Return `0` if the reference could be peeled, a negative error code
+ * otherwise.
+ */
+int reference_get_peeled_oid(struct repository *repo,
+ const struct reference *ref,
+ struct object_id *peeled_oid);
+
/*
* The signature for the callback function for the for_each_*()
* functions below. The memory pointed to by the `struct reference`
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 711e07f832..1fefefd54e 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -963,6 +963,7 @@ static int next_record(struct packed_ref_iterator *iter)
iter->base.ref.flags &= ~REF_KNOWS_PEELED;
} else {
iter->base.ref.flags |= REF_KNOWS_PEELED;
+ iter->base.ref.peeled_oid = &iter->peeled;
}
} else {
oidclr(&iter->peeled, iter->repo->hash_algo);
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 1e047fddae..0468f62a7e 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -546,6 +546,7 @@ struct reftable_ref_iterator {
struct reftable_iterator iter;
struct reftable_ref_record ref;
struct object_id oid;
+ struct object_id peeled_oid;
char *prefix;
size_t prefix_len;
@@ -670,6 +671,8 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
case REFTABLE_REF_VAL2:
oidread(&iter->oid, iter->ref.value.val2.value,
refs->base.repo->hash_algo);
+ oidread(&iter->peeled_oid, iter->ref.value.val2.target_value,
+ refs->base.repo->hash_algo);
break;
case REFTABLE_REF_SYMREF:
referent = refs_resolve_ref_unsafe(&iter->refs->base,
@@ -707,6 +710,8 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
iter->base.ref.name = iter->ref.refname;
iter->base.ref.target = referent;
iter->base.ref.oid = &iter->oid;
+ if (iter->ref.value_type == REFTABLE_REF_VAL2)
+ iter->base.ref.peeled_oid = &iter->peeled_oid;
iter->base.ref.flags = flags;
break;
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v2 06/14] upload-pack: convert to use `reference_get_peeled_oid()`
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
` (4 preceding siblings ...)
2025-10-08 15:50 ` [PATCH v2 05/14] refs: expose peeled object ID via the iterator Patrick Steinhardt
@ 2025-10-08 15:50 ` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 07/14] ref-filter: propagate peeled object ID Patrick Steinhardt
` (8 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 15:50 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
The `write_v0_ref()` callback is invoked from two callsites:
- Once via `send_ref()` which is a callback passed to
`for_each_namespaced_ref_1()` and `refs_head_ref_namespaced()`.
- Once manually to announce capabilities.
When sending references to the client we also send the peeled value of
tags. As we don't have a `struct reference` available in the second
case, we cannot easily peel by calling `reference_get_peeled_oid()`, but
we instead have to depend on on global state via `peel_iterated_oid()`.
We do have a reference available though in the first case, it's only the
second case that keeps us from using `reference_get_peeled_oid()`. But
that second case only announces capabilities anyway, so we're not really
handling a reference at all here.
Adapt that case to construct a reference manually and pass that to
`write_v0_ref()`. Start to use `reference_get_peeled_oid()` now that we
always have a `struct reference` available.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
upload-pack.c | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/upload-pack.c b/upload-pack.c
index 0d563ae74e..2d2b70cbf2 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -1249,15 +1249,15 @@ static void format_session_id(struct strbuf *buf, struct upload_pack_data *d) {
}
static void write_v0_ref(struct upload_pack_data *data,
- const char *refname, const char *refname_nons,
- const struct object_id *oid)
+ const struct reference *ref,
+ const char *refname_nons)
{
static const char *capabilities = "multi_ack thin-pack side-band"
" side-band-64k ofs-delta shallow deepen-since deepen-not"
" deepen-relative no-progress include-tag multi_ack_detailed";
struct object_id peeled;
- if (mark_our_ref(refname_nons, refname, oid, &data->hidden_refs))
+ if (mark_our_ref(refname_nons, ref->name, ref->oid, &data->hidden_refs))
return;
if (capabilities) {
@@ -1267,7 +1267,7 @@ static void write_v0_ref(struct upload_pack_data *data,
format_symref_info(&symref_info, &data->symref);
format_session_id(&session_id, data);
packet_fwrite_fmt(stdout, "%s %s%c%s%s%s%s%s%s%s object-format=%s agent=%s\n",
- oid_to_hex(oid), refname_nons,
+ oid_to_hex(ref->oid), refname_nons,
0, capabilities,
(data->allow_uor & ALLOW_TIP_SHA1) ?
" allow-tip-sha1-in-want" : "",
@@ -1283,17 +1283,17 @@ static void write_v0_ref(struct upload_pack_data *data,
strbuf_release(&session_id);
data->sent_capabilities = 1;
} else {
- packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(oid), refname_nons);
+ packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(ref->oid), refname_nons);
}
capabilities = NULL;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
packet_fwrite_fmt(stdout, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
return;
}
static int send_ref(const struct reference *ref, void *cb_data)
{
- write_v0_ref(cb_data, ref->name, strip_namespace(ref->name), ref->oid);
+ write_v0_ref(cb_data, ref, strip_namespace(ref->name));
return 0;
}
@@ -1442,8 +1442,12 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
send_ref, &data);
for_each_namespaced_ref_1(send_ref, &data);
if (!data.sent_capabilities) {
- const char *refname = "capabilities^{}";
- write_v0_ref(&data, refname, refname, null_oid(the_hash_algo));
+ struct reference ref = {
+ .name = "capabilities^{}",
+ .oid = null_oid(the_hash_algo),
+ };
+
+ write_v0_ref(&data, &ref, ref.name);
}
/*
* fflush stdout before calling advertise_shallow_grafts because send_ref
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v2 07/14] ref-filter: propagate peeled object ID
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
` (5 preceding siblings ...)
2025-10-08 15:50 ` [PATCH v2 06/14] upload-pack: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
@ 2025-10-08 15:50 ` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 08/14] builtin/show-ref: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
` (7 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 15:50 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
When queueing a reference in the "ref-filter" subsystem we end up
creating a new ref array item that contains the reference's info. One
bit of info that we always discard though is the peeled object ID, and
because of that we are forced to use `peel_iterated_oid()`.
Refactor the code to propagate the peeled object ID via the ref array,
if available. This allows us to manually peel tags without having to go
through the object database.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/ls-remote.c | 2 +-
builtin/tag.c | 2 +-
builtin/verify-tag.c | 2 +-
ref-filter.c | 66 ++++++++++++++++++++++++++++++----------------------
ref-filter.h | 5 +++-
5 files changed, 45 insertions(+), 32 deletions(-)
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index df09000b30..fe77829557 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -156,7 +156,7 @@ int cmd_ls_remote(int argc,
continue;
if (!tail_match(&pattern, ref->name))
continue;
- item = ref_array_push(&ref_array, ref->name, &ref->old_oid);
+ item = ref_array_push(&ref_array, ref->name, &ref->old_oid, NULL);
item->symref = xstrdup_or_null(ref->symref);
}
diff --git a/builtin/tag.c b/builtin/tag.c
index f0665af3ac..01eba90c5c 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -153,7 +153,7 @@ static int verify_tag(const char *name, const char *ref UNUSED,
return -1;
if (format->format)
- pretty_print_ref(name, oid, format);
+ pretty_print_ref(name, oid, NULL, format);
return 0;
}
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index cd6bc11095..558121eaa1 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -67,7 +67,7 @@ int cmd_verify_tag(int argc,
}
if (format.format)
- pretty_print_ref(name, &oid, &format);
+ pretty_print_ref(name, &oid, NULL, &format);
}
return had_error;
}
diff --git a/ref-filter.c b/ref-filter.c
index 7740f35e93..b18a032e57 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2578,8 +2578,15 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
* If it is a tag object, see if we use the peeled value. If we do,
* grab the peeled OID.
*/
- if (need_tagged && peel_iterated_oid(the_repository, &obj->oid, &oi_deref.oid))
- die("bad tag");
+ if (need_tagged) {
+ if (!is_null_oid(&ref->peeled_oid)) {
+ oidcpy(&oi_deref.oid, &ref->peeled_oid);
+ } else if (!peel_object(the_repository, &obj->oid, &oi_deref.oid)) {
+ /* We managed to peel the object ourselves. */
+ } else {
+ die("bad tag");
+ }
+ }
return get_object(ref, 1, &obj, &oi_deref, err);
}
@@ -2807,12 +2814,15 @@ static int match_points_at(struct oid_array *points_at,
* Callers can then fill in other struct members at their leisure.
*/
static struct ref_array_item *new_ref_array_item(const char *refname,
- const struct object_id *oid)
+ const struct object_id *oid,
+ const struct object_id *peeled_oid)
{
struct ref_array_item *ref;
FLEX_ALLOC_STR(ref, refname, refname);
oidcpy(&ref->objectname, oid);
+ if (peeled_oid)
+ oidcpy(&ref->peeled_oid, peeled_oid);
ref->rest = NULL;
return ref;
@@ -2826,9 +2836,10 @@ static void ref_array_append(struct ref_array *array, struct ref_array_item *ref
struct ref_array_item *ref_array_push(struct ref_array *array,
const char *refname,
- const struct object_id *oid)
+ const struct object_id *oid,
+ const struct object_id *peeled_oid)
{
- struct ref_array_item *ref = new_ref_array_item(refname, oid);
+ struct ref_array_item *ref = new_ref_array_item(refname, oid, peeled_oid);
ref_array_append(array, ref);
return ref;
}
@@ -2871,25 +2882,25 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)
return ref_kind_from_refname(refname);
}
-static struct ref_array_item *apply_ref_filter(const char *refname, const char *referent, const struct object_id *oid,
- int flag, struct ref_filter *filter)
+static struct ref_array_item *apply_ref_filter(const struct reference *ref,
+ struct ref_filter *filter)
{
- struct ref_array_item *ref;
+ struct ref_array_item *item;
struct commit *commit = NULL;
unsigned int kind;
- if (flag & REF_BAD_NAME) {
- warning(_("ignoring ref with broken name %s"), refname);
+ if (ref->flags & REF_BAD_NAME) {
+ warning(_("ignoring ref with broken name %s"), ref->name);
return NULL;
}
- if (flag & REF_ISBROKEN) {
- warning(_("ignoring broken ref %s"), refname);
+ if (ref->flags & REF_ISBROKEN) {
+ warning(_("ignoring broken ref %s"), ref->name);
return NULL;
}
/* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */
- kind = filter_ref_kind(filter, refname);
+ kind = filter_ref_kind(filter, ref->name);
/*
* Generally HEAD refs are printed with special description denoting a rebase,
@@ -2902,13 +2913,13 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const char *
else if (!(kind & filter->kind))
return NULL;
- if (!filter_pattern_match(filter, refname))
+ if (!filter_pattern_match(filter, ref->name))
return NULL;
- if (filter_exclude_match(filter, refname))
+ if (filter_exclude_match(filter, ref->name))
return NULL;
- if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname))
+ if (filter->points_at.nr && !match_points_at(&filter->points_at, ref->oid, ref->name))
return NULL;
/*
@@ -2918,7 +2929,7 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const char *
*/
if (filter->reachable_from || filter->unreachable_from ||
filter->with_commit || filter->no_commit || filter->verbose) {
- commit = lookup_commit_reference_gently(the_repository, oid, 1);
+ commit = lookup_commit_reference_gently(the_repository, ref->oid, 1);
if (!commit)
return NULL;
/* We perform the filtering for the '--contains' option... */
@@ -2936,13 +2947,13 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const char *
* to do its job and the resulting list may yet to be pruned
* by maxcount logic.
*/
- ref = new_ref_array_item(refname, oid);
- ref->commit = commit;
- ref->flag = flag;
- ref->kind = kind;
- ref->symref = xstrdup_or_null(referent);
+ item = new_ref_array_item(ref->name, ref->oid, ref->peeled_oid);
+ item->commit = commit;
+ item->flag = ref->flags;
+ item->kind = kind;
+ item->symref = xstrdup_or_null(ref->target);
- return ref;
+ return item;
}
struct ref_filter_cbdata {
@@ -2959,8 +2970,7 @@ static int filter_one(const struct reference *ref, void *cb_data)
struct ref_filter_cbdata *ref_cbdata = cb_data;
struct ref_array_item *item;
- item = apply_ref_filter(ref->name, ref->target, ref->oid,
- ref->flags, ref_cbdata->filter);
+ item = apply_ref_filter(ref, ref_cbdata->filter);
if (item)
ref_array_append(ref_cbdata->array, item);
@@ -2997,8 +3007,7 @@ static int filter_and_format_one(const struct reference *ref, void *cb_data)
struct ref_array_item *item;
struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
- item = apply_ref_filter(ref->name, ref->target, ref->oid,
- ref->flags, ref_cbdata->filter);
+ item = apply_ref_filter(ref, ref_cbdata->filter);
if (!item)
return 0;
@@ -3585,13 +3594,14 @@ void print_formatted_ref_array(struct ref_array *array, struct ref_format *forma
}
void pretty_print_ref(const char *name, const struct object_id *oid,
+ const struct object_id *peeled_oid,
struct ref_format *format)
{
struct ref_array_item *ref_item;
struct strbuf output = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
- ref_item = new_ref_array_item(name, oid);
+ ref_item = new_ref_array_item(name, oid, peeled_oid);
ref_item->kind = ref_kind_from_refname(name);
if (format_ref_array_item(ref_item, format, &output, &err))
die("%s", err.buf);
diff --git a/ref-filter.h b/ref-filter.h
index 81f2c229a9..1126826201 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -41,6 +41,7 @@ enum ref_sorting_order {
struct ref_array_item {
struct object_id objectname;
+ struct object_id peeled_oid;
const char *rest;
int flag;
unsigned int kind;
@@ -185,6 +186,7 @@ void print_formatted_ref_array(struct ref_array *array, struct ref_format *forma
* name must be a fully qualified refname.
*/
void pretty_print_ref(const char *name, const struct object_id *oid,
+ const struct object_id *peeled_oid,
struct ref_format *format);
/*
@@ -193,7 +195,8 @@ void pretty_print_ref(const char *name, const struct object_id *oid,
*/
struct ref_array_item *ref_array_push(struct ref_array *array,
const char *refname,
- const struct object_id *oid);
+ const struct object_id *oid,
+ const struct object_id *peeled_oid);
/*
* If the provided format includes ahead-behind atoms, then compute the
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v2 08/14] builtin/show-ref: convert to use `reference_get_peeled_oid()`
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
` (6 preceding siblings ...)
2025-10-08 15:50 ` [PATCH v2 07/14] ref-filter: propagate peeled object ID Patrick Steinhardt
@ 2025-10-08 15:50 ` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 09/14] refs: drop `current_ref_iter` hack Patrick Steinhardt
` (6 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 15:50 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
The git-show-ref(1) command has multiple different modes:
- It knows to show all references matching a pattern.
- It knows to list all references that are an exact match to whatever
the user has provided.
- It knows to check for reference existence.
The first two commands use mostly the same infrastructure to print the
references via `show_one()`. But while the former mode uses a proper
iterator and thus has a `struct reference` available in its context, the
latter calls `refs_read_ref()` and thus doesn't. Consequently, we cannot
easily use `reference_get_peeled_oid()` to print the peeled value.
Adapt the code so that we manually construct a `struct reference` when
verifying refs. We wouldn't ever have the peeled value available anyway
as we're not using an iterator here, so we can simply plug in the values
we _do_ have.
With this change we now have a `struct reference` available at both
callsites of `show_one()` and can thus pass it, which allows us to use
`reference_get_peeled_oid()` instead of `peel_iterated_oid()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/show-ref.c | 32 +++++++++++++++++++-------------
1 file changed, 19 insertions(+), 13 deletions(-)
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 4803b5e598..4d4984e4e0 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -31,31 +31,31 @@ struct show_one_options {
};
static void show_one(const struct show_one_options *opts,
- const char *refname, const struct object_id *oid)
+ const struct reference *ref)
{
const char *hex;
struct object_id peeled;
- if (!odb_has_object(the_repository->objects, oid,
+ if (!odb_has_object(the_repository->objects, ref->oid,
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
- die("git show-ref: bad ref %s (%s)", refname,
- oid_to_hex(oid));
+ die("git show-ref: bad ref %s (%s)", ref->name,
+ oid_to_hex(ref->oid));
if (opts->quiet)
return;
- hex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev);
+ hex = repo_find_unique_abbrev(the_repository, ref->oid, opts->abbrev);
if (opts->hash_only)
printf("%s\n", hex);
else
- printf("%s %s\n", hex, refname);
+ printf("%s %s\n", hex, ref->name);
if (!opts->deref_tags)
return;
- if (!peel_iterated_oid(the_repository, oid, &peeled)) {
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
hex = repo_find_unique_abbrev(the_repository, &peeled, opts->abbrev);
- printf("%s %s^{}\n", hex, refname);
+ printf("%s %s^{}\n", hex, ref->name);
}
}
@@ -93,7 +93,7 @@ static int show_ref(const struct reference *ref, void *cbdata)
match:
data->found_match++;
- show_one(data->show_one_opts, ref->name, ref->oid);
+ show_one(data->show_one_opts, ref);
return 0;
}
@@ -175,12 +175,18 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) &&
!refs_read_ref(get_main_ref_store(the_repository), *refs, &oid)) {
- show_one(show_one_opts, *refs, &oid);
- }
- else if (!show_one_opts->quiet)
+ struct reference ref = {
+ .name = *refs,
+ .oid = &oid,
+ };
+
+ show_one(show_one_opts, &ref);
+ } else if (!show_one_opts->quiet) {
die("'%s' - not a valid ref", *refs);
- else
+ } else {
return 1;
+ }
+
refs++;
}
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v2 09/14] refs: drop `current_ref_iter` hack
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
` (7 preceding siblings ...)
2025-10-08 15:50 ` [PATCH v2 08/14] builtin/show-ref: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
@ 2025-10-08 15:50 ` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 10/14] refs: drop infrastructure to peel via iterators Patrick Steinhardt
` (5 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 15:50 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
In preceding commits we have refactored all callers of
`peel_iterated_oid()` to instead use `reference_get_peeled_oid()`. This
allows us to thus get rid of the former function.
Getting rid of that function is nice, but even nicer is that this also
allows us to get rid of the `current_ref_iter` hack. This global
variable tracked the currently-active ref iterator so that we can use it
to peel an object ID. Now that the peeled object ID is propagated via
`struct reference` though we don't have to depend on this hack anymore,
which makes for a more robust and easier-to-understand infrastructure.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs.c | 10 ----------
refs/iterator.c | 5 -----
refs/refs-internal.h | 13 -------------
3 files changed, 28 deletions(-)
diff --git a/refs.c b/refs.c
index 5002e56435..b0ceba8bc3 100644
--- a/refs.c
+++ b/refs.c
@@ -2323,16 +2323,6 @@ int refs_optimize(struct ref_store *refs, struct pack_refs_opts *opts)
return refs->be->optimize(refs, opts);
}
-int peel_iterated_oid(struct repository *r, const struct object_id *base, struct object_id *peeled)
-{
- if (current_ref_iter &&
- (current_ref_iter->ref.oid == base ||
- oideq(current_ref_iter->ref.oid, base)))
- return ref_iterator_peel(current_ref_iter, peeled);
-
- return peel_object(r, base, peeled) ? -1 : 0;
-}
-
int reference_get_peeled_oid(struct repository *repo,
const struct reference *ref,
struct object_id *peeled_oid)
diff --git a/refs/iterator.c b/refs/iterator.c
index fe5980e1b6..072c6aacdb 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -458,15 +458,11 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
return ref_iterator;
}
-struct ref_iterator *current_ref_iter = NULL;
-
int do_for_each_ref_iterator(struct ref_iterator *iter,
each_ref_fn fn, void *cb_data)
{
int retval = 0, ok;
- struct ref_iterator *old_ref_iter = current_ref_iter;
- current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
retval = fn(&iter->ref, cb_data);
if (retval)
@@ -474,7 +470,6 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
}
out:
- current_ref_iter = old_ref_iter;
if (ok == ITER_ERROR)
retval = -1;
ref_iterator_free(iter);
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index ed749d1657..f4f845bbea 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -376,19 +376,6 @@ struct ref_iterator_vtable {
ref_iterator_release_fn *release;
};
-/*
- * current_ref_iter is a performance hack: when iterating over
- * references using the for_each_ref*() functions, current_ref_iter is
- * set to the reference iterator before calling the callback function.
- * If the callback function calls peel_ref(), then peel_ref() first
- * checks whether the reference to be peeled is the one referred to by
- * the iterator (it usually is) and if so, asks the iterator for the
- * peeled version of the reference if it is available. This avoids a
- * refname lookup in a common case. current_ref_iter is set to NULL
- * when the iteration is over.
- */
-extern struct ref_iterator *current_ref_iter;
-
struct ref_store;
/* refs backends */
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v2 10/14] refs: drop infrastructure to peel via iterators
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
` (8 preceding siblings ...)
2025-10-08 15:50 ` [PATCH v2 09/14] refs: drop `current_ref_iter` hack Patrick Steinhardt
@ 2025-10-08 15:50 ` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 11/14] object: add flag to `peel_object()` to verify object type Patrick Steinhardt
` (4 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 15:50 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
Now that the peeled object ID gets propagated via the `struct reference`
there is no need anymore to call into the reference iterator itself to
dereference an object. Remove this infrastructure.
Most of the changes are straight-forward deletions of code. There is one
exception though in `refs/packed-backend.c::write_with_updates()`. Here
we stop peeling the iterator and instead just pass the peeled object ID
of that iterator directly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs.h | 14 --------------
refs/debug.c | 11 -----------
refs/files-backend.c | 17 -----------------
refs/iterator.c | 36 ------------------------------------
refs/packed-backend.c | 24 +-----------------------
refs/ref-cache.c | 9 ---------
refs/refs-internal.h | 7 -------
refs/reftable-backend.c | 24 ------------------------
8 files changed, 1 insertion(+), 141 deletions(-)
diff --git a/refs.h b/refs.h
index 886ed2c0f4..2dd7ac1a16 100644
--- a/refs.h
+++ b/refs.h
@@ -1289,10 +1289,6 @@ int repo_migrate_ref_storage_format(struct repository *repo,
* to the next entry, ref_iterator_advance() aborts the iteration,
* frees the ref_iterator, and returns ITER_ERROR.
*
- * The reference currently being looked at can be peeled by calling
- * ref_iterator_peel(). This function is often faster than peel_ref(),
- * so it should be preferred when iterating over references.
- *
* Putting it all together, a typical iteration looks like this:
*
* int ok;
@@ -1307,9 +1303,6 @@ int repo_migrate_ref_storage_format(struct repository *repo,
* // Access information about the current reference:
* if (!(iter->flags & REF_ISSYMREF))
* printf("%s is %s\n", iter->refname, oid_to_hex(iter->oid));
- *
- * // If you need to peel the reference:
- * ref_iterator_peel(iter, &oid);
* }
*
* if (ok != ITER_DONE)
@@ -1400,13 +1393,6 @@ enum ref_iterator_seek_flag {
int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname,
unsigned int flags);
-/*
- * If possible, peel the reference currently being viewed by the
- * iterator. Return 0 on success.
- */
-int ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled);
-
/* Free the reference iterator and any associated resources. */
void ref_iterator_free(struct ref_iterator *ref_iterator);
diff --git a/refs/debug.c b/refs/debug.c
index 7a26035617..162c24e5cc 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -178,16 +178,6 @@ static int debug_ref_iterator_seek(struct ref_iterator *ref_iterator,
return res;
}
-static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct debug_ref_iterator *diter =
- (struct debug_ref_iterator *)ref_iterator;
- int res = diter->iter->vtable->peel(diter->iter, peeled);
- trace_printf_key(&trace_refs, "iterator_peel: %s: %d\n", diter->iter->ref.name, res);
- return res;
-}
-
static void debug_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct debug_ref_iterator *diter =
@@ -199,7 +189,6 @@ static void debug_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable debug_ref_iterator_vtable = {
.advance = debug_ref_iterator_advance,
.seek = debug_ref_iterator_seek,
- .peel = debug_ref_iterator_peel,
.release = debug_ref_iterator_release,
};
diff --git a/refs/files-backend.c b/refs/files-backend.c
index d34fbe55d6..a4cda57981 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -994,15 +994,6 @@ static int files_ref_iterator_seek(struct ref_iterator *ref_iterator,
return ref_iterator_seek(iter->iter0, refname, flags);
}
-static int files_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct files_ref_iterator *iter =
- (struct files_ref_iterator *)ref_iterator;
-
- return ref_iterator_peel(iter->iter0, peeled);
-}
-
static void files_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct files_ref_iterator *iter =
@@ -1013,7 +1004,6 @@ static void files_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable files_ref_iterator_vtable = {
.advance = files_ref_iterator_advance,
.seek = files_ref_iterator_seek,
- .peel = files_ref_iterator_peel,
.release = files_ref_iterator_release,
};
@@ -2389,12 +2379,6 @@ static int files_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
BUG("ref_iterator_seek() called for reflog_iterator");
}
-static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
- struct object_id *peeled UNUSED)
-{
- BUG("ref_iterator_peel() called for reflog_iterator");
-}
-
static void files_reflog_iterator_release(struct ref_iterator *ref_iterator)
{
struct files_reflog_iterator *iter =
@@ -2405,7 +2389,6 @@ static void files_reflog_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable files_reflog_iterator_vtable = {
.advance = files_reflog_iterator_advance,
.seek = files_reflog_iterator_seek,
- .peel = files_reflog_iterator_peel,
.release = files_reflog_iterator_release,
};
diff --git a/refs/iterator.c b/refs/iterator.c
index 072c6aacdb..d79aa5ec82 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -21,12 +21,6 @@ int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname,
return ref_iterator->vtable->seek(ref_iterator, refname, flags);
}
-int ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- return ref_iterator->vtable->peel(ref_iterator, peeled);
-}
-
void ref_iterator_free(struct ref_iterator *ref_iterator)
{
if (ref_iterator) {
@@ -60,12 +54,6 @@ static int empty_ref_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
return 0;
}
-static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
- struct object_id *peeled UNUSED)
-{
- BUG("peel called for empty iterator");
-}
-
static void empty_ref_iterator_release(struct ref_iterator *ref_iterator UNUSED)
{
}
@@ -73,7 +61,6 @@ static void empty_ref_iterator_release(struct ref_iterator *ref_iterator UNUSED)
static struct ref_iterator_vtable empty_ref_iterator_vtable = {
.advance = empty_ref_iterator_advance,
.seek = empty_ref_iterator_seek,
- .peel = empty_ref_iterator_peel,
.release = empty_ref_iterator_release,
};
@@ -240,18 +227,6 @@ static int merge_ref_iterator_seek(struct ref_iterator *ref_iterator,
return 0;
}
-static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct merge_ref_iterator *iter =
- (struct merge_ref_iterator *)ref_iterator;
-
- if (!iter->current) {
- BUG("peel called before advance for merge iterator");
- }
- return ref_iterator_peel(*iter->current, peeled);
-}
-
static void merge_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct merge_ref_iterator *iter =
@@ -263,7 +238,6 @@ static void merge_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable merge_ref_iterator_vtable = {
.advance = merge_ref_iterator_advance,
.seek = merge_ref_iterator_seek,
- .peel = merge_ref_iterator_peel,
.release = merge_ref_iterator_release,
};
@@ -412,15 +386,6 @@ static int prefix_ref_iterator_seek(struct ref_iterator *ref_iterator,
return ref_iterator_seek(iter->iter0, refname, flags);
}
-static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct prefix_ref_iterator *iter =
- (struct prefix_ref_iterator *)ref_iterator;
-
- return ref_iterator_peel(iter->iter0, peeled);
-}
-
static void prefix_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct prefix_ref_iterator *iter =
@@ -432,7 +397,6 @@ static void prefix_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable prefix_ref_iterator_vtable = {
.advance = prefix_ref_iterator_advance,
.seek = prefix_ref_iterator_seek,
- .peel = prefix_ref_iterator_peel,
.release = prefix_ref_iterator_release,
};
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 1fefefd54e..6fa229edd0 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1030,22 +1030,6 @@ static int packed_ref_iterator_seek(struct ref_iterator *ref_iterator,
return 0;
}
-static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct packed_ref_iterator *iter =
- (struct packed_ref_iterator *)ref_iterator;
-
- if ((iter->base.ref.flags & REF_KNOWS_PEELED)) {
- oidcpy(peeled, &iter->peeled);
- return is_null_oid(&iter->peeled) ? -1 : 0;
- } else if ((iter->base.ref.flags & (REF_ISBROKEN | REF_ISSYMREF))) {
- return -1;
- } else {
- return peel_object(iter->repo, &iter->oid, peeled) ? -1 : 0;
- }
-}
-
static void packed_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct packed_ref_iterator *iter =
@@ -1059,7 +1043,6 @@ static void packed_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable packed_ref_iterator_vtable = {
.advance = packed_ref_iterator_advance,
.seek = packed_ref_iterator_seek,
- .peel = packed_ref_iterator_peel,
.release = packed_ref_iterator_release,
};
@@ -1525,13 +1508,8 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
if (cmp < 0) {
/* Pass the old reference through. */
-
- struct object_id peeled;
- int peel_error = ref_iterator_peel(iter, &peeled);
-
if (write_packed_entry(out, iter->ref.name,
- iter->ref.oid,
- peel_error ? NULL : &peeled))
+ iter->ref.oid, iter->ref.peeled_oid))
goto write_error;
if ((ok = ref_iterator_advance(iter)) != ITER_OK) {
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index 4726de430d..9d0cae10b5 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -546,14 +546,6 @@ static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator,
return 0;
}
-static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct cache_ref_iterator *iter =
- (struct cache_ref_iterator *)ref_iterator;
- return peel_object(iter->repo, ref_iterator->ref.oid, peeled) ? -1 : 0;
-}
-
static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct cache_ref_iterator *iter =
@@ -565,7 +557,6 @@ static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable cache_ref_iterator_vtable = {
.advance = cache_ref_iterator_advance,
.seek = cache_ref_iterator_seek,
- .peel = cache_ref_iterator_peel,
.release = cache_ref_iterator_release,
};
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index f4f845bbea..4671517dad 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -357,12 +357,6 @@ typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator);
typedef int ref_iterator_seek_fn(struct ref_iterator *ref_iterator,
const char *refname, unsigned int flags);
-/*
- * Peels the current ref, returning 0 for success or -1 for failure.
- */
-typedef int ref_iterator_peel_fn(struct ref_iterator *ref_iterator,
- struct object_id *peeled);
-
/*
* Implementations of this function should free any resources specific
* to the derived class.
@@ -372,7 +366,6 @@ typedef void ref_iterator_release_fn(struct ref_iterator *ref_iterator);
struct ref_iterator_vtable {
ref_iterator_advance_fn *advance;
ref_iterator_seek_fn *seek;
- ref_iterator_peel_fn *peel;
ref_iterator_release_fn *release;
};
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 0468f62a7e..d9447f8fa7 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -743,21 +743,6 @@ static int reftable_ref_iterator_seek(struct ref_iterator *ref_iterator,
return iter->err;
}
-static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct reftable_ref_iterator *iter =
- (struct reftable_ref_iterator *)ref_iterator;
-
- if (iter->ref.value_type == REFTABLE_REF_VAL2) {
- oidread(peeled, iter->ref.value.val2.target_value,
- iter->refs->base.repo->hash_algo);
- return 0;
- }
-
- return -1;
-}
-
static void reftable_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct reftable_ref_iterator *iter =
@@ -775,7 +760,6 @@ static void reftable_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable reftable_ref_iterator_vtable = {
.advance = reftable_ref_iterator_advance,
.seek = reftable_ref_iterator_seek,
- .peel = reftable_ref_iterator_peel,
.release = reftable_ref_iterator_release,
};
@@ -2097,13 +2081,6 @@ static int reftable_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSE
return -1;
}
-static int reftable_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
- struct object_id *peeled UNUSED)
-{
- BUG("reftable reflog iterator cannot be peeled");
- return -1;
-}
-
static void reftable_reflog_iterator_release(struct ref_iterator *ref_iterator)
{
struct reftable_reflog_iterator *iter =
@@ -2116,7 +2093,6 @@ static void reftable_reflog_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable reftable_reflog_iterator_vtable = {
.advance = reftable_reflog_iterator_advance,
.seek = reftable_reflog_iterator_seek,
- .peel = reftable_reflog_iterator_peel,
.release = reftable_reflog_iterator_release,
};
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v2 11/14] object: add flag to `peel_object()` to verify object type
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
` (9 preceding siblings ...)
2025-10-08 15:50 ` [PATCH v2 10/14] refs: drop infrastructure to peel via iterators Patrick Steinhardt
@ 2025-10-08 15:50 ` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 12/14] refs: don't store peeled object IDs for invalid tags Patrick Steinhardt
` (3 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 15:50 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
When peeling a tag to a non-tag object we repeatedly call
`parse_object()` on the tagged object until we find the first object
that isn't a tag. While this feels sensible at first, there is a big
catch here: `parse_object()` doesn't actually verify the type of the
tagged object.
The relevant code path here eventually ends up in `parse_tag_buffer()`.
Here, we parse the various fields of the tag, including the "type". Once
we've figured out the type and the tagged object ID, we call one of the
`lookup_${type}()` functions for whatever type we have found. There is
two possible outcomes in the successful case:
1. The object is already part of our cached objects. In that case we
double-check whether the type we're trying to look up matches the
type that was cached.
2. The object is _not_ part of our cached objects. In that case, we
simply create a new object with the expected type, but we don't
parse that object.
In the first case we might notice type mismatches, but only in the case
where our cache has the object with the correct type. In the second
case, we'll blindly assume that the type is correct and then go with it.
We'll only notice that the type might be wrong when we try to parse the
object at a later point.
Now arguably, we could change `parse_tag_buffer()` to verify the tagged
object's type for us. But that would have the effect that such a tag
cannot be parsed at all anymore, and we have a small bunch of tests for
exactly this case that assert we still can open such tags. So this
change does not feel like something we can retroactively tighten, even
though one shouldn't ever hit such corrupted tags.
Instead, add a new `flags` field to `peel_object()` that allows the
caller to opt in to strict object verification. This will be wired up at
a subset of callsites over the next few commits.
Note that this change also inlines `deref_tag_noverify()`. There's only
been two callsites of that function, the one we're changing and one in
our test helpers. The latter callsite can trivially use `deref_tag()`
instead, so by inlining the function we avoid having to pass down the
flag.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
object.c | 20 +++++++++++++++++---
object.h | 15 ++++++++++++++-
ref-filter.c | 2 +-
refs.c | 2 +-
refs/packed-backend.c | 5 ++---
refs/reftable-backend.c | 4 ++--
t/helper/test-reach.c | 2 +-
tag.c | 12 ------------
tag.h | 1 -
9 files changed, 38 insertions(+), 25 deletions(-)
diff --git a/object.c b/object.c
index 986114a6dba..e72b0ed4360 100644
--- a/object.c
+++ b/object.c
@@ -209,11 +209,12 @@ struct object *lookup_object_by_type(struct repository *r,
enum peel_status peel_object(struct repository *r,
const struct object_id *name,
- struct object_id *oid)
+ struct object_id *oid,
+ unsigned flags)
{
struct object *o = lookup_unknown_object(r, name);
- if (o->type == OBJ_NONE) {
+ if (o->type == OBJ_NONE || flags & PEEL_OBJECT_VERIFY_OBJECT_TYPE) {
int type = odb_read_object_info(r->objects, name, NULL);
if (type < 0 || !object_as_type(o, type, 0))
return PEEL_INVALID;
@@ -222,7 +223,20 @@ enum peel_status peel_object(struct repository *r,
if (o->type != OBJ_TAG)
return PEEL_NON_TAG;
- o = deref_tag_noverify(r, o);
+ while (o && o->type == OBJ_TAG) {
+ o = parse_object(r, &o->oid);
+ if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged) {
+ o = ((struct tag *)o)->tagged;
+
+ if (flags & PEEL_OBJECT_VERIFY_OBJECT_TYPE) {
+ int type = odb_read_object_info(r->objects, &o->oid, NULL);
+ if (type < 0 || !object_as_type(o, type, 0))
+ return PEEL_INVALID;
+ }
+ } else {
+ o = NULL;
+ }
+ }
if (!o)
return PEEL_INVALID;
diff --git a/object.h b/object.h
index 8c3c1c46e1b..1499f63d507 100644
--- a/object.h
+++ b/object.h
@@ -287,6 +287,17 @@ enum peel_status {
PEEL_BROKEN = -4
};
+enum peel_object_flags {
+ /*
+ * Always verify the object type, even in the case where the looked-up
+ * object already has an object type. This can be useful when the
+ * stored object type may be invalid. One such case is when looking up
+ * objects via tags, where we blindly trust the object type declared by
+ * the tag.
+ */
+ PEEL_OBJECT_VERIFY_OBJECT_TYPE = (1 << 0),
+};
+
/*
* Peel the named object; i.e., if the object is a tag, resolve the
* tag recursively until a non-tag is found. If successful, store the
@@ -295,7 +306,9 @@ enum peel_status {
* and leave oid unchanged.
*/
enum peel_status peel_object(struct repository *r,
- const struct object_id *name, struct object_id *oid);
+ const struct object_id *name,
+ struct object_id *oid,
+ unsigned flags);
struct object_list *object_list_insert(struct object *item,
struct object_list **list_p);
diff --git a/ref-filter.c b/ref-filter.c
index b18a032e575..72e5a221ff3 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2581,7 +2581,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
if (need_tagged) {
if (!is_null_oid(&ref->peeled_oid)) {
oidcpy(&oi_deref.oid, &ref->peeled_oid);
- } else if (!peel_object(the_repository, &obj->oid, &oi_deref.oid)) {
+ } else if (!peel_object(the_repository, &oi.oid, &oi_deref.oid, 0)) {
/* We managed to peel the object ourselves. */
} else {
die("bad tag");
diff --git a/refs.c b/refs.c
index b0ceba8bc38..40acaa3f42f 100644
--- a/refs.c
+++ b/refs.c
@@ -2332,7 +2332,7 @@ int reference_get_peeled_oid(struct repository *repo,
return 0;
}
- return peel_object(repo, ref->oid, peeled_oid) ? -1 : 0;
+ return peel_object(repo, ref->oid, peeled_oid, 0) ? -1 : 0;
}
int refs_update_symref(struct ref_store *refs, const char *ref,
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 6fa229edd0f..4752d3f3981 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1527,9 +1527,8 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
i++;
} else {
struct object_id peeled;
- int peel_error = peel_object(refs->base.repo,
- &update->new_oid,
- &peeled);
+ int peel_error = peel_object(refs->base.repo, &update->new_oid,
+ &peeled, 0);
if (write_packed_entry(out, update->refname,
&update->new_oid,
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index d9447f8fa72..32ee2ce22a1 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1631,7 +1631,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
ref.refname = (char *)u->refname;
ref.update_index = ts;
- peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled);
+ peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled, 0);
if (!peel_error) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
@@ -2496,7 +2496,7 @@ static int write_reflog_expiry_table(struct reftable_writer *writer, void *cb_da
ref.refname = (char *)arg->refname;
ref.update_index = ts;
- if (!peel_object(arg->refs->base.repo, &arg->update_oid, &peeled)) {
+ if (!peel_object(arg->refs->base.repo, &arg->update_oid, &peeled, 0)) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
memcpy(ref.value.val2.value, arg->update_oid.hash, GIT_MAX_RAWSZ);
diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c
index 028ec003067..c58c93800f3 100644
--- a/t/helper/test-reach.c
+++ b/t/helper/test-reach.c
@@ -63,7 +63,7 @@ int cmd__reach(int ac, const char **av)
die("failed to resolve %s", buf.buf + 2);
orig = parse_object(r, &oid);
- peeled = deref_tag_noverify(the_repository, orig);
+ peeled = deref_tag(the_repository, orig, NULL, 0);
if (!peeled)
die("failed to load commit for input %s resulting in oid %s",
diff --git a/tag.c b/tag.c
index 1d52686ee10..f5c232d2f1f 100644
--- a/tag.c
+++ b/tag.c
@@ -94,18 +94,6 @@ struct object *deref_tag(struct repository *r, struct object *o, const char *war
return o;
}
-struct object *deref_tag_noverify(struct repository *r, struct object *o)
-{
- while (o && o->type == OBJ_TAG) {
- o = parse_object(r, &o->oid);
- if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged)
- o = ((struct tag *)o)->tagged;
- else
- o = NULL;
- }
- return o;
-}
-
struct tag *lookup_tag(struct repository *r, const struct object_id *oid)
{
struct object *obj = lookup_object(r, oid);
diff --git a/tag.h b/tag.h
index c49d7c19ad3..ef12a610372 100644
--- a/tag.h
+++ b/tag.h
@@ -16,7 +16,6 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
int parse_tag(struct tag *item);
void release_tag_memory(struct tag *t);
struct object *deref_tag(struct repository *r, struct object *, const char *, int);
-struct object *deref_tag_noverify(struct repository *r, struct object *);
int gpg_verify_tag(const struct object_id *oid,
const char *name_to_report, unsigned flags);
struct object_id *get_tagged_oid(struct tag *tag);
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v2 12/14] refs: don't store peeled object IDs for invalid tags
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
` (10 preceding siblings ...)
2025-10-08 15:50 ` [PATCH v2 11/14] object: add flag to `peel_object()` to verify object type Patrick Steinhardt
@ 2025-10-08 15:50 ` Patrick Steinhardt
2025-10-08 16:27 ` shejialuo
2025-10-08 15:50 ` [PATCH v2 13/14] ref-filter: detect broken tags when dereferencing them Patrick Steinhardt
` (2 subsequent siblings)
14 siblings, 1 reply; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 15:50 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
Both the "files" and "reftable" backend store peeled object IDs for
references that point to tags:
- The "files" backend stores the value when packing refs, where each
peeled object ID is prefixed with "^".
- The "reftable" backend stores the value whenever writing a new
reference that points to a tag via a special ref record type.
Both of these backends use `peel_object()` to find the peeled object ID.
But as explained in the preceding commit, that function does not detect
the case where the tag's tagged object and its claimed type mismatch.
The consequence of storing these bogus peeled object IDs is that we're
less likely to detect such corruption in other parts of Git.
git-for-each-ref(1) for example does not notice anymore that the tag is
broken when using "--format=%(*objectname)" to dereference tags.
One could claim that this is good, because it still allows us to mostly
use the tag as intended. But the biggest problem here is that we now
have different behaviour for such a broken tag depending on whether or
not we have its peeled value in the refdb.
Fix the issue by verifying the object type when peeling the object. If
that verification fails we simply skip storing the peeled value in
either of the reference formats.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs/packed-backend.c | 2 +-
refs/reftable-backend.c | 3 ++-
t/pack-refs-tests.sh | 32 ++++++++++++++++++++++++++++++++
t/t0610-reftable-basics.sh | 28 ++++++++++++++++++++++++++++
4 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 4752d3f398..1ab0c50393 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1528,7 +1528,7 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
} else {
struct object_id peeled;
int peel_error = peel_object(refs->base.repo, &update->new_oid,
- &peeled, 0);
+ &peeled, PEEL_OBJECT_VERIFY_OBJECT_TYPE);
if (write_packed_entry(out, update->refname,
&update->new_oid,
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 32ee2ce22a..0b7ec3ae15 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1631,7 +1631,8 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
ref.refname = (char *)u->refname;
ref.update_index = ts;
- peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled, 0);
+ peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled,
+ PEEL_OBJECT_VERIFY_OBJECT_TYPE);
if (!peel_error) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
diff --git a/t/pack-refs-tests.sh b/t/pack-refs-tests.sh
index 3dbcc01718..095823d915 100644
--- a/t/pack-refs-tests.sh
+++ b/t/pack-refs-tests.sh
@@ -428,4 +428,36 @@ do
'
done
+test_expect_success 'pack-refs does not store invalid peeled tag value' '
+ test_when_finished rm -rf repo &&
+ git init repo &&
+ (
+ cd repo &&
+ git commit --allow-empty --message initial &&
+
+ echo garbage >blob-content &&
+ blob_id=$(git hash-object -w -t blob blob-content) &&
+
+ # Write an invalid tag into the object database. The tag itself
+ # is well-formed, but the tagged object is a blob while we
+ # claim that it is a commit.
+ cat >tag-content <<-EOF &&
+ object $blob_id
+ type commit
+ tag bad-tag
+ tagger C O Mitter <committer@example.com> 1112354055 +0200
+
+ annotated
+ EOF
+ tag_id=$(git hash-object -w -t tag tag-content) &&
+ git update-ref refs/tags/bad-tag "$tag_id" &&
+
+ # The packed-refs file should not contain the peeled object ID.
+ # If it did this would cause commands that use the peeled value
+ # to not notice this corrupted tag.
+ git pack-refs --all &&
+ test_grep ! "^\^" .git/packed-refs
+ )
+'
+
test_done
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
index 3ea5d51532..6575528f21 100755
--- a/t/t0610-reftable-basics.sh
+++ b/t/t0610-reftable-basics.sh
@@ -1135,4 +1135,32 @@ test_expect_success 'fetch: accessing FETCH_HEAD special ref works' '
test_cmp expect actual
'
+test_expect_success 'writes do not persist peeled value for invalid tags' '
+ test_when_finished rm -rf repo &&
+ git init repo &&
+ (
+ cd repo &&
+ git commit --allow-empty --message initial &&
+
+ # We cannot easily verify that the peeled value is not stored
+ # in the tables. Instead, we test this indirectly: we create
+ # two tags that both point to the same object, but they claim
+ # different object types. If we parse both tags we notice that
+ # the parsed tagged object has a mismatch between the two tags
+ # and bail out.
+ #
+ # If we instead use the persisted peeled value we would not
+ # even parse the tags. As such, we would not notice the
+ # discrepancy either and thus listing these tags would succeed.
+ git tag tag-1 -m "tag 1" &&
+ git cat-file tag tag-1 >raw-tag &&
+ sed "s/^type commit$/type blob/" <raw-tag >broken-tag &&
+ broken_tag_id=$(git hash-object -w -t tag broken-tag) &&
+ git update-ref refs/tags/tag-2 $broken_tag_id &&
+
+ test_must_fail git for-each-ref --format="%(*objectname)" refs/tags/ 2>err &&
+ test_grep "bad tag pointer" err
+ )
+'
+
test_done
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v2 13/14] ref-filter: detect broken tags when dereferencing them
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
` (11 preceding siblings ...)
2025-10-08 15:50 ` [PATCH v2 12/14] refs: don't store peeled object IDs for invalid tags Patrick Steinhardt
@ 2025-10-08 15:50 ` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 14/14] ref-filter: parse objects on demand Patrick Steinhardt
2025-10-09 5:38 ` [PATCH v2 00/14] refs: improvements and fixes for peeling tags Jeff King
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 15:50 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
Users can ask git-for-each-ref(1) to peel tags and return information of
the tagged object by adding an asterisk to the format, like for example
"%(*$objectname)". If so, git-for-each-ref(1) peels that object to the
first non-tag object and then returns its values.
As mentioned in preceding commits, it can happen that the tagged object
type and the claimed object type differ, effectively resulting in a
corrupt tag. git-for-each-ref(1) would notice this mismatch, print an
error and then bail out when trying to peel the tag.
But we only notice this corruption in some very specific edge cases!
While we have a test in "t/for-each-ref-tests.sh" that verifies the
above scenario, this test is specifically crafted to detect the issue at
hand. Namely, we create two tags:
- One tag points to a specific object with the correct type.
- The other tag points to the *same* object with a different type.
The fact that both tags point to the same object is important here:
`peel_object()` wouldn't notice the corruption if the tagged objects
were different.
The root cause is that `peel_object()` calls `lookup_${type}()`
eventually, where the type is the same type declared in the tag object.
Consequently, when we have two tags pointing to the same object but with
different declared types we'll call two different lookup functions. The
first lookup will store the object with an unverified type A, whereas
the second lookup will try to look up the object with a different
unverified type B. And it is only now that we notice the discrepancy in
object types, even though type A could've already been the wrong type.
Fix the issue by verifying the object type in `populate_value()`. With
this change we'll also notice type mismatches when only dereferencing a
tag once.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
ref-filter.c | 3 ++-
t/for-each-ref-tests.sh | 4 +++-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/ref-filter.c b/ref-filter.c
index 72e5a221ff..72cf85c8c6 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2581,7 +2581,8 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
if (need_tagged) {
if (!is_null_oid(&ref->peeled_oid)) {
oidcpy(&oi_deref.oid, &ref->peeled_oid);
- } else if (!peel_object(the_repository, &oi.oid, &oi_deref.oid, 0)) {
+ } else if (!peel_object(the_repository, &oi.oid, &oi_deref.oid,
+ PEEL_OBJECT_VERIFY_OBJECT_TYPE)) {
/* We managed to peel the object ourselves. */
} else {
die("bad tag");
diff --git a/t/for-each-ref-tests.sh b/t/for-each-ref-tests.sh
index e3ad19298a..4593be5fd5 100644
--- a/t/for-each-ref-tests.sh
+++ b/t/for-each-ref-tests.sh
@@ -1809,7 +1809,9 @@ test_expect_success "${git_for_each_ref} reports broken tags" '
bad=$(git hash-object -w -t tag bad) &&
git update-ref refs/tags/broken-tag-bad $bad &&
test_must_fail ${git_for_each_ref} --format="%(*objectname)" \
- refs/tags/broken-tag-*
+ refs/tags/broken-tag-* &&
+ test_must_fail ${git_for_each_ref} --format="%(*objectname)" \
+ refs/tags/broken-tag-bad
'
test_expect_success 'set up tag with signature and no blank lines' '
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v2 14/14] ref-filter: parse objects on demand
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
` (12 preceding siblings ...)
2025-10-08 15:50 ` [PATCH v2 13/14] ref-filter: detect broken tags when dereferencing them Patrick Steinhardt
@ 2025-10-08 15:50 ` Patrick Steinhardt
2025-10-09 5:38 ` [PATCH v2 00/14] refs: improvements and fixes for peeling tags Jeff King
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-08 15:50 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
When formatting an arbitrary object we parse that object regardless of
whether or not we actually need any parsed data. In fact, many of the
atoms we have don't require any.
Refactor the code so that we parse the data on demand when we see an
atom that wants to access the objects. This leads to a small speedup,
for example in the Chromium repository with around 40000 refs:
Benchmark 1: for-each-ref --format='%(raw)' (HEAD~)
Time (mean ± σ): 388.7 ms ± 1.1 ms [User: 322.2 ms, System: 65.0 ms]
Range (min … max): 387.3 ms … 390.8 ms 10 runs
Benchmark 2: for-each-ref --format='%(raw)' (HEAD)
Time (mean ± σ): 344.7 ms ± 0.7 ms [User: 287.8 ms, System: 55.1 ms]
Range (min … max): 343.9 ms … 345.7 ms 10 runs
Summary
for-each-ref --format='%(raw)' (HEAD) ran
1.13 ± 0.00 times faster than for-each-ref --format='%(raw)' (HEAD~)
With this change, we now spend ~90% of the time decompressing objects,
which is almost as good as it gets regarding git-for-each-ref(1)'s own
infrastructure.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
ref-filter.c | 156 ++++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 117 insertions(+), 39 deletions(-)
diff --git a/ref-filter.c b/ref-filter.c
index 72cf85c8c6..537c7babac 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -91,6 +91,7 @@ static struct expand_data {
struct object_id delta_base_oid;
void *content;
+ struct object *maybe_object;
struct object_info info;
} oi, oi_deref;
@@ -1475,11 +1476,28 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_
}
}
+static int get_or_parse_object(struct expand_data *data, const char *refname,
+ struct object **object, struct strbuf *err, int *eaten)
+{
+ if (!data->maybe_object) {
+ data->maybe_object = parse_object_buffer(the_repository, &data->oid, data->type,
+ data->size, data->content, eaten);
+ if (!data->maybe_object)
+ return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
+ oid_to_hex(&data->oid), refname);
+ }
+
+ *object = data->maybe_object;
+ return 0;
+}
+
/* See grab_values */
-static void grab_tag_values(struct atom_value *val, int deref, struct object *obj)
+static int grab_tag_values(struct atom_value *val, int deref,
+ struct expand_data *data, const char *refname,
+ struct strbuf *err, int *eaten)
{
- int i;
- struct tag *tag = (struct tag *) obj;
+ struct tag *tag = NULL;
+ int i, ret;
for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i].name;
@@ -1487,6 +1505,17 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
struct atom_value *v = &val[i];
if (!!deref != (*name == '*'))
continue;
+
+ if (!tag) {
+ struct object *object;
+
+ ret = get_or_parse_object(data, refname, &object, err, eaten);
+ if (ret < 0)
+ return ret;
+
+ tag = (struct tag *) object;
+ }
+
if (deref)
name++;
if (atom_type == ATOM_TAG)
@@ -1496,22 +1525,38 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
else if (atom_type == ATOM_OBJECT && tag->tagged)
v->s = xstrdup(oid_to_hex(&tag->tagged->oid));
}
+
+ return 0;
}
/* See grab_values */
-static void grab_commit_values(struct atom_value *val, int deref, struct object *obj)
+static int grab_commit_values(struct atom_value *val, int deref,
+ struct expand_data *data, const char *refname,
+ struct strbuf *err, int *eaten)
{
- int i;
- struct commit *commit = (struct commit *) obj;
+ int i, ret;
+ struct commit *commit = NULL;
for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i].name;
enum atom_type atom_type = used_atom[i].atom_type;
struct atom_value *v = &val[i];
+
if (!!deref != (*name == '*'))
continue;
if (deref)
name++;
+
+ if (!commit) {
+ struct object *object;
+
+ ret = get_or_parse_object(data, refname, &object, err, eaten);
+ if (ret < 0)
+ return ret;
+
+ commit = (struct commit *) object;
+ }
+
if (atom_type == ATOM_TREE &&
grab_oid(name, "tree", get_commit_tree_oid(commit), v, &used_atom[i]))
continue;
@@ -1531,6 +1576,8 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
v->s = strbuf_detach(&s, NULL);
}
}
+
+ return 0;
}
static const char *find_wholine(const char *who, int wholen, const char *buf)
@@ -1759,10 +1806,12 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void
}
}
-static void grab_signature(struct atom_value *val, int deref, struct object *obj)
+static int grab_signature(struct atom_value *val, int deref,
+ struct expand_data *data, const char *refname,
+ struct strbuf *err, int *eaten)
{
- int i;
- struct commit *commit = (struct commit *) obj;
+ int i, ret;
+ struct commit *commit = NULL;
struct signature_check sigc = { 0 };
int signature_checked = 0;
@@ -1790,6 +1839,16 @@ static void grab_signature(struct atom_value *val, int deref, struct object *obj
continue;
if (!signature_checked) {
+ if (!commit) {
+ struct object *object;
+
+ ret = get_or_parse_object(data, refname, &object, err, eaten);
+ if (ret < 0)
+ return ret;
+
+ commit = (struct commit *) object;
+ }
+
check_commit_signature(commit, &sigc);
signature_checked = 1;
}
@@ -1843,6 +1902,8 @@ static void grab_signature(struct atom_value *val, int deref, struct object *obj
if (signature_checked)
signature_check_clear(&sigc);
+
+ return 0;
}
static void find_subpos(const char *buf,
@@ -1920,9 +1981,8 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size
}
static void grab_describe_values(struct atom_value *val, int deref,
- struct object *obj)
+ struct expand_data *data)
{
- struct commit *commit = (struct commit *)obj;
int i;
for (i = 0; i < used_atom_cnt; i++) {
@@ -1944,7 +2004,7 @@ static void grab_describe_values(struct atom_value *val, int deref,
cmd.git_cmd = 1;
strvec_push(&cmd.args, "describe");
strvec_pushv(&cmd.args, atom->u.describe_args.v);
- strvec_push(&cmd.args, oid_to_hex(&commit->object.oid));
+ strvec_push(&cmd.args, oid_to_hex(&data->oid));
if (pipe_command(&cmd, NULL, 0, &out, 0, &err, 0) < 0) {
error(_("failed to run 'describe'"));
v->s = xstrdup("");
@@ -2066,24 +2126,36 @@ static void fill_missing_values(struct atom_value *val)
* pointed at by the ref itself; otherwise it is the object the
* ref (which is a tag) refers to.
*/
-static void grab_values(struct atom_value *val, int deref, struct object *obj, struct expand_data *data)
+static int grab_values(struct atom_value *val, int deref, struct expand_data *data,
+ const char *refname, struct strbuf *err, int *eaten)
{
void *buf = data->content;
+ int ret;
- switch (obj->type) {
+ switch (data->type) {
case OBJ_TAG:
- grab_tag_values(val, deref, obj);
+ ret = grab_tag_values(val, deref, data, refname, err, eaten);
+ if (ret < 0)
+ goto out;
+
grab_sub_body_contents(val, deref, data);
grab_person("tagger", val, deref, buf);
- grab_describe_values(val, deref, obj);
+ grab_describe_values(val, deref, data);
break;
case OBJ_COMMIT:
- grab_commit_values(val, deref, obj);
+ ret = grab_commit_values(val, deref, data, refname, err, eaten);
+ if (ret < 0)
+ goto out;
+
grab_sub_body_contents(val, deref, data);
grab_person("author", val, deref, buf);
grab_person("committer", val, deref, buf);
- grab_signature(val, deref, obj);
- grab_describe_values(val, deref, obj);
+
+ ret = grab_signature(val, deref, data, refname, err, eaten);
+ if (ret < 0)
+ goto out;
+
+ grab_describe_values(val, deref, data);
break;
case OBJ_TREE:
/* grab_tree_values(val, deref, obj, buf, sz); */
@@ -2094,8 +2166,12 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s
grab_sub_body_contents(val, deref, data);
break;
default:
- die("Eh? Object of type %d?", obj->type);
+ die("Eh? Object of type %d?", data->type);
}
+
+ ret = 0;
+out:
+ return ret;
}
static inline char *copy_advance(char *dst, const char *src)
@@ -2292,38 +2368,41 @@ static const char *get_refname(struct used_atom *atom, struct ref_array_item *re
return show_ref(&atom->u.refname, ref->refname);
}
-static int get_object(struct ref_array_item *ref, int deref, struct object **obj,
+static int get_object(struct ref_array_item *ref, int deref,
struct expand_data *oi, struct strbuf *err)
{
- /* parse_object_buffer() will set eaten to 0 if free() will be needed */
- int eaten = 1;
+ /* parse_object_buffer() will set eaten to 1 if free() will be needed */
+ int eaten = 0;
+ int ret;
+
if (oi->info.contentp) {
/* We need to know that to use parse_object_buffer properly */
oi->info.sizep = &oi->size;
oi->info.typep = &oi->type;
}
+
if (odb_read_object_info_extended(the_repository->objects, &oi->oid, &oi->info,
- OBJECT_INFO_LOOKUP_REPLACE))
- return strbuf_addf_ret(err, -1, _("missing object %s for %s"),
- oid_to_hex(&oi->oid), ref->refname);
+ OBJECT_INFO_LOOKUP_REPLACE)) {
+ ret = strbuf_addf_ret(err, -1, _("missing object %s for %s"),
+ oid_to_hex(&oi->oid), ref->refname);
+ goto out;
+ }
if (oi->info.disk_sizep && oi->disk_size < 0)
BUG("Object size is less than zero.");
if (oi->info.contentp) {
- *obj = parse_object_buffer(the_repository, &oi->oid, oi->type, oi->size, oi->content, &eaten);
- if (!*obj) {
- if (!eaten)
- free(oi->content);
- return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
- oid_to_hex(&oi->oid), ref->refname);
- }
- grab_values(ref->value, deref, *obj, oi);
+ ret = grab_values(ref->value, deref, oi, ref->refname, err, &eaten);
+ if (ret < 0)
+ goto out;
}
grab_common_values(ref->value, deref, oi);
+ ret = 0;
+
+out:
if (!eaten)
free(oi->content);
- return 0;
+ return ret;
}
static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees)
@@ -2376,7 +2455,6 @@ static char *get_worktree_path(const struct ref_array_item *ref)
*/
static int populate_value(struct ref_array_item *ref, struct strbuf *err)
{
- struct object *obj;
int i;
struct object_info empty = OBJECT_INFO_INIT;
int ahead_behind_atoms = 0;
@@ -2564,14 +2642,14 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
oi.oid = ref->objectname;
- if (get_object(ref, 0, &obj, &oi, err))
+ if (get_object(ref, 0, &oi, err))
return -1;
/*
* If there is no atom that wants to know about tagged
* object, we are done.
*/
- if (!need_tagged || (obj->type != OBJ_TAG))
+ if (!need_tagged || (oi.type != OBJ_TAG))
return 0;
/*
@@ -2589,7 +2667,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
}
}
- return get_object(ref, 1, &obj, &oi_deref, err);
+ return get_object(ref, 1, &oi_deref, err);
}
/*
--
2.51.0.764.g787ff6f08a.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* Re: [PATCH 01/13] refs: introduce wrapper struct for `each_ref_fn`
2025-10-07 10:58 ` [PATCH 01/13] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
2025-10-07 18:05 ` Justin Tobler
2025-10-07 21:56 ` Taylor Blau
@ 2025-10-08 15:52 ` shejialuo
2025-10-09 6:03 ` Patrick Steinhardt
2 siblings, 1 reply; 106+ messages in thread
From: shejialuo @ 2025-10-08 15:52 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
On Tue, Oct 07, 2025 at 12:58:38PM +0200, Patrick Steinhardt wrote:
> diff --git a/refs.h b/refs.h
> index 4e6bd63aa86..2b24a3d9974 100644
> --- a/refs.h
> +++ b/refs.h
> @@ -355,14 +355,32 @@ struct ref_transaction;
> */
> #define REF_BAD_NAME 0x08
>
> +/* A reference passed to `for_each_ref()`-style callbacks. */
> +struct reference {
> + /* The fully-qualified name of the reference. */
> + const char *name;
> +
> + /* The target of a symbolic ref. `NULL` for direct references. */
> + const char *target;
> +
I have a question here, why do we name this `target` instead of
`referent`? Oh, I somehow know your motivation as we already create a
structure `struct reference`, it is redudant that we use `referent` as
the name.
> + /*
> + * The object ID of a reference. Either the direct object ID or the
> + * resolved object ID in the case of a symbolic ref. May be the zero
> + * object ID in case the symbolic ref cannot be resolved.
> + */
> + const struct object_id *oid;
> +
> + /* A bitfield of `REF_` flags. */
> + int flags;
> +};
> +
Thanks,
Jialuo
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v2 12/14] refs: don't store peeled object IDs for invalid tags
2025-10-08 15:50 ` [PATCH v2 12/14] refs: don't store peeled object IDs for invalid tags Patrick Steinhardt
@ 2025-10-08 16:27 ` shejialuo
2025-10-09 5:22 ` Patrick Steinhardt
0 siblings, 1 reply; 106+ messages in thread
From: shejialuo @ 2025-10-08 16:27 UTC (permalink / raw)
To: Patrick Steinhardt
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Junio C Hamano, Justin Tobler
On Wed, Oct 08, 2025 at 05:50:27PM +0200, Patrick Steinhardt wrote:
> Both the "files" and "reftable" backend store peeled object IDs for
> references that point to tags:
>
> - The "files" backend stores the value when packing refs, where each
> peeled object ID is prefixed with "^".
>
> - The "reftable" backend stores the value whenever writing a new
> reference that points to a tag via a special ref record type.
>
> Both of these backends use `peel_object()` to find the peeled object ID.
> But as explained in the preceding commit, that function does not detect
> the case where the tag's tagged object and its claimed type mismatch.
>
> The consequence of storing these bogus peeled object IDs is that we're
> less likely to detect such corruption in other parts of Git.
> git-for-each-ref(1) for example does not notice anymore that the tag is
> broken when using "--format=%(*objectname)" to dereference tags.
>
> One could claim that this is good, because it still allows us to mostly
> use the tag as intended. But the biggest problem here is that we now
> have different behaviour for such a broken tag depending on whether or
> not we have its peeled value in the refdb.
>
> Fix the issue by verifying the object type when peeling the object. If
> that verification fails we simply skip storing the peeled value in
> either of the reference formats.
>
I have a design question here: should we just report an error to the
user or just die instead of skipping storing the peeled value? If the
annotated tag is corrupted in the first place, it means the refdb is
also corrupted. And "git-fsck(1)" would definitely report an error to
the user. But here we just ignore the problem and give an illusion that
everything is fine.
Thanks,
Jialuo
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v2 12/14] refs: don't store peeled object IDs for invalid tags
2025-10-08 16:27 ` shejialuo
@ 2025-10-09 5:22 ` Patrick Steinhardt
0 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-09 5:22 UTC (permalink / raw)
To: shejialuo
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Junio C Hamano, Justin Tobler
On Thu, Oct 09, 2025 at 12:27:58AM +0800, shejialuo wrote:
> On Wed, Oct 08, 2025 at 05:50:27PM +0200, Patrick Steinhardt wrote:
> > Both the "files" and "reftable" backend store peeled object IDs for
> > references that point to tags:
> >
> > - The "files" backend stores the value when packing refs, where each
> > peeled object ID is prefixed with "^".
> >
> > - The "reftable" backend stores the value whenever writing a new
> > reference that points to a tag via a special ref record type.
> >
> > Both of these backends use `peel_object()` to find the peeled object ID.
> > But as explained in the preceding commit, that function does not detect
> > the case where the tag's tagged object and its claimed type mismatch.
> >
> > The consequence of storing these bogus peeled object IDs is that we're
> > less likely to detect such corruption in other parts of Git.
> > git-for-each-ref(1) for example does not notice anymore that the tag is
> > broken when using "--format=%(*objectname)" to dereference tags.
> >
> > One could claim that this is good, because it still allows us to mostly
> > use the tag as intended. But the biggest problem here is that we now
> > have different behaviour for such a broken tag depending on whether or
> > not we have its peeled value in the refdb.
> >
> > Fix the issue by verifying the object type when peeling the object. If
> > that verification fails we simply skip storing the peeled value in
> > either of the reference formats.
> >
>
> I have a design question here: should we just report an error to the
> user or just die instead of skipping storing the peeled value? If the
> annotated tag is corrupted in the first place, it means the refdb is
> also corrupted. And "git-fsck(1)" would definitely report an error to
> the user. But here we just ignore the problem and give an illusion that
> everything is fine.
The question is whether the user can do anything about it. The tag may
exist due to whatever reason, and it may not be prunable from the repo's
references.
Tools like git-fsck(1) should definitely complain about this, and they
in fact already do:
$ git fsck
Checking ref database: 100% (1/1), done.
error: object d10476e1da82e779f64cfa12bd655b579c3fddbe is a commit, not a blob
error: bad tag pointer to d10476e1da82e779f64cfa12bd655b579c3fddbe in ef5b01be3c1ad24fae2181040ced5776456a197a
error: ef5b01be3c1ad24fae2181040ced5776456a197a: object could not be parsed: .git/objects/ef/5b01be3c1ad24fae2181040ced5776456a197a
Checking object directories: 100% (256/256), done.
error: object d10476e1da82e779f64cfa12bd655b579c3fddbe is a commit, not a blob
error: bad tag pointer to d10476e1da82e779f64cfa12bd655b579c3fddbe in ef5b01be3c1ad24fae2181040ced5776456a197a
error: refs/tags/tag-2: invalid sha1 pointer ef5b01be3c1ad24fae2181040ced5776456a197a
But for operations like optimizing references it is not as clean-cut
from my perspective. We definitely don't want to error out, as it would
mean that the user cannot have their reference optimized as long as such
a broken reference exist. And other operations should make sure that
they don't return invalid data in face of such a corrupted repository,
too.
We may want to add a warning in such cases though? I'd like to have some
more opinions on this.
Patrick
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v2 00/14] refs: improvements and fixes for peeling tags
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
` (13 preceding siblings ...)
2025-10-08 15:50 ` [PATCH v2 14/14] ref-filter: parse objects on demand Patrick Steinhardt
@ 2025-10-09 5:38 ` Jeff King
2025-10-09 6:09 ` Patrick Steinhardt
2025-10-09 19:37 ` Junio C Hamano
14 siblings, 2 replies; 106+ messages in thread
From: Jeff King @ 2025-10-09 5:38 UTC (permalink / raw)
To: Patrick Steinhardt
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Junio C Hamano, Justin Tobler
On Wed, Oct 08, 2025 at 05:50:15PM +0200, Patrick Steinhardt wrote:
> - Patches 1 to 8 refactor our codebase so that we don't have the
> `peel_iterated_object()` hack anymore. I just found it hard to
> follow and thought it shouldn't be too hard to get rid of it.
I'm really happy to see this hack go away. I've wanted to fix it for
ages, but didn't want to bite the bullet on changing all of the
each_ref_fn callbacks. The approach you used here to pass through a
struct looks good to me.
I do have one minor complaint, though: the name of that struct. I have a
feeling that the name "struct reference" may cause confusion down the
road because it's so generic, and because "references" and "refs" are so
common in the code. From the names, when would I know when to use
"struct reference" and when "struct ref"?
Could we give it a name that ties it to the iteration interface?
Something like iterated_ref, each_ref_data, etc?
I know this is minor (and will be annoying to adjust your series), but
I'd rather raise the point now than realize later that it's confusing
and try to change it then.
-Peff
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 01/13] refs: introduce wrapper struct for `each_ref_fn`
2025-10-08 15:52 ` shejialuo
@ 2025-10-09 6:03 ` Patrick Steinhardt
0 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-09 6:03 UTC (permalink / raw)
To: shejialuo; +Cc: git
On Wed, Oct 08, 2025 at 11:52:26PM +0800, shejialuo wrote:
> On Tue, Oct 07, 2025 at 12:58:38PM +0200, Patrick Steinhardt wrote:
> > diff --git a/refs.h b/refs.h
> > index 4e6bd63aa86..2b24a3d9974 100644
> > --- a/refs.h
> > +++ b/refs.h
> > @@ -355,14 +355,32 @@ struct ref_transaction;
> > */
> > #define REF_BAD_NAME 0x08
> >
> > +/* A reference passed to `for_each_ref()`-style callbacks. */
> > +struct reference {
> > + /* The fully-qualified name of the reference. */
> > + const char *name;
> > +
> > + /* The target of a symbolic ref. `NULL` for direct references. */
> > + const char *target;
> > +
>
> I have a question here, why do we name this `target` instead of
> `referent`? Oh, I somehow know your motivation as we already create a
> structure `struct reference`, it is redudant that we use `referent` as
> the name.
I don't really have a strong reason to be honest, I think both "target"
and "referent" would be fine.
Patrick
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v2 00/14] refs: improvements and fixes for peeling tags
2025-10-09 5:38 ` [PATCH v2 00/14] refs: improvements and fixes for peeling tags Jeff King
@ 2025-10-09 6:09 ` Patrick Steinhardt
2025-10-09 6:39 ` Jeff King
2025-10-09 19:37 ` Junio C Hamano
1 sibling, 1 reply; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-09 6:09 UTC (permalink / raw)
To: Jeff King
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Junio C Hamano, Justin Tobler
On Thu, Oct 09, 2025 at 01:38:25AM -0400, Jeff King wrote:
> On Wed, Oct 08, 2025 at 05:50:15PM +0200, Patrick Steinhardt wrote:
>
> > - Patches 1 to 8 refactor our codebase so that we don't have the
> > `peel_iterated_object()` hack anymore. I just found it hard to
> > follow and thought it shouldn't be too hard to get rid of it.
>
> I'm really happy to see this hack go away. I've wanted to fix it for
> ages, but didn't want to bite the bullet on changing all of the
> each_ref_fn callbacks. The approach you used here to pass through a
> struct looks good to me.
>
> I do have one minor complaint, though: the name of that struct. I have a
> feeling that the name "struct reference" may cause confusion down the
> road because it's so generic, and because "references" and "refs" are so
> common in the code. From the names, when would I know when to use
> "struct reference" and when "struct ref"?
>
> Could we give it a name that ties it to the iteration interface?
> Something like iterated_ref, each_ref_data, etc?
>
> I know this is minor (and will be annoying to adjust your series), but
> I'd rather raise the point now than realize later that it's confusing
> and try to change it then.
It is puzzling indeed. I would claim that in this case it is not `struct
reference` that is misnamed: what it contains is as close as you get to
a representation of a reference. It's rather `struct ref` that is
misnamed, as it carries a lot of data that is only valid in the context
of a remote.
Another approach could thus be to rename `struct ref` to `struct
remote_ref`, which I would claim would be a clear win for better semantics.
It's used in lots of places though, which is a valid counter argument.
WDYT?
Patrick
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v2 00/14] refs: improvements and fixes for peeling tags
2025-10-09 6:09 ` Patrick Steinhardt
@ 2025-10-09 6:39 ` Jeff King
2025-10-09 7:24 ` Patrick Steinhardt
2025-10-09 10:11 ` Toon Claes
0 siblings, 2 replies; 106+ messages in thread
From: Jeff King @ 2025-10-09 6:39 UTC (permalink / raw)
To: Patrick Steinhardt
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Junio C Hamano, Justin Tobler
On Thu, Oct 09, 2025 at 08:09:53AM +0200, Patrick Steinhardt wrote:
> > I do have one minor complaint, though: the name of that struct. I have a
> > feeling that the name "struct reference" may cause confusion down the
> > road because it's so generic, and because "references" and "refs" are so
> > common in the code. From the names, when would I know when to use
> > "struct reference" and when "struct ref"?
> >
> > Could we give it a name that ties it to the iteration interface?
> > Something like iterated_ref, each_ref_data, etc?
> >
> > I know this is minor (and will be annoying to adjust your series), but
> > I'd rather raise the point now than realize later that it's confusing
> > and try to change it then.
>
> It is puzzling indeed. I would claim that in this case it is not `struct
> reference` that is misnamed: what it contains is as close as you get to
> a representation of a reference. It's rather `struct ref` that is
> misnamed, as it carries a lot of data that is only valid in the context
> of a remote.
>
> Another approach could thus be to rename `struct ref` to `struct
> remote_ref`, which I would claim would be a clear win for better semantics.
> It's used in lots of places though, which is a valid counter argument.
I am not so much arguing that "struct reference" is misnamed, as that it
is sufficiently generic that people will reach for it when it is not the
appropriate tool. It is for passing the ref data to the iterator
callback, but it probably doesn't make sense in other contexts. Would we
ever expect anybody to declare their own "struct reference" in a local
function? I don't think so.
And yes, "struct ref" suffers somewhat from the same problem. It is
mostly about using refs in one specific space, but the name does not
really help clarify that. I wouldn't mind seeing that improved, but yes,
it would be a noisy patch. I don't know if remote_ref is the right name,
though (the "peer_ref" links mean we store both local and remote refs in
it, IIRC).
-Peff
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v2 00/14] refs: improvements and fixes for peeling tags
2025-10-09 6:39 ` Jeff King
@ 2025-10-09 7:24 ` Patrick Steinhardt
2025-10-10 5:12 ` Jeff King
2025-10-09 10:11 ` Toon Claes
1 sibling, 1 reply; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-09 7:24 UTC (permalink / raw)
To: Jeff King
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Junio C Hamano, Justin Tobler
On Thu, Oct 09, 2025 at 02:39:56AM -0400, Jeff King wrote:
> On Thu, Oct 09, 2025 at 08:09:53AM +0200, Patrick Steinhardt wrote:
>
> > > I do have one minor complaint, though: the name of that struct. I have a
> > > feeling that the name "struct reference" may cause confusion down the
> > > road because it's so generic, and because "references" and "refs" are so
> > > common in the code. From the names, when would I know when to use
> > > "struct reference" and when "struct ref"?
> > >
> > > Could we give it a name that ties it to the iteration interface?
> > > Something like iterated_ref, each_ref_data, etc?
> > >
> > > I know this is minor (and will be annoying to adjust your series), but
> > > I'd rather raise the point now than realize later that it's confusing
> > > and try to change it then.
> >
> > It is puzzling indeed. I would claim that in this case it is not `struct
> > reference` that is misnamed: what it contains is as close as you get to
> > a representation of a reference. It's rather `struct ref` that is
> > misnamed, as it carries a lot of data that is only valid in the context
> > of a remote.
> >
> > Another approach could thus be to rename `struct ref` to `struct
> > remote_ref`, which I would claim would be a clear win for better semantics.
> > It's used in lots of places though, which is a valid counter argument.
>
> I am not so much arguing that "struct reference" is misnamed, as that it
> is sufficiently generic that people will reach for it when it is not the
> appropriate tool. It is for passing the ref data to the iterator
> callback, but it probably doesn't make sense in other contexts. Would we
> ever expect anybody to declare their own "struct reference" in a local
> function? I don't think so.
Hm. The thing is: if `struct reference` is established in our code base,
and if it is a simple representation of a reference, then I think it
might even be a good thing to having it. I could for example see that we
gain more interfaces over time that use it.
For example, functions like `refs_read_ref()` could totally be adapted
to use the same struct, and I would claim that this is a good thing.
We'd basically have a single central structure that allows us to get a
reference out of the "refs" subsystem with metadata.
This would for example allow us to make the peeling infrastructure
available in contexts where we don't use an iterator, which is something
that we cannot do right now. In the end, what this would enable is to
have higher-level interfaces for references.
So yes, `struct reference` is rather generic. But if it's the ref system
owning it I think it's sensible, because references are basically what
this subsystem is all about. This is somewhat equivalent to `struct
object`: the struct name is generic, but it's the basic building block
for our object subsystem.
> And yes, "struct ref" suffers somewhat from the same problem. It is
> mostly about using refs in one specific space, but the name does not
> really help clarify that. I wouldn't mind seeing that improved, but yes,
> it would be a noisy patch. I don't know if remote_ref is the right name,
> though (the "peer_ref" links mean we store both local and remote refs in
> it, IIRC).
If we decide to do it it should definitely be a standalone patch (or
patch series). I'm also open for different naming suggestions, but for
now this is the best I could come up with.
Thanks!
Patrick
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v2 00/14] refs: improvements and fixes for peeling tags
2025-10-09 6:39 ` Jeff King
2025-10-09 7:24 ` Patrick Steinhardt
@ 2025-10-09 10:11 ` Toon Claes
1 sibling, 0 replies; 106+ messages in thread
From: Toon Claes @ 2025-10-09 10:11 UTC (permalink / raw)
To: Jeff King, Patrick Steinhardt
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Junio C Hamano, Justin Tobler
Jeff King <peff@peff.net> writes:
>> > I do have one minor complaint, though: the name of that struct. I have a
>> > feeling that the name "struct reference" may cause confusion down the
>> > road because it's so generic, and because "references" and "refs" are so
>> > common in the code. From the names, when would I know when to use
>> > "struct reference" and when "struct ref"?
>> >
>> > Could we give it a name that ties it to the iteration interface?
>> > Something like iterated_ref, each_ref_data, etc?
>> >
>> > I know this is minor (and will be annoying to adjust your series), but
>> > I'd rather raise the point now than realize later that it's confusing
>> > and try to change it then.
When I was reviewing these patches, I also had some doubts whether
`reference` was the best name for this struct. Especially because the
comment above says:
/* A reference passed to `for_each_ref()`-style callbacks. */
But on the other hand, I agree with Patrick:
>> what it contains is as close as you get to a representation of a
>> reference.
So, I'd be fine if this comment was rewritten a little bit, into
something like:
/* A representation of a reference, for example to be passed to
* `for_each_ref()`-style callbacks. */
> I am not so much arguing that "struct reference" is misnamed, as that it
> is sufficiently generic that people will reach for it when it is not the
> appropriate tool. It is for passing the ref data to the iterator
> callback, but it probably doesn't make sense in other contexts. Would we
> ever expect anybody to declare their own "struct reference" in a local
> function? I don't think so.
Personally I'd say it's hard to predict something like that. Usually I
tend to lean toward naming that doesn't assume anything that might
happen in the future. And because I think this struct contains what a
reference _is_, I think this name is fine. And whenever we run into the
issue that we need something else to be named `reference`, we revisit
this decision.
> And yes, "struct ref" suffers somewhat from the same problem. It is
> mostly about using refs in one specific space, but the name does not
> really help clarify that. I wouldn't mind seeing that improved, but yes,
> it would be a noisy patch. I don't know if remote_ref is the right name,
> though (the "peer_ref" links mean we store both local and remote refs in
> it, IIRC).
--
Cheers,
Toon
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v2 00/14] refs: improvements and fixes for peeling tags
2025-10-09 5:38 ` [PATCH v2 00/14] refs: improvements and fixes for peeling tags Jeff King
2025-10-09 6:09 ` Patrick Steinhardt
@ 2025-10-09 19:37 ` Junio C Hamano
1 sibling, 0 replies; 106+ messages in thread
From: Junio C Hamano @ 2025-10-09 19:37 UTC (permalink / raw)
To: Jeff King
Cc: Patrick Steinhardt, git, Kristoffer Haugsbakk, Karthik Nayak,
Taylor Blau, Justin Tobler
Jeff King <peff@peff.net> writes:
> On Wed, Oct 08, 2025 at 05:50:15PM +0200, Patrick Steinhardt wrote:
>
>> - Patches 1 to 8 refactor our codebase so that we don't have the
>> `peel_iterated_object()` hack anymore. I just found it hard to
>> follow and thought it shouldn't be too hard to get rid of it.
>
> I'm really happy to see this hack go away. I've wanted to fix it for
> ages, but didn't want to bite the bullet on changing all of the
> each_ref_fn callbacks. The approach you used here to pass through a
> struct looks good to me.
>
> I do have one minor complaint, though: the name of that struct. I have a
> feeling that the name "struct reference" may cause confusion down the
> road because it's so generic, and because "references" and "refs" are so
> common in the code. From the names, when would I know when to use
> "struct reference" and when "struct ref"?
>
> Could we give it a name that ties it to the iteration interface?
> Something like iterated_ref, each_ref_data, etc?
>
> I know this is minor (and will be annoying to adjust your series), but
> I'd rather raise the point now than realize later that it's confusing
> and try to change it then.
Thanks for saving me from saying the above ;-) My feeling exactly,
both on the happiness of finally see the hack go, and also the
naming.
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v2 00/14] refs: improvements and fixes for peeling tags
2025-10-09 7:24 ` Patrick Steinhardt
@ 2025-10-10 5:12 ` Jeff King
2025-10-10 5:22 ` Patrick Steinhardt
0 siblings, 1 reply; 106+ messages in thread
From: Jeff King @ 2025-10-10 5:12 UTC (permalink / raw)
To: Patrick Steinhardt
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Junio C Hamano, Justin Tobler
On Thu, Oct 09, 2025 at 09:24:35AM +0200, Patrick Steinhardt wrote:
> > I am not so much arguing that "struct reference" is misnamed, as that it
> > is sufficiently generic that people will reach for it when it is not the
> > appropriate tool. It is for passing the ref data to the iterator
> > callback, but it probably doesn't make sense in other contexts. Would we
> > ever expect anybody to declare their own "struct reference" in a local
> > function? I don't think so.
>
> Hm. The thing is: if `struct reference` is established in our code base,
> and if it is a simple representation of a reference, then I think it
> might even be a good thing to having it. I could for example see that we
> gain more interfaces over time that use it.
>
> For example, functions like `refs_read_ref()` could totally be adapted
> to use the same struct, and I would claim that this is a good thing.
> We'd basically have a single central structure that allows us to get a
> reference out of the "refs" subsystem with metadata.
I agree that if it became the standard representation, then having the
generic name would be good. I guess I'm just skeptical that it will
become/remain that, and not grow gross appendages like the fetch/push
status fields of "struct ref". But maybe I am just too pessimistic. ;)
I do agree that it would be nice for refs_read_ref() and other refstore
functions to use this as a common type for returning results. Even if it
later gained more fields that were specific to the ref subsystem, they'd
still make sense in that context.
So I dunno. I could go either way (keeping your series as-is, or using a
new name).
> > And yes, "struct ref" suffers somewhat from the same problem. It is
> > mostly about using refs in one specific space, but the name does not
> > really help clarify that. I wouldn't mind seeing that improved, but yes,
> > it would be a noisy patch. I don't know if remote_ref is the right name,
> > though (the "peer_ref" links mean we store both local and remote refs in
> > it, IIRC).
>
> If we decide to do it it should definitely be a standalone patch (or
> patch series). I'm also open for different naming suggestions, but for
> now this is the best I could come up with.
That makes sense. The only reason to touch it here would be if we wanted
to free up the name "struct ref" to use right now. I think that is a
better name than "struct reference", but given all of the existing uses
of "struct ref", it is probably not worth the hassle to switch now.
-Peff
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v2 00/14] refs: improvements and fixes for peeling tags
2025-10-10 5:12 ` Jeff King
@ 2025-10-10 5:22 ` Patrick Steinhardt
2025-10-10 6:26 ` Jeff King
2025-10-10 15:29 ` Junio C Hamano
0 siblings, 2 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-10 5:22 UTC (permalink / raw)
To: Jeff King
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Junio C Hamano, Justin Tobler
On Fri, Oct 10, 2025 at 01:12:42AM -0400, Jeff King wrote:
> On Thu, Oct 09, 2025 at 09:24:35AM +0200, Patrick Steinhardt wrote:
>
> > > I am not so much arguing that "struct reference" is misnamed, as that it
> > > is sufficiently generic that people will reach for it when it is not the
> > > appropriate tool. It is for passing the ref data to the iterator
> > > callback, but it probably doesn't make sense in other contexts. Would we
> > > ever expect anybody to declare their own "struct reference" in a local
> > > function? I don't think so.
> >
> > Hm. The thing is: if `struct reference` is established in our code base,
> > and if it is a simple representation of a reference, then I think it
> > might even be a good thing to having it. I could for example see that we
> > gain more interfaces over time that use it.
> >
> > For example, functions like `refs_read_ref()` could totally be adapted
> > to use the same struct, and I would claim that this is a good thing.
> > We'd basically have a single central structure that allows us to get a
> > reference out of the "refs" subsystem with metadata.
>
> I agree that if it became the standard representation, then having the
> generic name would be good. I guess I'm just skeptical that it will
> become/remain that, and not grow gross appendages like the fetch/push
> status fields of "struct ref". But maybe I am just too pessimistic. ;)
>
> I do agree that it would be nice for refs_read_ref() and other refstore
> functions to use this as a common type for returning results. Even if it
> later gained more fields that were specific to the ref subsystem, they'd
> still make sense in that context.
>
> So I dunno. I could go either way (keeping your series as-is, or using a
> new name).
>
> > > And yes, "struct ref" suffers somewhat from the same problem. It is
> > > mostly about using refs in one specific space, but the name does not
> > > really help clarify that. I wouldn't mind seeing that improved, but yes,
> > > it would be a noisy patch. I don't know if remote_ref is the right name,
> > > though (the "peer_ref" links mean we store both local and remote refs in
> > > it, IIRC).
> >
> > If we decide to do it it should definitely be a standalone patch (or
> > patch series). I'm also open for different naming suggestions, but for
> > now this is the best I could come up with.
>
> That makes sense. The only reason to touch it here would be if we wanted
> to free up the name "struct ref" to use right now. I think that is a
> better name than "struct reference", but given all of the existing uses
> of "struct ref", it is probably not worth the hassle to switch now.
I think I'd like to avoid doing such a swap in this series, as it may
easily cause confusion and create problems for any in-flight series.
From my point of view I also think that neither of these names is
clearly superior over the other.
So to move forward, how about we land this as-is and I promise to follow
up with another series that:
- Renames `struct ref` as proposed.
- Introduces `struct reference` into more of our APIs?
Thanks!
Patrick
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v2 00/14] refs: improvements and fixes for peeling tags
2025-10-10 5:22 ` Patrick Steinhardt
@ 2025-10-10 6:26 ` Jeff King
2025-10-10 15:29 ` Junio C Hamano
1 sibling, 0 replies; 106+ messages in thread
From: Jeff King @ 2025-10-10 6:26 UTC (permalink / raw)
To: Patrick Steinhardt
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Junio C Hamano, Justin Tobler
On Fri, Oct 10, 2025 at 07:22:28AM +0200, Patrick Steinhardt wrote:
> I think I'd like to avoid doing such a swap in this series, as it may
> easily cause confusion and create problems for any in-flight series.
> From my point of view I also think that neither of these names is
> clearly superior over the other.
>
> So to move forward, how about we land this as-is and I promise to follow
> up with another series that:
>
> - Renames `struct ref` as proposed.
>
> - Introduces `struct reference` into more of our APIs?
OK by me. I am also OK if we just leave "struct ref" as-is. I like the
name "ref" more than "reference" (since that is the name we usually use,
and what the API is called!). But changing "struct ref" now may be a lot
of churn. So I will not hold you to your promise. :)
-Peff
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v2 00/14] refs: improvements and fixes for peeling tags
2025-10-10 5:22 ` Patrick Steinhardt
2025-10-10 6:26 ` Jeff King
@ 2025-10-10 15:29 ` Junio C Hamano
2025-10-14 6:31 ` Patrick Steinhardt
1 sibling, 1 reply; 106+ messages in thread
From: Junio C Hamano @ 2025-10-10 15:29 UTC (permalink / raw)
To: Patrick Steinhardt
Cc: Jeff King, git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Justin Tobler
Patrick Steinhardt <ps@pks.im> writes:
> So to move forward, how about we land this as-is and I promise to follow
> up with another series that:
>
> - Renames `struct ref` as proposed.
>
> - Introduces `struct reference` into more of our APIs?
Sorry, but I am not quite sure why we would want to do so.
Does "struct reference" sufficiently cover the things we want to do
with references and "struct ref" is not sufficient for that?
Comparing what 'struct ref' caters to its users and what 'struct
reference' offers to its users and declaring that one set of needs
is more generic to the 'reference API' than the other set risks
getting blinded by the area we happened to have been focusing on
recently.
Apparently, the above proposal is not claiming that what one wants
to do is a subset of what the other wants to do (if so, you'd rather
not be introducing a new "struct reference" but extending "struct
ref" to be usable for more things).
Or would we add more to it than what we see in this series, such
that it would no longer be "a subset" of needs various code paths
would have around the reference API? If so, is the longer-term plan
to have callers that use "struct ref" to eventually use "struct
reference"?
If not, they are serving different subset of the problem space, and
they will continue to do so. In that case, why wouldn't we rename
"struct reference" to something that is more focused on what it is
for? In the context of this topic, would that be "reference found
during iteration" or something?
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v2 00/14] refs: improvements and fixes for peeling tags
2025-10-10 15:29 ` Junio C Hamano
@ 2025-10-14 6:31 ` Patrick Steinhardt
2025-10-14 16:52 ` Junio C Hamano
0 siblings, 1 reply; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-14 6:31 UTC (permalink / raw)
To: Junio C Hamano
Cc: Jeff King, git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Justin Tobler
On Fri, Oct 10, 2025 at 08:29:32AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > So to move forward, how about we land this as-is and I promise to follow
> > up with another series that:
> >
> > - Renames `struct ref` as proposed.
> >
> > - Introduces `struct reference` into more of our APIs?
>
> Sorry, but I am not quite sure why we would want to do so.
>
> Does "struct reference" sufficiently cover the things we want to do
> with references and "struct ref" is not sufficient for that?
>
> Comparing what 'struct ref' caters to its users and what 'struct
> reference' offers to its users and declaring that one set of needs
> is more generic to the 'reference API' than the other set risks
> getting blinded by the area we happened to have been focusing on
> recently.
>
> Apparently, the above proposal is not claiming that what one wants
> to do is a subset of what the other wants to do (if so, you'd rather
> not be introducing a new "struct reference" but extending "struct
> ref" to be usable for more things).
>
> Or would we add more to it than what we see in this series, such
> that it would no longer be "a subset" of needs various code paths
> would have around the reference API? If so, is the longer-term plan
> to have callers that use "struct ref" to eventually use "struct
> reference"?
>
> If not, they are serving different subset of the problem space, and
> they will continue to do so. In that case, why wouldn't we rename
> "struct reference" to something that is more focused on what it is
> for? In the context of this topic, would that be "reference found
> during iteration" or something?
I think that `struct ref` and `struct reference` serve quite distinct
use cases. `struct ref` is all around references in the context of a
remote: they are only in our code that interacts with them like for
example "transport.c", "walker.c", "fetch-pack.c" and so on. As such,
this structure naturally contains a ton of fields that are relevant in
this context:
- It's a linked list that identifies all refs part of such a push.
- It contains new_old object IDs.
- It contains information whether or not such a reference should be
force-updated.
- It contains information whether the remote side has such a ref in
the first place.
- It encodes the FETCH_HEAD status.
There's much more, and nothing of this has anything to do with a plain
reference. As such, this type would be a very bad fit for use in the
"refs.c" subsystem, as the basic concepts are mismatching.
So what I'm proposing here is to introduce a `struct reference` that
really only cares about the specific concept of a plain reference. That
struct would thus only carry information that can be yielded by the ref
backends standalone.
The benefit is that it allows us to have a better abstraction boundary,
and it allows us to achieve things we cannot easily do right now. For
example, if functions like `refs_read_ref()` returned such a structure,
then we can now also peel such a ref with `reference_get_peeled_oid()`,
which is currently only possible when iterating over references. Other
higher-level functions like `reference_is_branch()` or similar things
can also be introduced. Last but not least, it also makes it obvious
what kind of "thing" we're working with in many cases, as a `struct
reference` will always be the fully-qualified and "raw" reference.
Hope that sheds some light on my thinking :)
Thanks!
Patrick
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v2 00/14] refs: improvements and fixes for peeling tags
2025-10-14 6:31 ` Patrick Steinhardt
@ 2025-10-14 16:52 ` Junio C Hamano
0 siblings, 0 replies; 106+ messages in thread
From: Junio C Hamano @ 2025-10-14 16:52 UTC (permalink / raw)
To: Patrick Steinhardt
Cc: Jeff King, git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Justin Tobler
Patrick Steinhardt <ps@pks.im> writes:
> I think that `struct ref` and `struct reference` serve quite distinct
> use cases. `struct ref` is all around references in the context of a
> remote: they are only in our code that interacts with them like for
> example "transport.c", "walker.c", "fetch-pack.c" and so on. As such,
> this structure naturally contains a ton of fields that are relevant in
> this context:
>
> - It's a linked list that identifies all refs part of such a push.
>
> - It contains new_old object IDs.
>
> - It contains information whether or not such a reference should be
> force-updated.
>
> - It contains information whether the remote side has such a ref in
> the first place.
>
> - It encodes the FETCH_HEAD status.
>
> There's much more, and nothing of this has anything to do with a plain
> reference. As such, this type would be a very bad fit for use in the
> "refs.c" subsystem, as the basic concepts are mismatching.
OK. The plain reference structure then would not be involved in
local ref update transactions, for example. It is just "here are
the refs we have", "this ref is of this type, at this path, with
this value", etc. Makes sense.
> So what I'm proposing here is to introduce a `struct reference` that
> really only cares about the specific concept of a plain reference. That
> struct would thus only carry information that can be yielded by the ref
> backends standalone.
OK. Makes sense. Thanks.
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH 04/13] refs: expose peeled object ID via the iterator
2025-10-08 13:45 ` Patrick Steinhardt
@ 2025-10-15 8:28 ` Karthik Nayak
0 siblings, 0 replies; 106+ messages in thread
From: Karthik Nayak @ 2025-10-15 8:28 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
[-- Attachment #1: Type: text/plain, Size: 752 bytes --]
Patrick Steinhardt <ps@pks.im> writes:
> On Tue, Oct 07, 2025 at 07:52:43AM -0700, Karthik Nayak wrote:
>> Patrick Steinhardt <ps@pks.im> writes:
>>
>> > Both the "files" and "reftable" backend are able to store peeled values
>> > for tags in the respective formats. This allows for a more efficient
>> > lookup of the target object of such a tag without having to manually
>> > peel via the object database.
>> >
>>
>> In the 'files' backend, I thought only packed-refs store peeled values?
>
> Yup, peeled values may or may not be available with the "files" backend,
> depending on whether or not a reference is packed. But the paragraph
> doesn't really contradict this, does it?
>
> Patrick
No it doesn't, was more rhetorical and self thought :)
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]
^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v3 00/14] refs: improvements and fixes for peeling tags
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
` (15 preceding siblings ...)
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
@ 2025-10-22 6:41 ` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 01/14] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
` (15 more replies)
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
17 siblings, 16 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-22 6:41 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
Hi,
originally, all I wanted to do was the last patch: a small performance
optimization that stops parsing objects in git-for-each-ref(1) unless we
really need to parse them. But that fix cause one specific test to fail,
and only with the reftable backend. So this led me down the rabbit hole
of tag peeling, ending up with this patch series.
The series is structured like follows:
- Patches 1 to 8 refactor our codebase so that we don't have the
`peel_iterated_object()` hack anymore. I just found it hard to
follow and thought it shouldn't be too hard to get rid of it.
- Patches 9 and 10 remove infrastructure that we don't need anymore
after the first couple of patches.
- Patches 11 to 13 fix a couple of issues with peeled tags that I
found. The underlying issue is that tags store both the tagged
object and their type, but this information may not match. We never
verify the actual object type though when allocating the tagged
object, so this only blows up much later.
- Patch 14 was my original motivation, a small performance
optimization.
I'm not particularly fond of the patches 11 to 13. It feels more like
playing whack-a-mole, and I very much assume that there still are edge
cases where we should properly verify the tagged object type. But
changing it in `parse_tag_buffer()` itself causes a bunch of tests to
fail where we intentionally create such corrupted tags. So I didn't
really dare to touch that part, to be honest.
If anybody has suggestions for an alternative approach I'd be very open
to it.
Changes in v3:
- I've rebuilt the topic on 133d151831 (The twenty-first batch, 2025-10-20) with
- tb/incremental-midx-part-3.1 at 935ab44a0a (builtin/repack.c:
clean up unused `#include`s, 2025-10-15)
- jt/16a93c03c7 at (builtin/repo: add progress meter for
structure stats, 2025-10-21)
merged into it. This is done to fix a couple of merge conflicts with
"seen". Both of the topics are only in "seen" right now, but they
are close to be merged.
- Link to v2: https://lore.kernel.org/r/20251008-b4-pks-ref-filter-skip-parsing-objects-v2-0-76e30d5c9542@pks.im
Changes in v2:
- A couple of improvements to commit messages.
- A new commit that ensures that `struct ref_iterator::ref` is always
zeroed out to protect against stale state.
- Link to v1: https://lore.kernel.org/r/20251007-b4-pks-ref-filter-skip-parsing-objects-v1-0-916cc7c6886b@pks.im
Thanks!
Patrick
---
Patrick Steinhardt (14):
refs: introduce wrapper struct for `each_ref_fn`
refs: introduce `.ref` field for the base iterator
refs: fully reset `struct ref_iterator::ref` on iteration
refs: refactor reference status flags
refs: expose peeled object ID via the iterator
upload-pack: convert to use `reference_get_peeled_oid()`
ref-filter: propagate peeled object ID
builtin/show-ref: convert to use `reference_get_peeled_oid()`
refs: drop `current_ref_iter` hack
refs: drop infrastructure to peel via iterators
object: add flag to `peel_object()` to verify object type
refs: don't store peeled object IDs for invalid tags
ref-filter: detect broken tags when dereferencing them
ref-filter: parse objects on demand
bisect.c | 24 ++---
builtin/bisect.c | 17 +---
builtin/checkout.c | 6 +-
builtin/describe.c | 18 ++--
builtin/fetch.c | 13 +--
builtin/fsck.c | 33 +++---
builtin/gc.c | 15 ++-
builtin/ls-remote.c | 2 +-
builtin/name-rev.c | 17 ++--
builtin/pack-objects.c | 28 +++---
builtin/receive-pack.c | 13 ++-
builtin/remote.c | 44 ++++----
builtin/replace.c | 21 ++--
builtin/repo.c | 9 +-
builtin/rev-parse.c | 12 +--
builtin/show-branch.c | 35 +++----
builtin/show-ref.c | 50 ++++-----
builtin/submodule--helper.c | 10 +-
builtin/tag.c | 2 +-
builtin/verify-tag.c | 2 +-
builtin/worktree.c | 6 +-
commit-graph.c | 14 ++-
delta-islands.c | 9 +-
fetch-pack.c | 16 +--
help.c | 10 +-
http-backend.c | 20 ++--
log-tree.c | 24 ++---
ls-refs.c | 36 ++++---
midx-write.c | 17 ++--
negotiator/default.c | 7 +-
negotiator/skipping.c | 7 +-
notes.c | 8 +-
object-name.c | 10 +-
object.c | 20 +++-
object.h | 15 ++-
pseudo-merge.c | 21 ++--
reachable.c | 9 +-
ref-filter.c | 239 ++++++++++++++++++++++++++++++--------------
ref-filter.h | 5 +-
reflog.c | 9 +-
refs.c | 85 +++++++++-------
refs.h | 88 ++++++++++------
refs/debug.c | 17 +---
refs/files-backend.c | 71 +++++--------
refs/iterator.c | 73 +++-----------
refs/packed-backend.c | 71 +++++--------
refs/ref-cache.c | 18 +---
refs/refs-internal.h | 25 +----
refs/reftable-backend.c | 47 +++------
remote.c | 27 +++--
repack-midx.c | 16 ++-
replace-object.c | 16 ++-
revision.c | 12 +--
server-info.c | 12 +--
shallow.c | 16 +--
submodule.c | 12 +--
t/for-each-ref-tests.sh | 4 +-
t/helper/test-reach.c | 2 +-
t/helper/test-ref-store.c | 5 +-
t/pack-refs-tests.sh | 32 ++++++
t/t0610-reftable-basics.sh | 28 ++++++
tag.c | 12 ---
tag.h | 1 -
upload-pack.c | 49 ++++-----
walker.c | 8 +-
worktree.c | 11 +-
66 files changed, 794 insertions(+), 837 deletions(-)
Range-diff versus v2:
1: 4a78d628cef ! 1: 05cfb7e0364 refs: introduce wrapper struct for `each_ref_fn`
@@ builtin/remote.c: static void free_remote_ref_states(struct ref_states *states)
string_list_append(&states->tracked, abbrev_branch(refspec.src));
free(refspec.src);
- ## builtin/repack.c ##
-@@ builtin/repack.c: struct midx_snapshot_ref_data {
- int preferred;
- };
-
--static int midx_snapshot_ref_one(const char *refname UNUSED,
-- const char *referent UNUSED,
-- const struct object_id *oid,
-- int flag UNUSED, void *_data)
-+static int midx_snapshot_ref_one(const struct reference *ref, void *_data)
- {
- struct midx_snapshot_ref_data *data = _data;
-+ const struct object_id *maybe_peeled = ref->oid;
- struct object_id peeled;
-
-- if (!peel_iterated_oid(the_repository, oid, &peeled))
-- oid = &peeled;
-+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
-+ maybe_peeled = &peeled;
-
-- if (oidset_insert(&data->seen, oid))
-+ if (oidset_insert(&data->seen, maybe_peeled))
- return 0; /* already seen */
-
-- if (odb_read_object_info(the_repository->objects, oid, NULL) != OBJ_COMMIT)
-+ if (odb_read_object_info(the_repository->objects, maybe_peeled, NULL) != OBJ_COMMIT)
- return 0;
-
- fprintf(data->f->fp, "%s%s\n", data->preferred ? "+" : "",
-- oid_to_hex(oid));
-+ oid_to_hex(maybe_peeled));
-
- return 0;
- }
-
## builtin/replace.c ##
@@ builtin/replace.c: struct show_data {
enum replace_format format;
@@ builtin/replace.c: struct show_data {
}
+ ## builtin/repo.c ##
+@@ builtin/repo.c: struct count_references_data {
+ struct progress *progress;
+ };
+
+-static int count_references(const char *refname,
+- const char *referent UNUSED,
+- const struct object_id *oid,
+- int flags UNUSED, void *cb_data)
++static int count_references(const struct reference *ref, void *cb_data)
+ {
+ struct count_references_data *data = cb_data;
+ struct ref_stats *stats = data->stats;
+ size_t ref_count;
+
+- switch (ref_kind_from_refname(refname)) {
++ switch (ref_kind_from_refname(ref->name)) {
+ case FILTER_REFS_BRANCHES:
+ stats->branches++;
+ break;
+@@ builtin/repo.c: static int count_references(const char *refname,
+ * While iterating through references for counting, also add OIDs in
+ * preparation for the path walk.
+ */
+- add_pending_oid(data->revs, NULL, oid, 0);
++ add_pending_oid(data->revs, NULL, ref->oid, 0);
+
+ ref_count = get_total_reference_count(stats);
+ display_progress(data->progress, ref_count);
+
## builtin/rev-parse.c ##
@@ builtin/rev-parse.c: static int show_default(void)
return 0;
@@ remote.c: static int get_stale_heads_cb(const char *refname, const char *referen
clean_exit:
+ ## repack-midx.c ##
+@@ repack-midx.c: struct midx_snapshot_ref_data {
+ int preferred;
+ };
+
+-static int midx_snapshot_ref_one(const char *refname UNUSED,
+- const char *referent UNUSED,
+- const struct object_id *oid,
+- int flag UNUSED, void *_data)
++static int midx_snapshot_ref_one(const struct reference *ref, void *_data)
+ {
+ struct midx_snapshot_ref_data *data = _data;
++ const struct object_id *maybe_peeled = ref->oid;
+ struct object_id peeled;
+
+- if (!peel_iterated_oid(data->repo, oid, &peeled))
+- oid = &peeled;
++ if (!peel_iterated_oid(data->repo, ref->oid, &peeled))
++ maybe_peeled = &peeled;
+
+- if (oidset_insert(&data->seen, oid))
++ if (oidset_insert(&data->seen, maybe_peeled))
+ return 0; /* already seen */
+
+- if (odb_read_object_info(data->repo->objects, oid, NULL) != OBJ_COMMIT)
++ if (odb_read_object_info(data->repo->objects, maybe_peeled, NULL) != OBJ_COMMIT)
+ return 0;
+
+ fprintf(data->f->fp, "%s%s\n", data->preferred ? "+" : "",
+- oid_to_hex(oid));
++ oid_to_hex(maybe_peeled));
+
+ return 0;
+ }
+
## replace-object.c ##
@@
#include "repository.h"
2: 9efd804f546 = 2: e3bebc60554 refs: introduce `.ref` field for the base iterator
3: a4d79cf863d = 3: 99849a0323a refs: fully reset `struct ref_iterator::ref` on iteration
4: b4dcba74daf = 4: 6b93b3601b8 refs: refactor reference status flags
5: 89cc42156b4 ! 5: 8756dc100ab refs: expose peeled object ID via the iterator
@@ builtin/pack-objects.c: static int mark_bitmap_preferred_tip(const struct refere
object = parse_object_or_die(the_repository, maybe_peeled, ref->name);
- ## builtin/repack.c ##
-@@ builtin/repack.c: static int midx_snapshot_ref_one(const struct reference *ref, void *_data)
- const struct object_id *maybe_peeled = ref->oid;
- struct object_id peeled;
-
-- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
-+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
- maybe_peeled = &peeled;
-
- if (oidset_insert(&data->seen, maybe_peeled))
-
## commit-graph.c ##
@@ commit-graph.c: static int add_ref_to_set(const struct reference *ref, void *cb_data)
struct object_id peeled;
@@ refs/reftable-backend.c: static int reftable_ref_iterator_advance(struct ref_ite
iter->base.ref.flags = flags;
break;
+
+ ## repack-midx.c ##
+@@ repack-midx.c: static int midx_snapshot_ref_one(const struct reference *ref, void *_data)
+ const struct object_id *maybe_peeled = ref->oid;
+ struct object_id peeled;
+
+- if (!peel_iterated_oid(data->repo, ref->oid, &peeled))
++ if (!reference_get_peeled_oid(data->repo, ref, &peeled))
+ maybe_peeled = &peeled;
+
+ if (oidset_insert(&data->seen, maybe_peeled))
6: 3168a0ca998 = 6: 93ef81780ec upload-pack: convert to use `reference_get_peeled_oid()`
7: e72d8ff494f = 7: 4546770861f ref-filter: propagate peeled object ID
8: 1eb5dee1e04 = 8: fe65e6040f1 builtin/show-ref: convert to use `reference_get_peeled_oid()`
9: f58de2e9eaa = 9: c656a638c2b refs: drop `current_ref_iter` hack
10: 1e62da6ed1e = 10: 03f85e095e8 refs: drop infrastructure to peel via iterators
11: af36e3bfe80 = 11: 8087e6db211 object: add flag to `peel_object()` to verify object type
12: 14859152c57 = 12: 1cc87f74e11 refs: don't store peeled object IDs for invalid tags
13: 2191cfee400 = 13: a5a6f27aea9 ref-filter: detect broken tags when dereferencing them
14: 0ef24754924 = 14: 2265eecd4ee ref-filter: parse objects on demand
---
base-commit: 5c120f01eb88f4be8b06fd4bc6893763204b78c4
change-id: 20250918-b4-pks-ref-filter-skip-parsing-objects-f0d1f6af4a9f
^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v3 01/14] refs: introduce wrapper struct for `each_ref_fn`
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
@ 2025-10-22 6:41 ` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 02/14] refs: introduce `.ref` field for the base iterator Patrick Steinhardt
` (14 subsequent siblings)
15 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-22 6:41 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
The `each_ref_fn` callback function type is used across our code base
for several different functions that iterate through reference. There's
a bunch of callbacks implementing this type, which makes any changes to
the callback signature extremely noisy. An example of the required churn
is e8207717f1 (refs: add referent to each_ref_fn, 2024-08-09): adding a
single argument required us to change 48 files.
It was already proposed back then [1] that we might want to introduce a
wrapper structure to alleviate the pain going forward. While this of
course requires the same kind of global refactoring as just introducing
a new parameter, it at least allows us to more change the callback type
afterwards by just extending the wrapper structure.
One counterargument to this refactoring is that it makes the structure
more opaque. While it is obvious which callsites need to be fixed up
when we change the function type, it's not obvious anymore once we use
a structure. That being said, we only have a handful of sites that
actually need to populate this wrapper structure: our ref backends,
"refs/iterator.c" as well as very few sites that invoke the iterator
callback functions directly.
Introduce this wrapper structure so that we can adapt the iterator
interfaces more readily.
[1]: <ZmarVcF5JjsZx0dl@tanuki>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
bisect.c | 24 +++++++---------
builtin/bisect.c | 17 ++++--------
builtin/checkout.c | 6 ++--
builtin/describe.c | 18 ++++++------
builtin/fetch.c | 13 +++------
builtin/fsck.c | 33 ++++++++++++----------
builtin/gc.c | 15 ++++------
builtin/name-rev.c | 17 ++++++------
builtin/pack-objects.c | 27 ++++++++----------
builtin/receive-pack.c | 13 ++++-----
builtin/remote.c | 44 +++++++++++++----------------
builtin/replace.c | 21 ++++++--------
builtin/repo.c | 9 ++----
builtin/rev-parse.c | 12 ++++----
builtin/show-branch.c | 35 +++++++++++------------
builtin/show-ref.c | 20 ++++++--------
builtin/submodule--helper.c | 10 ++-----
builtin/worktree.c | 6 +---
commit-graph.c | 14 ++++------
delta-islands.c | 9 +++---
fetch-pack.c | 16 +++--------
help.c | 10 +++----
http-backend.c | 20 ++++++--------
log-tree.c | 24 ++++++++--------
ls-refs.c | 36 ++++++++++++++----------
midx-write.c | 17 ++++++------
negotiator/default.c | 7 ++---
negotiator/skipping.c | 7 ++---
notes.c | 8 ++----
object-name.c | 10 +++----
pseudo-merge.c | 21 ++++++--------
reachable.c | 9 +++---
ref-filter.c | 24 ++++++++--------
reflog.c | 9 ++----
refs.c | 67 +++++++++++++++++++++++++--------------------
refs.h | 26 +++++++++++++++---
refs/files-backend.c | 7 ++---
refs/iterator.c | 9 +++++-
remote.c | 27 ++++++++----------
repack-midx.c | 16 +++++------
replace-object.c | 16 ++++-------
revision.c | 12 ++++----
server-info.c | 12 ++++----
shallow.c | 16 +++--------
submodule.c | 12 ++------
t/helper/test-ref-store.c | 5 ++--
upload-pack.c | 29 +++++++++-----------
walker.c | 8 ++----
worktree.c | 11 ++++++--
49 files changed, 392 insertions(+), 462 deletions(-)
diff --git a/bisect.c b/bisect.c
index a6dc76b15c9..326b59c0dc7 100644
--- a/bisect.c
+++ b/bisect.c
@@ -450,21 +450,20 @@ void find_bisection(struct commit_list **commit_list, int *reaches,
clear_commit_weight(&commit_weight);
}
-static int register_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb_data UNUSED)
+static int register_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct strbuf good_prefix = STRBUF_INIT;
strbuf_addstr(&good_prefix, term_good);
strbuf_addstr(&good_prefix, "-");
- if (!strcmp(refname, term_bad)) {
+ if (!strcmp(ref->name, term_bad)) {
free(current_bad_oid);
current_bad_oid = xmalloc(sizeof(*current_bad_oid));
- oidcpy(current_bad_oid, oid);
- } else if (starts_with(refname, good_prefix.buf)) {
- oid_array_append(&good_revs, oid);
- } else if (starts_with(refname, "skip-")) {
- oid_array_append(&skipped_revs, oid);
+ oidcpy(current_bad_oid, ref->oid);
+ } else if (starts_with(ref->name, good_prefix.buf)) {
+ oid_array_append(&good_revs, ref->oid);
+ } else if (starts_with(ref->name, "skip-")) {
+ oid_array_append(&skipped_revs, ref->oid);
}
strbuf_release(&good_prefix);
@@ -1178,14 +1177,11 @@ int estimate_bisect_steps(int all)
return (e < 3 * x) ? n : n - 1;
}
-static int mark_for_removal(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cb_data)
+static int mark_for_removal(const struct reference *ref, void *cb_data)
{
struct string_list *refs = cb_data;
- char *ref = xstrfmt("refs/bisect%s", refname);
- string_list_append(refs, ref);
+ char *bisect_ref = xstrfmt("refs/bisect%s", ref->name);
+ string_list_append(refs, bisect_ref);
return 0;
}
diff --git a/builtin/bisect.c b/builtin/bisect.c
index 8b8d870cd1e..5b2024be62d 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -358,10 +358,7 @@ static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)
return 0;
}
-static int inc_nr(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cb_data)
+static int inc_nr(const struct reference *ref UNUSED, void *cb_data)
{
unsigned int *nr = (unsigned int *)cb_data;
(*nr)++;
@@ -549,12 +546,11 @@ static int bisect_append_log_quoted(const char **argv)
return res;
}
-static int add_bisect_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb)
+static int add_bisect_ref(const struct reference *ref, void *cb)
{
struct add_bisect_ref_data *data = cb;
- add_pending_oid(data->revs, refname, oid, data->object_flags);
+ add_pending_oid(data->revs, ref->name, ref->oid, data->object_flags);
return 0;
}
@@ -1165,12 +1161,9 @@ static int bisect_visualize(struct bisect_terms *terms, int argc,
return run_command(&cmd);
}
-static int get_first_good(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int get_first_good(const struct reference *ref, void *cb_data)
{
- oidcpy(cb_data, oid);
+ oidcpy(cb_data, ref->oid);
return 1;
}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f9453473fe2..66b69df6e67 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1063,11 +1063,9 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
report_tracking(new_branch_info);
}
-static int add_pending_uninteresting_ref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int add_pending_uninteresting_ref(const struct reference *ref, void *cb_data)
{
- add_pending_oid(cb_data, refname, oid, UNINTERESTING);
+ add_pending_oid(cb_data, ref->name, ref->oid, UNINTERESTING);
return 0;
}
diff --git a/builtin/describe.c b/builtin/describe.c
index ffaf8d9f0aa..79545350443 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -154,20 +154,19 @@ static void add_to_known_names(const char *path,
}
}
-static int get_name(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int get_name(const struct reference *ref, void *cb_data UNUSED)
{
int is_tag = 0;
struct object_id peeled;
int is_annotated, prio;
const char *path_to_match = NULL;
- if (skip_prefix(path, "refs/tags/", &path_to_match)) {
+ if (skip_prefix(ref->name, "refs/tags/", &path_to_match)) {
is_tag = 1;
} else if (all) {
if ((exclude_patterns.nr || patterns.nr) &&
- !skip_prefix(path, "refs/heads/", &path_to_match) &&
- !skip_prefix(path, "refs/remotes/", &path_to_match)) {
+ !skip_prefix(ref->name, "refs/heads/", &path_to_match) &&
+ !skip_prefix(ref->name, "refs/remotes/", &path_to_match)) {
/* Only accept reference of known type if there are match/exclude patterns */
return 0;
}
@@ -209,10 +208,10 @@ static int get_name(const char *path, const char *referent UNUSED, const struct
}
/* Is it annotated? */
- if (!peel_iterated_oid(the_repository, oid, &peeled)) {
- is_annotated = !oideq(oid, &peeled);
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled)) {
+ is_annotated = !oideq(ref->oid, &peeled);
} else {
- oidcpy(&peeled, oid);
+ oidcpy(&peeled, ref->oid);
is_annotated = 0;
}
@@ -229,7 +228,8 @@ static int get_name(const char *path, const char *referent UNUSED, const struct
else
prio = 0;
- add_to_known_names(all ? path + 5 : path + 10, &peeled, prio, oid);
+ add_to_known_names(all ? ref->name + 5 : ref->name + 10,
+ &peeled, prio, ref->oid);
return 0;
}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index c7ff3480fb1..7052e6ff215 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -289,13 +289,11 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,
return ent;
}
-static int add_one_refname(const char *refname, const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *cbdata)
+static int add_one_refname(const struct reference *ref, void *cbdata)
{
struct hashmap *refname_map = cbdata;
- (void) refname_hash_add(refname_map, refname, oid);
+ (void) refname_hash_add(refname_map, ref->name, ref->oid);
return 0;
}
@@ -1416,14 +1414,11 @@ static void set_option(struct transport *transport, const char *name, const char
}
-static int add_oid(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int add_oid(const struct reference *ref, void *cb_data)
{
struct oid_array *oids = cb_data;
- oid_array_append(oids, oid);
+ oid_array_append(oids, ref->oid);
return 0;
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 8ee95e0d67c..ed4eea16803 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -530,14 +530,13 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)
return 0;
}
-static int fsck_handle_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object *obj;
- obj = parse_object(the_repository, oid);
+ obj = parse_object(the_repository, ref->oid);
if (!obj) {
- if (is_promisor_object(the_repository, oid)) {
+ if (is_promisor_object(the_repository, ref->oid)) {
/*
* Increment default_refs anyway, because this is a
* valid ref.
@@ -546,19 +545,19 @@ static int fsck_handle_ref(const char *refname, const char *referent UNUSED, con
return 0;
}
error(_("%s: invalid sha1 pointer %s"),
- refname, oid_to_hex(oid));
+ ref->name, oid_to_hex(ref->oid));
errors_found |= ERROR_REACHABLE;
/* We'll continue with the rest despite the error.. */
return 0;
}
- if (obj->type != OBJ_COMMIT && is_branch(refname)) {
- error(_("%s: not a commit"), refname);
+ if (obj->type != OBJ_COMMIT && is_branch(ref->name)) {
+ error(_("%s: not a commit"), ref->name);
errors_found |= ERROR_REFS;
}
default_refs++;
obj->flags |= USED;
fsck_put_object_name(&fsck_walk_options,
- oid, "%s", refname);
+ ref->oid, "%s", ref->name);
mark_object_reachable(obj);
return 0;
@@ -580,13 +579,19 @@ static void get_default_heads(void)
worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
- struct strbuf ref = STRBUF_INIT;
+ struct strbuf refname = STRBUF_INIT;
- strbuf_worktree_ref(wt, &ref, "HEAD");
- fsck_head_link(ref.buf, &head_points_at, &head_oid);
- if (head_points_at && !is_null_oid(&head_oid))
- fsck_handle_ref(ref.buf, NULL, &head_oid, 0, NULL);
- strbuf_release(&ref);
+ strbuf_worktree_ref(wt, &refname, "HEAD");
+ fsck_head_link(refname.buf, &head_points_at, &head_oid);
+ if (head_points_at && !is_null_oid(&head_oid)) {
+ struct reference ref = {
+ .name = refname.buf,
+ .oid = &head_oid,
+ };
+
+ fsck_handle_ref(&ref, NULL);
+ }
+ strbuf_release(&refname);
if (include_reflogs)
refs_for_each_reflog(get_worktree_ref_store(wt),
diff --git a/builtin/gc.c b/builtin/gc.c
index e19e13d9788..9de5de175f6 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -1100,24 +1100,21 @@ struct cg_auto_data {
int limit;
};
-static int dfs_on_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int dfs_on_ref(const struct reference *ref, void *cb_data)
{
struct cg_auto_data *data = (struct cg_auto_data *)cb_data;
int result = 0;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct commit_list *stack = NULL;
struct commit *commit;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
- if (odb_read_object_info(the_repository->objects, oid, NULL) != OBJ_COMMIT)
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ maybe_peeled = &peeled;
+ if (odb_read_object_info(the_repository->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
- commit = lookup_commit(the_repository, oid);
+ commit = lookup_commit(the_repository, maybe_peeled);
if (!commit)
return 0;
if (repo_parse_commit(the_repository, commit) ||
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 74512e54a38..615f7d1aae4 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -339,10 +339,9 @@ static int cmp_by_tag_and_age(const void *a_, const void *b_)
return a->taggerdate != b->taggerdate;
}
-static int name_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int name_ref(const struct reference *ref, void *cb_data)
{
- struct object *o = parse_object(the_repository, oid);
+ struct object *o = parse_object(the_repository, ref->oid);
struct name_ref_data *data = cb_data;
int can_abbreviate_output = data->tags_only && data->name_only;
int deref = 0;
@@ -350,14 +349,14 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
struct commit *commit = NULL;
timestamp_t taggerdate = TIME_MAX;
- if (data->tags_only && !starts_with(path, "refs/tags/"))
+ if (data->tags_only && !starts_with(ref->name, "refs/tags/"))
return 0;
if (data->exclude_filters.nr) {
struct string_list_item *item;
for_each_string_list_item(item, &data->exclude_filters) {
- if (subpath_matches(path, item->string) >= 0)
+ if (subpath_matches(ref->name, item->string) >= 0)
return 0;
}
}
@@ -378,7 +377,7 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
* shouldn't stop when seeing 'refs/tags/v1.4' matches
* 'refs/tags/v*'. We should show it as 'v1.4'.
*/
- switch (subpath_matches(path, item->string)) {
+ switch (subpath_matches(ref->name, item->string)) {
case -1: /* did not match */
break;
case 0: /* matched fully */
@@ -406,13 +405,13 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
}
if (o && o->type == OBJ_COMMIT) {
commit = (struct commit *)o;
- from_tag = starts_with(path, "refs/tags/");
+ from_tag = starts_with(ref->name, "refs/tags/");
if (taggerdate == TIME_MAX)
taggerdate = commit->date;
}
- add_to_tip_table(oid, path, can_abbreviate_output, commit, taggerdate,
- from_tag, deref);
+ add_to_tip_table(ref->oid, ref->name, can_abbreviate_output,
+ commit, taggerdate, from_tag, deref);
return 0;
}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 5bdc44fb2de..39633a0158e 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -831,15 +831,14 @@ static enum write_one_status write_one(struct hashfile *f,
return WRITE_ONE_WRITTEN;
}
-static int mark_tagged(const char *path UNUSED, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int mark_tagged(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
- struct object_entry *entry = packlist_find(&to_pack, oid);
+ struct object_entry *entry = packlist_find(&to_pack, ref->oid);
if (entry)
entry->tagged = 1;
- if (!peel_iterated_oid(the_repository, oid, &peeled)) {
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled)) {
entry = packlist_find(&to_pack, &peeled);
if (entry)
entry->tagged = 1;
@@ -3306,13 +3305,12 @@ static void add_tag_chain(const struct object_id *oid)
}
}
-static int add_ref_tag(const char *tag UNUSED, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int add_ref_tag(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, oid, &peeled) && obj_is_packed(&peeled))
- add_tag_chain(oid);
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled) && obj_is_packed(&peeled))
+ add_tag_chain(ref->oid);
return 0;
}
@@ -4533,19 +4531,16 @@ static void record_recent_commit(struct commit *commit, void *data UNUSED)
oid_array_append(&recent_objects, &commit->object.oid);
}
-static int mark_bitmap_preferred_tip(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *data UNUSED)
+static int mark_bitmap_preferred_tip(const struct reference *ref, void *data UNUSED)
{
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct object *object;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ maybe_peeled = &peeled;
- object = parse_object_or_die(the_repository, oid, refname);
+ object = parse_object_or_die(the_repository, maybe_peeled, ref->name);
if (object->type == OBJ_COMMIT)
object->flags |= NEEDS_BITMAP;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index c9288a9c7e3..e8ee0e73217 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -305,13 +305,12 @@ static void show_ref(const char *path, const struct object_id *oid)
}
}
-static int show_ref_cb(const char *path_full, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *data)
+static int show_ref_cb(const struct reference *ref, void *data)
{
struct oidset *seen = data;
- const char *path = strip_namespace(path_full);
+ const char *path = strip_namespace(ref->name);
- if (ref_is_hidden(path, path_full, &hidden_refs))
+ if (ref_is_hidden(path, ref->name, &hidden_refs))
return 0;
/*
@@ -320,13 +319,13 @@ static int show_ref_cb(const char *path_full, const char *referent UNUSED, const
* transfer but will otherwise ignore them.
*/
if (!path) {
- if (oidset_insert(seen, oid))
+ if (oidset_insert(seen, ref->oid))
return 0;
path = ".have";
} else {
- oidset_insert(seen, oid);
+ oidset_insert(seen, ref->oid);
}
- show_ref(path, oid);
+ show_ref(path, ref->oid);
return 0;
}
diff --git a/builtin/remote.c b/builtin/remote.c
index 8a7ed4299a4..7ffc14ba157 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -570,17 +570,14 @@ struct branches_for_remote {
struct known_remotes *keep;
};
-static int add_branch_for_removal(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED, void *cb_data)
+static int add_branch_for_removal(const struct reference *ref, void *cb_data)
{
struct branches_for_remote *branches = cb_data;
struct refspec_item refspec;
struct known_remote *kr;
memset(&refspec, 0, sizeof(refspec));
- refspec.dst = (char *)refname;
+ refspec.dst = (char *)ref->name;
if (remote_find_tracking(branches->remote, &refspec))
return 0;
free(refspec.src);
@@ -588,7 +585,7 @@ static int add_branch_for_removal(const char *refname,
/* don't delete a branch if another remote also uses it */
for (kr = branches->keep->list; kr; kr = kr->next) {
memset(&refspec, 0, sizeof(refspec));
- refspec.dst = (char *)refname;
+ refspec.dst = (char *)ref->name;
if (!remote_find_tracking(kr->remote, &refspec)) {
free(refspec.src);
return 0;
@@ -596,16 +593,16 @@ static int add_branch_for_removal(const char *refname,
}
/* don't delete non-remote-tracking refs */
- if (!starts_with(refname, "refs/remotes/")) {
+ if (!starts_with(ref->name, "refs/remotes/")) {
/* advise user how to delete local branches */
- if (starts_with(refname, "refs/heads/"))
+ if (starts_with(ref->name, "refs/heads/"))
string_list_append(branches->skipped,
- abbrev_branch(refname));
+ abbrev_branch(ref->name));
/* silently skip over other non-remote refs */
return 0;
}
- string_list_append(branches->branches, refname);
+ string_list_append(branches->branches, ref->name);
return 0;
}
@@ -713,18 +710,18 @@ static int rename_one_reflog(const char *old_refname,
return error;
}
-static int rename_one_ref(const char *old_refname, const char *referent,
- const struct object_id *oid,
- int flags, void *cb_data)
+static int rename_one_ref(const struct reference *ref, void *cb_data)
{
struct strbuf new_referent = STRBUF_INIT;
struct strbuf new_refname = STRBUF_INIT;
struct rename_info *rename = cb_data;
+ const struct object_id *oid = ref->oid;
+ const char *referent = ref->target;
int error;
- compute_renamed_ref(rename, old_refname, &new_refname);
+ compute_renamed_ref(rename, ref->name, &new_refname);
- if (flags & REF_ISSYMREF) {
+ if (ref->flags & REF_ISSYMREF) {
/*
* Stupidly enough `referent` is not pointing to the immediate
* target of a symref, but it's the recursively resolved value.
@@ -732,25 +729,25 @@ static int rename_one_ref(const char *old_refname, const char *referent,
* unborn symrefs don't have any value for the `referent` at all.
*/
referent = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- old_refname, RESOLVE_REF_NO_RECURSE,
+ ref->name, RESOLVE_REF_NO_RECURSE,
NULL, NULL);
compute_renamed_ref(rename, referent, &new_referent);
oid = NULL;
}
- error = ref_transaction_delete(rename->transaction, old_refname,
+ error = ref_transaction_delete(rename->transaction, ref->name,
oid, referent, REF_NO_DEREF, NULL, rename->err);
if (error < 0)
goto out;
error = ref_transaction_update(rename->transaction, new_refname.buf, oid, null_oid(the_hash_algo),
- (flags & REF_ISSYMREF) ? new_referent.buf : NULL, NULL,
+ (ref->flags & REF_ISSYMREF) ? new_referent.buf : NULL, NULL,
REF_SKIP_CREATE_REFLOG | REF_NO_DEREF | REF_SKIP_OID_VERIFICATION,
NULL, rename->err);
if (error < 0)
goto out;
- error = rename_one_reflog(old_refname, oid, rename);
+ error = rename_one_reflog(ref->name, oid, rename);
if (error < 0)
goto out;
@@ -1125,19 +1122,16 @@ static void free_remote_ref_states(struct ref_states *states)
string_list_clear_func(&states->push, clear_push_info);
}
-static int append_ref_to_tracked_list(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags, void *cb_data)
+static int append_ref_to_tracked_list(const struct reference *ref, void *cb_data)
{
struct ref_states *states = cb_data;
struct refspec_item refspec;
- if (flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
return 0;
memset(&refspec, 0, sizeof(refspec));
- refspec.dst = (char *)refname;
+ refspec.dst = (char *)ref->name;
if (!remote_find_tracking(states->remote, &refspec)) {
string_list_append(&states->tracked, abbrev_branch(refspec.src));
free(refspec.src);
diff --git a/builtin/replace.c b/builtin/replace.c
index 900b560a77d..4c62c5ab58b 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -47,30 +47,27 @@ struct show_data {
enum replace_format format;
};
-static int show_reference(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int show_reference(const struct reference *ref, void *cb_data)
{
struct show_data *data = cb_data;
- if (!wildmatch(data->pattern, refname, 0)) {
+ if (!wildmatch(data->pattern, ref->name, 0)) {
if (data->format == REPLACE_FORMAT_SHORT)
- printf("%s\n", refname);
+ printf("%s\n", ref->name);
else if (data->format == REPLACE_FORMAT_MEDIUM)
- printf("%s -> %s\n", refname, oid_to_hex(oid));
+ printf("%s -> %s\n", ref->name, oid_to_hex(ref->oid));
else { /* data->format == REPLACE_FORMAT_LONG */
struct object_id object;
enum object_type obj_type, repl_type;
- if (repo_get_oid(data->repo, refname, &object))
- return error(_("failed to resolve '%s' as a valid ref"), refname);
+ if (repo_get_oid(data->repo, ref->name, &object))
+ return error(_("failed to resolve '%s' as a valid ref"), ref->name);
obj_type = odb_read_object_info(data->repo->objects, &object, NULL);
- repl_type = odb_read_object_info(data->repo->objects, oid, NULL);
+ repl_type = odb_read_object_info(data->repo->objects, ref->oid, NULL);
- printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
- oid_to_hex(oid), type_name(repl_type));
+ printf("%s (%s) -> %s (%s)\n", ref->name, type_name(obj_type),
+ oid_to_hex(ref->oid), type_name(repl_type));
}
}
diff --git a/builtin/repo.c b/builtin/repo.c
index 9d4749f79be..f26640bd6ea 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -366,16 +366,13 @@ struct count_references_data {
struct progress *progress;
};
-static int count_references(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int count_references(const struct reference *ref, void *cb_data)
{
struct count_references_data *data = cb_data;
struct ref_stats *stats = data->stats;
size_t ref_count;
- switch (ref_kind_from_refname(refname)) {
+ switch (ref_kind_from_refname(ref->name)) {
case FILTER_REFS_BRANCHES:
stats->branches++;
break;
@@ -396,7 +393,7 @@ static int count_references(const char *refname,
* While iterating through references for counting, also add OIDs in
* preparation for the path walk.
*/
- add_pending_oid(data->revs, NULL, oid, 0);
+ add_pending_oid(data->revs, NULL, ref->oid, 0);
ref_count = get_total_reference_count(stats);
display_progress(data->progress, ref_count);
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 9da92b990d0..3578591b4f2 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -217,19 +217,17 @@ static int show_default(void)
return 0;
}
-static int show_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int show_reference(const struct reference *ref, void *cb_data UNUSED)
{
- if (ref_excluded(&ref_excludes, refname))
+ if (ref_excluded(&ref_excludes, ref->name))
return 0;
- show_rev(NORMAL, oid, refname);
+ show_rev(NORMAL, ref->oid, ref->name);
return 0;
}
-static int anti_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int anti_reference(const struct reference *ref, void *cb_data UNUSED)
{
- show_rev(REVERSED, oid, refname);
+ show_rev(REVERSED, ref->oid, ref->name);
return 0;
}
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 441babf2e35..10475a6b5ed 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -413,34 +413,32 @@ static int append_ref(const char *refname, const struct object_id *oid,
return 0;
}
-static int append_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int append_head_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id tmp;
int ofs = 11;
- if (!starts_with(refname, "refs/heads/"))
+ if (!starts_with(ref->name, "refs/heads/"))
return 0;
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
+ if (repo_get_oid(the_repository, ref->name + ofs, &tmp) || !oideq(&tmp, ref->oid))
ofs = 5;
- return append_ref(refname + ofs, oid, 0);
+ return append_ref(ref->name + ofs, ref->oid, 0);
}
-static int append_remote_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int append_remote_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id tmp;
int ofs = 13;
- if (!starts_with(refname, "refs/remotes/"))
+ if (!starts_with(ref->name, "refs/remotes/"))
return 0;
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
+ if (repo_get_oid(the_repository, ref->name + ofs, &tmp) || !oideq(&tmp, ref->oid))
ofs = 5;
- return append_ref(refname + ofs, oid, 0);
+ return append_ref(ref->name + ofs, ref->oid, 0);
}
static int append_tag_ref(const char *refname, const struct object_id *oid,
@@ -454,27 +452,26 @@ static int append_tag_ref(const char *refname, const struct object_id *oid,
static const char *match_ref_pattern = NULL;
static int match_ref_slash = 0;
-static int append_matching_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int append_matching_ref(const struct reference *ref, void *cb_data)
{
/* we want to allow pattern hold/<asterisk> to show all
* branches under refs/heads/hold/, and v0.99.9? to show
* refs/tags/v0.99.9a and friends.
*/
const char *tail;
- int slash = count_slashes(refname);
- for (tail = refname; *tail && match_ref_slash < slash; )
+ int slash = count_slashes(ref->name);
+ for (tail = ref->name; *tail && match_ref_slash < slash; )
if (*tail++ == '/')
slash--;
if (!*tail)
return 0;
if (wildmatch(match_ref_pattern, tail, 0))
return 0;
- if (starts_with(refname, "refs/heads/"))
- return append_head_ref(refname, NULL, oid, flag, cb_data);
- if (starts_with(refname, "refs/tags/"))
- return append_tag_ref(refname, oid, flag, cb_data);
- return append_ref(refname, oid, 0);
+ if (starts_with(ref->name, "refs/heads/"))
+ return append_head_ref(ref, cb_data);
+ if (starts_with(ref->name, "refs/tags/"))
+ return append_tag_ref(ref->name, ref->oid, ref->flags, cb_data);
+ return append_ref(ref->name, ref->oid, 0);
}
static void snarf_refs(int head, int remotes)
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 0b6f9edf86c..4803b5e5986 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -66,26 +66,25 @@ struct show_ref_data {
int show_head;
};
-static int show_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cbdata)
+static int show_ref(const struct reference *ref, void *cbdata)
{
struct show_ref_data *data = cbdata;
- if (data->show_head && !strcmp(refname, "HEAD"))
+ if (data->show_head && !strcmp(ref->name, "HEAD"))
goto match;
if (data->patterns) {
- int reflen = strlen(refname);
+ int reflen = strlen(ref->name);
const char **p = data->patterns, *m;
while ((m = *p++) != NULL) {
int len = strlen(m);
if (len > reflen)
continue;
- if (memcmp(m, refname + reflen - len, len))
+ if (memcmp(m, ref->name + reflen - len, len))
continue;
if (len == reflen)
goto match;
- if (refname[reflen - len - 1] == '/')
+ if (ref->name[reflen - len - 1] == '/')
goto match;
}
return 0;
@@ -94,18 +93,15 @@ static int show_ref(const char *refname, const char *referent UNUSED, const stru
match:
data->found_match++;
- show_one(data->show_one_opts, refname, oid);
+ show_one(data->show_one_opts, ref->name, ref->oid);
return 0;
}
-static int add_existing(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cbdata)
+static int add_existing(const struct reference *ref, void *cbdata)
{
struct string_list *list = (struct string_list *)cbdata;
- string_list_insert(list, refname);
+ string_list_insert(list, ref->name);
return 0;
}
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index fcd73abe533..35f6cf735e5 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -593,16 +593,12 @@ static void print_status(unsigned int flags, char state, const char *path,
printf("\n");
}
-static int handle_submodule_head_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int handle_submodule_head_ref(const struct reference *ref, void *cb_data)
{
struct object_id *output = cb_data;
- if (oid)
- oidcpy(output, oid);
+ if (ref->oid)
+ oidcpy(output, ref->oid);
return 0;
}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 812774a5ca9..b7f323b5e4d 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -635,11 +635,7 @@ static void print_preparing_worktree_line(int detach,
*
* Returns 0 on failure and non-zero on success.
*/
-static int first_valid_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED,
- void *cb_data UNUSED)
+static int first_valid_ref(const struct reference *ref UNUSED, void *cb_data UNUSED)
{
return 1;
}
diff --git a/commit-graph.c b/commit-graph.c
index 474454db73d..f91af416259 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1851,18 +1851,16 @@ struct refs_cb_data {
struct progress *progress;
};
-static int add_ref_to_set(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int add_ref_to_set(const struct reference *ref, void *cb_data)
{
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct refs_cb_data *data = (struct refs_cb_data *)cb_data;
- if (!peel_iterated_oid(data->repo, oid, &peeled))
- oid = &peeled;
- if (odb_read_object_info(data->repo->objects, oid, NULL) == OBJ_COMMIT)
- oidset_insert(data->commits, oid);
+ if (!peel_iterated_oid(data->repo, ref->oid, &peeled))
+ maybe_peeled = &peeled;
+ if (odb_read_object_info(data->repo->objects, maybe_peeled, NULL) == OBJ_COMMIT)
+ oidset_insert(data->commits, maybe_peeled);
display_progress(data->progress, oidset_size(data->commits));
diff --git a/delta-islands.c b/delta-islands.c
index 36c94799d69..7cfebc4162b 100644
--- a/delta-islands.c
+++ b/delta-islands.c
@@ -390,8 +390,7 @@ static void add_ref_to_island(kh_str_t *remote_islands, const char *island_name,
rl->hash += sha_core;
}
-static int find_island_for_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb)
+static int find_island_for_ref(const struct reference *ref, void *cb)
{
struct island_load_data *ild = cb;
@@ -406,7 +405,7 @@ static int find_island_for_ref(const char *refname, const char *referent UNUSED,
/* walk backwards to get last-one-wins ordering */
for (i = ild->nr - 1; i >= 0; i--) {
- if (!regexec(&ild->rx[i], refname,
+ if (!regexec(&ild->rx[i], ref->name,
ARRAY_SIZE(matches), matches, 0))
break;
}
@@ -428,10 +427,10 @@ static int find_island_for_ref(const char *refname, const char *referent UNUSED,
if (island_name.len)
strbuf_addch(&island_name, '-');
- strbuf_add(&island_name, refname + match->rm_so, match->rm_eo - match->rm_so);
+ strbuf_add(&island_name, ref->name + match->rm_so, match->rm_eo - match->rm_so);
}
- add_ref_to_island(ild->remote_islands, island_name.buf, oid);
+ add_ref_to_island(ild->remote_islands, island_name.buf, ref->oid);
strbuf_release(&island_name);
return 0;
}
diff --git a/fetch-pack.c b/fetch-pack.c
index fe7a84bf2f9..78c45d4a155 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -188,13 +188,9 @@ static int rev_list_insert_ref(struct fetch_negotiator *negotiator,
return 0;
}
-static int rev_list_insert_ref_oid(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int rev_list_insert_ref_oid(const struct reference *ref, void *cb_data)
{
- return rev_list_insert_ref(cb_data, oid);
+ return rev_list_insert_ref(cb_data, ref->oid);
}
enum ack_type {
@@ -616,13 +612,9 @@ static int mark_complete(const struct object_id *oid)
return 0;
}
-static int mark_complete_oid(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED,
- void *cb_data UNUSED)
+static int mark_complete_oid(const struct reference *ref, void *cb_data UNUSED)
{
- return mark_complete(oid);
+ return mark_complete(ref->oid);
}
static void mark_recent_complete_commits(struct fetch_pack_args *args,
diff --git a/help.c b/help.c
index 5854dd4a7e4..20e114432d7 100644
--- a/help.c
+++ b/help.c
@@ -851,18 +851,16 @@ struct similar_ref_cb {
struct string_list *similar_refs;
};
-static int append_similar_ref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED, void *cb_data)
+static int append_similar_ref(const struct reference *ref, void *cb_data)
{
struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
- char *branch = strrchr(refname, '/') + 1;
+ char *branch = strrchr(ref->name, '/') + 1;
/* A remote branch of the same name is deemed similar */
- if (starts_with(refname, "refs/remotes/") &&
+ if (starts_with(ref->name, "refs/remotes/") &&
!strcmp(branch, cb->base_ref))
string_list_append_nodup(cb->similar_refs,
- refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), refname, 1));
+ refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), ref->name, 1));
return 0;
}
diff --git a/http-backend.c b/http-backend.c
index 9084058f1e9..92e1733f140 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -513,18 +513,17 @@ static void run_service(const char **argv, int buffer_input)
exit(1);
}
-static int show_text_ref(const char *name, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int show_text_ref(const struct reference *ref, void *cb_data)
{
- const char *name_nons = strip_namespace(name);
+ const char *name_nons = strip_namespace(ref->name);
struct strbuf *buf = cb_data;
- struct object *o = parse_object(the_repository, oid);
+ struct object *o = parse_object(the_repository, ref->oid);
if (!o)
return 0;
- strbuf_addf(buf, "%s\t%s\n", oid_to_hex(oid), name_nons);
+ strbuf_addf(buf, "%s\t%s\n", oid_to_hex(ref->oid), name_nons);
if (o->type == OBJ_TAG) {
- o = deref_tag(the_repository, o, name, 0);
+ o = deref_tag(the_repository, o, ref->name, 0);
if (!o)
return 0;
strbuf_addf(buf, "%s\t%s^{}\n", oid_to_hex(&o->oid),
@@ -569,21 +568,20 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)
strbuf_release(&buf);
}
-static int show_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int show_head_ref(const struct reference *ref, void *cb_data)
{
struct strbuf *buf = cb_data;
- if (flag & REF_ISSYMREF) {
+ if (ref->flags & REF_ISSYMREF) {
const char *target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- refname,
+ ref->name,
RESOLVE_REF_READING,
NULL, NULL);
if (target)
strbuf_addf(buf, "ref: %s\n", strip_namespace(target));
} else {
- strbuf_addf(buf, "%s\n", oid_to_hex(oid));
+ strbuf_addf(buf, "%s\n", oid_to_hex(ref->oid));
}
return 0;
diff --git a/log-tree.c b/log-tree.c
index 7d917f2a83d..1729b0c2012 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -147,9 +147,7 @@ static int ref_filter_match(const char *refname,
return 1;
}
-static int add_ref_decoration(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int add_ref_decoration(const struct reference *ref, void *cb_data)
{
int i;
struct object *obj;
@@ -158,16 +156,16 @@ static int add_ref_decoration(const char *refname, const char *referent UNUSED,
struct decoration_filter *filter = (struct decoration_filter *)cb_data;
const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref;
- if (filter && !ref_filter_match(refname, filter))
+ if (filter && !ref_filter_match(ref->name, filter))
return 0;
- if (starts_with(refname, git_replace_ref_base)) {
+ if (starts_with(ref->name, git_replace_ref_base)) {
struct object_id original_oid;
if (!replace_refs_enabled(the_repository))
return 0;
- if (get_oid_hex(refname + strlen(git_replace_ref_base),
+ if (get_oid_hex(ref->name + strlen(git_replace_ref_base),
&original_oid)) {
- warning("invalid replace ref %s", refname);
+ warning("invalid replace ref %s", ref->name);
return 0;
}
obj = parse_object(the_repository, &original_oid);
@@ -176,10 +174,10 @@ static int add_ref_decoration(const char *refname, const char *referent UNUSED,
return 0;
}
- objtype = odb_read_object_info(the_repository->objects, oid, NULL);
+ objtype = odb_read_object_info(the_repository->objects, ref->oid, NULL);
if (objtype < 0)
return 0;
- obj = lookup_object_by_type(the_repository, oid, objtype);
+ obj = lookup_object_by_type(the_repository, ref->oid, objtype);
for (i = 0; i < ARRAY_SIZE(ref_namespace); i++) {
struct ref_namespace_info *info = &ref_namespace[i];
@@ -187,24 +185,24 @@ static int add_ref_decoration(const char *refname, const char *referent UNUSED,
if (!info->decoration)
continue;
if (info->exact) {
- if (!strcmp(refname, info->ref)) {
+ if (!strcmp(ref->name, info->ref)) {
deco_type = info->decoration;
break;
}
- } else if (starts_with(refname, info->ref)) {
+ } else if (starts_with(ref->name, info->ref)) {
deco_type = info->decoration;
break;
}
}
- add_name_decoration(deco_type, refname, obj);
+ add_name_decoration(deco_type, ref->name, obj);
while (obj->type == OBJ_TAG) {
if (!obj->parsed)
parse_object(the_repository, &obj->oid);
obj = ((struct tag *)obj)->tagged;
if (!obj)
break;
- add_name_decoration(DECORATION_REF_TAG, refname, obj);
+ add_name_decoration(DECORATION_REF_TAG, ref->name, obj);
}
return 0;
}
diff --git a/ls-refs.c b/ls-refs.c
index c47acde07f3..64d02723691 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -75,42 +75,42 @@ struct ls_refs_data {
unsigned unborn : 1;
};
-static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int send_ref(const struct reference *ref, void *cb_data)
{
struct ls_refs_data *data = cb_data;
- const char *refname_nons = strip_namespace(refname);
+ const char *refname_nons = strip_namespace(ref->name);
strbuf_reset(&data->buf);
- if (ref_is_hidden(refname_nons, refname, &data->hidden_refs))
+ if (ref_is_hidden(refname_nons, ref->name, &data->hidden_refs))
return 0;
if (!ref_match(&data->prefixes, refname_nons))
return 0;
- if (oid)
- strbuf_addf(&data->buf, "%s %s", oid_to_hex(oid), refname_nons);
+ if (ref->oid)
+ strbuf_addf(&data->buf, "%s %s", oid_to_hex(ref->oid), refname_nons);
else
strbuf_addf(&data->buf, "unborn %s", refname_nons);
- if (data->symrefs && flag & REF_ISSYMREF) {
+ if (data->symrefs && ref->flags & REF_ISSYMREF) {
+ int unused_flag;
struct object_id unused;
const char *symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- refname,
+ ref->name,
0,
&unused,
- &flag);
+ &unused_flag);
if (!symref_target)
- die("'%s' is a symref but it is not?", refname);
+ die("'%s' is a symref but it is not?", ref->name);
strbuf_addf(&data->buf, " symref-target:%s",
strip_namespace(symref_target));
}
- if (data->peel && oid) {
+ if (data->peel && ref->oid) {
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
strbuf_addf(&data->buf, " peeled:%s", oid_to_hex(&peeled));
}
@@ -131,9 +131,17 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)
if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), namespaced.buf, 0, &oid, &flag))
return; /* bad ref */
oid_is_null = is_null_oid(&oid);
+
if (!oid_is_null ||
- (data->unborn && data->symrefs && (flag & REF_ISSYMREF)))
- send_ref(namespaced.buf, NULL, oid_is_null ? NULL : &oid, flag, data);
+ (data->unborn && data->symrefs && (flag & REF_ISSYMREF))) {
+ struct reference ref = {
+ .name = namespaced.buf,
+ .oid = oid_is_null ? NULL : &oid,
+ .flags = flag,
+ };
+
+ send_ref(&ref, data);
+ }
strbuf_release(&namespaced);
}
diff --git a/midx-write.c b/midx-write.c
index c73010df6d3..f4dd875747a 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -697,28 +697,27 @@ static void prepare_midx_packing_data(struct packing_data *pdata,
trace2_region_leave("midx", "prepare_midx_packing_data", ctx->repo);
}
-static int add_ref_to_pending(const char *refname, const char *referent UNUSED,
- const struct object_id *oid,
- int flag, void *cb_data)
+static int add_ref_to_pending(const struct reference *ref, void *cb_data)
{
struct rev_info *revs = (struct rev_info*)cb_data;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct object *object;
- if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
- warning("symbolic ref is dangling: %s", refname);
+ if ((ref->flags & REF_ISSYMREF) && (ref->flags & REF_ISBROKEN)) {
+ warning("symbolic ref is dangling: %s", ref->name);
return 0;
}
- if (!peel_iterated_oid(revs->repo, oid, &peeled))
- oid = &peeled;
+ if (!peel_iterated_oid(revs->repo, ref->oid, &peeled))
+ maybe_peeled = &peeled;
- object = parse_object_or_die(revs->repo, oid, refname);
+ object = parse_object_or_die(revs->repo, maybe_peeled, ref->name);
if (object->type != OBJ_COMMIT)
return 0;
add_pending_object(revs, object, "");
- if (bitmap_is_preferred_refname(revs->repo, refname))
+ if (bitmap_is_preferred_refname(revs->repo, ref->name))
object->flags |= NEEDS_BITMAP;
return 0;
}
diff --git a/negotiator/default.c b/negotiator/default.c
index c479da9b091..116dedcf830 100644
--- a/negotiator/default.c
+++ b/negotiator/default.c
@@ -38,11 +38,10 @@ static void rev_list_push(struct negotiation_state *ns,
}
}
-static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data UNUSED)
+static int clear_marks(const struct reference *ref, void *cb_data UNUSED)
{
- struct object *o = deref_tag(the_repository, parse_object(the_repository, oid), refname, 0);
+ struct object *o = deref_tag(the_repository, parse_object(the_repository, ref->oid),
+ ref->name, 0);
if (o && o->type == OBJ_COMMIT)
clear_commit_marks((struct commit *)o,
diff --git a/negotiator/skipping.c b/negotiator/skipping.c
index 616df6bf3af..0a272130fb1 100644
--- a/negotiator/skipping.c
+++ b/negotiator/skipping.c
@@ -75,11 +75,10 @@ static struct entry *rev_list_push(struct data *data, struct commit *commit, int
return entry;
}
-static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data UNUSED)
+static int clear_marks(const struct reference *ref, void *cb_data UNUSED)
{
- struct object *o = deref_tag(the_repository, parse_object(the_repository, oid), refname, 0);
+ struct object *o = deref_tag(the_repository, parse_object(the_repository, ref->oid),
+ ref->name, 0);
if (o && o->type == OBJ_COMMIT)
clear_commit_marks((struct commit *)o,
diff --git a/notes.c b/notes.c
index 9a2e9181fe6..8e00fd8c470 100644
--- a/notes.c
+++ b/notes.c
@@ -938,13 +938,11 @@ int combine_notes_cat_sort_uniq(struct object_id *cur_oid,
return ret;
}
-static int string_list_add_one_ref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cb)
+static int string_list_add_one_ref(const struct reference *ref, void *cb)
{
struct string_list *refs = cb;
- if (!unsorted_string_list_has_string(refs, refname))
- string_list_append(refs, refname);
+ if (!unsorted_string_list_has_string(refs, ref->name))
+ string_list_append(refs, ref->name);
return 0;
}
diff --git a/object-name.c b/object-name.c
index f6902e140dd..7e8109f25fb 100644
--- a/object-name.c
+++ b/object-name.c
@@ -1444,18 +1444,16 @@ struct handle_one_ref_cb {
struct commit_list **list;
};
-static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int handle_one_ref(const struct reference *ref, void *cb_data)
{
struct handle_one_ref_cb *cb = cb_data;
struct commit_list **list = cb->list;
- struct object *object = parse_object(cb->repo, oid);
+ struct object *object = parse_object(cb->repo, ref->oid);
if (!object)
return 0;
if (object->type == OBJ_TAG) {
- object = deref_tag(cb->repo, object, path,
- strlen(path));
+ object = deref_tag(cb->repo, object, ref->name,
+ strlen(ref->name));
if (!object)
return 0;
}
diff --git a/pseudo-merge.c b/pseudo-merge.c
index 893b763fe45..0abd51b42c1 100644
--- a/pseudo-merge.c
+++ b/pseudo-merge.c
@@ -221,28 +221,25 @@ void load_pseudo_merges_from_config(struct repository *r,
}
}
-static int find_pseudo_merge_group_for_ref(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *_data)
+static int find_pseudo_merge_group_for_ref(const struct reference *ref, void *_data)
{
struct bitmap_writer *writer = _data;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct commit *c;
uint32_t i;
int has_bitmap;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ maybe_peeled = &peeled;
- c = lookup_commit(the_repository, oid);
+ c = lookup_commit(the_repository, maybe_peeled);
if (!c)
return 0;
- if (!packlist_find(writer->to_pack, oid))
+ if (!packlist_find(writer->to_pack, maybe_peeled))
return 0;
- has_bitmap = bitmap_writer_has_bitmapped_object_id(writer, oid);
+ has_bitmap = bitmap_writer_has_bitmapped_object_id(writer, maybe_peeled);
for (i = 0; i < writer->pseudo_merge_groups.nr; i++) {
struct pseudo_merge_group *group;
@@ -252,7 +249,7 @@ static int find_pseudo_merge_group_for_ref(const char *refname,
size_t j;
group = writer->pseudo_merge_groups.items[i].util;
- if (regexec(group->pattern, refname, ARRAY_SIZE(captures),
+ if (regexec(group->pattern, ref->name, ARRAY_SIZE(captures),
captures, 0))
continue;
@@ -269,7 +266,7 @@ static int find_pseudo_merge_group_for_ref(const char *refname,
if (group_name.len)
strbuf_addch(&group_name, '-');
- strbuf_add(&group_name, refname + match->rm_so,
+ strbuf_add(&group_name, ref->name + match->rm_so,
match->rm_eo - match->rm_so);
}
diff --git a/reachable.c b/reachable.c
index 22266db5233..b753c395530 100644
--- a/reachable.c
+++ b/reachable.c
@@ -83,18 +83,17 @@ static void add_rebase_files(struct rev_info *revs)
free_worktrees(worktrees);
}
-static int add_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int add_one_ref(const struct reference *ref, void *cb_data)
{
struct rev_info *revs = (struct rev_info *)cb_data;
struct object *object;
- if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
- warning("symbolic ref is dangling: %s", path);
+ if ((ref->flags & REF_ISSYMREF) && (ref->flags & REF_ISBROKEN)) {
+ warning("symbolic ref is dangling: %s", ref->name);
return 0;
}
- object = parse_object_or_die(the_repository, oid, path);
+ object = parse_object_or_die(the_repository, ref->oid, ref->name);
add_pending_object(revs, object, "");
return 0;
diff --git a/ref-filter.c b/ref-filter.c
index 30cc488d8ab..6837fa60a9b 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2954,14 +2954,15 @@ struct ref_filter_cbdata {
* A call-back given to for_each_ref(). Filter refs and keep them for
* later object processing.
*/
-static int filter_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)
+static int filter_one(const struct reference *ref, void *cb_data)
{
struct ref_filter_cbdata *ref_cbdata = cb_data;
- struct ref_array_item *ref;
+ struct ref_array_item *item;
- ref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);
- if (ref)
- ref_array_append(ref_cbdata->array, ref);
+ item = apply_ref_filter(ref->name, ref->target, ref->oid,
+ ref->flags, ref_cbdata->filter);
+ if (item)
+ ref_array_append(ref_cbdata->array, item);
return 0;
}
@@ -2990,17 +2991,18 @@ struct ref_filter_and_format_cbdata {
} internal;
};
-static int filter_and_format_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)
+static int filter_and_format_one(const struct reference *ref, void *cb_data)
{
struct ref_filter_and_format_cbdata *ref_cbdata = cb_data;
- struct ref_array_item *ref;
+ struct ref_array_item *item;
struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
- ref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);
- if (!ref)
+ item = apply_ref_filter(ref->name, ref->target, ref->oid,
+ ref->flags, ref_cbdata->filter);
+ if (!item)
return 0;
- if (format_ref_array_item(ref, ref_cbdata->format, &output, &err))
+ if (format_ref_array_item(item, ref_cbdata->format, &output, &err))
die("%s", err.buf);
if (output.len || !ref_cbdata->format->array_opts.omit_empty) {
@@ -3010,7 +3012,7 @@ static int filter_and_format_one(const char *refname, const char *referent, cons
strbuf_release(&output);
strbuf_release(&err);
- free_array_item(ref);
+ free_array_item(item);
/*
* Increment the running count of refs that match the filter. If
diff --git a/reflog.c b/reflog.c
index 65ef259b4f5..ac87e20c4f9 100644
--- a/reflog.c
+++ b/reflog.c
@@ -423,16 +423,13 @@ int should_expire_reflog_ent_verbose(struct object_id *ooid,
return expire;
}
-static int push_tip_to_list(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags, void *cb_data)
+static int push_tip_to_list(const struct reference *ref, void *cb_data)
{
struct commit_list **list = cb_data;
struct commit *tip_commit;
- if (flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
return 0;
- tip_commit = lookup_commit_reference_gently(the_repository, oid, 1);
+ tip_commit = lookup_commit_reference_gently(the_repository, ref->oid, 1);
if (!tip_commit)
return 0;
commit_list_insert(tip_commit, list);
diff --git a/refs.c b/refs.c
index 965381367e0..25f0579d610 100644
--- a/refs.c
+++ b/refs.c
@@ -426,17 +426,19 @@ int refs_ref_exists(struct ref_store *refs, const char *refname)
NULL, NULL);
}
-static int for_each_filter_refs(const char *refname, const char *referent,
- const struct object_id *oid,
- int flags, void *data)
+static int for_each_filter_refs(const struct reference *ref, void *data)
{
struct for_each_ref_filter *filter = data;
- if (wildmatch(filter->pattern, refname, 0))
+ if (wildmatch(filter->pattern, ref->name, 0))
return 0;
- if (filter->prefix)
- skip_prefix(refname, filter->prefix, &refname);
- return filter->fn(refname, referent, oid, flags, filter->cb_data);
+ if (filter->prefix) {
+ struct reference skipped = *ref;
+ skip_prefix(skipped.name, filter->prefix, &skipped.name);
+ return filter->fn(&skipped, filter->cb_data);
+ } else {
+ return filter->fn(ref, filter->cb_data);
+ }
}
struct warn_if_dangling_data {
@@ -447,17 +449,15 @@ struct warn_if_dangling_data {
int dry_run;
};
-static int warn_if_dangling_symref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags, void *cb_data)
+static int warn_if_dangling_symref(const struct reference *ref, void *cb_data)
{
struct warn_if_dangling_data *d = cb_data;
const char *resolves_to, *msg;
- if (!(flags & REF_ISSYMREF))
+ if (!(ref->flags & REF_ISSYMREF))
return 0;
- resolves_to = refs_resolve_ref_unsafe(d->refs, refname, 0, NULL, NULL);
+ resolves_to = refs_resolve_ref_unsafe(d->refs, ref->name, 0, NULL, NULL);
if (!resolves_to
|| !string_list_has_string(d->refnames, resolves_to)) {
return 0;
@@ -466,7 +466,7 @@ static int warn_if_dangling_symref(const char *refname, const char *referent UNU
msg = d->dry_run
? _("%s%s will become dangling after %s is deleted\n")
: _("%s%s has become dangling after %s was deleted\n");
- fprintf(d->fp, msg, d->indent, refname, resolves_to);
+ fprintf(d->fp, msg, d->indent, ref->name, resolves_to);
return 0;
}
@@ -507,8 +507,15 @@ int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_da
int flag;
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
- if (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag))
- ret = fn(buf.buf, NULL, &oid, flag, cb_data);
+ if (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag)) {
+ struct reference ref = {
+ .name = buf.buf,
+ .oid = &oid,
+ .flags = flag,
+ };
+
+ ret = fn(&ref, cb_data);
+ }
strbuf_release(&buf);
return ret;
@@ -1741,8 +1748,15 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
int flag;
if (refs_resolve_ref_unsafe(refs, "HEAD", RESOLVE_REF_READING,
- &oid, &flag))
- return fn("HEAD", NULL, &oid, flag, cb_data);
+ &oid, &flag)) {
+ struct reference ref = {
+ .name = "HEAD",
+ .oid = &oid,
+ .flags = flag,
+ };
+
+ return fn(&ref, cb_data);
+ }
return 0;
}
@@ -2753,14 +2767,10 @@ struct do_for_each_reflog_help {
void *cb_data;
};
-static int do_for_each_reflog_helper(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED,
- void *cb_data)
+static int do_for_each_reflog_helper(const struct reference *ref, void *cb_data)
{
struct do_for_each_reflog_help *hp = cb_data;
- return hp->fn(refname, hp->cb_data);
+ return hp->fn(ref->name, hp->cb_data);
}
int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data)
@@ -2976,25 +2986,24 @@ struct migration_data {
uint64_t index;
};
-static int migrate_one_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags, void *cb_data)
+static int migrate_one_ref(const struct reference *ref, void *cb_data)
{
struct migration_data *data = cb_data;
struct strbuf symref_target = STRBUF_INIT;
int ret;
- if (flags & REF_ISSYMREF) {
- ret = refs_read_symbolic_ref(data->old_refs, refname, &symref_target);
+ if (ref->flags & REF_ISSYMREF) {
+ ret = refs_read_symbolic_ref(data->old_refs, ref->name, &symref_target);
if (ret < 0)
goto done;
- ret = ref_transaction_update(data->transaction, refname, NULL, null_oid(the_hash_algo),
+ ret = ref_transaction_update(data->transaction, ref->name, NULL, null_oid(the_hash_algo),
symref_target.buf, NULL,
REF_SKIP_CREATE_REFLOG | REF_NO_DEREF, NULL, data->errbuf);
if (ret < 0)
goto done;
} else {
- ret = ref_transaction_create(data->transaction, refname, oid, NULL,
+ ret = ref_transaction_create(data->transaction, ref->name, ref->oid, NULL,
REF_SKIP_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION,
NULL, data->errbuf);
if (ret < 0)
diff --git a/refs.h b/refs.h
index 4e6bd63aa86..68d235438c2 100644
--- a/refs.h
+++ b/refs.h
@@ -355,14 +355,32 @@ struct ref_transaction;
*/
#define REF_BAD_NAME 0x08
+/* A reference passed to `for_each_ref()`-style callbacks. */
+struct reference {
+ /* The fully-qualified name of the reference. */
+ const char *name;
+
+ /* The target of a symbolic ref. `NULL` for direct references. */
+ const char *target;
+
+ /*
+ * The object ID of a reference. Either the direct object ID or the
+ * resolved object ID in the case of a symbolic ref. May be the zero
+ * object ID in case the symbolic ref cannot be resolved.
+ */
+ const struct object_id *oid;
+
+ /* A bitfield of `REF_` flags. */
+ int flags;
+};
+
/*
* The signature for the callback function for the for_each_*()
- * functions below. The memory pointed to by the refname and oid
- * arguments is only guaranteed to be valid for the duration of a
+ * functions below. The memory pointed to by the `struct reference`
+ * argument is only guaranteed to be valid for the duration of a
* single callback invocation.
*/
-typedef int each_ref_fn(const char *refname, const char *referent,
- const struct object_id *oid, int flags, void *cb_data);
+typedef int each_ref_fn(const struct reference *ref, void *cb_data);
/*
* The following functions invoke the specified callback function for
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 8d7007f4aaa..eb3142f8f2d 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3150,14 +3150,11 @@ static int parse_and_write_reflog(struct files_ref_store *refs,
return 0;
}
-static int ref_present(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED,
- void *cb_data)
+static int ref_present(const struct reference *ref, void *cb_data)
{
struct string_list *affected_refnames = cb_data;
- return string_list_has_string(affected_refnames, refname);
+ return string_list_has_string(affected_refnames, ref->name);
}
static int files_transaction_finish_initial(struct files_ref_store *refs,
diff --git a/refs/iterator.c b/refs/iterator.c
index 17ef841d8a3..7f2e718f1c9 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -476,7 +476,14 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
- retval = fn(iter->refname, iter->referent, iter->oid, iter->flags, cb_data);
+ struct reference ref = {
+ .name = iter->refname,
+ .target = iter->referent,
+ .oid = iter->oid,
+ .flags = iter->flags,
+ };
+
+ retval = fn(&ref, cb_data);
if (retval)
goto out;
}
diff --git a/remote.c b/remote.c
index df9675cd330..59b37151208 100644
--- a/remote.c
+++ b/remote.c
@@ -2315,21 +2315,19 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
return 1;
}
-static int one_local_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int one_local_ref(const struct reference *ref, void *cb_data)
{
struct ref ***local_tail = cb_data;
- struct ref *ref;
+ struct ref *local_ref;
/* we already know it starts with refs/ to get here */
- if (check_refname_format(refname + 5, 0))
+ if (check_refname_format(ref->name + 5, 0))
return 0;
- ref = alloc_ref(refname);
- oidcpy(&ref->new_oid, oid);
- **local_tail = ref;
- *local_tail = &ref->next;
+ local_ref = alloc_ref(ref->name);
+ oidcpy(&local_ref->new_oid, ref->oid);
+ **local_tail = local_ref;
+ *local_tail = &local_ref->next;
return 0;
}
@@ -2402,15 +2400,14 @@ struct stale_heads_info {
struct refspec *rs;
};
-static int get_stale_heads_cb(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags, void *cb_data)
+static int get_stale_heads_cb(const struct reference *ref, void *cb_data)
{
struct stale_heads_info *info = cb_data;
struct string_list matches = STRING_LIST_INIT_DUP;
struct refspec_item query;
int i, stale = 1;
memset(&query, 0, sizeof(struct refspec_item));
- query.dst = (char *)refname;
+ query.dst = (char *)ref->name;
refspec_find_all_matches(info->rs, &query, &matches);
if (matches.nr == 0)
@@ -2423,7 +2420,7 @@ static int get_stale_heads_cb(const char *refname, const char *referent UNUSED,
* overlapping refspecs, we need to go over all of the
* matching refs.
*/
- if (flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
goto clean_exit;
for (i = 0; stale && i < matches.nr; i++)
@@ -2431,8 +2428,8 @@ static int get_stale_heads_cb(const char *refname, const char *referent UNUSED,
stale = 0;
if (stale) {
- struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
- oidcpy(&ref->new_oid, oid);
+ struct ref *linked_ref = make_linked_ref(ref->name, &info->stale_refs_tail);
+ oidcpy(&linked_ref->new_oid, ref->oid);
}
clean_exit:
diff --git a/repack-midx.c b/repack-midx.c
index 6f6202c5bcc..349f7e20b53 100644
--- a/repack-midx.c
+++ b/repack-midx.c
@@ -16,25 +16,23 @@ struct midx_snapshot_ref_data {
int preferred;
};
-static int midx_snapshot_ref_one(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *_data)
+static int midx_snapshot_ref_one(const struct reference *ref, void *_data)
{
struct midx_snapshot_ref_data *data = _data;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
- if (!peel_iterated_oid(data->repo, oid, &peeled))
- oid = &peeled;
+ if (!peel_iterated_oid(data->repo, ref->oid, &peeled))
+ maybe_peeled = &peeled;
- if (oidset_insert(&data->seen, oid))
+ if (oidset_insert(&data->seen, maybe_peeled))
return 0; /* already seen */
- if (odb_read_object_info(data->repo->objects, oid, NULL) != OBJ_COMMIT)
+ if (odb_read_object_info(data->repo->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
fprintf(data->f->fp, "%s%s\n", data->preferred ? "+" : "",
- oid_to_hex(oid));
+ oid_to_hex(maybe_peeled));
return 0;
}
diff --git a/replace-object.c b/replace-object.c
index 3eae0510745..03d0f1f083b 100644
--- a/replace-object.c
+++ b/replace-object.c
@@ -8,31 +8,27 @@
#include "repository.h"
#include "commit.h"
-static int register_replace_ref(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int register_replace_ref(const struct reference *ref, void *cb_data)
{
struct repository *r = cb_data;
/* Get sha1 from refname */
- const char *slash = strrchr(refname, '/');
- const char *hash = slash ? slash + 1 : refname;
+ const char *slash = strrchr(ref->name, '/');
+ const char *hash = slash ? slash + 1 : ref->name;
struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj));
if (get_oid_hex_algop(hash, &repl_obj->original.oid, r->hash_algo)) {
free(repl_obj);
- warning(_("bad replace ref name: %s"), refname);
+ warning(_("bad replace ref name: %s"), ref->name);
return 0;
}
/* Copy sha1 from the read ref */
- oidcpy(&repl_obj->replacement, oid);
+ oidcpy(&repl_obj->replacement, ref->oid);
/* Register new object */
if (oidmap_put(&r->objects->replace_map, repl_obj))
- die(_("duplicate replace ref: %s"), refname);
+ die(_("duplicate replace ref: %s"), ref->name);
return 0;
}
diff --git a/revision.c b/revision.c
index cf5e6c1ec9e..5f0850ae5c9 100644
--- a/revision.c
+++ b/revision.c
@@ -1644,19 +1644,17 @@ struct all_refs_cb {
struct worktree *wt;
};
-static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int handle_one_ref(const struct reference *ref, void *cb_data)
{
struct all_refs_cb *cb = cb_data;
struct object *object;
- if (ref_excluded(&cb->all_revs->ref_excludes, path))
+ if (ref_excluded(&cb->all_revs->ref_excludes, ref->name))
return 0;
- object = get_reference(cb->all_revs, path, oid, cb->all_flags);
- add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
- add_pending_object(cb->all_revs, object, path);
+ object = get_reference(cb->all_revs, ref->name, ref->oid, cb->all_flags);
+ add_rev_cmdline(cb->all_revs, object, ref->name, REV_CMD_REF, cb->all_flags);
+ add_pending_object(cb->all_revs, object, ref->name);
return 0;
}
diff --git a/server-info.c b/server-info.c
index 1d33de821e9..0a07c722e8b 100644
--- a/server-info.c
+++ b/server-info.c
@@ -148,23 +148,21 @@ static int update_info_file(struct repository *r, char *path,
return ret;
}
-static int add_info_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int add_info_ref(const struct reference *ref, void *cb_data)
{
struct update_info_ctx *uic = cb_data;
- struct object *o = parse_object(uic->repo, oid);
+ struct object *o = parse_object(uic->repo, ref->oid);
if (!o)
return -1;
- if (uic_printf(uic, "%s %s\n", oid_to_hex(oid), path) < 0)
+ if (uic_printf(uic, "%s %s\n", oid_to_hex(ref->oid), ref->name) < 0)
return -1;
if (o->type == OBJ_TAG) {
- o = deref_tag(uic->repo, o, path, 0);
+ o = deref_tag(uic->repo, o, ref->name, 0);
if (o)
if (uic_printf(uic, "%s %s^{}\n",
- oid_to_hex(&o->oid), path) < 0)
+ oid_to_hex(&o->oid), ref->name) < 0)
return -1;
}
return 0;
diff --git a/shallow.c b/shallow.c
index d9cd4e219cb..55b9cd9d3f2 100644
--- a/shallow.c
+++ b/shallow.c
@@ -626,14 +626,10 @@ static void paint_down(struct paint_info *info, const struct object_id *oid,
free(tmp);
}
-static int mark_uninteresting(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data UNUSED)
+static int mark_uninteresting(const struct reference *ref, void *cb_data UNUSED)
{
struct commit *commit = lookup_commit_reference_gently(the_repository,
- oid, 1);
+ ref->oid, 1);
if (!commit)
return 0;
commit->object.flags |= UNINTERESTING;
@@ -742,16 +738,12 @@ struct commit_array {
size_t nr, alloc;
};
-static int add_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int add_ref(const struct reference *ref, void *cb_data)
{
struct commit_array *ca = cb_data;
ALLOC_GROW(ca->commits, ca->nr + 1, ca->alloc);
ca->commits[ca->nr] = lookup_commit_reference_gently(the_repository,
- oid, 1);
+ ref->oid, 1);
if (ca->commits[ca->nr])
ca->nr++;
return 0;
diff --git a/submodule.c b/submodule.c
index 35c55155f7b..40a5c6fb9d1 100644
--- a/submodule.c
+++ b/submodule.c
@@ -934,10 +934,7 @@ static void free_submodules_data(struct string_list *submodules)
string_list_clear(submodules, 1);
}
-static int has_remote(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED, void *cb_data UNUSED)
+static int has_remote(const struct reference *ref UNUSED, void *cb_data UNUSED)
{
return 1;
}
@@ -1255,13 +1252,10 @@ int push_unpushed_submodules(struct repository *r,
return ret;
}
-static int append_oid_to_array(const char *ref UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *data)
+static int append_oid_to_array(const struct reference *ref, void *data)
{
struct oid_array *array = data;
- oid_array_append(array, oid);
+ oid_array_append(array, ref->oid);
return 0;
}
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 83b06d39a36..b1215947c5e 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -154,10 +154,9 @@ static int cmd_rename_ref(struct ref_store *refs, const char **argv)
return refs_rename_ref(refs, oldref, newref, logmsg);
}
-static int each_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags, void *cb_data UNUSED)
+static int each_ref(const struct reference *ref, void *cb_data UNUSED)
{
- printf("%s %s 0x%x\n", oid_to_hex(oid), refname, flags);
+ printf("%s %s 0x%x\n", oid_to_hex(ref->oid), ref->name, ref->flags);
return 0;
}
diff --git a/upload-pack.c b/upload-pack.c
index 1e87ae95593..0d563ae74e9 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -870,8 +870,8 @@ static void send_unshallow(struct upload_pack_data *data)
}
}
-static int check_ref(const char *refname_full, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data);
+static int check_ref(const struct reference *ref, void *cb_data);
+
static void deepen(struct upload_pack_data *data, int depth)
{
if (depth == INFINITE_DEPTH && !is_repository_shallow(the_repository)) {
@@ -1224,13 +1224,12 @@ static int mark_our_ref(const char *refname, const char *refname_full,
return 0;
}
-static int check_ref(const char *refname_full, const char *referent UNUSED,const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int check_ref(const struct reference *ref, void *cb_data)
{
- const char *refname = strip_namespace(refname_full);
+ const char *refname = strip_namespace(ref->name);
struct upload_pack_data *data = cb_data;
- mark_our_ref(refname, refname_full, oid, &data->hidden_refs);
+ mark_our_ref(refname, ref->name, ref->oid, &data->hidden_refs);
return 0;
}
@@ -1292,27 +1291,25 @@ static void write_v0_ref(struct upload_pack_data *data,
return;
}
-static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int send_ref(const struct reference *ref, void *cb_data)
{
- write_v0_ref(cb_data, refname, strip_namespace(refname), oid);
+ write_v0_ref(cb_data, ref->name, strip_namespace(ref->name), ref->oid);
return 0;
}
-static int find_symref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag, void *cb_data)
+static int find_symref(const struct reference *ref, void *cb_data)
{
const char *symref_target;
struct string_list_item *item;
+ int flag;
- if ((flag & REF_ISSYMREF) == 0)
+ if ((ref->flags & REF_ISSYMREF) == 0)
return 0;
symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- refname, 0, NULL, &flag);
+ ref->name, 0, NULL, &flag);
if (!symref_target || (flag & REF_ISSYMREF) == 0)
- die("'%s' is a symref but it is not?", refname);
- item = string_list_append(cb_data, strip_namespace(refname));
+ die("'%s' is a symref but it is not?", ref->name);
+ item = string_list_append(cb_data, strip_namespace(ref->name));
item->util = xstrdup(strip_namespace(symref_target));
return 0;
}
diff --git a/walker.c b/walker.c
index 80737545172..409b646578a 100644
--- a/walker.c
+++ b/walker.c
@@ -226,14 +226,10 @@ static int interpret_target(struct walker *walker, char *target, struct object_i
return -1;
}
-static int mark_complete(const char *path UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED,
- void *cb_data UNUSED)
+static int mark_complete(const struct reference *ref, void *cb_data UNUSED)
{
struct commit *commit = lookup_commit_reference_gently(the_repository,
- oid, 1);
+ ref->oid, 1);
if (commit) {
commit->object.flags |= COMPLETE;
diff --git a/worktree.c b/worktree.c
index a2a5f51f29f..9308389cb6f 100644
--- a/worktree.c
+++ b/worktree.c
@@ -595,8 +595,15 @@ int other_head_refs(each_ref_fn fn, void *cb_data)
if (refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
refname.buf,
RESOLVE_REF_READING,
- &oid, &flag))
- ret = fn(refname.buf, NULL, &oid, flag, cb_data);
+ &oid, &flag)) {
+ struct reference ref = {
+ .name = refname.buf,
+ .oid = &oid,
+ .flags = flag,
+ };
+
+ ret = fn(&ref, cb_data);
+ }
if (ret)
break;
}
--
2.51.1.851.g4ebd6896fd.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v3 02/14] refs: introduce `.ref` field for the base iterator
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 01/14] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
@ 2025-10-22 6:41 ` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 03/14] refs: fully reset `struct ref_iterator::ref` on iteration Patrick Steinhardt
` (13 subsequent siblings)
15 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-22 6:41 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
The base iterator has a couple of fields that tracks the name, target,
object ID and flags for the current reference. Due to this design we
have to create a new `struct reference` whenever we want to hand over
that reference to the callback function, which is tedious and not very
efficient.
Convert the structure to instead contain a `struct reference` as member.
This member is expected to be populated by the implementations of the
iterator and is handed over to the callback directly.
While at it, simplify `should_pack_ref()` to take a `struct reference`
directly instead of passing its respective fields.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs.c | 8 ++++----
refs/debug.c | 8 +++-----
refs/files-backend.c | 47 +++++++++++++++++++++--------------------------
refs/iterator.c | 39 ++++++++++++---------------------------
refs/packed-backend.c | 46 +++++++++++++++++++++++-----------------------
refs/ref-cache.c | 10 +++++-----
refs/refs-internal.h | 5 +----
refs/reftable-backend.c | 12 ++++++------
8 files changed, 75 insertions(+), 100 deletions(-)
diff --git a/refs.c b/refs.c
index 25f0579d610..f96cf43b128 100644
--- a/refs.c
+++ b/refs.c
@@ -2327,8 +2327,8 @@ int refs_optimize(struct ref_store *refs, struct pack_refs_opts *opts)
int peel_iterated_oid(struct repository *r, const struct object_id *base, struct object_id *peeled)
{
if (current_ref_iter &&
- (current_ref_iter->oid == base ||
- oideq(current_ref_iter->oid, base)))
+ (current_ref_iter->ref.oid == base ||
+ oideq(current_ref_iter->ref.oid, base)))
return ref_iterator_peel(current_ref_iter, peeled);
return peel_object(r, base, peeled) ? -1 : 0;
@@ -2703,7 +2703,7 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
if (skip &&
- string_list_has_string(skip, iter->refname))
+ string_list_has_string(skip, iter->ref.name))
continue;
if (transaction && ref_transaction_maybe_set_rejected(
@@ -2712,7 +2712,7 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
continue;
strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
- iter->refname, refname);
+ iter->ref.name, refname);
goto cleanup;
}
diff --git a/refs/debug.c b/refs/debug.c
index 697adbd0dc3..67718bd1f49 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -160,11 +160,9 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
trace_printf_key(&trace_refs, "iterator_advance: (%d)\n", res);
else
trace_printf_key(&trace_refs, "iterator_advance: %s (0)\n",
- diter->iter->refname);
+ diter->iter->ref.name);
- diter->base.refname = diter->iter->refname;
- diter->base.oid = diter->iter->oid;
- diter->base.flags = diter->iter->flags;
+ diter->base.ref = diter->iter->ref;
return res;
}
@@ -185,7 +183,7 @@ static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct debug_ref_iterator *diter =
(struct debug_ref_iterator *)ref_iterator;
int res = diter->iter->vtable->peel(diter->iter, peeled);
- trace_printf_key(&trace_refs, "iterator_peel: %s: %d\n", diter->iter->refname, res);
+ trace_printf_key(&trace_refs, "iterator_peel: %s: %d\n", diter->iter->ref.name, res);
return res;
}
diff --git a/refs/files-backend.c b/refs/files-backend.c
index eb3142f8f2d..fac53fa052d 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -961,26 +961,23 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
- parse_worktree_ref(iter->iter0->refname, NULL, NULL,
+ parse_worktree_ref(iter->iter0->ref.name, NULL, NULL,
NULL) != REF_WORKTREE_CURRENT)
continue;
if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) &&
- (iter->iter0->flags & REF_ISSYMREF) &&
- (iter->iter0->flags & REF_ISBROKEN))
+ (iter->iter0->ref.flags & REF_ISSYMREF) &&
+ (iter->iter0->ref.flags & REF_ISBROKEN))
continue;
if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
- !ref_resolves_to_object(iter->iter0->refname,
+ !ref_resolves_to_object(iter->iter0->ref.name,
iter->repo,
- iter->iter0->oid,
- iter->iter0->flags))
+ iter->iter0->ref.oid,
+ iter->iter0->ref.flags))
continue;
- iter->base.refname = iter->iter0->refname;
- iter->base.oid = iter->iter0->oid;
- iter->base.flags = iter->iter0->flags;
- iter->base.referent = iter->iter0->referent;
+ iter->base.ref = iter->iter0->ref;
return ITER_OK;
}
@@ -1367,30 +1364,29 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune **refs_
* Return true if the specified reference should be packed.
*/
static int should_pack_ref(struct files_ref_store *refs,
- const char *refname,
- const struct object_id *oid, unsigned int ref_flags,
+ const struct reference *ref,
struct pack_refs_opts *opts)
{
struct string_list_item *item;
/* Do not pack per-worktree refs: */
- if (parse_worktree_ref(refname, NULL, NULL, NULL) !=
+ if (parse_worktree_ref(ref->name, NULL, NULL, NULL) !=
REF_WORKTREE_SHARED)
return 0;
/* Do not pack symbolic refs: */
- if (ref_flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
return 0;
/* Do not pack broken refs: */
- if (!ref_resolves_to_object(refname, refs->base.repo, oid, ref_flags))
+ if (!ref_resolves_to_object(ref->name, refs->base.repo, ref->oid, ref->flags))
return 0;
- if (ref_excluded(opts->exclusions, refname))
+ if (ref_excluded(opts->exclusions, ref->name))
return 0;
for_each_string_list_item(item, opts->includes)
- if (!wildmatch(item->string, refname, 0))
+ if (!wildmatch(item->string, ref->name, 0))
return 1;
return 0;
@@ -1443,8 +1439,7 @@ static int should_pack_refs(struct files_ref_store *refs,
iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, 0), NULL,
refs->base.repo, 0);
while ((ret = ref_iterator_advance(iter)) == ITER_OK) {
- if (should_pack_ref(refs, iter->refname, iter->oid,
- iter->flags, opts))
+ if (should_pack_ref(refs, &iter->ref, opts))
refcount++;
if (refcount >= limit) {
ref_iterator_free(iter);
@@ -1489,24 +1484,24 @@ static int files_pack_refs(struct ref_store *ref_store,
* in the packed ref cache. If the reference should be
* pruned, also add it to refs_to_prune.
*/
- if (!should_pack_ref(refs, iter->refname, iter->oid, iter->flags, opts))
+ if (!should_pack_ref(refs, &iter->ref, opts))
continue;
/*
* Add a reference creation for this reference to the
* packed-refs transaction:
*/
- if (ref_transaction_update(transaction, iter->refname,
- iter->oid, NULL, NULL, NULL,
+ if (ref_transaction_update(transaction, iter->ref.name,
+ iter->ref.oid, NULL, NULL, NULL,
REF_NO_DEREF, NULL, &err))
die("failure preparing to create packed reference %s: %s",
- iter->refname, err.buf);
+ iter->ref.name, err.buf);
/* Schedule the loose reference for pruning if requested. */
if ((opts->flags & PACK_REFS_PRUNE)) {
struct ref_to_prune *n;
- FLEX_ALLOC_STR(n, name, iter->refname);
- oidcpy(&n->oid, iter->oid);
+ FLEX_ALLOC_STR(n, name, iter->ref.name);
+ oidcpy(&n->oid, iter->ref.oid);
n->next = refs_to_prune;
refs_to_prune = n;
}
@@ -2379,7 +2374,7 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
REFNAME_ALLOW_ONELEVEL))
continue;
- iter->base.refname = diter->relative_path;
+ iter->base.ref.name = diter->relative_path;
return ITER_OK;
}
diff --git a/refs/iterator.c b/refs/iterator.c
index 7f2e718f1c9..fe5980e1b6c 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -41,10 +41,7 @@ void base_ref_iterator_init(struct ref_iterator *iter,
struct ref_iterator_vtable *vtable)
{
iter->vtable = vtable;
- iter->refname = NULL;
- iter->referent = NULL;
- iter->oid = NULL;
- iter->flags = 0;
+ memset(&iter->ref, 0, sizeof(iter->ref));
}
struct empty_ref_iterator {
@@ -127,8 +124,8 @@ enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
* latter.
*/
if (iter_worktree) {
- int cmp = strcmp(iter_worktree->refname,
- iter_common->refname);
+ int cmp = strcmp(iter_worktree->ref.name,
+ iter_common->ref.name);
if (cmp < 0)
return ITER_SELECT_0;
else if (!cmp)
@@ -139,7 +136,7 @@ enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
* We now know that the lexicographically-next ref is a common
* ref. When the common ref is a shared one we return it.
*/
- if (parse_worktree_ref(iter_common->refname, NULL, NULL,
+ if (parse_worktree_ref(iter_common->ref.name, NULL, NULL,
NULL) == REF_WORKTREE_SHARED)
return ITER_SELECT_1;
@@ -212,10 +209,7 @@ static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
}
if (selection & ITER_YIELD_CURRENT) {
- iter->base.referent = (*iter->current)->referent;
- iter->base.refname = (*iter->current)->refname;
- iter->base.oid = (*iter->current)->oid;
- iter->base.flags = (*iter->current)->flags;
+ iter->base.ref = (*iter->current)->ref;
return ITER_OK;
}
}
@@ -313,7 +307,7 @@ static enum iterator_selection overlay_iterator_select(
else if (!front)
return ITER_SELECT_1;
- cmp = strcmp(front->refname, back->refname);
+ cmp = strcmp(front->ref.name, back->ref.name);
if (cmp < 0)
return ITER_SELECT_0;
@@ -371,7 +365,7 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
int ok;
while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
- int cmp = compare_prefix(iter->iter0->refname, iter->prefix);
+ int cmp = compare_prefix(iter->iter0->ref.name, iter->prefix);
if (cmp < 0)
continue;
/*
@@ -382,6 +376,8 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
if (cmp > 0)
return ITER_DONE;
+ iter->base.ref = iter->iter0->ref;
+
if (iter->trim) {
/*
* It is nonsense to trim off characters that
@@ -392,15 +388,11 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
* one character left in the refname after
* trimming, report it as a bug:
*/
- if (strlen(iter->iter0->refname) <= iter->trim)
+ if (strlen(iter->base.ref.name) <= iter->trim)
BUG("attempt to trim too many characters");
- iter->base.refname = iter->iter0->refname + iter->trim;
- } else {
- iter->base.refname = iter->iter0->refname;
+ iter->base.ref.name += iter->trim;
}
- iter->base.oid = iter->iter0->oid;
- iter->base.flags = iter->iter0->flags;
return ITER_OK;
}
@@ -476,14 +468,7 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
- struct reference ref = {
- .name = iter->refname,
- .target = iter->referent,
- .oid = iter->oid,
- .flags = iter->flags,
- };
-
- retval = fn(&ref, cb_data);
+ retval = fn(&iter->ref, cb_data);
if (retval)
goto out;
}
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index a8c22a0a7ff..7987acdc96a 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -908,7 +908,7 @@ static int next_record(struct packed_ref_iterator *iter)
if (iter->pos == iter->eof)
return ITER_DONE;
- iter->base.flags = REF_ISPACKED;
+ iter->base.ref.flags = REF_ISPACKED;
p = iter->pos;
if (iter->eof - p < snapshot_hexsz(iter->snapshot) + 2 ||
@@ -923,22 +923,22 @@ static int next_record(struct packed_ref_iterator *iter)
iter->pos, iter->eof - iter->pos);
strbuf_add(&iter->refname_buf, p, eol - p);
- iter->base.refname = iter->refname_buf.buf;
+ iter->base.ref.name = iter->refname_buf.buf;
if (refname_contains_nul(&iter->refname_buf))
- die("packed refname contains embedded NULL: %s", iter->base.refname);
+ die("packed refname contains embedded NULL: %s", iter->base.ref.name);
- if (check_refname_format(iter->base.refname, REFNAME_ALLOW_ONELEVEL)) {
- if (!refname_is_safe(iter->base.refname))
+ if (check_refname_format(iter->base.ref.name, REFNAME_ALLOW_ONELEVEL)) {
+ if (!refname_is_safe(iter->base.ref.name))
die("packed refname is dangerous: %s",
- iter->base.refname);
+ iter->base.ref.name);
oidclr(&iter->oid, iter->repo->hash_algo);
- iter->base.flags |= REF_BAD_NAME | REF_ISBROKEN;
+ iter->base.ref.flags |= REF_BAD_NAME | REF_ISBROKEN;
}
if (iter->snapshot->peeled == PEELED_FULLY ||
(iter->snapshot->peeled == PEELED_TAGS &&
- starts_with(iter->base.refname, "refs/tags/")))
- iter->base.flags |= REF_KNOWS_PEELED;
+ starts_with(iter->base.ref.name, "refs/tags/")))
+ iter->base.ref.flags |= REF_KNOWS_PEELED;
iter->pos = eol + 1;
@@ -956,11 +956,11 @@ static int next_record(struct packed_ref_iterator *iter)
* definitely know the value of *this* reference. But
* we suppress it if the reference is broken:
*/
- if ((iter->base.flags & REF_ISBROKEN)) {
+ if ((iter->base.ref.flags & REF_ISBROKEN)) {
oidclr(&iter->peeled, iter->repo->hash_algo);
- iter->base.flags &= ~REF_KNOWS_PEELED;
+ iter->base.ref.flags &= ~REF_KNOWS_PEELED;
} else {
- iter->base.flags |= REF_KNOWS_PEELED;
+ iter->base.ref.flags |= REF_KNOWS_PEELED;
}
} else {
oidclr(&iter->peeled, iter->repo->hash_algo);
@@ -976,15 +976,15 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
int ok;
while ((ok = next_record(iter)) == ITER_OK) {
- const char *refname = iter->base.refname;
+ const char *refname = iter->base.ref.name;
const char *prefix = iter->prefix;
if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
- !is_per_worktree_ref(iter->base.refname))
+ !is_per_worktree_ref(iter->base.ref.name))
continue;
if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
- !ref_resolves_to_object(iter->base.refname, iter->repo,
+ !ref_resolves_to_object(iter->base.ref.name, iter->repo,
&iter->oid, iter->flags))
continue;
@@ -1033,10 +1033,10 @@ static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct packed_ref_iterator *iter =
(struct packed_ref_iterator *)ref_iterator;
- if ((iter->base.flags & REF_KNOWS_PEELED)) {
+ if ((iter->base.ref.flags & REF_KNOWS_PEELED)) {
oidcpy(peeled, &iter->peeled);
return is_null_oid(&iter->peeled) ? -1 : 0;
- } else if ((iter->base.flags & (REF_ISBROKEN | REF_ISSYMREF))) {
+ } else if ((iter->base.ref.flags & (REF_ISBROKEN | REF_ISSYMREF))) {
return -1;
} else {
return peel_object(iter->repo, &iter->oid, peeled) ? -1 : 0;
@@ -1194,7 +1194,7 @@ static struct ref_iterator *packed_ref_iterator_begin(
iter->snapshot = snapshot;
acquire_snapshot(snapshot);
strbuf_init(&iter->refname_buf, 0);
- iter->base.oid = &iter->oid;
+ iter->base.ref.oid = &iter->oid;
iter->repo = ref_store->repo;
iter->flags = flags;
@@ -1436,7 +1436,7 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
if (!iter)
cmp = +1;
else
- cmp = strcmp(iter->refname, update->refname);
+ cmp = strcmp(iter->ref.name, update->refname);
}
if (!cmp) {
@@ -1459,11 +1459,11 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
}
goto error;
- } else if (!oideq(&update->old_oid, iter->oid)) {
+ } else if (!oideq(&update->old_oid, iter->ref.oid)) {
strbuf_addf(err, "cannot update ref '%s': "
"is at %s but expected %s",
update->refname,
- oid_to_hex(iter->oid),
+ oid_to_hex(iter->ref.oid),
oid_to_hex(&update->old_oid));
ret = REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE;
@@ -1527,8 +1527,8 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
struct object_id peeled;
int peel_error = ref_iterator_peel(iter, &peeled);
- if (write_packed_entry(out, iter->refname,
- iter->oid,
+ if (write_packed_entry(out, iter->ref.name,
+ iter->ref.oid,
peel_error ? NULL : &peeled))
goto write_error;
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index e5e5df16d85..f1abc396241 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -425,10 +425,10 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
level->prefix_state = entry_prefix_state;
level->index = -1;
} else {
- iter->base.refname = entry->name;
- iter->base.referent = entry->u.value.referent;
- iter->base.oid = &entry->u.value.oid;
- iter->base.flags = entry->flag;
+ iter->base.ref.name = entry->name;
+ iter->base.ref.target = entry->u.value.referent;
+ iter->base.ref.oid = &entry->u.value.oid;
+ iter->base.ref.flags = entry->flag;
return ITER_OK;
}
}
@@ -550,7 +550,7 @@ static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
{
struct cache_ref_iterator *iter =
(struct cache_ref_iterator *)ref_iterator;
- return peel_object(iter->repo, ref_iterator->oid, peeled) ? -1 : 0;
+ return peel_object(iter->repo, ref_iterator->ref.oid, peeled) ? -1 : 0;
}
static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 4ef3bd75c6a..ed749d16572 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -249,10 +249,7 @@ const char *find_descendant_ref(const char *dirname,
*/
struct ref_iterator {
struct ref_iterator_vtable *vtable;
- const char *refname;
- const char *referent;
- const struct object_id *oid;
- unsigned int flags;
+ struct reference ref;
};
/*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index d4b79286202..0e47986cb5b 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -704,10 +704,10 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
&iter->oid, flags))
continue;
- iter->base.refname = iter->ref.refname;
- iter->base.referent = referent;
- iter->base.oid = &iter->oid;
- iter->base.flags = flags;
+ iter->base.ref.name = iter->ref.refname;
+ iter->base.ref.target = referent;
+ iter->base.ref.oid = &iter->oid;
+ iter->base.ref.flags = flags;
break;
}
@@ -828,7 +828,7 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
iter = xcalloc(1, sizeof(*iter));
base_ref_iterator_init(&iter->base, &reftable_ref_iterator_vtable);
- iter->base.oid = &iter->oid;
+ iter->base.ref.oid = &iter->oid;
iter->flags = flags;
iter->refs = refs;
iter->exclude_patterns = filter_exclude_patterns(exclude_patterns);
@@ -2072,7 +2072,7 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
strbuf_reset(&iter->last_name);
strbuf_addstr(&iter->last_name, iter->log.refname);
- iter->base.refname = iter->log.refname;
+ iter->base.ref.name = iter->log.refname;
break;
}
--
2.51.1.851.g4ebd6896fd.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v3 03/14] refs: fully reset `struct ref_iterator::ref` on iteration
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 01/14] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 02/14] refs: introduce `.ref` field for the base iterator Patrick Steinhardt
@ 2025-10-22 6:41 ` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 04/14] refs: refactor reference status flags Patrick Steinhardt
` (12 subsequent siblings)
15 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-22 6:41 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
With the introduction of the `struct ref_iterator::ref` field it now is
a whole lot easier to introduce new fields that become accessible to the
caller without having to adapt every single callsite. But there's a
downside: when a new field is introduced we always have to adapt all
backends to set that field.
This isn't something we can avoid in the general case: when the new
field is expected to be populated by all backends we of course cannot
avoid doing so. But new fields may be entirely optional, in which case
we'd still have such churn. And furthermore, it is very easy right now
to leak state from a previous iteration into the next iteration.
Address this issue by ensuring that the reference backends all fully
reset the field on every single iteration. This ensures that no state
from previous iterations can leak into the next one. And it ensures that
any newly introduced fields will be zeroed out by default.
Note that we don't have to explicitly adapt the "files" backend, as it
uses the `cache_ref_iterator` internally. Furthermore, other "wrapping"
iterators like for example the `prefix_ref_iterator` copy around the
whole reference, so these don't need to be adapted either.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs/packed-backend.c | 3 ++-
refs/ref-cache.c | 1 +
refs/reftable-backend.c | 1 +
3 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 7987acdc96a..711e07f8326 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -882,6 +882,7 @@ static int next_record(struct packed_ref_iterator *iter)
{
const char *p, *eol;
+ memset(&iter->base.ref, 0, sizeof(iter->base.ref));
strbuf_reset(&iter->refname_buf);
/*
@@ -916,6 +917,7 @@ static int next_record(struct packed_ref_iterator *iter)
!isspace(*p++))
die_invalid_line(iter->snapshot->refs->path,
iter->pos, iter->eof - iter->pos);
+ iter->base.ref.oid = &iter->oid;
eol = memchr(p, '\n', iter->eof - p);
if (!eol)
@@ -1194,7 +1196,6 @@ static struct ref_iterator *packed_ref_iterator_begin(
iter->snapshot = snapshot;
acquire_snapshot(snapshot);
strbuf_init(&iter->refname_buf, 0);
- iter->base.ref.oid = &iter->oid;
iter->repo = ref_store->repo;
iter->flags = flags;
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index f1abc396241..e427848879d 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -425,6 +425,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
level->prefix_state = entry_prefix_state;
level->index = -1;
} else {
+ memset(&iter->base.ref, 0, sizeof(iter->base.ref));
iter->base.ref.name = entry->name;
iter->base.ref.target = entry->u.value.referent;
iter->base.ref.oid = &entry->u.value.oid;
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 0e47986cb5b..728886eafd3 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -704,6 +704,7 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
&iter->oid, flags))
continue;
+ memset(&iter->base.ref, 0, sizeof(iter->base.ref));
iter->base.ref.name = iter->ref.refname;
iter->base.ref.target = referent;
iter->base.ref.oid = &iter->oid;
--
2.51.1.851.g4ebd6896fd.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v3 04/14] refs: refactor reference status flags
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
` (2 preceding siblings ...)
2025-10-22 6:41 ` [PATCH v3 03/14] refs: fully reset `struct ref_iterator::ref` on iteration Patrick Steinhardt
@ 2025-10-22 6:41 ` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 05/14] refs: expose peeled object ID via the iterator Patrick Steinhardt
` (11 subsequent siblings)
15 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-22 6:41 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
The reference flags encode information like whether or not a reference
is a symbolic reference or whether it may be broken. This information is
stored in a `int flags` bitfield, which is in conflict with our modern
best practices; we tend to use an unsigned integer to store flags.
Change the type of the field to be `unsigned`. While at it, refactor the
individual flags to be part of an `enum` instead of using preprocessor
defines.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs.h | 41 +++++++++++++++++++++--------------------
1 file changed, 21 insertions(+), 20 deletions(-)
diff --git a/refs.h b/refs.h
index 68d235438c2..4f0a685714f 100644
--- a/refs.h
+++ b/refs.h
@@ -333,27 +333,28 @@ struct ref_transaction;
* stored in ref_iterator::flags. Other bits are for internal use
* only:
*/
+enum reference_status {
+ /* Reference is a symbolic reference. */
+ REF_ISSYMREF = (1 << 0),
-/* Reference is a symbolic reference. */
-#define REF_ISSYMREF 0x01
+ /* Reference is a packed reference. */
+ REF_ISPACKED = (1 << 1),
-/* Reference is a packed reference. */
-#define REF_ISPACKED 0x02
-
-/*
- * Reference cannot be resolved to an object name: dangling symbolic
- * reference (directly or indirectly), corrupt reference file,
- * reference exists but name is bad, or symbolic reference refers to
- * ill-formatted reference name.
- */
-#define REF_ISBROKEN 0x04
+ /*
+ * Reference cannot be resolved to an object name: dangling symbolic
+ * reference (directly or indirectly), corrupt reference file,
+ * reference exists but name is bad, or symbolic reference refers to
+ * ill-formatted reference name.
+ */
+ REF_ISBROKEN = (1 << 2),
-/*
- * Reference name is not well formed.
- *
- * See git-check-ref-format(1) for the definition of well formed ref names.
- */
-#define REF_BAD_NAME 0x08
+ /*
+ * Reference name is not well formed.
+ *
+ * See git-check-ref-format(1) for the definition of well formed ref names.
+ */
+ REF_BAD_NAME = (1 << 3),
+};
/* A reference passed to `for_each_ref()`-style callbacks. */
struct reference {
@@ -370,8 +371,8 @@ struct reference {
*/
const struct object_id *oid;
- /* A bitfield of `REF_` flags. */
- int flags;
+ /* A bitfield of `enum reference_status` flags. */
+ unsigned flags;
};
/*
--
2.51.1.851.g4ebd6896fd.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v3 05/14] refs: expose peeled object ID via the iterator
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
` (3 preceding siblings ...)
2025-10-22 6:41 ` [PATCH v3 04/14] refs: refactor reference status flags Patrick Steinhardt
@ 2025-10-22 6:41 ` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 06/14] upload-pack: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
` (10 subsequent siblings)
15 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-22 6:41 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
Both the "files" and "reftable" backend are able to store peeled values
for tags in the respective formats. This allows for a more efficient
lookup of the target object of such a tag without having to manually
peel via the object database.
The infrastructure to access these peeled object IDs is somewhat funky
though. When iterating through objects, we store a pointer reference to
the current iterator in a global variable. The callbacks invoked by that
iterator are then expected to call `peel_iterated_oid()`, which checks
whether the globally-stored iterator's current reference refers to the
one handed into that function. If so, we ask the iterator to peel the
object, otherwise we manually peel the object via the object database.
Depending on global state like this is somewhat weird and also quite
fragile.
Introduce a new `struct reference::peeled_oid` field that can be
populated by the reference backends. This field can be accessed via a
new function `reference_get_peeled_oid()` that either uses that value,
if set, or alternatively peels via the ODB. With this change we don't
have to rely on global state anymore, but make the peeled object ID
available to the callback functions directly.
Adjust trivial callers that already have a `struct reference` available.
Remaining callers will be adjusted in subsequent commits.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/describe.c | 2 +-
builtin/gc.c | 2 +-
builtin/pack-objects.c | 7 ++++---
commit-graph.c | 2 +-
ls-refs.c | 2 +-
midx-write.c | 2 +-
pseudo-merge.c | 2 +-
refs.c | 12 ++++++++++++
refs.h | 19 +++++++++++++++++++
refs/packed-backend.c | 1 +
refs/reftable-backend.c | 5 +++++
repack-midx.c | 2 +-
12 files changed, 48 insertions(+), 10 deletions(-)
diff --git a/builtin/describe.c b/builtin/describe.c
index 79545350443..443546aaac9 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -208,7 +208,7 @@ static int get_name(const struct reference *ref, void *cb_data UNUSED)
}
/* Is it annotated? */
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled)) {
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
is_annotated = !oideq(ref->oid, &peeled);
} else {
oidcpy(&peeled, ref->oid);
diff --git a/builtin/gc.c b/builtin/gc.c
index 9de5de175f6..f0cf20d4238 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -1109,7 +1109,7 @@ static int dfs_on_ref(const struct reference *ref, void *cb_data)
struct commit_list *stack = NULL;
struct commit *commit;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
if (odb_read_object_info(the_repository->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 39633a0158e..1613fecb669 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -838,7 +838,7 @@ static int mark_tagged(const struct reference *ref, void *cb_data UNUSED)
if (entry)
entry->tagged = 1;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled)) {
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
entry = packlist_find(&to_pack, &peeled);
if (entry)
entry->tagged = 1;
@@ -3309,7 +3309,8 @@ static int add_ref_tag(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled) && obj_is_packed(&peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled) &&
+ obj_is_packed(&peeled))
add_tag_chain(ref->oid);
return 0;
}
@@ -4537,7 +4538,7 @@ static int mark_bitmap_preferred_tip(const struct reference *ref, void *data UNU
struct object_id peeled;
struct object *object;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
object = parse_object_or_die(the_repository, maybe_peeled, ref->name);
diff --git a/commit-graph.c b/commit-graph.c
index f91af416259..80be2ff2c39 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1857,7 +1857,7 @@ static int add_ref_to_set(const struct reference *ref, void *cb_data)
struct object_id peeled;
struct refs_cb_data *data = (struct refs_cb_data *)cb_data;
- if (!peel_iterated_oid(data->repo, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(data->repo, ref, &peeled))
maybe_peeled = &peeled;
if (odb_read_object_info(data->repo->objects, maybe_peeled, NULL) == OBJ_COMMIT)
oidset_insert(data->commits, maybe_peeled);
diff --git a/ls-refs.c b/ls-refs.c
index 64d02723691..8641281b86c 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -110,7 +110,7 @@ static int send_ref(const struct reference *ref, void *cb_data)
if (data->peel && ref->oid) {
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
strbuf_addf(&data->buf, " peeled:%s", oid_to_hex(&peeled));
}
diff --git a/midx-write.c b/midx-write.c
index f4dd875747a..23e61cb0001 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -709,7 +709,7 @@ static int add_ref_to_pending(const struct reference *ref, void *cb_data)
return 0;
}
- if (!peel_iterated_oid(revs->repo, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(revs->repo, ref, &peeled))
maybe_peeled = &peeled;
object = parse_object_or_die(revs->repo, maybe_peeled, ref->name);
diff --git a/pseudo-merge.c b/pseudo-merge.c
index 0abd51b42c1..a2d5bd85f95 100644
--- a/pseudo-merge.c
+++ b/pseudo-merge.c
@@ -230,7 +230,7 @@ static int find_pseudo_merge_group_for_ref(const struct reference *ref, void *_d
uint32_t i;
int has_bitmap;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
c = lookup_commit(the_repository, maybe_peeled);
diff --git a/refs.c b/refs.c
index f96cf43b128..1b1551f9814 100644
--- a/refs.c
+++ b/refs.c
@@ -2334,6 +2334,18 @@ int peel_iterated_oid(struct repository *r, const struct object_id *base, struct
return peel_object(r, base, peeled) ? -1 : 0;
}
+int reference_get_peeled_oid(struct repository *repo,
+ const struct reference *ref,
+ struct object_id *peeled_oid)
+{
+ if (ref->peeled_oid) {
+ oidcpy(peeled_oid, ref->peeled_oid);
+ return 0;
+ }
+
+ return peel_object(repo, ref->oid, peeled_oid) ? -1 : 0;
+}
+
int refs_update_symref(struct ref_store *refs, const char *ref,
const char *target, const char *logmsg)
{
diff --git a/refs.h b/refs.h
index 4f0a685714f..886ed2c0f43 100644
--- a/refs.h
+++ b/refs.h
@@ -371,10 +371,29 @@ struct reference {
*/
const struct object_id *oid;
+ /*
+ * An optional peeled object ID. This field _may_ be set for tags in
+ * case the peeled value is present in the backend. Please refer to
+ * `reference_get_peeled_oid()`.
+ */
+ const struct object_id *peeled_oid;
+
/* A bitfield of `enum reference_status` flags. */
unsigned flags;
};
+/*
+ * Peel the tag to a non-tag commit. If present, this uses the peeled object ID
+ * exposed by the reference backend. Otherwise, the object is peeled via the
+ * object database, which is less efficient.
+ *
+ * Return `0` if the reference could be peeled, a negative error code
+ * otherwise.
+ */
+int reference_get_peeled_oid(struct repository *repo,
+ const struct reference *ref,
+ struct object_id *peeled_oid);
+
/*
* The signature for the callback function for the for_each_*()
* functions below. The memory pointed to by the `struct reference`
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 711e07f8326..1fefefd54ed 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -963,6 +963,7 @@ static int next_record(struct packed_ref_iterator *iter)
iter->base.ref.flags &= ~REF_KNOWS_PEELED;
} else {
iter->base.ref.flags |= REF_KNOWS_PEELED;
+ iter->base.ref.peeled_oid = &iter->peeled;
}
} else {
oidclr(&iter->peeled, iter->repo->hash_algo);
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 728886eafd3..e214e120d77 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -547,6 +547,7 @@ struct reftable_ref_iterator {
struct reftable_iterator iter;
struct reftable_ref_record ref;
struct object_id oid;
+ struct object_id peeled_oid;
char *prefix;
size_t prefix_len;
@@ -671,6 +672,8 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
case REFTABLE_REF_VAL2:
oidread(&iter->oid, iter->ref.value.val2.value,
refs->base.repo->hash_algo);
+ oidread(&iter->peeled_oid, iter->ref.value.val2.target_value,
+ refs->base.repo->hash_algo);
break;
case REFTABLE_REF_SYMREF:
referent = refs_resolve_ref_unsafe(&iter->refs->base,
@@ -708,6 +711,8 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
iter->base.ref.name = iter->ref.refname;
iter->base.ref.target = referent;
iter->base.ref.oid = &iter->oid;
+ if (iter->ref.value_type == REFTABLE_REF_VAL2)
+ iter->base.ref.peeled_oid = &iter->peeled_oid;
iter->base.ref.flags = flags;
break;
diff --git a/repack-midx.c b/repack-midx.c
index 349f7e20b53..74bdfa3a6e9 100644
--- a/repack-midx.c
+++ b/repack-midx.c
@@ -22,7 +22,7 @@ static int midx_snapshot_ref_one(const struct reference *ref, void *_data)
const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
- if (!peel_iterated_oid(data->repo, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(data->repo, ref, &peeled))
maybe_peeled = &peeled;
if (oidset_insert(&data->seen, maybe_peeled))
--
2.51.1.851.g4ebd6896fd.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v3 06/14] upload-pack: convert to use `reference_get_peeled_oid()`
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
` (4 preceding siblings ...)
2025-10-22 6:41 ` [PATCH v3 05/14] refs: expose peeled object ID via the iterator Patrick Steinhardt
@ 2025-10-22 6:41 ` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 07/14] ref-filter: propagate peeled object ID Patrick Steinhardt
` (9 subsequent siblings)
15 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-22 6:41 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
The `write_v0_ref()` callback is invoked from two callsites:
- Once via `send_ref()` which is a callback passed to
`for_each_namespaced_ref_1()` and `refs_head_ref_namespaced()`.
- Once manually to announce capabilities.
When sending references to the client we also send the peeled value of
tags. As we don't have a `struct reference` available in the second
case, we cannot easily peel by calling `reference_get_peeled_oid()`, but
we instead have to depend on on global state via `peel_iterated_oid()`.
We do have a reference available though in the first case, it's only the
second case that keeps us from using `reference_get_peeled_oid()`. But
that second case only announces capabilities anyway, so we're not really
handling a reference at all here.
Adapt that case to construct a reference manually and pass that to
`write_v0_ref()`. Start to use `reference_get_peeled_oid()` now that we
always have a `struct reference` available.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
upload-pack.c | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/upload-pack.c b/upload-pack.c
index 0d563ae74e9..2d2b70cbf2d 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -1249,15 +1249,15 @@ static void format_session_id(struct strbuf *buf, struct upload_pack_data *d) {
}
static void write_v0_ref(struct upload_pack_data *data,
- const char *refname, const char *refname_nons,
- const struct object_id *oid)
+ const struct reference *ref,
+ const char *refname_nons)
{
static const char *capabilities = "multi_ack thin-pack side-band"
" side-band-64k ofs-delta shallow deepen-since deepen-not"
" deepen-relative no-progress include-tag multi_ack_detailed";
struct object_id peeled;
- if (mark_our_ref(refname_nons, refname, oid, &data->hidden_refs))
+ if (mark_our_ref(refname_nons, ref->name, ref->oid, &data->hidden_refs))
return;
if (capabilities) {
@@ -1267,7 +1267,7 @@ static void write_v0_ref(struct upload_pack_data *data,
format_symref_info(&symref_info, &data->symref);
format_session_id(&session_id, data);
packet_fwrite_fmt(stdout, "%s %s%c%s%s%s%s%s%s%s object-format=%s agent=%s\n",
- oid_to_hex(oid), refname_nons,
+ oid_to_hex(ref->oid), refname_nons,
0, capabilities,
(data->allow_uor & ALLOW_TIP_SHA1) ?
" allow-tip-sha1-in-want" : "",
@@ -1283,17 +1283,17 @@ static void write_v0_ref(struct upload_pack_data *data,
strbuf_release(&session_id);
data->sent_capabilities = 1;
} else {
- packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(oid), refname_nons);
+ packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(ref->oid), refname_nons);
}
capabilities = NULL;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
packet_fwrite_fmt(stdout, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
return;
}
static int send_ref(const struct reference *ref, void *cb_data)
{
- write_v0_ref(cb_data, ref->name, strip_namespace(ref->name), ref->oid);
+ write_v0_ref(cb_data, ref, strip_namespace(ref->name));
return 0;
}
@@ -1442,8 +1442,12 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
send_ref, &data);
for_each_namespaced_ref_1(send_ref, &data);
if (!data.sent_capabilities) {
- const char *refname = "capabilities^{}";
- write_v0_ref(&data, refname, refname, null_oid(the_hash_algo));
+ struct reference ref = {
+ .name = "capabilities^{}",
+ .oid = null_oid(the_hash_algo),
+ };
+
+ write_v0_ref(&data, &ref, ref.name);
}
/*
* fflush stdout before calling advertise_shallow_grafts because send_ref
--
2.51.1.851.g4ebd6896fd.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v3 07/14] ref-filter: propagate peeled object ID
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
` (5 preceding siblings ...)
2025-10-22 6:41 ` [PATCH v3 06/14] upload-pack: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
@ 2025-10-22 6:41 ` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 08/14] builtin/show-ref: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
` (8 subsequent siblings)
15 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-22 6:41 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
When queueing a reference in the "ref-filter" subsystem we end up
creating a new ref array item that contains the reference's info. One
bit of info that we always discard though is the peeled object ID, and
because of that we are forced to use `peel_iterated_oid()`.
Refactor the code to propagate the peeled object ID via the ref array,
if available. This allows us to manually peel tags without having to go
through the object database.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/ls-remote.c | 2 +-
builtin/tag.c | 2 +-
builtin/verify-tag.c | 2 +-
ref-filter.c | 66 ++++++++++++++++++++++++++++++----------------------
ref-filter.h | 5 +++-
5 files changed, 45 insertions(+), 32 deletions(-)
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index df09000b30d..fe77829557f 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -156,7 +156,7 @@ int cmd_ls_remote(int argc,
continue;
if (!tail_match(&pattern, ref->name))
continue;
- item = ref_array_push(&ref_array, ref->name, &ref->old_oid);
+ item = ref_array_push(&ref_array, ref->name, &ref->old_oid, NULL);
item->symref = xstrdup_or_null(ref->symref);
}
diff --git a/builtin/tag.c b/builtin/tag.c
index f0665af3acd..01eba90c5c7 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -153,7 +153,7 @@ static int verify_tag(const char *name, const char *ref UNUSED,
return -1;
if (format->format)
- pretty_print_ref(name, oid, format);
+ pretty_print_ref(name, oid, NULL, format);
return 0;
}
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index cd6bc11095d..558121eaa16 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -67,7 +67,7 @@ int cmd_verify_tag(int argc,
}
if (format.format)
- pretty_print_ref(name, &oid, &format);
+ pretty_print_ref(name, &oid, NULL, &format);
}
return had_error;
}
diff --git a/ref-filter.c b/ref-filter.c
index 6837fa60a9b..7fd8babec8f 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2578,8 +2578,15 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
* If it is a tag object, see if we use the peeled value. If we do,
* grab the peeled OID.
*/
- if (need_tagged && peel_iterated_oid(the_repository, &obj->oid, &oi_deref.oid))
- die("bad tag");
+ if (need_tagged) {
+ if (!is_null_oid(&ref->peeled_oid)) {
+ oidcpy(&oi_deref.oid, &ref->peeled_oid);
+ } else if (!peel_object(the_repository, &obj->oid, &oi_deref.oid)) {
+ /* We managed to peel the object ourselves. */
+ } else {
+ die("bad tag");
+ }
+ }
return get_object(ref, 1, &obj, &oi_deref, err);
}
@@ -2807,12 +2814,15 @@ static int match_points_at(struct oid_array *points_at,
* Callers can then fill in other struct members at their leisure.
*/
static struct ref_array_item *new_ref_array_item(const char *refname,
- const struct object_id *oid)
+ const struct object_id *oid,
+ const struct object_id *peeled_oid)
{
struct ref_array_item *ref;
FLEX_ALLOC_STR(ref, refname, refname);
oidcpy(&ref->objectname, oid);
+ if (peeled_oid)
+ oidcpy(&ref->peeled_oid, peeled_oid);
ref->rest = NULL;
return ref;
@@ -2826,9 +2836,10 @@ static void ref_array_append(struct ref_array *array, struct ref_array_item *ref
struct ref_array_item *ref_array_push(struct ref_array *array,
const char *refname,
- const struct object_id *oid)
+ const struct object_id *oid,
+ const struct object_id *peeled_oid)
{
- struct ref_array_item *ref = new_ref_array_item(refname, oid);
+ struct ref_array_item *ref = new_ref_array_item(refname, oid, peeled_oid);
ref_array_append(array, ref);
return ref;
}
@@ -2871,25 +2882,25 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)
return ref_kind_from_refname(refname);
}
-static struct ref_array_item *apply_ref_filter(const char *refname, const char *referent, const struct object_id *oid,
- int flag, struct ref_filter *filter)
+static struct ref_array_item *apply_ref_filter(const struct reference *ref,
+ struct ref_filter *filter)
{
- struct ref_array_item *ref;
+ struct ref_array_item *item;
struct commit *commit = NULL;
unsigned int kind;
- if (flag & REF_BAD_NAME) {
- warning(_("ignoring ref with broken name %s"), refname);
+ if (ref->flags & REF_BAD_NAME) {
+ warning(_("ignoring ref with broken name %s"), ref->name);
return NULL;
}
- if (flag & REF_ISBROKEN) {
- warning(_("ignoring broken ref %s"), refname);
+ if (ref->flags & REF_ISBROKEN) {
+ warning(_("ignoring broken ref %s"), ref->name);
return NULL;
}
/* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */
- kind = filter_ref_kind(filter, refname);
+ kind = filter_ref_kind(filter, ref->name);
/*
* Generally HEAD refs are printed with special description denoting a rebase,
@@ -2902,13 +2913,13 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const char *
else if (!(kind & filter->kind))
return NULL;
- if (!filter_pattern_match(filter, refname))
+ if (!filter_pattern_match(filter, ref->name))
return NULL;
- if (filter_exclude_match(filter, refname))
+ if (filter_exclude_match(filter, ref->name))
return NULL;
- if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname))
+ if (filter->points_at.nr && !match_points_at(&filter->points_at, ref->oid, ref->name))
return NULL;
/*
@@ -2918,7 +2929,7 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const char *
*/
if (filter->reachable_from || filter->unreachable_from ||
filter->with_commit || filter->no_commit || filter->verbose) {
- commit = lookup_commit_reference_gently(the_repository, oid, 1);
+ commit = lookup_commit_reference_gently(the_repository, ref->oid, 1);
if (!commit)
return NULL;
/* We perform the filtering for the '--contains' option... */
@@ -2936,13 +2947,13 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const char *
* to do its job and the resulting list may yet to be pruned
* by maxcount logic.
*/
- ref = new_ref_array_item(refname, oid);
- ref->commit = commit;
- ref->flag = flag;
- ref->kind = kind;
- ref->symref = xstrdup_or_null(referent);
+ item = new_ref_array_item(ref->name, ref->oid, ref->peeled_oid);
+ item->commit = commit;
+ item->flag = ref->flags;
+ item->kind = kind;
+ item->symref = xstrdup_or_null(ref->target);
- return ref;
+ return item;
}
struct ref_filter_cbdata {
@@ -2959,8 +2970,7 @@ static int filter_one(const struct reference *ref, void *cb_data)
struct ref_filter_cbdata *ref_cbdata = cb_data;
struct ref_array_item *item;
- item = apply_ref_filter(ref->name, ref->target, ref->oid,
- ref->flags, ref_cbdata->filter);
+ item = apply_ref_filter(ref, ref_cbdata->filter);
if (item)
ref_array_append(ref_cbdata->array, item);
@@ -2997,8 +3007,7 @@ static int filter_and_format_one(const struct reference *ref, void *cb_data)
struct ref_array_item *item;
struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
- item = apply_ref_filter(ref->name, ref->target, ref->oid,
- ref->flags, ref_cbdata->filter);
+ item = apply_ref_filter(ref, ref_cbdata->filter);
if (!item)
return 0;
@@ -3585,13 +3594,14 @@ void print_formatted_ref_array(struct ref_array *array, struct ref_format *forma
}
void pretty_print_ref(const char *name, const struct object_id *oid,
+ const struct object_id *peeled_oid,
struct ref_format *format)
{
struct ref_array_item *ref_item;
struct strbuf output = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
- ref_item = new_ref_array_item(name, oid);
+ ref_item = new_ref_array_item(name, oid, peeled_oid);
ref_item->kind = ref_kind_from_refname(name);
if (format_ref_array_item(ref_item, format, &output, &err))
die("%s", err.buf);
diff --git a/ref-filter.h b/ref-filter.h
index 235c60f79c9..120221b47fa 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -41,6 +41,7 @@ enum ref_sorting_order {
struct ref_array_item {
struct object_id objectname;
+ struct object_id peeled_oid;
const char *rest;
int flag;
unsigned int kind;
@@ -187,6 +188,7 @@ void print_formatted_ref_array(struct ref_array *array, struct ref_format *forma
* name must be a fully qualified refname.
*/
void pretty_print_ref(const char *name, const struct object_id *oid,
+ const struct object_id *peeled_oid,
struct ref_format *format);
/*
@@ -195,7 +197,8 @@ void pretty_print_ref(const char *name, const struct object_id *oid,
*/
struct ref_array_item *ref_array_push(struct ref_array *array,
const char *refname,
- const struct object_id *oid);
+ const struct object_id *oid,
+ const struct object_id *peeled_oid);
/*
* If the provided format includes ahead-behind atoms, then compute the
--
2.51.1.851.g4ebd6896fd.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v3 08/14] builtin/show-ref: convert to use `reference_get_peeled_oid()`
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
` (6 preceding siblings ...)
2025-10-22 6:41 ` [PATCH v3 07/14] ref-filter: propagate peeled object ID Patrick Steinhardt
@ 2025-10-22 6:41 ` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 09/14] refs: drop `current_ref_iter` hack Patrick Steinhardt
` (7 subsequent siblings)
15 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-22 6:41 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
The git-show-ref(1) command has multiple different modes:
- It knows to show all references matching a pattern.
- It knows to list all references that are an exact match to whatever
the user has provided.
- It knows to check for reference existence.
The first two commands use mostly the same infrastructure to print the
references via `show_one()`. But while the former mode uses a proper
iterator and thus has a `struct reference` available in its context, the
latter calls `refs_read_ref()` and thus doesn't. Consequently, we cannot
easily use `reference_get_peeled_oid()` to print the peeled value.
Adapt the code so that we manually construct a `struct reference` when
verifying refs. We wouldn't ever have the peeled value available anyway
as we're not using an iterator here, so we can simply plug in the values
we _do_ have.
With this change we now have a `struct reference` available at both
callsites of `show_one()` and can thus pass it, which allows us to use
`reference_get_peeled_oid()` instead of `peel_iterated_oid()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/show-ref.c | 32 +++++++++++++++++++-------------
1 file changed, 19 insertions(+), 13 deletions(-)
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 4803b5e5986..4d4984e4e0c 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -31,31 +31,31 @@ struct show_one_options {
};
static void show_one(const struct show_one_options *opts,
- const char *refname, const struct object_id *oid)
+ const struct reference *ref)
{
const char *hex;
struct object_id peeled;
- if (!odb_has_object(the_repository->objects, oid,
+ if (!odb_has_object(the_repository->objects, ref->oid,
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
- die("git show-ref: bad ref %s (%s)", refname,
- oid_to_hex(oid));
+ die("git show-ref: bad ref %s (%s)", ref->name,
+ oid_to_hex(ref->oid));
if (opts->quiet)
return;
- hex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev);
+ hex = repo_find_unique_abbrev(the_repository, ref->oid, opts->abbrev);
if (opts->hash_only)
printf("%s\n", hex);
else
- printf("%s %s\n", hex, refname);
+ printf("%s %s\n", hex, ref->name);
if (!opts->deref_tags)
return;
- if (!peel_iterated_oid(the_repository, oid, &peeled)) {
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
hex = repo_find_unique_abbrev(the_repository, &peeled, opts->abbrev);
- printf("%s %s^{}\n", hex, refname);
+ printf("%s %s^{}\n", hex, ref->name);
}
}
@@ -93,7 +93,7 @@ static int show_ref(const struct reference *ref, void *cbdata)
match:
data->found_match++;
- show_one(data->show_one_opts, ref->name, ref->oid);
+ show_one(data->show_one_opts, ref);
return 0;
}
@@ -175,12 +175,18 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) &&
!refs_read_ref(get_main_ref_store(the_repository), *refs, &oid)) {
- show_one(show_one_opts, *refs, &oid);
- }
- else if (!show_one_opts->quiet)
+ struct reference ref = {
+ .name = *refs,
+ .oid = &oid,
+ };
+
+ show_one(show_one_opts, &ref);
+ } else if (!show_one_opts->quiet) {
die("'%s' - not a valid ref", *refs);
- else
+ } else {
return 1;
+ }
+
refs++;
}
--
2.51.1.851.g4ebd6896fd.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v3 09/14] refs: drop `current_ref_iter` hack
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
` (7 preceding siblings ...)
2025-10-22 6:41 ` [PATCH v3 08/14] builtin/show-ref: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
@ 2025-10-22 6:41 ` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 10/14] refs: drop infrastructure to peel via iterators Patrick Steinhardt
` (6 subsequent siblings)
15 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-22 6:41 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
In preceding commits we have refactored all callers of
`peel_iterated_oid()` to instead use `reference_get_peeled_oid()`. This
allows us to thus get rid of the former function.
Getting rid of that function is nice, but even nicer is that this also
allows us to get rid of the `current_ref_iter` hack. This global
variable tracked the currently-active ref iterator so that we can use it
to peel an object ID. Now that the peeled object ID is propagated via
`struct reference` though we don't have to depend on this hack anymore,
which makes for a more robust and easier-to-understand infrastructure.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs.c | 10 ----------
refs/iterator.c | 5 -----
refs/refs-internal.h | 13 -------------
3 files changed, 28 deletions(-)
diff --git a/refs.c b/refs.c
index 1b1551f9814..9d8f0a9ca4a 100644
--- a/refs.c
+++ b/refs.c
@@ -2324,16 +2324,6 @@ int refs_optimize(struct ref_store *refs, struct pack_refs_opts *opts)
return refs->be->optimize(refs, opts);
}
-int peel_iterated_oid(struct repository *r, const struct object_id *base, struct object_id *peeled)
-{
- if (current_ref_iter &&
- (current_ref_iter->ref.oid == base ||
- oideq(current_ref_iter->ref.oid, base)))
- return ref_iterator_peel(current_ref_iter, peeled);
-
- return peel_object(r, base, peeled) ? -1 : 0;
-}
-
int reference_get_peeled_oid(struct repository *repo,
const struct reference *ref,
struct object_id *peeled_oid)
diff --git a/refs/iterator.c b/refs/iterator.c
index fe5980e1b6c..072c6aacdb0 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -458,15 +458,11 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
return ref_iterator;
}
-struct ref_iterator *current_ref_iter = NULL;
-
int do_for_each_ref_iterator(struct ref_iterator *iter,
each_ref_fn fn, void *cb_data)
{
int retval = 0, ok;
- struct ref_iterator *old_ref_iter = current_ref_iter;
- current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
retval = fn(&iter->ref, cb_data);
if (retval)
@@ -474,7 +470,6 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
}
out:
- current_ref_iter = old_ref_iter;
if (ok == ITER_ERROR)
retval = -1;
ref_iterator_free(iter);
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index ed749d16572..f4f845bbeaf 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -376,19 +376,6 @@ struct ref_iterator_vtable {
ref_iterator_release_fn *release;
};
-/*
- * current_ref_iter is a performance hack: when iterating over
- * references using the for_each_ref*() functions, current_ref_iter is
- * set to the reference iterator before calling the callback function.
- * If the callback function calls peel_ref(), then peel_ref() first
- * checks whether the reference to be peeled is the one referred to by
- * the iterator (it usually is) and if so, asks the iterator for the
- * peeled version of the reference if it is available. This avoids a
- * refname lookup in a common case. current_ref_iter is set to NULL
- * when the iteration is over.
- */
-extern struct ref_iterator *current_ref_iter;
-
struct ref_store;
/* refs backends */
--
2.51.1.851.g4ebd6896fd.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v3 10/14] refs: drop infrastructure to peel via iterators
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
` (8 preceding siblings ...)
2025-10-22 6:41 ` [PATCH v3 09/14] refs: drop `current_ref_iter` hack Patrick Steinhardt
@ 2025-10-22 6:41 ` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 11/14] object: add flag to `peel_object()` to verify object type Patrick Steinhardt
` (5 subsequent siblings)
15 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-22 6:41 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
Now that the peeled object ID gets propagated via the `struct reference`
there is no need anymore to call into the reference iterator itself to
dereference an object. Remove this infrastructure.
Most of the changes are straight-forward deletions of code. There is one
exception though in `refs/packed-backend.c::write_with_updates()`. Here
we stop peeling the iterator and instead just pass the peeled object ID
of that iterator directly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs.h | 14 --------------
refs/debug.c | 11 -----------
refs/files-backend.c | 17 -----------------
refs/iterator.c | 36 ------------------------------------
refs/packed-backend.c | 24 +-----------------------
refs/ref-cache.c | 9 ---------
refs/refs-internal.h | 7 -------
refs/reftable-backend.c | 24 ------------------------
8 files changed, 1 insertion(+), 141 deletions(-)
diff --git a/refs.h b/refs.h
index 886ed2c0f43..2dd7ac1a16a 100644
--- a/refs.h
+++ b/refs.h
@@ -1289,10 +1289,6 @@ int repo_migrate_ref_storage_format(struct repository *repo,
* to the next entry, ref_iterator_advance() aborts the iteration,
* frees the ref_iterator, and returns ITER_ERROR.
*
- * The reference currently being looked at can be peeled by calling
- * ref_iterator_peel(). This function is often faster than peel_ref(),
- * so it should be preferred when iterating over references.
- *
* Putting it all together, a typical iteration looks like this:
*
* int ok;
@@ -1307,9 +1303,6 @@ int repo_migrate_ref_storage_format(struct repository *repo,
* // Access information about the current reference:
* if (!(iter->flags & REF_ISSYMREF))
* printf("%s is %s\n", iter->refname, oid_to_hex(iter->oid));
- *
- * // If you need to peel the reference:
- * ref_iterator_peel(iter, &oid);
* }
*
* if (ok != ITER_DONE)
@@ -1400,13 +1393,6 @@ enum ref_iterator_seek_flag {
int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname,
unsigned int flags);
-/*
- * If possible, peel the reference currently being viewed by the
- * iterator. Return 0 on success.
- */
-int ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled);
-
/* Free the reference iterator and any associated resources. */
void ref_iterator_free(struct ref_iterator *ref_iterator);
diff --git a/refs/debug.c b/refs/debug.c
index 67718bd1f49..01499b9033c 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -177,16 +177,6 @@ static int debug_ref_iterator_seek(struct ref_iterator *ref_iterator,
return res;
}
-static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct debug_ref_iterator *diter =
- (struct debug_ref_iterator *)ref_iterator;
- int res = diter->iter->vtable->peel(diter->iter, peeled);
- trace_printf_key(&trace_refs, "iterator_peel: %s: %d\n", diter->iter->ref.name, res);
- return res;
-}
-
static void debug_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct debug_ref_iterator *diter =
@@ -198,7 +188,6 @@ static void debug_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable debug_ref_iterator_vtable = {
.advance = debug_ref_iterator_advance,
.seek = debug_ref_iterator_seek,
- .peel = debug_ref_iterator_peel,
.release = debug_ref_iterator_release,
};
diff --git a/refs/files-backend.c b/refs/files-backend.c
index fac53fa052d..5aeb454fb47 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -993,15 +993,6 @@ static int files_ref_iterator_seek(struct ref_iterator *ref_iterator,
return ref_iterator_seek(iter->iter0, refname, flags);
}
-static int files_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct files_ref_iterator *iter =
- (struct files_ref_iterator *)ref_iterator;
-
- return ref_iterator_peel(iter->iter0, peeled);
-}
-
static void files_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct files_ref_iterator *iter =
@@ -1012,7 +1003,6 @@ static void files_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable files_ref_iterator_vtable = {
.advance = files_ref_iterator_advance,
.seek = files_ref_iterator_seek,
- .peel = files_ref_iterator_peel,
.release = files_ref_iterator_release,
};
@@ -2388,12 +2378,6 @@ static int files_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
BUG("ref_iterator_seek() called for reflog_iterator");
}
-static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
- struct object_id *peeled UNUSED)
-{
- BUG("ref_iterator_peel() called for reflog_iterator");
-}
-
static void files_reflog_iterator_release(struct ref_iterator *ref_iterator)
{
struct files_reflog_iterator *iter =
@@ -2404,7 +2388,6 @@ static void files_reflog_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable files_reflog_iterator_vtable = {
.advance = files_reflog_iterator_advance,
.seek = files_reflog_iterator_seek,
- .peel = files_reflog_iterator_peel,
.release = files_reflog_iterator_release,
};
diff --git a/refs/iterator.c b/refs/iterator.c
index 072c6aacdb0..d79aa5ec82d 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -21,12 +21,6 @@ int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname,
return ref_iterator->vtable->seek(ref_iterator, refname, flags);
}
-int ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- return ref_iterator->vtable->peel(ref_iterator, peeled);
-}
-
void ref_iterator_free(struct ref_iterator *ref_iterator)
{
if (ref_iterator) {
@@ -60,12 +54,6 @@ static int empty_ref_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
return 0;
}
-static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
- struct object_id *peeled UNUSED)
-{
- BUG("peel called for empty iterator");
-}
-
static void empty_ref_iterator_release(struct ref_iterator *ref_iterator UNUSED)
{
}
@@ -73,7 +61,6 @@ static void empty_ref_iterator_release(struct ref_iterator *ref_iterator UNUSED)
static struct ref_iterator_vtable empty_ref_iterator_vtable = {
.advance = empty_ref_iterator_advance,
.seek = empty_ref_iterator_seek,
- .peel = empty_ref_iterator_peel,
.release = empty_ref_iterator_release,
};
@@ -240,18 +227,6 @@ static int merge_ref_iterator_seek(struct ref_iterator *ref_iterator,
return 0;
}
-static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct merge_ref_iterator *iter =
- (struct merge_ref_iterator *)ref_iterator;
-
- if (!iter->current) {
- BUG("peel called before advance for merge iterator");
- }
- return ref_iterator_peel(*iter->current, peeled);
-}
-
static void merge_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct merge_ref_iterator *iter =
@@ -263,7 +238,6 @@ static void merge_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable merge_ref_iterator_vtable = {
.advance = merge_ref_iterator_advance,
.seek = merge_ref_iterator_seek,
- .peel = merge_ref_iterator_peel,
.release = merge_ref_iterator_release,
};
@@ -412,15 +386,6 @@ static int prefix_ref_iterator_seek(struct ref_iterator *ref_iterator,
return ref_iterator_seek(iter->iter0, refname, flags);
}
-static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct prefix_ref_iterator *iter =
- (struct prefix_ref_iterator *)ref_iterator;
-
- return ref_iterator_peel(iter->iter0, peeled);
-}
-
static void prefix_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct prefix_ref_iterator *iter =
@@ -432,7 +397,6 @@ static void prefix_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable prefix_ref_iterator_vtable = {
.advance = prefix_ref_iterator_advance,
.seek = prefix_ref_iterator_seek,
- .peel = prefix_ref_iterator_peel,
.release = prefix_ref_iterator_release,
};
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 1fefefd54ed..6fa229edd0f 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1030,22 +1030,6 @@ static int packed_ref_iterator_seek(struct ref_iterator *ref_iterator,
return 0;
}
-static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct packed_ref_iterator *iter =
- (struct packed_ref_iterator *)ref_iterator;
-
- if ((iter->base.ref.flags & REF_KNOWS_PEELED)) {
- oidcpy(peeled, &iter->peeled);
- return is_null_oid(&iter->peeled) ? -1 : 0;
- } else if ((iter->base.ref.flags & (REF_ISBROKEN | REF_ISSYMREF))) {
- return -1;
- } else {
- return peel_object(iter->repo, &iter->oid, peeled) ? -1 : 0;
- }
-}
-
static void packed_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct packed_ref_iterator *iter =
@@ -1059,7 +1043,6 @@ static void packed_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable packed_ref_iterator_vtable = {
.advance = packed_ref_iterator_advance,
.seek = packed_ref_iterator_seek,
- .peel = packed_ref_iterator_peel,
.release = packed_ref_iterator_release,
};
@@ -1525,13 +1508,8 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
if (cmp < 0) {
/* Pass the old reference through. */
-
- struct object_id peeled;
- int peel_error = ref_iterator_peel(iter, &peeled);
-
if (write_packed_entry(out, iter->ref.name,
- iter->ref.oid,
- peel_error ? NULL : &peeled))
+ iter->ref.oid, iter->ref.peeled_oid))
goto write_error;
if ((ok = ref_iterator_advance(iter)) != ITER_OK) {
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index e427848879d..ffef01a5975 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -546,14 +546,6 @@ static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator,
return 0;
}
-static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct cache_ref_iterator *iter =
- (struct cache_ref_iterator *)ref_iterator;
- return peel_object(iter->repo, ref_iterator->ref.oid, peeled) ? -1 : 0;
-}
-
static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct cache_ref_iterator *iter =
@@ -565,7 +557,6 @@ static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable cache_ref_iterator_vtable = {
.advance = cache_ref_iterator_advance,
.seek = cache_ref_iterator_seek,
- .peel = cache_ref_iterator_peel,
.release = cache_ref_iterator_release,
};
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index f4f845bbeaf..4671517dade 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -357,12 +357,6 @@ typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator);
typedef int ref_iterator_seek_fn(struct ref_iterator *ref_iterator,
const char *refname, unsigned int flags);
-/*
- * Peels the current ref, returning 0 for success or -1 for failure.
- */
-typedef int ref_iterator_peel_fn(struct ref_iterator *ref_iterator,
- struct object_id *peeled);
-
/*
* Implementations of this function should free any resources specific
* to the derived class.
@@ -372,7 +366,6 @@ typedef void ref_iterator_release_fn(struct ref_iterator *ref_iterator);
struct ref_iterator_vtable {
ref_iterator_advance_fn *advance;
ref_iterator_seek_fn *seek;
- ref_iterator_peel_fn *peel;
ref_iterator_release_fn *release;
};
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index e214e120d77..e329d4a423a 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -744,21 +744,6 @@ static int reftable_ref_iterator_seek(struct ref_iterator *ref_iterator,
return iter->err;
}
-static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct reftable_ref_iterator *iter =
- (struct reftable_ref_iterator *)ref_iterator;
-
- if (iter->ref.value_type == REFTABLE_REF_VAL2) {
- oidread(peeled, iter->ref.value.val2.target_value,
- iter->refs->base.repo->hash_algo);
- return 0;
- }
-
- return -1;
-}
-
static void reftable_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct reftable_ref_iterator *iter =
@@ -776,7 +761,6 @@ static void reftable_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable reftable_ref_iterator_vtable = {
.advance = reftable_ref_iterator_advance,
.seek = reftable_ref_iterator_seek,
- .peel = reftable_ref_iterator_peel,
.release = reftable_ref_iterator_release,
};
@@ -2098,13 +2082,6 @@ static int reftable_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSE
return -1;
}
-static int reftable_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
- struct object_id *peeled UNUSED)
-{
- BUG("reftable reflog iterator cannot be peeled");
- return -1;
-}
-
static void reftable_reflog_iterator_release(struct ref_iterator *ref_iterator)
{
struct reftable_reflog_iterator *iter =
@@ -2117,7 +2094,6 @@ static void reftable_reflog_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable reftable_reflog_iterator_vtable = {
.advance = reftable_reflog_iterator_advance,
.seek = reftable_reflog_iterator_seek,
- .peel = reftable_reflog_iterator_peel,
.release = reftable_reflog_iterator_release,
};
--
2.51.1.851.g4ebd6896fd.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v3 11/14] object: add flag to `peel_object()` to verify object type
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
` (9 preceding siblings ...)
2025-10-22 6:41 ` [PATCH v3 10/14] refs: drop infrastructure to peel via iterators Patrick Steinhardt
@ 2025-10-22 6:41 ` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 12/14] refs: don't store peeled object IDs for invalid tags Patrick Steinhardt
` (4 subsequent siblings)
15 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-22 6:41 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
When peeling a tag to a non-tag object we repeatedly call
`parse_object()` on the tagged object until we find the first object
that isn't a tag. While this feels sensible at first, there is a big
catch here: `parse_object()` doesn't actually verify the type of the
tagged object.
The relevant code path here eventually ends up in `parse_tag_buffer()`.
Here, we parse the various fields of the tag, including the "type". Once
we've figured out the type and the tagged object ID, we call one of the
`lookup_${type}()` functions for whatever type we have found. There is
two possible outcomes in the successful case:
1. The object is already part of our cached objects. In that case we
double-check whether the type we're trying to look up matches the
type that was cached.
2. The object is _not_ part of our cached objects. In that case, we
simply create a new object with the expected type, but we don't
parse that object.
In the first case we might notice type mismatches, but only in the case
where our cache has the object with the correct type. In the second
case, we'll blindly assume that the type is correct and then go with it.
We'll only notice that the type might be wrong when we try to parse the
object at a later point.
Now arguably, we could change `parse_tag_buffer()` to verify the tagged
object's type for us. But that would have the effect that such a tag
cannot be parsed at all anymore, and we have a small bunch of tests for
exactly this case that assert we still can open such tags. So this
change does not feel like something we can retroactively tighten, even
though one shouldn't ever hit such corrupted tags.
Instead, add a new `flags` field to `peel_object()` that allows the
caller to opt in to strict object verification. This will be wired up at
a subset of callsites over the next few commits.
Note that this change also inlines `deref_tag_noverify()`. There's only
been two callsites of that function, the one we're changing and one in
our test helpers. The latter callsite can trivially use `deref_tag()`
instead, so by inlining the function we avoid having to pass down the
flag.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
object.c | 20 +++++++++++++++++---
object.h | 15 ++++++++++++++-
ref-filter.c | 2 +-
refs.c | 2 +-
refs/packed-backend.c | 5 ++---
refs/reftable-backend.c | 4 ++--
t/helper/test-reach.c | 2 +-
tag.c | 12 ------------
tag.h | 1 -
9 files changed, 38 insertions(+), 25 deletions(-)
diff --git a/object.c b/object.c
index 986114a6dba..e72b0ed4360 100644
--- a/object.c
+++ b/object.c
@@ -209,11 +209,12 @@ struct object *lookup_object_by_type(struct repository *r,
enum peel_status peel_object(struct repository *r,
const struct object_id *name,
- struct object_id *oid)
+ struct object_id *oid,
+ unsigned flags)
{
struct object *o = lookup_unknown_object(r, name);
- if (o->type == OBJ_NONE) {
+ if (o->type == OBJ_NONE || flags & PEEL_OBJECT_VERIFY_OBJECT_TYPE) {
int type = odb_read_object_info(r->objects, name, NULL);
if (type < 0 || !object_as_type(o, type, 0))
return PEEL_INVALID;
@@ -222,7 +223,20 @@ enum peel_status peel_object(struct repository *r,
if (o->type != OBJ_TAG)
return PEEL_NON_TAG;
- o = deref_tag_noverify(r, o);
+ while (o && o->type == OBJ_TAG) {
+ o = parse_object(r, &o->oid);
+ if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged) {
+ o = ((struct tag *)o)->tagged;
+
+ if (flags & PEEL_OBJECT_VERIFY_OBJECT_TYPE) {
+ int type = odb_read_object_info(r->objects, &o->oid, NULL);
+ if (type < 0 || !object_as_type(o, type, 0))
+ return PEEL_INVALID;
+ }
+ } else {
+ o = NULL;
+ }
+ }
if (!o)
return PEEL_INVALID;
diff --git a/object.h b/object.h
index 8c3c1c46e1b..1499f63d507 100644
--- a/object.h
+++ b/object.h
@@ -287,6 +287,17 @@ enum peel_status {
PEEL_BROKEN = -4
};
+enum peel_object_flags {
+ /*
+ * Always verify the object type, even in the case where the looked-up
+ * object already has an object type. This can be useful when the
+ * stored object type may be invalid. One such case is when looking up
+ * objects via tags, where we blindly trust the object type declared by
+ * the tag.
+ */
+ PEEL_OBJECT_VERIFY_OBJECT_TYPE = (1 << 0),
+};
+
/*
* Peel the named object; i.e., if the object is a tag, resolve the
* tag recursively until a non-tag is found. If successful, store the
@@ -295,7 +306,9 @@ enum peel_status {
* and leave oid unchanged.
*/
enum peel_status peel_object(struct repository *r,
- const struct object_id *name, struct object_id *oid);
+ const struct object_id *name,
+ struct object_id *oid,
+ unsigned flags);
struct object_list *object_list_insert(struct object *item,
struct object_list **list_p);
diff --git a/ref-filter.c b/ref-filter.c
index 7fd8babec8f..9a8ed8c8fc1 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2581,7 +2581,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
if (need_tagged) {
if (!is_null_oid(&ref->peeled_oid)) {
oidcpy(&oi_deref.oid, &ref->peeled_oid);
- } else if (!peel_object(the_repository, &obj->oid, &oi_deref.oid)) {
+ } else if (!peel_object(the_repository, &oi.oid, &oi_deref.oid, 0)) {
/* We managed to peel the object ourselves. */
} else {
die("bad tag");
diff --git a/refs.c b/refs.c
index 9d8f0a9ca4a..a41a94ae55b 100644
--- a/refs.c
+++ b/refs.c
@@ -2333,7 +2333,7 @@ int reference_get_peeled_oid(struct repository *repo,
return 0;
}
- return peel_object(repo, ref->oid, peeled_oid) ? -1 : 0;
+ return peel_object(repo, ref->oid, peeled_oid, 0) ? -1 : 0;
}
int refs_update_symref(struct ref_store *refs, const char *ref,
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 6fa229edd0f..4752d3f3981 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1527,9 +1527,8 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
i++;
} else {
struct object_id peeled;
- int peel_error = peel_object(refs->base.repo,
- &update->new_oid,
- &peeled);
+ int peel_error = peel_object(refs->base.repo, &update->new_oid,
+ &peeled, 0);
if (write_packed_entry(out, update->refname,
&update->new_oid,
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index e329d4a423a..9febb2322c3 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1632,7 +1632,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
ref.refname = (char *)u->refname;
ref.update_index = ts;
- peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled);
+ peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled, 0);
if (!peel_error) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
@@ -2497,7 +2497,7 @@ static int write_reflog_expiry_table(struct reftable_writer *writer, void *cb_da
ref.refname = (char *)arg->refname;
ref.update_index = ts;
- if (!peel_object(arg->refs->base.repo, &arg->update_oid, &peeled)) {
+ if (!peel_object(arg->refs->base.repo, &arg->update_oid, &peeled, 0)) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
memcpy(ref.value.val2.value, arg->update_oid.hash, GIT_MAX_RAWSZ);
diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c
index 028ec003067..c58c93800f3 100644
--- a/t/helper/test-reach.c
+++ b/t/helper/test-reach.c
@@ -63,7 +63,7 @@ int cmd__reach(int ac, const char **av)
die("failed to resolve %s", buf.buf + 2);
orig = parse_object(r, &oid);
- peeled = deref_tag_noverify(the_repository, orig);
+ peeled = deref_tag(the_repository, orig, NULL, 0);
if (!peeled)
die("failed to load commit for input %s resulting in oid %s",
diff --git a/tag.c b/tag.c
index 1d52686ee10..f5c232d2f1f 100644
--- a/tag.c
+++ b/tag.c
@@ -94,18 +94,6 @@ struct object *deref_tag(struct repository *r, struct object *o, const char *war
return o;
}
-struct object *deref_tag_noverify(struct repository *r, struct object *o)
-{
- while (o && o->type == OBJ_TAG) {
- o = parse_object(r, &o->oid);
- if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged)
- o = ((struct tag *)o)->tagged;
- else
- o = NULL;
- }
- return o;
-}
-
struct tag *lookup_tag(struct repository *r, const struct object_id *oid)
{
struct object *obj = lookup_object(r, oid);
diff --git a/tag.h b/tag.h
index c49d7c19ad3..ef12a610372 100644
--- a/tag.h
+++ b/tag.h
@@ -16,7 +16,6 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
int parse_tag(struct tag *item);
void release_tag_memory(struct tag *t);
struct object *deref_tag(struct repository *r, struct object *, const char *, int);
-struct object *deref_tag_noverify(struct repository *r, struct object *);
int gpg_verify_tag(const struct object_id *oid,
const char *name_to_report, unsigned flags);
struct object_id *get_tagged_oid(struct tag *tag);
--
2.51.1.851.g4ebd6896fd.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v3 12/14] refs: don't store peeled object IDs for invalid tags
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
` (10 preceding siblings ...)
2025-10-22 6:41 ` [PATCH v3 11/14] object: add flag to `peel_object()` to verify object type Patrick Steinhardt
@ 2025-10-22 6:41 ` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 13/14] ref-filter: detect broken tags when dereferencing them Patrick Steinhardt
` (3 subsequent siblings)
15 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-22 6:41 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
Both the "files" and "reftable" backend store peeled object IDs for
references that point to tags:
- The "files" backend stores the value when packing refs, where each
peeled object ID is prefixed with "^".
- The "reftable" backend stores the value whenever writing a new
reference that points to a tag via a special ref record type.
Both of these backends use `peel_object()` to find the peeled object ID.
But as explained in the preceding commit, that function does not detect
the case where the tag's tagged object and its claimed type mismatch.
The consequence of storing these bogus peeled object IDs is that we're
less likely to detect such corruption in other parts of Git.
git-for-each-ref(1) for example does not notice anymore that the tag is
broken when using "--format=%(*objectname)" to dereference tags.
One could claim that this is good, because it still allows us to mostly
use the tag as intended. But the biggest problem here is that we now
have different behaviour for such a broken tag depending on whether or
not we have its peeled value in the refdb.
Fix the issue by verifying the object type when peeling the object. If
that verification fails we simply skip storing the peeled value in
either of the reference formats.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs/packed-backend.c | 2 +-
refs/reftable-backend.c | 3 ++-
t/pack-refs-tests.sh | 32 ++++++++++++++++++++++++++++++++
t/t0610-reftable-basics.sh | 28 ++++++++++++++++++++++++++++
4 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 4752d3f3981..1ab0c503930 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1528,7 +1528,7 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
} else {
struct object_id peeled;
int peel_error = peel_object(refs->base.repo, &update->new_oid,
- &peeled, 0);
+ &peeled, PEEL_OBJECT_VERIFY_OBJECT_TYPE);
if (write_packed_entry(out, update->refname,
&update->new_oid,
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 9febb2322c3..6bbfd5618da 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1632,7 +1632,8 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
ref.refname = (char *)u->refname;
ref.update_index = ts;
- peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled, 0);
+ peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled,
+ PEEL_OBJECT_VERIFY_OBJECT_TYPE);
if (!peel_error) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
diff --git a/t/pack-refs-tests.sh b/t/pack-refs-tests.sh
index 3dbcc01718e..095823d915f 100644
--- a/t/pack-refs-tests.sh
+++ b/t/pack-refs-tests.sh
@@ -428,4 +428,36 @@ do
'
done
+test_expect_success 'pack-refs does not store invalid peeled tag value' '
+ test_when_finished rm -rf repo &&
+ git init repo &&
+ (
+ cd repo &&
+ git commit --allow-empty --message initial &&
+
+ echo garbage >blob-content &&
+ blob_id=$(git hash-object -w -t blob blob-content) &&
+
+ # Write an invalid tag into the object database. The tag itself
+ # is well-formed, but the tagged object is a blob while we
+ # claim that it is a commit.
+ cat >tag-content <<-EOF &&
+ object $blob_id
+ type commit
+ tag bad-tag
+ tagger C O Mitter <committer@example.com> 1112354055 +0200
+
+ annotated
+ EOF
+ tag_id=$(git hash-object -w -t tag tag-content) &&
+ git update-ref refs/tags/bad-tag "$tag_id" &&
+
+ # The packed-refs file should not contain the peeled object ID.
+ # If it did this would cause commands that use the peeled value
+ # to not notice this corrupted tag.
+ git pack-refs --all &&
+ test_grep ! "^\^" .git/packed-refs
+ )
+'
+
test_done
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
index 3ea5d51532a..6575528f212 100755
--- a/t/t0610-reftable-basics.sh
+++ b/t/t0610-reftable-basics.sh
@@ -1135,4 +1135,32 @@ test_expect_success 'fetch: accessing FETCH_HEAD special ref works' '
test_cmp expect actual
'
+test_expect_success 'writes do not persist peeled value for invalid tags' '
+ test_when_finished rm -rf repo &&
+ git init repo &&
+ (
+ cd repo &&
+ git commit --allow-empty --message initial &&
+
+ # We cannot easily verify that the peeled value is not stored
+ # in the tables. Instead, we test this indirectly: we create
+ # two tags that both point to the same object, but they claim
+ # different object types. If we parse both tags we notice that
+ # the parsed tagged object has a mismatch between the two tags
+ # and bail out.
+ #
+ # If we instead use the persisted peeled value we would not
+ # even parse the tags. As such, we would not notice the
+ # discrepancy either and thus listing these tags would succeed.
+ git tag tag-1 -m "tag 1" &&
+ git cat-file tag tag-1 >raw-tag &&
+ sed "s/^type commit$/type blob/" <raw-tag >broken-tag &&
+ broken_tag_id=$(git hash-object -w -t tag broken-tag) &&
+ git update-ref refs/tags/tag-2 $broken_tag_id &&
+
+ test_must_fail git for-each-ref --format="%(*objectname)" refs/tags/ 2>err &&
+ test_grep "bad tag pointer" err
+ )
+'
+
test_done
--
2.51.1.851.g4ebd6896fd.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v3 13/14] ref-filter: detect broken tags when dereferencing them
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
` (11 preceding siblings ...)
2025-10-22 6:41 ` [PATCH v3 12/14] refs: don't store peeled object IDs for invalid tags Patrick Steinhardt
@ 2025-10-22 6:41 ` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 14/14] ref-filter: parse objects on demand Patrick Steinhardt
` (2 subsequent siblings)
15 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-22 6:41 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
Users can ask git-for-each-ref(1) to peel tags and return information of
the tagged object by adding an asterisk to the format, like for example
"%(*$objectname)". If so, git-for-each-ref(1) peels that object to the
first non-tag object and then returns its values.
As mentioned in preceding commits, it can happen that the tagged object
type and the claimed object type differ, effectively resulting in a
corrupt tag. git-for-each-ref(1) would notice this mismatch, print an
error and then bail out when trying to peel the tag.
But we only notice this corruption in some very specific edge cases!
While we have a test in "t/for-each-ref-tests.sh" that verifies the
above scenario, this test is specifically crafted to detect the issue at
hand. Namely, we create two tags:
- One tag points to a specific object with the correct type.
- The other tag points to the *same* object with a different type.
The fact that both tags point to the same object is important here:
`peel_object()` wouldn't notice the corruption if the tagged objects
were different.
The root cause is that `peel_object()` calls `lookup_${type}()`
eventually, where the type is the same type declared in the tag object.
Consequently, when we have two tags pointing to the same object but with
different declared types we'll call two different lookup functions. The
first lookup will store the object with an unverified type A, whereas
the second lookup will try to look up the object with a different
unverified type B. And it is only now that we notice the discrepancy in
object types, even though type A could've already been the wrong type.
Fix the issue by verifying the object type in `populate_value()`. With
this change we'll also notice type mismatches when only dereferencing a
tag once.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
ref-filter.c | 3 ++-
t/for-each-ref-tests.sh | 4 +++-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/ref-filter.c b/ref-filter.c
index 9a8ed8c8fc1..c54025d6b4c 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2581,7 +2581,8 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
if (need_tagged) {
if (!is_null_oid(&ref->peeled_oid)) {
oidcpy(&oi_deref.oid, &ref->peeled_oid);
- } else if (!peel_object(the_repository, &oi.oid, &oi_deref.oid, 0)) {
+ } else if (!peel_object(the_repository, &oi.oid, &oi_deref.oid,
+ PEEL_OBJECT_VERIFY_OBJECT_TYPE)) {
/* We managed to peel the object ourselves. */
} else {
die("bad tag");
diff --git a/t/for-each-ref-tests.sh b/t/for-each-ref-tests.sh
index e3ad19298ac..4593be5fd54 100644
--- a/t/for-each-ref-tests.sh
+++ b/t/for-each-ref-tests.sh
@@ -1809,7 +1809,9 @@ test_expect_success "${git_for_each_ref} reports broken tags" '
bad=$(git hash-object -w -t tag bad) &&
git update-ref refs/tags/broken-tag-bad $bad &&
test_must_fail ${git_for_each_ref} --format="%(*objectname)" \
- refs/tags/broken-tag-*
+ refs/tags/broken-tag-* &&
+ test_must_fail ${git_for_each_ref} --format="%(*objectname)" \
+ refs/tags/broken-tag-bad
'
test_expect_success 'set up tag with signature and no blank lines' '
--
2.51.1.851.g4ebd6896fd.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v3 14/14] ref-filter: parse objects on demand
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
` (12 preceding siblings ...)
2025-10-22 6:41 ` [PATCH v3 13/14] ref-filter: detect broken tags when dereferencing them Patrick Steinhardt
@ 2025-10-22 6:41 ` Patrick Steinhardt
2025-10-22 15:27 ` Junio C Hamano
2025-10-22 10:57 ` [PATCH v3 00/14] refs: improvements and fixes for peeling tags Karthik Nayak
2025-10-22 14:47 ` Junio C Hamano
15 siblings, 1 reply; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-22 6:41 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
When formatting an arbitrary object we parse that object regardless of
whether or not we actually need any parsed data. In fact, many of the
atoms we have don't require any.
Refactor the code so that we parse the data on demand when we see an
atom that wants to access the objects. This leads to a small speedup,
for example in the Chromium repository with around 40000 refs:
Benchmark 1: for-each-ref --format='%(raw)' (HEAD~)
Time (mean ± σ): 388.7 ms ± 1.1 ms [User: 322.2 ms, System: 65.0 ms]
Range (min … max): 387.3 ms … 390.8 ms 10 runs
Benchmark 2: for-each-ref --format='%(raw)' (HEAD)
Time (mean ± σ): 344.7 ms ± 0.7 ms [User: 287.8 ms, System: 55.1 ms]
Range (min … max): 343.9 ms … 345.7 ms 10 runs
Summary
for-each-ref --format='%(raw)' (HEAD) ran
1.13 ± 0.00 times faster than for-each-ref --format='%(raw)' (HEAD~)
With this change, we now spend ~90% of the time decompressing objects,
which is almost as good as it gets regarding git-for-each-ref(1)'s own
infrastructure.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
ref-filter.c | 156 ++++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 117 insertions(+), 39 deletions(-)
diff --git a/ref-filter.c b/ref-filter.c
index c54025d6b4..ca58870aed 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -91,6 +91,7 @@ static struct expand_data {
struct object_id delta_base_oid;
void *content;
+ struct object *maybe_object;
struct object_info info;
} oi, oi_deref;
@@ -1475,11 +1476,28 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_
}
}
+static int get_or_parse_object(struct expand_data *data, const char *refname,
+ struct object **object, struct strbuf *err, int *eaten)
+{
+ if (!data->maybe_object) {
+ data->maybe_object = parse_object_buffer(the_repository, &data->oid, data->type,
+ data->size, data->content, eaten);
+ if (!data->maybe_object)
+ return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
+ oid_to_hex(&data->oid), refname);
+ }
+
+ *object = data->maybe_object;
+ return 0;
+}
+
/* See grab_values */
-static void grab_tag_values(struct atom_value *val, int deref, struct object *obj)
+static int grab_tag_values(struct atom_value *val, int deref,
+ struct expand_data *data, const char *refname,
+ struct strbuf *err, int *eaten)
{
- int i;
- struct tag *tag = (struct tag *) obj;
+ struct tag *tag = NULL;
+ int i, ret;
for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i].name;
@@ -1487,6 +1505,17 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
struct atom_value *v = &val[i];
if (!!deref != (*name == '*'))
continue;
+
+ if (!tag) {
+ struct object *object;
+
+ ret = get_or_parse_object(data, refname, &object, err, eaten);
+ if (ret < 0)
+ return ret;
+
+ tag = (struct tag *) object;
+ }
+
if (deref)
name++;
if (atom_type == ATOM_TAG)
@@ -1496,22 +1525,38 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
else if (atom_type == ATOM_OBJECT && tag->tagged)
v->s = xstrdup(oid_to_hex(&tag->tagged->oid));
}
+
+ return 0;
}
/* See grab_values */
-static void grab_commit_values(struct atom_value *val, int deref, struct object *obj)
+static int grab_commit_values(struct atom_value *val, int deref,
+ struct expand_data *data, const char *refname,
+ struct strbuf *err, int *eaten)
{
- int i;
- struct commit *commit = (struct commit *) obj;
+ int i, ret;
+ struct commit *commit = NULL;
for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i].name;
enum atom_type atom_type = used_atom[i].atom_type;
struct atom_value *v = &val[i];
+
if (!!deref != (*name == '*'))
continue;
if (deref)
name++;
+
+ if (!commit) {
+ struct object *object;
+
+ ret = get_or_parse_object(data, refname, &object, err, eaten);
+ if (ret < 0)
+ return ret;
+
+ commit = (struct commit *) object;
+ }
+
if (atom_type == ATOM_TREE &&
grab_oid(name, "tree", get_commit_tree_oid(commit), v, &used_atom[i]))
continue;
@@ -1531,6 +1576,8 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
v->s = strbuf_detach(&s, NULL);
}
}
+
+ return 0;
}
static const char *find_wholine(const char *who, int wholen, const char *buf)
@@ -1759,10 +1806,12 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void
}
}
-static void grab_signature(struct atom_value *val, int deref, struct object *obj)
+static int grab_signature(struct atom_value *val, int deref,
+ struct expand_data *data, const char *refname,
+ struct strbuf *err, int *eaten)
{
- int i;
- struct commit *commit = (struct commit *) obj;
+ int i, ret;
+ struct commit *commit = NULL;
struct signature_check sigc = { 0 };
int signature_checked = 0;
@@ -1790,6 +1839,16 @@ static void grab_signature(struct atom_value *val, int deref, struct object *obj
continue;
if (!signature_checked) {
+ if (!commit) {
+ struct object *object;
+
+ ret = get_or_parse_object(data, refname, &object, err, eaten);
+ if (ret < 0)
+ return ret;
+
+ commit = (struct commit *) object;
+ }
+
check_commit_signature(commit, &sigc);
signature_checked = 1;
}
@@ -1843,6 +1902,8 @@ static void grab_signature(struct atom_value *val, int deref, struct object *obj
if (signature_checked)
signature_check_clear(&sigc);
+
+ return 0;
}
static void find_subpos(const char *buf,
@@ -1920,9 +1981,8 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size
}
static void grab_describe_values(struct atom_value *val, int deref,
- struct object *obj)
+ struct expand_data *data)
{
- struct commit *commit = (struct commit *)obj;
int i;
for (i = 0; i < used_atom_cnt; i++) {
@@ -1944,7 +2004,7 @@ static void grab_describe_values(struct atom_value *val, int deref,
cmd.git_cmd = 1;
strvec_push(&cmd.args, "describe");
strvec_pushv(&cmd.args, atom->u.describe_args.v);
- strvec_push(&cmd.args, oid_to_hex(&commit->object.oid));
+ strvec_push(&cmd.args, oid_to_hex(&data->oid));
if (pipe_command(&cmd, NULL, 0, &out, 0, &err, 0) < 0) {
error(_("failed to run 'describe'"));
v->s = xstrdup("");
@@ -2066,24 +2126,36 @@ static void fill_missing_values(struct atom_value *val)
* pointed at by the ref itself; otherwise it is the object the
* ref (which is a tag) refers to.
*/
-static void grab_values(struct atom_value *val, int deref, struct object *obj, struct expand_data *data)
+static int grab_values(struct atom_value *val, int deref, struct expand_data *data,
+ const char *refname, struct strbuf *err, int *eaten)
{
void *buf = data->content;
+ int ret;
- switch (obj->type) {
+ switch (data->type) {
case OBJ_TAG:
- grab_tag_values(val, deref, obj);
+ ret = grab_tag_values(val, deref, data, refname, err, eaten);
+ if (ret < 0)
+ goto out;
+
grab_sub_body_contents(val, deref, data);
grab_person("tagger", val, deref, buf);
- grab_describe_values(val, deref, obj);
+ grab_describe_values(val, deref, data);
break;
case OBJ_COMMIT:
- grab_commit_values(val, deref, obj);
+ ret = grab_commit_values(val, deref, data, refname, err, eaten);
+ if (ret < 0)
+ goto out;
+
grab_sub_body_contents(val, deref, data);
grab_person("author", val, deref, buf);
grab_person("committer", val, deref, buf);
- grab_signature(val, deref, obj);
- grab_describe_values(val, deref, obj);
+
+ ret = grab_signature(val, deref, data, refname, err, eaten);
+ if (ret < 0)
+ goto out;
+
+ grab_describe_values(val, deref, data);
break;
case OBJ_TREE:
/* grab_tree_values(val, deref, obj, buf, sz); */
@@ -2094,8 +2166,12 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s
grab_sub_body_contents(val, deref, data);
break;
default:
- die("Eh? Object of type %d?", obj->type);
+ die("Eh? Object of type %d?", data->type);
}
+
+ ret = 0;
+out:
+ return ret;
}
static inline char *copy_advance(char *dst, const char *src)
@@ -2292,38 +2368,41 @@ static const char *get_refname(struct used_atom *atom, struct ref_array_item *re
return show_ref(&atom->u.refname, ref->refname);
}
-static int get_object(struct ref_array_item *ref, int deref, struct object **obj,
+static int get_object(struct ref_array_item *ref, int deref,
struct expand_data *oi, struct strbuf *err)
{
- /* parse_object_buffer() will set eaten to 0 if free() will be needed */
- int eaten = 1;
+ /* parse_object_buffer() will set eaten to 1 if free() will be needed */
+ int eaten = 0;
+ int ret;
+
if (oi->info.contentp) {
/* We need to know that to use parse_object_buffer properly */
oi->info.sizep = &oi->size;
oi->info.typep = &oi->type;
}
+
if (odb_read_object_info_extended(the_repository->objects, &oi->oid, &oi->info,
- OBJECT_INFO_LOOKUP_REPLACE))
- return strbuf_addf_ret(err, -1, _("missing object %s for %s"),
- oid_to_hex(&oi->oid), ref->refname);
+ OBJECT_INFO_LOOKUP_REPLACE)) {
+ ret = strbuf_addf_ret(err, -1, _("missing object %s for %s"),
+ oid_to_hex(&oi->oid), ref->refname);
+ goto out;
+ }
if (oi->info.disk_sizep && oi->disk_size < 0)
BUG("Object size is less than zero.");
if (oi->info.contentp) {
- *obj = parse_object_buffer(the_repository, &oi->oid, oi->type, oi->size, oi->content, &eaten);
- if (!*obj) {
- if (!eaten)
- free(oi->content);
- return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
- oid_to_hex(&oi->oid), ref->refname);
- }
- grab_values(ref->value, deref, *obj, oi);
+ ret = grab_values(ref->value, deref, oi, ref->refname, err, &eaten);
+ if (ret < 0)
+ goto out;
}
grab_common_values(ref->value, deref, oi);
+ ret = 0;
+
+out:
if (!eaten)
free(oi->content);
- return 0;
+ return ret;
}
static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees)
@@ -2376,7 +2455,6 @@ static char *get_worktree_path(const struct ref_array_item *ref)
*/
static int populate_value(struct ref_array_item *ref, struct strbuf *err)
{
- struct object *obj;
int i;
struct object_info empty = OBJECT_INFO_INIT;
int ahead_behind_atoms = 0;
@@ -2564,14 +2642,14 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
oi.oid = ref->objectname;
- if (get_object(ref, 0, &obj, &oi, err))
+ if (get_object(ref, 0, &oi, err))
return -1;
/*
* If there is no atom that wants to know about tagged
* object, we are done.
*/
- if (!need_tagged || (obj->type != OBJ_TAG))
+ if (!need_tagged || (oi.type != OBJ_TAG))
return 0;
/*
@@ -2589,7 +2667,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
}
}
- return get_object(ref, 1, &obj, &oi_deref, err);
+ return get_object(ref, 1, &oi_deref, err);
}
/*
--
2.51.1.851.g4ebd6896fd.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* Re: [PATCH v3 00/14] refs: improvements and fixes for peeling tags
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
` (13 preceding siblings ...)
2025-10-22 6:41 ` [PATCH v3 14/14] ref-filter: parse objects on demand Patrick Steinhardt
@ 2025-10-22 10:57 ` Karthik Nayak
2025-10-22 14:47 ` Junio C Hamano
15 siblings, 0 replies; 106+ messages in thread
From: Karthik Nayak @ 2025-10-22 10:57 UTC (permalink / raw)
To: Patrick Steinhardt, git
Cc: Kristoffer Haugsbakk, Taylor Blau, Junio C Hamano, Justin Tobler
[-- Attachment #1: Type: text/plain, Size: 2397 bytes --]
Patrick Steinhardt <ps@pks.im> writes:
> Hi,
>
> originally, all I wanted to do was the last patch: a small performance
> optimization that stops parsing objects in git-for-each-ref(1) unless we
> really need to parse them. But that fix cause one specific test to fail,
> and only with the reftable backend. So this led me down the rabbit hole
> of tag peeling, ending up with this patch series.
>
> The series is structured like follows:
>
> - Patches 1 to 8 refactor our codebase so that we don't have the
> `peel_iterated_object()` hack anymore. I just found it hard to
> follow and thought it shouldn't be too hard to get rid of it.
>
> - Patches 9 and 10 remove infrastructure that we don't need anymore
> after the first couple of patches.
>
> - Patches 11 to 13 fix a couple of issues with peeled tags that I
> found. The underlying issue is that tags store both the tagged
> object and their type, but this information may not match. We never
> verify the actual object type though when allocating the tagged
> object, so this only blows up much later.
>
> - Patch 14 was my original motivation, a small performance
> optimization.
>
> I'm not particularly fond of the patches 11 to 13. It feels more like
> playing whack-a-mole, and I very much assume that there still are edge
> cases where we should properly verify the tagged object type. But
> changing it in `parse_tag_buffer()` itself causes a bunch of tests to
> fail where we intentionally create such corrupted tags. So I didn't
> really dare to touch that part, to be honest.
>
> If anybody has suggestions for an alternative approach I'd be very open
> to it.
>
> Changes in v3:
> - I've rebuilt the topic on 133d151831 (The twenty-first batch, 2025-10-20) with
> - tb/incremental-midx-part-3.1 at 935ab44a0a (builtin/repack.c:
> clean up unused `#include`s, 2025-10-15)
> - jt/16a93c03c7 at (builtin/repo: add progress meter for
> structure stats, 2025-10-21)
> merged into it. This is done to fix a couple of merge conflicts with
> "seen". Both of the topics are only in "seen" right now, but they
> are close to be merged.
> - Link to v2: https://lore.kernel.org/r/20251008-b4-pks-ref-filter-skip-parsing-objects-v2-0-76e30d5c9542@pks.im
>
I had already reviewed version 1, the changes from v2 and v3 look good
to me! :)
Karthik
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v3 00/14] refs: improvements and fixes for peeling tags
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
` (14 preceding siblings ...)
2025-10-22 10:57 ` [PATCH v3 00/14] refs: improvements and fixes for peeling tags Karthik Nayak
@ 2025-10-22 14:47 ` Junio C Hamano
2025-10-23 5:52 ` Patrick Steinhardt
15 siblings, 1 reply; 106+ messages in thread
From: Junio C Hamano @ 2025-10-22 14:47 UTC (permalink / raw)
To: Patrick Steinhardt
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Justin Tobler
Patrick Steinhardt <ps@pks.im> writes:
> Changes in v3:
> - I've rebuilt the topic on 133d151831 (The twenty-first batch, 2025-10-20) with
> - tb/incremental-midx-part-3.1 at 935ab44a0a (builtin/repack.c:
> clean up unused `#include`s, 2025-10-15)
> - jt/16a93c03c7 at (builtin/repo: add progress meter for
> structure stats, 2025-10-21)
> merged into it. This is done to fix a couple of merge conflicts with
> "seen". Both of the topics are only in "seen" right now, but they
> are close to be merged.
The latter reference is weird, but I think I know which topic you
meant, as I just finished preparing a merge-fix to account for that
topic.
Will re-queue.
Thanks.
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v3 14/14] ref-filter: parse objects on demand
2025-10-22 6:41 ` [PATCH v3 14/14] ref-filter: parse objects on demand Patrick Steinhardt
@ 2025-10-22 15:27 ` Junio C Hamano
2025-10-23 6:00 ` Patrick Steinhardt
0 siblings, 1 reply; 106+ messages in thread
From: Junio C Hamano @ 2025-10-22 15:27 UTC (permalink / raw)
To: Patrick Steinhardt
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Justin Tobler
Patrick Steinhardt <ps@pks.im> writes:
> +static int get_or_parse_object(struct expand_data *data, const char *refname,
> + struct object **object, struct strbuf *err, int *eaten)
> +{
> + if (!data->maybe_object) {
> + data->maybe_object = parse_object_buffer(the_repository, &data->oid, data->type,
> + data->size, data->content, eaten);
> + if (!data->maybe_object)
> + return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
> + oid_to_hex(&data->oid), refname);
> + }
> +
> + *object = data->maybe_object;
> + return 0;
> +}
I wonder if the calling convention for this function can use further
ergonomic improvements, after seeing that ...
> + if (!tag) {
> + struct object *object;
> +
> + ret = get_or_parse_object(data, refname, &object, err, eaten);
> + if (ret < 0)
> + return ret;
> +
> + tag = (struct tag *) object;
> + }
... this pattern is repeated three times in the patch. If the
helper cannot legitimately return 0 (success) while stuffing NULL to
the object, then something like this
if (!tag) {
tag = (struct tag *) get_or_parse(data, refname, err, eaten);
if (!tag)
return -1;
}
ought to be sufficient for this caller and the other two. I also
wonder if this can further be simplified, but stopping at the above
is fine to me.
In any case, the overall idea of this step is very much in line with
the original vision of ref-filter.c:populate_value() to try avoiding
unnecessary work as much as possible, and it is very much welcome.
Nice performance improvements.
Will queue. Thanks.
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v3 00/14] refs: improvements and fixes for peeling tags
2025-10-22 14:47 ` Junio C Hamano
@ 2025-10-23 5:52 ` Patrick Steinhardt
0 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 5:52 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Justin Tobler
On Wed, Oct 22, 2025 at 07:47:55AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > Changes in v3:
> > - I've rebuilt the topic on 133d151831 (The twenty-first batch, 2025-10-20) with
> > - tb/incremental-midx-part-3.1 at 935ab44a0a (builtin/repack.c:
> > clean up unused `#include`s, 2025-10-15)
> > - jt/16a93c03c7 at (builtin/repo: add progress meter for
> > structure stats, 2025-10-21)
> > merged into it. This is done to fix a couple of merge conflicts with
> > "seen". Both of the topics are only in "seen" right now, but they
> > are close to be merged.
>
> The latter reference is weird, but I think I know which topic you
> meant, as I just finished preparing a merge-fix to account for that
> topic.
>
> Will re-queue.
Oh, right, that was supposed to be jt/repo-structure. But I see that you
managed to figure that out correctly, thanks!
Patrick
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v3 14/14] ref-filter: parse objects on demand
2025-10-22 15:27 ` Junio C Hamano
@ 2025-10-23 6:00 ` Patrick Steinhardt
0 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 6:00 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Justin Tobler
On Wed, Oct 22, 2025 at 08:27:39AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > +static int get_or_parse_object(struct expand_data *data, const char *refname,
> > + struct object **object, struct strbuf *err, int *eaten)
> > +{
> > + if (!data->maybe_object) {
> > + data->maybe_object = parse_object_buffer(the_repository, &data->oid, data->type,
> > + data->size, data->content, eaten);
> > + if (!data->maybe_object)
> > + return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
> > + oid_to_hex(&data->oid), refname);
> > + }
> > +
> > + *object = data->maybe_object;
> > + return 0;
> > +}
>
> I wonder if the calling convention for this function can use further
> ergonomic improvements, after seeing that ...
>
> > + if (!tag) {
> > + struct object *object;
> > +
> > + ret = get_or_parse_object(data, refname, &object, err, eaten);
> > + if (ret < 0)
> > + return ret;
> > +
> > + tag = (struct tag *) object;
> > + }
>
> ... this pattern is repeated three times in the patch. If the
> helper cannot legitimately return 0 (success) while stuffing NULL to
> the object, then something like this
>
> if (!tag) {
> tag = (struct tag *) get_or_parse(data, refname, err, eaten);
> if (!tag)
> return -1;
> }
>
> ought to be sufficient for this caller and the other two. I also
> wonder if this can further be simplified, but stopping at the above
> is fine to me.
Good suggestion indeed. Let me use this style and send another version.
Thanks!
Patrick
^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v4 00/14] refs: improvements and fixes for peeling tags
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
` (16 preceding siblings ...)
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
@ 2025-10-23 7:16 ` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 01/14] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
` (14 more replies)
17 siblings, 15 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 7:16 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
Hi,
originally, all I wanted to do was the last patch: a small performance
optimization that stops parsing objects in git-for-each-ref(1) unless we
really need to parse them. But that fix cause one specific test to fail,
and only with the reftable backend. So this led me down the rabbit hole
of tag peeling, ending up with this patch series.
The series is structured like follows:
- Patches 1 to 8 refactor our codebase so that we don't have the
`peel_iterated_object()` hack anymore. I just found it hard to
follow and thought it shouldn't be too hard to get rid of it.
- Patches 9 and 10 remove infrastructure that we don't need anymore
after the first couple of patches.
- Patches 11 to 13 fix a couple of issues with peeled tags that I
found. The underlying issue is that tags store both the tagged
object and their type, but this information may not match. We never
verify the actual object type though when allocating the tagged
object, so this only blows up much later.
- Patch 14 was my original motivation, a small performance
optimization.
I'm not particularly fond of the patches 11 to 13. It feels more like
playing whack-a-mole, and I very much assume that there still are edge
cases where we should properly verify the tagged object type. But
changing it in `parse_tag_buffer()` itself causes a bunch of tests to
fail where we intentionally create such corrupted tags. So I didn't
really dare to touch that part, to be honest.
If anybody has suggestions for an alternative approach I'd be very open
to it.
Changes in v4:
- Improve `get_or_parse_object()` to have better ergonomics.
- Link to v3: https://lore.kernel.org/r/20251022-b4-pks-ref-filter-skip-parsing-objects-v3-0-eb9f71985ef0@pks.im
Changes in v3:
- I've rebuilt the topic on 133d151831 (The twenty-first batch, 2025-10-20) with
- tb/incremental-midx-part-3.1 at 935ab44a0a (builtin/repack.c:
clean up unused `#include`s, 2025-10-15)
- jt/repo-structure 16a93c03c7 at (builtin/repo: add progress meter for
structure stats, 2025-10-21)
merged into it. This is done to fix a couple of merge conflicts with
"seen". Both of the topics are only in "seen" right now, but they
are close to be merged.
- Link to v2: https://lore.kernel.org/r/20251008-b4-pks-ref-filter-skip-parsing-objects-v2-0-76e30d5c9542@pks.im
Changes in v2:
- A couple of improvements to commit messages.
- A new commit that ensures that `struct ref_iterator::ref` is always
zeroed out to protect against stale state.
- Link to v1: https://lore.kernel.org/r/20251007-b4-pks-ref-filter-skip-parsing-objects-v1-0-916cc7c6886b@pks.im
Thanks!
Patrick
---
Patrick Steinhardt (14):
refs: introduce wrapper struct for `each_ref_fn`
refs: introduce `.ref` field for the base iterator
refs: fully reset `struct ref_iterator::ref` on iteration
refs: refactor reference status flags
refs: expose peeled object ID via the iterator
upload-pack: convert to use `reference_get_peeled_oid()`
ref-filter: propagate peeled object ID
builtin/show-ref: convert to use `reference_get_peeled_oid()`
refs: drop `current_ref_iter` hack
refs: drop infrastructure to peel via iterators
object: add flag to `peel_object()` to verify object type
refs: don't store peeled object IDs for invalid tags
ref-filter: detect broken tags when dereferencing them
ref-filter: parse objects on demand
bisect.c | 24 ++---
builtin/bisect.c | 17 +---
builtin/checkout.c | 6 +-
builtin/describe.c | 18 ++--
builtin/fetch.c | 13 +--
builtin/fsck.c | 33 ++++---
builtin/gc.c | 15 ++-
builtin/ls-remote.c | 2 +-
builtin/name-rev.c | 17 ++--
builtin/pack-objects.c | 28 +++---
builtin/receive-pack.c | 13 ++-
builtin/remote.c | 44 ++++-----
builtin/replace.c | 21 ++---
builtin/repo.c | 9 +-
builtin/rev-parse.c | 12 +--
builtin/show-branch.c | 35 ++++---
builtin/show-ref.c | 50 +++++-----
builtin/submodule--helper.c | 10 +-
builtin/tag.c | 2 +-
builtin/verify-tag.c | 2 +-
builtin/worktree.c | 6 +-
commit-graph.c | 14 ++-
delta-islands.c | 9 +-
fetch-pack.c | 16 +---
help.c | 10 +-
http-backend.c | 20 ++--
log-tree.c | 24 +++--
ls-refs.c | 36 ++++---
midx-write.c | 17 ++--
negotiator/default.c | 7 +-
negotiator/skipping.c | 7 +-
notes.c | 8 +-
object-name.c | 10 +-
object.c | 20 +++-
object.h | 15 ++-
pseudo-merge.c | 21 ++---
reachable.c | 9 +-
ref-filter.c | 225 ++++++++++++++++++++++++++++++--------------
ref-filter.h | 5 +-
reflog.c | 9 +-
refs.c | 85 +++++++++--------
refs.h | 88 ++++++++++-------
refs/debug.c | 17 +---
refs/files-backend.c | 71 +++++---------
refs/iterator.c | 73 +++-----------
refs/packed-backend.c | 71 +++++---------
refs/ref-cache.c | 18 +---
refs/refs-internal.h | 25 +----
refs/reftable-backend.c | 47 +++------
remote.c | 27 +++---
repack-midx.c | 16 ++--
replace-object.c | 16 ++--
revision.c | 12 +--
server-info.c | 12 +--
shallow.c | 16 +---
submodule.c | 12 +--
t/for-each-ref-tests.sh | 4 +-
t/helper/test-reach.c | 2 +-
t/helper/test-ref-store.c | 5 +-
t/pack-refs-tests.sh | 32 +++++++
t/t0610-reftable-basics.sh | 28 ++++++
tag.c | 12 ---
tag.h | 1 -
upload-pack.c | 49 +++++-----
walker.c | 8 +-
worktree.c | 11 ++-
66 files changed, 783 insertions(+), 834 deletions(-)
Range-diff versus v3:
1: 3c95bfae6a0 = 1: a8aafa4392b refs: introduce wrapper struct for `each_ref_fn`
2: f8a24267571 = 2: cf5faae21bd refs: introduce `.ref` field for the base iterator
3: e4e576977bf = 3: c0715cf44d8 refs: fully reset `struct ref_iterator::ref` on iteration
4: ea00af0721f = 4: 52f65b25e89 refs: refactor reference status flags
5: c3254472ddc = 5: 230cc4e7431 refs: expose peeled object ID via the iterator
6: 9df9622a93c = 6: bca033ba86d upload-pack: convert to use `reference_get_peeled_oid()`
7: b09a02a7f53 = 7: fa914f4cede ref-filter: propagate peeled object ID
8: 160788e6a7c = 8: 1d9ba79bcf7 builtin/show-ref: convert to use `reference_get_peeled_oid()`
9: f5793b2f1af = 9: 8451059caff refs: drop `current_ref_iter` hack
10: a467e8f55d1 = 10: de3e67c31d2 refs: drop infrastructure to peel via iterators
11: 49f45d1d680 = 11: 8782d6729df object: add flag to `peel_object()` to verify object type
12: 4e355684a9a = 12: ba7f85c8fef refs: don't store peeled object IDs for invalid tags
13: 1f709cd4b9c = 13: 74ad4413535 ref-filter: detect broken tags when dereferencing them
14: ef24fc33852 ! 14: e833e9943e2 ref-filter: parse objects on demand
@@ ref-filter.c: static void grab_common_values(struct atom_value *val, int deref,
}
}
-+static int get_or_parse_object(struct expand_data *data, const char *refname,
-+ struct object **object, struct strbuf *err, int *eaten)
++static struct object *get_or_parse_object(struct expand_data *data, const char *refname,
++ struct strbuf *err, int *eaten)
+{
+ if (!data->maybe_object) {
+ data->maybe_object = parse_object_buffer(the_repository, &data->oid, data->type,
+ data->size, data->content, eaten);
-+ if (!data->maybe_object)
-+ return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
-+ oid_to_hex(&data->oid), refname);
++ if (!data->maybe_object) {
++ strbuf_addf(err, _("parse_object_buffer failed on %s for %s"),
++ oid_to_hex(&data->oid), refname);
++ return NULL;
++ }
+ }
+
-+ *object = data->maybe_object;
-+ return 0;
++ return data->maybe_object;
+}
+
/* See grab_values */
@@ ref-filter.c: static void grab_common_values(struct atom_value *val, int deref,
+ struct expand_data *data, const char *refname,
+ struct strbuf *err, int *eaten)
{
-- int i;
-- struct tag *tag = (struct tag *) obj;
+ struct tag *tag = NULL;
-+ int i, ret;
+ int i;
+- struct tag *tag = (struct tag *) obj;
for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i].name;
@@ ref-filter.c: static void grab_tag_values(struct atom_value *val, int deref, str
continue;
+
+ if (!tag) {
-+ struct object *object;
-+
-+ ret = get_or_parse_object(data, refname, &object, err, eaten);
-+ if (ret < 0)
-+ return ret;
-+
-+ tag = (struct tag *) object;
++ tag = (struct tag *) get_or_parse_object(data, refname,
++ err, eaten);
++ if (!tag)
++ return -1;
+ }
+
if (deref)
@@ ref-filter.c: static void grab_tag_values(struct atom_value *val, int deref, str
+ struct expand_data *data, const char *refname,
+ struct strbuf *err, int *eaten)
{
-- int i;
+ int i;
- struct commit *commit = (struct commit *) obj;
-+ int i, ret;
+ struct commit *commit = NULL;
for (i = 0; i < used_atom_cnt; i++) {
@@ ref-filter.c: static void grab_tag_values(struct atom_value *val, int deref, str
name++;
+
+ if (!commit) {
-+ struct object *object;
-+
-+ ret = get_or_parse_object(data, refname, &object, err, eaten);
-+ if (ret < 0)
-+ return ret;
-+
-+ commit = (struct commit *) object;
++ commit = (struct commit *) get_or_parse_object(data, refname,
++ err, eaten);
++ if (!commit)
++ return -1;
+ }
+
if (atom_type == ATOM_TREE &&
@@ ref-filter.c: static void grab_person(const char *who, struct atom_value *val, i
+ struct expand_data *data, const char *refname,
+ struct strbuf *err, int *eaten)
{
-- int i;
+ int i;
- struct commit *commit = (struct commit *) obj;
-+ int i, ret;
+ struct commit *commit = NULL;
struct signature_check sigc = { 0 };
int signature_checked = 0;
@@ ref-filter.c: static void grab_signature(struct atom_value *val, int deref, stru
if (!signature_checked) {
+ if (!commit) {
-+ struct object *object;
-+
-+ ret = get_or_parse_object(data, refname, &object, err, eaten);
-+ if (ret < 0)
-+ return ret;
-+
-+ commit = (struct commit *) object;
++ commit = (struct commit *) get_or_parse_object(data, refname,
++ err, eaten);
++ if (!commit)
++ return -1;
+ }
+
check_commit_signature(commit, &sigc);
---
base-commit: 5c120f01eb88f4be8b06fd4bc6893763204b78c4
change-id: 20250918-b4-pks-ref-filter-skip-parsing-objects-f0d1f6af4a9f
^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v4 01/14] refs: introduce wrapper struct for `each_ref_fn`
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
@ 2025-10-23 7:16 ` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 02/14] refs: introduce `.ref` field for the base iterator Patrick Steinhardt
` (13 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 7:16 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
The `each_ref_fn` callback function type is used across our code base
for several different functions that iterate through reference. There's
a bunch of callbacks implementing this type, which makes any changes to
the callback signature extremely noisy. An example of the required churn
is e8207717f1 (refs: add referent to each_ref_fn, 2024-08-09): adding a
single argument required us to change 48 files.
It was already proposed back then [1] that we might want to introduce a
wrapper structure to alleviate the pain going forward. While this of
course requires the same kind of global refactoring as just introducing
a new parameter, it at least allows us to more change the callback type
afterwards by just extending the wrapper structure.
One counterargument to this refactoring is that it makes the structure
more opaque. While it is obvious which callsites need to be fixed up
when we change the function type, it's not obvious anymore once we use
a structure. That being said, we only have a handful of sites that
actually need to populate this wrapper structure: our ref backends,
"refs/iterator.c" as well as very few sites that invoke the iterator
callback functions directly.
Introduce this wrapper structure so that we can adapt the iterator
interfaces more readily.
[1]: <ZmarVcF5JjsZx0dl@tanuki>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
bisect.c | 24 +++++++---------
builtin/bisect.c | 17 ++++--------
builtin/checkout.c | 6 ++--
builtin/describe.c | 18 ++++++------
builtin/fetch.c | 13 +++------
builtin/fsck.c | 33 ++++++++++++----------
builtin/gc.c | 15 ++++------
builtin/name-rev.c | 17 ++++++------
builtin/pack-objects.c | 27 ++++++++----------
builtin/receive-pack.c | 13 ++++-----
builtin/remote.c | 44 +++++++++++++----------------
builtin/replace.c | 21 ++++++--------
builtin/repo.c | 9 ++----
builtin/rev-parse.c | 12 ++++----
builtin/show-branch.c | 35 +++++++++++------------
builtin/show-ref.c | 20 ++++++--------
builtin/submodule--helper.c | 10 ++-----
builtin/worktree.c | 6 +---
commit-graph.c | 14 ++++------
delta-islands.c | 9 +++---
fetch-pack.c | 16 +++--------
help.c | 10 +++----
http-backend.c | 20 ++++++--------
log-tree.c | 24 ++++++++--------
ls-refs.c | 36 ++++++++++++++----------
midx-write.c | 17 ++++++------
negotiator/default.c | 7 ++---
negotiator/skipping.c | 7 ++---
notes.c | 8 ++----
object-name.c | 10 +++----
pseudo-merge.c | 21 ++++++--------
reachable.c | 9 +++---
ref-filter.c | 24 ++++++++--------
reflog.c | 9 ++----
refs.c | 67 +++++++++++++++++++++++++--------------------
refs.h | 26 +++++++++++++++---
refs/files-backend.c | 7 ++---
refs/iterator.c | 9 +++++-
remote.c | 27 ++++++++----------
repack-midx.c | 16 +++++------
replace-object.c | 16 ++++-------
revision.c | 12 ++++----
server-info.c | 12 ++++----
shallow.c | 16 +++--------
submodule.c | 12 ++------
t/helper/test-ref-store.c | 5 ++--
upload-pack.c | 29 +++++++++-----------
walker.c | 8 ++----
worktree.c | 11 ++++++--
49 files changed, 392 insertions(+), 462 deletions(-)
diff --git a/bisect.c b/bisect.c
index a6dc76b15c9..326b59c0dc7 100644
--- a/bisect.c
+++ b/bisect.c
@@ -450,21 +450,20 @@ void find_bisection(struct commit_list **commit_list, int *reaches,
clear_commit_weight(&commit_weight);
}
-static int register_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb_data UNUSED)
+static int register_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct strbuf good_prefix = STRBUF_INIT;
strbuf_addstr(&good_prefix, term_good);
strbuf_addstr(&good_prefix, "-");
- if (!strcmp(refname, term_bad)) {
+ if (!strcmp(ref->name, term_bad)) {
free(current_bad_oid);
current_bad_oid = xmalloc(sizeof(*current_bad_oid));
- oidcpy(current_bad_oid, oid);
- } else if (starts_with(refname, good_prefix.buf)) {
- oid_array_append(&good_revs, oid);
- } else if (starts_with(refname, "skip-")) {
- oid_array_append(&skipped_revs, oid);
+ oidcpy(current_bad_oid, ref->oid);
+ } else if (starts_with(ref->name, good_prefix.buf)) {
+ oid_array_append(&good_revs, ref->oid);
+ } else if (starts_with(ref->name, "skip-")) {
+ oid_array_append(&skipped_revs, ref->oid);
}
strbuf_release(&good_prefix);
@@ -1178,14 +1177,11 @@ int estimate_bisect_steps(int all)
return (e < 3 * x) ? n : n - 1;
}
-static int mark_for_removal(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cb_data)
+static int mark_for_removal(const struct reference *ref, void *cb_data)
{
struct string_list *refs = cb_data;
- char *ref = xstrfmt("refs/bisect%s", refname);
- string_list_append(refs, ref);
+ char *bisect_ref = xstrfmt("refs/bisect%s", ref->name);
+ string_list_append(refs, bisect_ref);
return 0;
}
diff --git a/builtin/bisect.c b/builtin/bisect.c
index 8b8d870cd1e..5b2024be62d 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -358,10 +358,7 @@ static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)
return 0;
}
-static int inc_nr(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cb_data)
+static int inc_nr(const struct reference *ref UNUSED, void *cb_data)
{
unsigned int *nr = (unsigned int *)cb_data;
(*nr)++;
@@ -549,12 +546,11 @@ static int bisect_append_log_quoted(const char **argv)
return res;
}
-static int add_bisect_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb)
+static int add_bisect_ref(const struct reference *ref, void *cb)
{
struct add_bisect_ref_data *data = cb;
- add_pending_oid(data->revs, refname, oid, data->object_flags);
+ add_pending_oid(data->revs, ref->name, ref->oid, data->object_flags);
return 0;
}
@@ -1165,12 +1161,9 @@ static int bisect_visualize(struct bisect_terms *terms, int argc,
return run_command(&cmd);
}
-static int get_first_good(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int get_first_good(const struct reference *ref, void *cb_data)
{
- oidcpy(cb_data, oid);
+ oidcpy(cb_data, ref->oid);
return 1;
}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f9453473fe2..66b69df6e67 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1063,11 +1063,9 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
report_tracking(new_branch_info);
}
-static int add_pending_uninteresting_ref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int add_pending_uninteresting_ref(const struct reference *ref, void *cb_data)
{
- add_pending_oid(cb_data, refname, oid, UNINTERESTING);
+ add_pending_oid(cb_data, ref->name, ref->oid, UNINTERESTING);
return 0;
}
diff --git a/builtin/describe.c b/builtin/describe.c
index ffaf8d9f0aa..79545350443 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -154,20 +154,19 @@ static void add_to_known_names(const char *path,
}
}
-static int get_name(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int get_name(const struct reference *ref, void *cb_data UNUSED)
{
int is_tag = 0;
struct object_id peeled;
int is_annotated, prio;
const char *path_to_match = NULL;
- if (skip_prefix(path, "refs/tags/", &path_to_match)) {
+ if (skip_prefix(ref->name, "refs/tags/", &path_to_match)) {
is_tag = 1;
} else if (all) {
if ((exclude_patterns.nr || patterns.nr) &&
- !skip_prefix(path, "refs/heads/", &path_to_match) &&
- !skip_prefix(path, "refs/remotes/", &path_to_match)) {
+ !skip_prefix(ref->name, "refs/heads/", &path_to_match) &&
+ !skip_prefix(ref->name, "refs/remotes/", &path_to_match)) {
/* Only accept reference of known type if there are match/exclude patterns */
return 0;
}
@@ -209,10 +208,10 @@ static int get_name(const char *path, const char *referent UNUSED, const struct
}
/* Is it annotated? */
- if (!peel_iterated_oid(the_repository, oid, &peeled)) {
- is_annotated = !oideq(oid, &peeled);
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled)) {
+ is_annotated = !oideq(ref->oid, &peeled);
} else {
- oidcpy(&peeled, oid);
+ oidcpy(&peeled, ref->oid);
is_annotated = 0;
}
@@ -229,7 +228,8 @@ static int get_name(const char *path, const char *referent UNUSED, const struct
else
prio = 0;
- add_to_known_names(all ? path + 5 : path + 10, &peeled, prio, oid);
+ add_to_known_names(all ? ref->name + 5 : ref->name + 10,
+ &peeled, prio, ref->oid);
return 0;
}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index c7ff3480fb1..7052e6ff215 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -289,13 +289,11 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,
return ent;
}
-static int add_one_refname(const char *refname, const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *cbdata)
+static int add_one_refname(const struct reference *ref, void *cbdata)
{
struct hashmap *refname_map = cbdata;
- (void) refname_hash_add(refname_map, refname, oid);
+ (void) refname_hash_add(refname_map, ref->name, ref->oid);
return 0;
}
@@ -1416,14 +1414,11 @@ static void set_option(struct transport *transport, const char *name, const char
}
-static int add_oid(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int add_oid(const struct reference *ref, void *cb_data)
{
struct oid_array *oids = cb_data;
- oid_array_append(oids, oid);
+ oid_array_append(oids, ref->oid);
return 0;
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 8ee95e0d67c..ed4eea16803 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -530,14 +530,13 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)
return 0;
}
-static int fsck_handle_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object *obj;
- obj = parse_object(the_repository, oid);
+ obj = parse_object(the_repository, ref->oid);
if (!obj) {
- if (is_promisor_object(the_repository, oid)) {
+ if (is_promisor_object(the_repository, ref->oid)) {
/*
* Increment default_refs anyway, because this is a
* valid ref.
@@ -546,19 +545,19 @@ static int fsck_handle_ref(const char *refname, const char *referent UNUSED, con
return 0;
}
error(_("%s: invalid sha1 pointer %s"),
- refname, oid_to_hex(oid));
+ ref->name, oid_to_hex(ref->oid));
errors_found |= ERROR_REACHABLE;
/* We'll continue with the rest despite the error.. */
return 0;
}
- if (obj->type != OBJ_COMMIT && is_branch(refname)) {
- error(_("%s: not a commit"), refname);
+ if (obj->type != OBJ_COMMIT && is_branch(ref->name)) {
+ error(_("%s: not a commit"), ref->name);
errors_found |= ERROR_REFS;
}
default_refs++;
obj->flags |= USED;
fsck_put_object_name(&fsck_walk_options,
- oid, "%s", refname);
+ ref->oid, "%s", ref->name);
mark_object_reachable(obj);
return 0;
@@ -580,13 +579,19 @@ static void get_default_heads(void)
worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
- struct strbuf ref = STRBUF_INIT;
+ struct strbuf refname = STRBUF_INIT;
- strbuf_worktree_ref(wt, &ref, "HEAD");
- fsck_head_link(ref.buf, &head_points_at, &head_oid);
- if (head_points_at && !is_null_oid(&head_oid))
- fsck_handle_ref(ref.buf, NULL, &head_oid, 0, NULL);
- strbuf_release(&ref);
+ strbuf_worktree_ref(wt, &refname, "HEAD");
+ fsck_head_link(refname.buf, &head_points_at, &head_oid);
+ if (head_points_at && !is_null_oid(&head_oid)) {
+ struct reference ref = {
+ .name = refname.buf,
+ .oid = &head_oid,
+ };
+
+ fsck_handle_ref(&ref, NULL);
+ }
+ strbuf_release(&refname);
if (include_reflogs)
refs_for_each_reflog(get_worktree_ref_store(wt),
diff --git a/builtin/gc.c b/builtin/gc.c
index e19e13d9788..9de5de175f6 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -1100,24 +1100,21 @@ struct cg_auto_data {
int limit;
};
-static int dfs_on_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int dfs_on_ref(const struct reference *ref, void *cb_data)
{
struct cg_auto_data *data = (struct cg_auto_data *)cb_data;
int result = 0;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct commit_list *stack = NULL;
struct commit *commit;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
- if (odb_read_object_info(the_repository->objects, oid, NULL) != OBJ_COMMIT)
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ maybe_peeled = &peeled;
+ if (odb_read_object_info(the_repository->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
- commit = lookup_commit(the_repository, oid);
+ commit = lookup_commit(the_repository, maybe_peeled);
if (!commit)
return 0;
if (repo_parse_commit(the_repository, commit) ||
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 74512e54a38..615f7d1aae4 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -339,10 +339,9 @@ static int cmp_by_tag_and_age(const void *a_, const void *b_)
return a->taggerdate != b->taggerdate;
}
-static int name_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int name_ref(const struct reference *ref, void *cb_data)
{
- struct object *o = parse_object(the_repository, oid);
+ struct object *o = parse_object(the_repository, ref->oid);
struct name_ref_data *data = cb_data;
int can_abbreviate_output = data->tags_only && data->name_only;
int deref = 0;
@@ -350,14 +349,14 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
struct commit *commit = NULL;
timestamp_t taggerdate = TIME_MAX;
- if (data->tags_only && !starts_with(path, "refs/tags/"))
+ if (data->tags_only && !starts_with(ref->name, "refs/tags/"))
return 0;
if (data->exclude_filters.nr) {
struct string_list_item *item;
for_each_string_list_item(item, &data->exclude_filters) {
- if (subpath_matches(path, item->string) >= 0)
+ if (subpath_matches(ref->name, item->string) >= 0)
return 0;
}
}
@@ -378,7 +377,7 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
* shouldn't stop when seeing 'refs/tags/v1.4' matches
* 'refs/tags/v*'. We should show it as 'v1.4'.
*/
- switch (subpath_matches(path, item->string)) {
+ switch (subpath_matches(ref->name, item->string)) {
case -1: /* did not match */
break;
case 0: /* matched fully */
@@ -406,13 +405,13 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
}
if (o && o->type == OBJ_COMMIT) {
commit = (struct commit *)o;
- from_tag = starts_with(path, "refs/tags/");
+ from_tag = starts_with(ref->name, "refs/tags/");
if (taggerdate == TIME_MAX)
taggerdate = commit->date;
}
- add_to_tip_table(oid, path, can_abbreviate_output, commit, taggerdate,
- from_tag, deref);
+ add_to_tip_table(ref->oid, ref->name, can_abbreviate_output,
+ commit, taggerdate, from_tag, deref);
return 0;
}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 5bdc44fb2de..39633a0158e 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -831,15 +831,14 @@ static enum write_one_status write_one(struct hashfile *f,
return WRITE_ONE_WRITTEN;
}
-static int mark_tagged(const char *path UNUSED, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int mark_tagged(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
- struct object_entry *entry = packlist_find(&to_pack, oid);
+ struct object_entry *entry = packlist_find(&to_pack, ref->oid);
if (entry)
entry->tagged = 1;
- if (!peel_iterated_oid(the_repository, oid, &peeled)) {
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled)) {
entry = packlist_find(&to_pack, &peeled);
if (entry)
entry->tagged = 1;
@@ -3306,13 +3305,12 @@ static void add_tag_chain(const struct object_id *oid)
}
}
-static int add_ref_tag(const char *tag UNUSED, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int add_ref_tag(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, oid, &peeled) && obj_is_packed(&peeled))
- add_tag_chain(oid);
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled) && obj_is_packed(&peeled))
+ add_tag_chain(ref->oid);
return 0;
}
@@ -4533,19 +4531,16 @@ static void record_recent_commit(struct commit *commit, void *data UNUSED)
oid_array_append(&recent_objects, &commit->object.oid);
}
-static int mark_bitmap_preferred_tip(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *data UNUSED)
+static int mark_bitmap_preferred_tip(const struct reference *ref, void *data UNUSED)
{
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct object *object;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ maybe_peeled = &peeled;
- object = parse_object_or_die(the_repository, oid, refname);
+ object = parse_object_or_die(the_repository, maybe_peeled, ref->name);
if (object->type == OBJ_COMMIT)
object->flags |= NEEDS_BITMAP;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index c9288a9c7e3..e8ee0e73217 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -305,13 +305,12 @@ static void show_ref(const char *path, const struct object_id *oid)
}
}
-static int show_ref_cb(const char *path_full, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *data)
+static int show_ref_cb(const struct reference *ref, void *data)
{
struct oidset *seen = data;
- const char *path = strip_namespace(path_full);
+ const char *path = strip_namespace(ref->name);
- if (ref_is_hidden(path, path_full, &hidden_refs))
+ if (ref_is_hidden(path, ref->name, &hidden_refs))
return 0;
/*
@@ -320,13 +319,13 @@ static int show_ref_cb(const char *path_full, const char *referent UNUSED, const
* transfer but will otherwise ignore them.
*/
if (!path) {
- if (oidset_insert(seen, oid))
+ if (oidset_insert(seen, ref->oid))
return 0;
path = ".have";
} else {
- oidset_insert(seen, oid);
+ oidset_insert(seen, ref->oid);
}
- show_ref(path, oid);
+ show_ref(path, ref->oid);
return 0;
}
diff --git a/builtin/remote.c b/builtin/remote.c
index 8a7ed4299a4..7ffc14ba157 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -570,17 +570,14 @@ struct branches_for_remote {
struct known_remotes *keep;
};
-static int add_branch_for_removal(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED, void *cb_data)
+static int add_branch_for_removal(const struct reference *ref, void *cb_data)
{
struct branches_for_remote *branches = cb_data;
struct refspec_item refspec;
struct known_remote *kr;
memset(&refspec, 0, sizeof(refspec));
- refspec.dst = (char *)refname;
+ refspec.dst = (char *)ref->name;
if (remote_find_tracking(branches->remote, &refspec))
return 0;
free(refspec.src);
@@ -588,7 +585,7 @@ static int add_branch_for_removal(const char *refname,
/* don't delete a branch if another remote also uses it */
for (kr = branches->keep->list; kr; kr = kr->next) {
memset(&refspec, 0, sizeof(refspec));
- refspec.dst = (char *)refname;
+ refspec.dst = (char *)ref->name;
if (!remote_find_tracking(kr->remote, &refspec)) {
free(refspec.src);
return 0;
@@ -596,16 +593,16 @@ static int add_branch_for_removal(const char *refname,
}
/* don't delete non-remote-tracking refs */
- if (!starts_with(refname, "refs/remotes/")) {
+ if (!starts_with(ref->name, "refs/remotes/")) {
/* advise user how to delete local branches */
- if (starts_with(refname, "refs/heads/"))
+ if (starts_with(ref->name, "refs/heads/"))
string_list_append(branches->skipped,
- abbrev_branch(refname));
+ abbrev_branch(ref->name));
/* silently skip over other non-remote refs */
return 0;
}
- string_list_append(branches->branches, refname);
+ string_list_append(branches->branches, ref->name);
return 0;
}
@@ -713,18 +710,18 @@ static int rename_one_reflog(const char *old_refname,
return error;
}
-static int rename_one_ref(const char *old_refname, const char *referent,
- const struct object_id *oid,
- int flags, void *cb_data)
+static int rename_one_ref(const struct reference *ref, void *cb_data)
{
struct strbuf new_referent = STRBUF_INIT;
struct strbuf new_refname = STRBUF_INIT;
struct rename_info *rename = cb_data;
+ const struct object_id *oid = ref->oid;
+ const char *referent = ref->target;
int error;
- compute_renamed_ref(rename, old_refname, &new_refname);
+ compute_renamed_ref(rename, ref->name, &new_refname);
- if (flags & REF_ISSYMREF) {
+ if (ref->flags & REF_ISSYMREF) {
/*
* Stupidly enough `referent` is not pointing to the immediate
* target of a symref, but it's the recursively resolved value.
@@ -732,25 +729,25 @@ static int rename_one_ref(const char *old_refname, const char *referent,
* unborn symrefs don't have any value for the `referent` at all.
*/
referent = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- old_refname, RESOLVE_REF_NO_RECURSE,
+ ref->name, RESOLVE_REF_NO_RECURSE,
NULL, NULL);
compute_renamed_ref(rename, referent, &new_referent);
oid = NULL;
}
- error = ref_transaction_delete(rename->transaction, old_refname,
+ error = ref_transaction_delete(rename->transaction, ref->name,
oid, referent, REF_NO_DEREF, NULL, rename->err);
if (error < 0)
goto out;
error = ref_transaction_update(rename->transaction, new_refname.buf, oid, null_oid(the_hash_algo),
- (flags & REF_ISSYMREF) ? new_referent.buf : NULL, NULL,
+ (ref->flags & REF_ISSYMREF) ? new_referent.buf : NULL, NULL,
REF_SKIP_CREATE_REFLOG | REF_NO_DEREF | REF_SKIP_OID_VERIFICATION,
NULL, rename->err);
if (error < 0)
goto out;
- error = rename_one_reflog(old_refname, oid, rename);
+ error = rename_one_reflog(ref->name, oid, rename);
if (error < 0)
goto out;
@@ -1125,19 +1122,16 @@ static void free_remote_ref_states(struct ref_states *states)
string_list_clear_func(&states->push, clear_push_info);
}
-static int append_ref_to_tracked_list(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags, void *cb_data)
+static int append_ref_to_tracked_list(const struct reference *ref, void *cb_data)
{
struct ref_states *states = cb_data;
struct refspec_item refspec;
- if (flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
return 0;
memset(&refspec, 0, sizeof(refspec));
- refspec.dst = (char *)refname;
+ refspec.dst = (char *)ref->name;
if (!remote_find_tracking(states->remote, &refspec)) {
string_list_append(&states->tracked, abbrev_branch(refspec.src));
free(refspec.src);
diff --git a/builtin/replace.c b/builtin/replace.c
index 900b560a77d..4c62c5ab58b 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -47,30 +47,27 @@ struct show_data {
enum replace_format format;
};
-static int show_reference(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int show_reference(const struct reference *ref, void *cb_data)
{
struct show_data *data = cb_data;
- if (!wildmatch(data->pattern, refname, 0)) {
+ if (!wildmatch(data->pattern, ref->name, 0)) {
if (data->format == REPLACE_FORMAT_SHORT)
- printf("%s\n", refname);
+ printf("%s\n", ref->name);
else if (data->format == REPLACE_FORMAT_MEDIUM)
- printf("%s -> %s\n", refname, oid_to_hex(oid));
+ printf("%s -> %s\n", ref->name, oid_to_hex(ref->oid));
else { /* data->format == REPLACE_FORMAT_LONG */
struct object_id object;
enum object_type obj_type, repl_type;
- if (repo_get_oid(data->repo, refname, &object))
- return error(_("failed to resolve '%s' as a valid ref"), refname);
+ if (repo_get_oid(data->repo, ref->name, &object))
+ return error(_("failed to resolve '%s' as a valid ref"), ref->name);
obj_type = odb_read_object_info(data->repo->objects, &object, NULL);
- repl_type = odb_read_object_info(data->repo->objects, oid, NULL);
+ repl_type = odb_read_object_info(data->repo->objects, ref->oid, NULL);
- printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
- oid_to_hex(oid), type_name(repl_type));
+ printf("%s (%s) -> %s (%s)\n", ref->name, type_name(obj_type),
+ oid_to_hex(ref->oid), type_name(repl_type));
}
}
diff --git a/builtin/repo.c b/builtin/repo.c
index 9d4749f79be..f26640bd6ea 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -366,16 +366,13 @@ struct count_references_data {
struct progress *progress;
};
-static int count_references(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int count_references(const struct reference *ref, void *cb_data)
{
struct count_references_data *data = cb_data;
struct ref_stats *stats = data->stats;
size_t ref_count;
- switch (ref_kind_from_refname(refname)) {
+ switch (ref_kind_from_refname(ref->name)) {
case FILTER_REFS_BRANCHES:
stats->branches++;
break;
@@ -396,7 +393,7 @@ static int count_references(const char *refname,
* While iterating through references for counting, also add OIDs in
* preparation for the path walk.
*/
- add_pending_oid(data->revs, NULL, oid, 0);
+ add_pending_oid(data->revs, NULL, ref->oid, 0);
ref_count = get_total_reference_count(stats);
display_progress(data->progress, ref_count);
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 9da92b990d0..3578591b4f2 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -217,19 +217,17 @@ static int show_default(void)
return 0;
}
-static int show_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int show_reference(const struct reference *ref, void *cb_data UNUSED)
{
- if (ref_excluded(&ref_excludes, refname))
+ if (ref_excluded(&ref_excludes, ref->name))
return 0;
- show_rev(NORMAL, oid, refname);
+ show_rev(NORMAL, ref->oid, ref->name);
return 0;
}
-static int anti_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int anti_reference(const struct reference *ref, void *cb_data UNUSED)
{
- show_rev(REVERSED, oid, refname);
+ show_rev(REVERSED, ref->oid, ref->name);
return 0;
}
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 441babf2e35..10475a6b5ed 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -413,34 +413,32 @@ static int append_ref(const char *refname, const struct object_id *oid,
return 0;
}
-static int append_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int append_head_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id tmp;
int ofs = 11;
- if (!starts_with(refname, "refs/heads/"))
+ if (!starts_with(ref->name, "refs/heads/"))
return 0;
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
+ if (repo_get_oid(the_repository, ref->name + ofs, &tmp) || !oideq(&tmp, ref->oid))
ofs = 5;
- return append_ref(refname + ofs, oid, 0);
+ return append_ref(ref->name + ofs, ref->oid, 0);
}
-static int append_remote_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int append_remote_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id tmp;
int ofs = 13;
- if (!starts_with(refname, "refs/remotes/"))
+ if (!starts_with(ref->name, "refs/remotes/"))
return 0;
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
+ if (repo_get_oid(the_repository, ref->name + ofs, &tmp) || !oideq(&tmp, ref->oid))
ofs = 5;
- return append_ref(refname + ofs, oid, 0);
+ return append_ref(ref->name + ofs, ref->oid, 0);
}
static int append_tag_ref(const char *refname, const struct object_id *oid,
@@ -454,27 +452,26 @@ static int append_tag_ref(const char *refname, const struct object_id *oid,
static const char *match_ref_pattern = NULL;
static int match_ref_slash = 0;
-static int append_matching_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int append_matching_ref(const struct reference *ref, void *cb_data)
{
/* we want to allow pattern hold/<asterisk> to show all
* branches under refs/heads/hold/, and v0.99.9? to show
* refs/tags/v0.99.9a and friends.
*/
const char *tail;
- int slash = count_slashes(refname);
- for (tail = refname; *tail && match_ref_slash < slash; )
+ int slash = count_slashes(ref->name);
+ for (tail = ref->name; *tail && match_ref_slash < slash; )
if (*tail++ == '/')
slash--;
if (!*tail)
return 0;
if (wildmatch(match_ref_pattern, tail, 0))
return 0;
- if (starts_with(refname, "refs/heads/"))
- return append_head_ref(refname, NULL, oid, flag, cb_data);
- if (starts_with(refname, "refs/tags/"))
- return append_tag_ref(refname, oid, flag, cb_data);
- return append_ref(refname, oid, 0);
+ if (starts_with(ref->name, "refs/heads/"))
+ return append_head_ref(ref, cb_data);
+ if (starts_with(ref->name, "refs/tags/"))
+ return append_tag_ref(ref->name, ref->oid, ref->flags, cb_data);
+ return append_ref(ref->name, ref->oid, 0);
}
static void snarf_refs(int head, int remotes)
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 0b6f9edf86c..4803b5e5986 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -66,26 +66,25 @@ struct show_ref_data {
int show_head;
};
-static int show_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cbdata)
+static int show_ref(const struct reference *ref, void *cbdata)
{
struct show_ref_data *data = cbdata;
- if (data->show_head && !strcmp(refname, "HEAD"))
+ if (data->show_head && !strcmp(ref->name, "HEAD"))
goto match;
if (data->patterns) {
- int reflen = strlen(refname);
+ int reflen = strlen(ref->name);
const char **p = data->patterns, *m;
while ((m = *p++) != NULL) {
int len = strlen(m);
if (len > reflen)
continue;
- if (memcmp(m, refname + reflen - len, len))
+ if (memcmp(m, ref->name + reflen - len, len))
continue;
if (len == reflen)
goto match;
- if (refname[reflen - len - 1] == '/')
+ if (ref->name[reflen - len - 1] == '/')
goto match;
}
return 0;
@@ -94,18 +93,15 @@ static int show_ref(const char *refname, const char *referent UNUSED, const stru
match:
data->found_match++;
- show_one(data->show_one_opts, refname, oid);
+ show_one(data->show_one_opts, ref->name, ref->oid);
return 0;
}
-static int add_existing(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cbdata)
+static int add_existing(const struct reference *ref, void *cbdata)
{
struct string_list *list = (struct string_list *)cbdata;
- string_list_insert(list, refname);
+ string_list_insert(list, ref->name);
return 0;
}
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index fcd73abe533..35f6cf735e5 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -593,16 +593,12 @@ static void print_status(unsigned int flags, char state, const char *path,
printf("\n");
}
-static int handle_submodule_head_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int handle_submodule_head_ref(const struct reference *ref, void *cb_data)
{
struct object_id *output = cb_data;
- if (oid)
- oidcpy(output, oid);
+ if (ref->oid)
+ oidcpy(output, ref->oid);
return 0;
}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 812774a5ca9..b7f323b5e4d 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -635,11 +635,7 @@ static void print_preparing_worktree_line(int detach,
*
* Returns 0 on failure and non-zero on success.
*/
-static int first_valid_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED,
- void *cb_data UNUSED)
+static int first_valid_ref(const struct reference *ref UNUSED, void *cb_data UNUSED)
{
return 1;
}
diff --git a/commit-graph.c b/commit-graph.c
index 474454db73d..f91af416259 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1851,18 +1851,16 @@ struct refs_cb_data {
struct progress *progress;
};
-static int add_ref_to_set(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int add_ref_to_set(const struct reference *ref, void *cb_data)
{
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct refs_cb_data *data = (struct refs_cb_data *)cb_data;
- if (!peel_iterated_oid(data->repo, oid, &peeled))
- oid = &peeled;
- if (odb_read_object_info(data->repo->objects, oid, NULL) == OBJ_COMMIT)
- oidset_insert(data->commits, oid);
+ if (!peel_iterated_oid(data->repo, ref->oid, &peeled))
+ maybe_peeled = &peeled;
+ if (odb_read_object_info(data->repo->objects, maybe_peeled, NULL) == OBJ_COMMIT)
+ oidset_insert(data->commits, maybe_peeled);
display_progress(data->progress, oidset_size(data->commits));
diff --git a/delta-islands.c b/delta-islands.c
index 36c94799d69..7cfebc4162b 100644
--- a/delta-islands.c
+++ b/delta-islands.c
@@ -390,8 +390,7 @@ static void add_ref_to_island(kh_str_t *remote_islands, const char *island_name,
rl->hash += sha_core;
}
-static int find_island_for_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb)
+static int find_island_for_ref(const struct reference *ref, void *cb)
{
struct island_load_data *ild = cb;
@@ -406,7 +405,7 @@ static int find_island_for_ref(const char *refname, const char *referent UNUSED,
/* walk backwards to get last-one-wins ordering */
for (i = ild->nr - 1; i >= 0; i--) {
- if (!regexec(&ild->rx[i], refname,
+ if (!regexec(&ild->rx[i], ref->name,
ARRAY_SIZE(matches), matches, 0))
break;
}
@@ -428,10 +427,10 @@ static int find_island_for_ref(const char *refname, const char *referent UNUSED,
if (island_name.len)
strbuf_addch(&island_name, '-');
- strbuf_add(&island_name, refname + match->rm_so, match->rm_eo - match->rm_so);
+ strbuf_add(&island_name, ref->name + match->rm_so, match->rm_eo - match->rm_so);
}
- add_ref_to_island(ild->remote_islands, island_name.buf, oid);
+ add_ref_to_island(ild->remote_islands, island_name.buf, ref->oid);
strbuf_release(&island_name);
return 0;
}
diff --git a/fetch-pack.c b/fetch-pack.c
index fe7a84bf2f9..78c45d4a155 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -188,13 +188,9 @@ static int rev_list_insert_ref(struct fetch_negotiator *negotiator,
return 0;
}
-static int rev_list_insert_ref_oid(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int rev_list_insert_ref_oid(const struct reference *ref, void *cb_data)
{
- return rev_list_insert_ref(cb_data, oid);
+ return rev_list_insert_ref(cb_data, ref->oid);
}
enum ack_type {
@@ -616,13 +612,9 @@ static int mark_complete(const struct object_id *oid)
return 0;
}
-static int mark_complete_oid(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED,
- void *cb_data UNUSED)
+static int mark_complete_oid(const struct reference *ref, void *cb_data UNUSED)
{
- return mark_complete(oid);
+ return mark_complete(ref->oid);
}
static void mark_recent_complete_commits(struct fetch_pack_args *args,
diff --git a/help.c b/help.c
index 5854dd4a7e4..20e114432d7 100644
--- a/help.c
+++ b/help.c
@@ -851,18 +851,16 @@ struct similar_ref_cb {
struct string_list *similar_refs;
};
-static int append_similar_ref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED, void *cb_data)
+static int append_similar_ref(const struct reference *ref, void *cb_data)
{
struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
- char *branch = strrchr(refname, '/') + 1;
+ char *branch = strrchr(ref->name, '/') + 1;
/* A remote branch of the same name is deemed similar */
- if (starts_with(refname, "refs/remotes/") &&
+ if (starts_with(ref->name, "refs/remotes/") &&
!strcmp(branch, cb->base_ref))
string_list_append_nodup(cb->similar_refs,
- refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), refname, 1));
+ refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), ref->name, 1));
return 0;
}
diff --git a/http-backend.c b/http-backend.c
index 9084058f1e9..92e1733f140 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -513,18 +513,17 @@ static void run_service(const char **argv, int buffer_input)
exit(1);
}
-static int show_text_ref(const char *name, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int show_text_ref(const struct reference *ref, void *cb_data)
{
- const char *name_nons = strip_namespace(name);
+ const char *name_nons = strip_namespace(ref->name);
struct strbuf *buf = cb_data;
- struct object *o = parse_object(the_repository, oid);
+ struct object *o = parse_object(the_repository, ref->oid);
if (!o)
return 0;
- strbuf_addf(buf, "%s\t%s\n", oid_to_hex(oid), name_nons);
+ strbuf_addf(buf, "%s\t%s\n", oid_to_hex(ref->oid), name_nons);
if (o->type == OBJ_TAG) {
- o = deref_tag(the_repository, o, name, 0);
+ o = deref_tag(the_repository, o, ref->name, 0);
if (!o)
return 0;
strbuf_addf(buf, "%s\t%s^{}\n", oid_to_hex(&o->oid),
@@ -569,21 +568,20 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)
strbuf_release(&buf);
}
-static int show_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int show_head_ref(const struct reference *ref, void *cb_data)
{
struct strbuf *buf = cb_data;
- if (flag & REF_ISSYMREF) {
+ if (ref->flags & REF_ISSYMREF) {
const char *target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- refname,
+ ref->name,
RESOLVE_REF_READING,
NULL, NULL);
if (target)
strbuf_addf(buf, "ref: %s\n", strip_namespace(target));
} else {
- strbuf_addf(buf, "%s\n", oid_to_hex(oid));
+ strbuf_addf(buf, "%s\n", oid_to_hex(ref->oid));
}
return 0;
diff --git a/log-tree.c b/log-tree.c
index 7d917f2a83d..1729b0c2012 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -147,9 +147,7 @@ static int ref_filter_match(const char *refname,
return 1;
}
-static int add_ref_decoration(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int add_ref_decoration(const struct reference *ref, void *cb_data)
{
int i;
struct object *obj;
@@ -158,16 +156,16 @@ static int add_ref_decoration(const char *refname, const char *referent UNUSED,
struct decoration_filter *filter = (struct decoration_filter *)cb_data;
const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref;
- if (filter && !ref_filter_match(refname, filter))
+ if (filter && !ref_filter_match(ref->name, filter))
return 0;
- if (starts_with(refname, git_replace_ref_base)) {
+ if (starts_with(ref->name, git_replace_ref_base)) {
struct object_id original_oid;
if (!replace_refs_enabled(the_repository))
return 0;
- if (get_oid_hex(refname + strlen(git_replace_ref_base),
+ if (get_oid_hex(ref->name + strlen(git_replace_ref_base),
&original_oid)) {
- warning("invalid replace ref %s", refname);
+ warning("invalid replace ref %s", ref->name);
return 0;
}
obj = parse_object(the_repository, &original_oid);
@@ -176,10 +174,10 @@ static int add_ref_decoration(const char *refname, const char *referent UNUSED,
return 0;
}
- objtype = odb_read_object_info(the_repository->objects, oid, NULL);
+ objtype = odb_read_object_info(the_repository->objects, ref->oid, NULL);
if (objtype < 0)
return 0;
- obj = lookup_object_by_type(the_repository, oid, objtype);
+ obj = lookup_object_by_type(the_repository, ref->oid, objtype);
for (i = 0; i < ARRAY_SIZE(ref_namespace); i++) {
struct ref_namespace_info *info = &ref_namespace[i];
@@ -187,24 +185,24 @@ static int add_ref_decoration(const char *refname, const char *referent UNUSED,
if (!info->decoration)
continue;
if (info->exact) {
- if (!strcmp(refname, info->ref)) {
+ if (!strcmp(ref->name, info->ref)) {
deco_type = info->decoration;
break;
}
- } else if (starts_with(refname, info->ref)) {
+ } else if (starts_with(ref->name, info->ref)) {
deco_type = info->decoration;
break;
}
}
- add_name_decoration(deco_type, refname, obj);
+ add_name_decoration(deco_type, ref->name, obj);
while (obj->type == OBJ_TAG) {
if (!obj->parsed)
parse_object(the_repository, &obj->oid);
obj = ((struct tag *)obj)->tagged;
if (!obj)
break;
- add_name_decoration(DECORATION_REF_TAG, refname, obj);
+ add_name_decoration(DECORATION_REF_TAG, ref->name, obj);
}
return 0;
}
diff --git a/ls-refs.c b/ls-refs.c
index c47acde07f3..64d02723691 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -75,42 +75,42 @@ struct ls_refs_data {
unsigned unborn : 1;
};
-static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int send_ref(const struct reference *ref, void *cb_data)
{
struct ls_refs_data *data = cb_data;
- const char *refname_nons = strip_namespace(refname);
+ const char *refname_nons = strip_namespace(ref->name);
strbuf_reset(&data->buf);
- if (ref_is_hidden(refname_nons, refname, &data->hidden_refs))
+ if (ref_is_hidden(refname_nons, ref->name, &data->hidden_refs))
return 0;
if (!ref_match(&data->prefixes, refname_nons))
return 0;
- if (oid)
- strbuf_addf(&data->buf, "%s %s", oid_to_hex(oid), refname_nons);
+ if (ref->oid)
+ strbuf_addf(&data->buf, "%s %s", oid_to_hex(ref->oid), refname_nons);
else
strbuf_addf(&data->buf, "unborn %s", refname_nons);
- if (data->symrefs && flag & REF_ISSYMREF) {
+ if (data->symrefs && ref->flags & REF_ISSYMREF) {
+ int unused_flag;
struct object_id unused;
const char *symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- refname,
+ ref->name,
0,
&unused,
- &flag);
+ &unused_flag);
if (!symref_target)
- die("'%s' is a symref but it is not?", refname);
+ die("'%s' is a symref but it is not?", ref->name);
strbuf_addf(&data->buf, " symref-target:%s",
strip_namespace(symref_target));
}
- if (data->peel && oid) {
+ if (data->peel && ref->oid) {
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
strbuf_addf(&data->buf, " peeled:%s", oid_to_hex(&peeled));
}
@@ -131,9 +131,17 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)
if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), namespaced.buf, 0, &oid, &flag))
return; /* bad ref */
oid_is_null = is_null_oid(&oid);
+
if (!oid_is_null ||
- (data->unborn && data->symrefs && (flag & REF_ISSYMREF)))
- send_ref(namespaced.buf, NULL, oid_is_null ? NULL : &oid, flag, data);
+ (data->unborn && data->symrefs && (flag & REF_ISSYMREF))) {
+ struct reference ref = {
+ .name = namespaced.buf,
+ .oid = oid_is_null ? NULL : &oid,
+ .flags = flag,
+ };
+
+ send_ref(&ref, data);
+ }
strbuf_release(&namespaced);
}
diff --git a/midx-write.c b/midx-write.c
index c73010df6d3..f4dd875747a 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -697,28 +697,27 @@ static void prepare_midx_packing_data(struct packing_data *pdata,
trace2_region_leave("midx", "prepare_midx_packing_data", ctx->repo);
}
-static int add_ref_to_pending(const char *refname, const char *referent UNUSED,
- const struct object_id *oid,
- int flag, void *cb_data)
+static int add_ref_to_pending(const struct reference *ref, void *cb_data)
{
struct rev_info *revs = (struct rev_info*)cb_data;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct object *object;
- if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
- warning("symbolic ref is dangling: %s", refname);
+ if ((ref->flags & REF_ISSYMREF) && (ref->flags & REF_ISBROKEN)) {
+ warning("symbolic ref is dangling: %s", ref->name);
return 0;
}
- if (!peel_iterated_oid(revs->repo, oid, &peeled))
- oid = &peeled;
+ if (!peel_iterated_oid(revs->repo, ref->oid, &peeled))
+ maybe_peeled = &peeled;
- object = parse_object_or_die(revs->repo, oid, refname);
+ object = parse_object_or_die(revs->repo, maybe_peeled, ref->name);
if (object->type != OBJ_COMMIT)
return 0;
add_pending_object(revs, object, "");
- if (bitmap_is_preferred_refname(revs->repo, refname))
+ if (bitmap_is_preferred_refname(revs->repo, ref->name))
object->flags |= NEEDS_BITMAP;
return 0;
}
diff --git a/negotiator/default.c b/negotiator/default.c
index c479da9b091..116dedcf830 100644
--- a/negotiator/default.c
+++ b/negotiator/default.c
@@ -38,11 +38,10 @@ static void rev_list_push(struct negotiation_state *ns,
}
}
-static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data UNUSED)
+static int clear_marks(const struct reference *ref, void *cb_data UNUSED)
{
- struct object *o = deref_tag(the_repository, parse_object(the_repository, oid), refname, 0);
+ struct object *o = deref_tag(the_repository, parse_object(the_repository, ref->oid),
+ ref->name, 0);
if (o && o->type == OBJ_COMMIT)
clear_commit_marks((struct commit *)o,
diff --git a/negotiator/skipping.c b/negotiator/skipping.c
index 616df6bf3af..0a272130fb1 100644
--- a/negotiator/skipping.c
+++ b/negotiator/skipping.c
@@ -75,11 +75,10 @@ static struct entry *rev_list_push(struct data *data, struct commit *commit, int
return entry;
}
-static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data UNUSED)
+static int clear_marks(const struct reference *ref, void *cb_data UNUSED)
{
- struct object *o = deref_tag(the_repository, parse_object(the_repository, oid), refname, 0);
+ struct object *o = deref_tag(the_repository, parse_object(the_repository, ref->oid),
+ ref->name, 0);
if (o && o->type == OBJ_COMMIT)
clear_commit_marks((struct commit *)o,
diff --git a/notes.c b/notes.c
index 9a2e9181fe6..8e00fd8c470 100644
--- a/notes.c
+++ b/notes.c
@@ -938,13 +938,11 @@ int combine_notes_cat_sort_uniq(struct object_id *cur_oid,
return ret;
}
-static int string_list_add_one_ref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cb)
+static int string_list_add_one_ref(const struct reference *ref, void *cb)
{
struct string_list *refs = cb;
- if (!unsorted_string_list_has_string(refs, refname))
- string_list_append(refs, refname);
+ if (!unsorted_string_list_has_string(refs, ref->name))
+ string_list_append(refs, ref->name);
return 0;
}
diff --git a/object-name.c b/object-name.c
index f6902e140dd..7e8109f25fb 100644
--- a/object-name.c
+++ b/object-name.c
@@ -1444,18 +1444,16 @@ struct handle_one_ref_cb {
struct commit_list **list;
};
-static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int handle_one_ref(const struct reference *ref, void *cb_data)
{
struct handle_one_ref_cb *cb = cb_data;
struct commit_list **list = cb->list;
- struct object *object = parse_object(cb->repo, oid);
+ struct object *object = parse_object(cb->repo, ref->oid);
if (!object)
return 0;
if (object->type == OBJ_TAG) {
- object = deref_tag(cb->repo, object, path,
- strlen(path));
+ object = deref_tag(cb->repo, object, ref->name,
+ strlen(ref->name));
if (!object)
return 0;
}
diff --git a/pseudo-merge.c b/pseudo-merge.c
index 893b763fe45..0abd51b42c1 100644
--- a/pseudo-merge.c
+++ b/pseudo-merge.c
@@ -221,28 +221,25 @@ void load_pseudo_merges_from_config(struct repository *r,
}
}
-static int find_pseudo_merge_group_for_ref(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *_data)
+static int find_pseudo_merge_group_for_ref(const struct reference *ref, void *_data)
{
struct bitmap_writer *writer = _data;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct commit *c;
uint32_t i;
int has_bitmap;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
+ if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ maybe_peeled = &peeled;
- c = lookup_commit(the_repository, oid);
+ c = lookup_commit(the_repository, maybe_peeled);
if (!c)
return 0;
- if (!packlist_find(writer->to_pack, oid))
+ if (!packlist_find(writer->to_pack, maybe_peeled))
return 0;
- has_bitmap = bitmap_writer_has_bitmapped_object_id(writer, oid);
+ has_bitmap = bitmap_writer_has_bitmapped_object_id(writer, maybe_peeled);
for (i = 0; i < writer->pseudo_merge_groups.nr; i++) {
struct pseudo_merge_group *group;
@@ -252,7 +249,7 @@ static int find_pseudo_merge_group_for_ref(const char *refname,
size_t j;
group = writer->pseudo_merge_groups.items[i].util;
- if (regexec(group->pattern, refname, ARRAY_SIZE(captures),
+ if (regexec(group->pattern, ref->name, ARRAY_SIZE(captures),
captures, 0))
continue;
@@ -269,7 +266,7 @@ static int find_pseudo_merge_group_for_ref(const char *refname,
if (group_name.len)
strbuf_addch(&group_name, '-');
- strbuf_add(&group_name, refname + match->rm_so,
+ strbuf_add(&group_name, ref->name + match->rm_so,
match->rm_eo - match->rm_so);
}
diff --git a/reachable.c b/reachable.c
index 22266db5233..b753c395530 100644
--- a/reachable.c
+++ b/reachable.c
@@ -83,18 +83,17 @@ static void add_rebase_files(struct rev_info *revs)
free_worktrees(worktrees);
}
-static int add_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int add_one_ref(const struct reference *ref, void *cb_data)
{
struct rev_info *revs = (struct rev_info *)cb_data;
struct object *object;
- if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
- warning("symbolic ref is dangling: %s", path);
+ if ((ref->flags & REF_ISSYMREF) && (ref->flags & REF_ISBROKEN)) {
+ warning("symbolic ref is dangling: %s", ref->name);
return 0;
}
- object = parse_object_or_die(the_repository, oid, path);
+ object = parse_object_or_die(the_repository, ref->oid, ref->name);
add_pending_object(revs, object, "");
return 0;
diff --git a/ref-filter.c b/ref-filter.c
index 30cc488d8ab..6837fa60a9b 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2954,14 +2954,15 @@ struct ref_filter_cbdata {
* A call-back given to for_each_ref(). Filter refs and keep them for
* later object processing.
*/
-static int filter_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)
+static int filter_one(const struct reference *ref, void *cb_data)
{
struct ref_filter_cbdata *ref_cbdata = cb_data;
- struct ref_array_item *ref;
+ struct ref_array_item *item;
- ref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);
- if (ref)
- ref_array_append(ref_cbdata->array, ref);
+ item = apply_ref_filter(ref->name, ref->target, ref->oid,
+ ref->flags, ref_cbdata->filter);
+ if (item)
+ ref_array_append(ref_cbdata->array, item);
return 0;
}
@@ -2990,17 +2991,18 @@ struct ref_filter_and_format_cbdata {
} internal;
};
-static int filter_and_format_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)
+static int filter_and_format_one(const struct reference *ref, void *cb_data)
{
struct ref_filter_and_format_cbdata *ref_cbdata = cb_data;
- struct ref_array_item *ref;
+ struct ref_array_item *item;
struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
- ref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);
- if (!ref)
+ item = apply_ref_filter(ref->name, ref->target, ref->oid,
+ ref->flags, ref_cbdata->filter);
+ if (!item)
return 0;
- if (format_ref_array_item(ref, ref_cbdata->format, &output, &err))
+ if (format_ref_array_item(item, ref_cbdata->format, &output, &err))
die("%s", err.buf);
if (output.len || !ref_cbdata->format->array_opts.omit_empty) {
@@ -3010,7 +3012,7 @@ static int filter_and_format_one(const char *refname, const char *referent, cons
strbuf_release(&output);
strbuf_release(&err);
- free_array_item(ref);
+ free_array_item(item);
/*
* Increment the running count of refs that match the filter. If
diff --git a/reflog.c b/reflog.c
index 65ef259b4f5..ac87e20c4f9 100644
--- a/reflog.c
+++ b/reflog.c
@@ -423,16 +423,13 @@ int should_expire_reflog_ent_verbose(struct object_id *ooid,
return expire;
}
-static int push_tip_to_list(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags, void *cb_data)
+static int push_tip_to_list(const struct reference *ref, void *cb_data)
{
struct commit_list **list = cb_data;
struct commit *tip_commit;
- if (flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
return 0;
- tip_commit = lookup_commit_reference_gently(the_repository, oid, 1);
+ tip_commit = lookup_commit_reference_gently(the_repository, ref->oid, 1);
if (!tip_commit)
return 0;
commit_list_insert(tip_commit, list);
diff --git a/refs.c b/refs.c
index 965381367e0..25f0579d610 100644
--- a/refs.c
+++ b/refs.c
@@ -426,17 +426,19 @@ int refs_ref_exists(struct ref_store *refs, const char *refname)
NULL, NULL);
}
-static int for_each_filter_refs(const char *refname, const char *referent,
- const struct object_id *oid,
- int flags, void *data)
+static int for_each_filter_refs(const struct reference *ref, void *data)
{
struct for_each_ref_filter *filter = data;
- if (wildmatch(filter->pattern, refname, 0))
+ if (wildmatch(filter->pattern, ref->name, 0))
return 0;
- if (filter->prefix)
- skip_prefix(refname, filter->prefix, &refname);
- return filter->fn(refname, referent, oid, flags, filter->cb_data);
+ if (filter->prefix) {
+ struct reference skipped = *ref;
+ skip_prefix(skipped.name, filter->prefix, &skipped.name);
+ return filter->fn(&skipped, filter->cb_data);
+ } else {
+ return filter->fn(ref, filter->cb_data);
+ }
}
struct warn_if_dangling_data {
@@ -447,17 +449,15 @@ struct warn_if_dangling_data {
int dry_run;
};
-static int warn_if_dangling_symref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags, void *cb_data)
+static int warn_if_dangling_symref(const struct reference *ref, void *cb_data)
{
struct warn_if_dangling_data *d = cb_data;
const char *resolves_to, *msg;
- if (!(flags & REF_ISSYMREF))
+ if (!(ref->flags & REF_ISSYMREF))
return 0;
- resolves_to = refs_resolve_ref_unsafe(d->refs, refname, 0, NULL, NULL);
+ resolves_to = refs_resolve_ref_unsafe(d->refs, ref->name, 0, NULL, NULL);
if (!resolves_to
|| !string_list_has_string(d->refnames, resolves_to)) {
return 0;
@@ -466,7 +466,7 @@ static int warn_if_dangling_symref(const char *refname, const char *referent UNU
msg = d->dry_run
? _("%s%s will become dangling after %s is deleted\n")
: _("%s%s has become dangling after %s was deleted\n");
- fprintf(d->fp, msg, d->indent, refname, resolves_to);
+ fprintf(d->fp, msg, d->indent, ref->name, resolves_to);
return 0;
}
@@ -507,8 +507,15 @@ int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_da
int flag;
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
- if (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag))
- ret = fn(buf.buf, NULL, &oid, flag, cb_data);
+ if (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag)) {
+ struct reference ref = {
+ .name = buf.buf,
+ .oid = &oid,
+ .flags = flag,
+ };
+
+ ret = fn(&ref, cb_data);
+ }
strbuf_release(&buf);
return ret;
@@ -1741,8 +1748,15 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
int flag;
if (refs_resolve_ref_unsafe(refs, "HEAD", RESOLVE_REF_READING,
- &oid, &flag))
- return fn("HEAD", NULL, &oid, flag, cb_data);
+ &oid, &flag)) {
+ struct reference ref = {
+ .name = "HEAD",
+ .oid = &oid,
+ .flags = flag,
+ };
+
+ return fn(&ref, cb_data);
+ }
return 0;
}
@@ -2753,14 +2767,10 @@ struct do_for_each_reflog_help {
void *cb_data;
};
-static int do_for_each_reflog_helper(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED,
- void *cb_data)
+static int do_for_each_reflog_helper(const struct reference *ref, void *cb_data)
{
struct do_for_each_reflog_help *hp = cb_data;
- return hp->fn(refname, hp->cb_data);
+ return hp->fn(ref->name, hp->cb_data);
}
int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data)
@@ -2976,25 +2986,24 @@ struct migration_data {
uint64_t index;
};
-static int migrate_one_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags, void *cb_data)
+static int migrate_one_ref(const struct reference *ref, void *cb_data)
{
struct migration_data *data = cb_data;
struct strbuf symref_target = STRBUF_INIT;
int ret;
- if (flags & REF_ISSYMREF) {
- ret = refs_read_symbolic_ref(data->old_refs, refname, &symref_target);
+ if (ref->flags & REF_ISSYMREF) {
+ ret = refs_read_symbolic_ref(data->old_refs, ref->name, &symref_target);
if (ret < 0)
goto done;
- ret = ref_transaction_update(data->transaction, refname, NULL, null_oid(the_hash_algo),
+ ret = ref_transaction_update(data->transaction, ref->name, NULL, null_oid(the_hash_algo),
symref_target.buf, NULL,
REF_SKIP_CREATE_REFLOG | REF_NO_DEREF, NULL, data->errbuf);
if (ret < 0)
goto done;
} else {
- ret = ref_transaction_create(data->transaction, refname, oid, NULL,
+ ret = ref_transaction_create(data->transaction, ref->name, ref->oid, NULL,
REF_SKIP_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION,
NULL, data->errbuf);
if (ret < 0)
diff --git a/refs.h b/refs.h
index 4e6bd63aa86..68d235438c2 100644
--- a/refs.h
+++ b/refs.h
@@ -355,14 +355,32 @@ struct ref_transaction;
*/
#define REF_BAD_NAME 0x08
+/* A reference passed to `for_each_ref()`-style callbacks. */
+struct reference {
+ /* The fully-qualified name of the reference. */
+ const char *name;
+
+ /* The target of a symbolic ref. `NULL` for direct references. */
+ const char *target;
+
+ /*
+ * The object ID of a reference. Either the direct object ID or the
+ * resolved object ID in the case of a symbolic ref. May be the zero
+ * object ID in case the symbolic ref cannot be resolved.
+ */
+ const struct object_id *oid;
+
+ /* A bitfield of `REF_` flags. */
+ int flags;
+};
+
/*
* The signature for the callback function for the for_each_*()
- * functions below. The memory pointed to by the refname and oid
- * arguments is only guaranteed to be valid for the duration of a
+ * functions below. The memory pointed to by the `struct reference`
+ * argument is only guaranteed to be valid for the duration of a
* single callback invocation.
*/
-typedef int each_ref_fn(const char *refname, const char *referent,
- const struct object_id *oid, int flags, void *cb_data);
+typedef int each_ref_fn(const struct reference *ref, void *cb_data);
/*
* The following functions invoke the specified callback function for
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 8d7007f4aaa..eb3142f8f2d 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3150,14 +3150,11 @@ static int parse_and_write_reflog(struct files_ref_store *refs,
return 0;
}
-static int ref_present(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED,
- void *cb_data)
+static int ref_present(const struct reference *ref, void *cb_data)
{
struct string_list *affected_refnames = cb_data;
- return string_list_has_string(affected_refnames, refname);
+ return string_list_has_string(affected_refnames, ref->name);
}
static int files_transaction_finish_initial(struct files_ref_store *refs,
diff --git a/refs/iterator.c b/refs/iterator.c
index 17ef841d8a3..7f2e718f1c9 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -476,7 +476,14 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
- retval = fn(iter->refname, iter->referent, iter->oid, iter->flags, cb_data);
+ struct reference ref = {
+ .name = iter->refname,
+ .target = iter->referent,
+ .oid = iter->oid,
+ .flags = iter->flags,
+ };
+
+ retval = fn(&ref, cb_data);
if (retval)
goto out;
}
diff --git a/remote.c b/remote.c
index df9675cd330..59b37151208 100644
--- a/remote.c
+++ b/remote.c
@@ -2315,21 +2315,19 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
return 1;
}
-static int one_local_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int one_local_ref(const struct reference *ref, void *cb_data)
{
struct ref ***local_tail = cb_data;
- struct ref *ref;
+ struct ref *local_ref;
/* we already know it starts with refs/ to get here */
- if (check_refname_format(refname + 5, 0))
+ if (check_refname_format(ref->name + 5, 0))
return 0;
- ref = alloc_ref(refname);
- oidcpy(&ref->new_oid, oid);
- **local_tail = ref;
- *local_tail = &ref->next;
+ local_ref = alloc_ref(ref->name);
+ oidcpy(&local_ref->new_oid, ref->oid);
+ **local_tail = local_ref;
+ *local_tail = &local_ref->next;
return 0;
}
@@ -2402,15 +2400,14 @@ struct stale_heads_info {
struct refspec *rs;
};
-static int get_stale_heads_cb(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags, void *cb_data)
+static int get_stale_heads_cb(const struct reference *ref, void *cb_data)
{
struct stale_heads_info *info = cb_data;
struct string_list matches = STRING_LIST_INIT_DUP;
struct refspec_item query;
int i, stale = 1;
memset(&query, 0, sizeof(struct refspec_item));
- query.dst = (char *)refname;
+ query.dst = (char *)ref->name;
refspec_find_all_matches(info->rs, &query, &matches);
if (matches.nr == 0)
@@ -2423,7 +2420,7 @@ static int get_stale_heads_cb(const char *refname, const char *referent UNUSED,
* overlapping refspecs, we need to go over all of the
* matching refs.
*/
- if (flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
goto clean_exit;
for (i = 0; stale && i < matches.nr; i++)
@@ -2431,8 +2428,8 @@ static int get_stale_heads_cb(const char *refname, const char *referent UNUSED,
stale = 0;
if (stale) {
- struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
- oidcpy(&ref->new_oid, oid);
+ struct ref *linked_ref = make_linked_ref(ref->name, &info->stale_refs_tail);
+ oidcpy(&linked_ref->new_oid, ref->oid);
}
clean_exit:
diff --git a/repack-midx.c b/repack-midx.c
index 6f6202c5bcc..349f7e20b53 100644
--- a/repack-midx.c
+++ b/repack-midx.c
@@ -16,25 +16,23 @@ struct midx_snapshot_ref_data {
int preferred;
};
-static int midx_snapshot_ref_one(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *_data)
+static int midx_snapshot_ref_one(const struct reference *ref, void *_data)
{
struct midx_snapshot_ref_data *data = _data;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
- if (!peel_iterated_oid(data->repo, oid, &peeled))
- oid = &peeled;
+ if (!peel_iterated_oid(data->repo, ref->oid, &peeled))
+ maybe_peeled = &peeled;
- if (oidset_insert(&data->seen, oid))
+ if (oidset_insert(&data->seen, maybe_peeled))
return 0; /* already seen */
- if (odb_read_object_info(data->repo->objects, oid, NULL) != OBJ_COMMIT)
+ if (odb_read_object_info(data->repo->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
fprintf(data->f->fp, "%s%s\n", data->preferred ? "+" : "",
- oid_to_hex(oid));
+ oid_to_hex(maybe_peeled));
return 0;
}
diff --git a/replace-object.c b/replace-object.c
index 3eae0510745..03d0f1f083b 100644
--- a/replace-object.c
+++ b/replace-object.c
@@ -8,31 +8,27 @@
#include "repository.h"
#include "commit.h"
-static int register_replace_ref(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int register_replace_ref(const struct reference *ref, void *cb_data)
{
struct repository *r = cb_data;
/* Get sha1 from refname */
- const char *slash = strrchr(refname, '/');
- const char *hash = slash ? slash + 1 : refname;
+ const char *slash = strrchr(ref->name, '/');
+ const char *hash = slash ? slash + 1 : ref->name;
struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj));
if (get_oid_hex_algop(hash, &repl_obj->original.oid, r->hash_algo)) {
free(repl_obj);
- warning(_("bad replace ref name: %s"), refname);
+ warning(_("bad replace ref name: %s"), ref->name);
return 0;
}
/* Copy sha1 from the read ref */
- oidcpy(&repl_obj->replacement, oid);
+ oidcpy(&repl_obj->replacement, ref->oid);
/* Register new object */
if (oidmap_put(&r->objects->replace_map, repl_obj))
- die(_("duplicate replace ref: %s"), refname);
+ die(_("duplicate replace ref: %s"), ref->name);
return 0;
}
diff --git a/revision.c b/revision.c
index cf5e6c1ec9e..5f0850ae5c9 100644
--- a/revision.c
+++ b/revision.c
@@ -1644,19 +1644,17 @@ struct all_refs_cb {
struct worktree *wt;
};
-static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int handle_one_ref(const struct reference *ref, void *cb_data)
{
struct all_refs_cb *cb = cb_data;
struct object *object;
- if (ref_excluded(&cb->all_revs->ref_excludes, path))
+ if (ref_excluded(&cb->all_revs->ref_excludes, ref->name))
return 0;
- object = get_reference(cb->all_revs, path, oid, cb->all_flags);
- add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
- add_pending_object(cb->all_revs, object, path);
+ object = get_reference(cb->all_revs, ref->name, ref->oid, cb->all_flags);
+ add_rev_cmdline(cb->all_revs, object, ref->name, REV_CMD_REF, cb->all_flags);
+ add_pending_object(cb->all_revs, object, ref->name);
return 0;
}
diff --git a/server-info.c b/server-info.c
index 1d33de821e9..0a07c722e8b 100644
--- a/server-info.c
+++ b/server-info.c
@@ -148,23 +148,21 @@ static int update_info_file(struct repository *r, char *path,
return ret;
}
-static int add_info_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED,
- void *cb_data)
+static int add_info_ref(const struct reference *ref, void *cb_data)
{
struct update_info_ctx *uic = cb_data;
- struct object *o = parse_object(uic->repo, oid);
+ struct object *o = parse_object(uic->repo, ref->oid);
if (!o)
return -1;
- if (uic_printf(uic, "%s %s\n", oid_to_hex(oid), path) < 0)
+ if (uic_printf(uic, "%s %s\n", oid_to_hex(ref->oid), ref->name) < 0)
return -1;
if (o->type == OBJ_TAG) {
- o = deref_tag(uic->repo, o, path, 0);
+ o = deref_tag(uic->repo, o, ref->name, 0);
if (o)
if (uic_printf(uic, "%s %s^{}\n",
- oid_to_hex(&o->oid), path) < 0)
+ oid_to_hex(&o->oid), ref->name) < 0)
return -1;
}
return 0;
diff --git a/shallow.c b/shallow.c
index d9cd4e219cb..55b9cd9d3f2 100644
--- a/shallow.c
+++ b/shallow.c
@@ -626,14 +626,10 @@ static void paint_down(struct paint_info *info, const struct object_id *oid,
free(tmp);
}
-static int mark_uninteresting(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data UNUSED)
+static int mark_uninteresting(const struct reference *ref, void *cb_data UNUSED)
{
struct commit *commit = lookup_commit_reference_gently(the_repository,
- oid, 1);
+ ref->oid, 1);
if (!commit)
return 0;
commit->object.flags |= UNINTERESTING;
@@ -742,16 +738,12 @@ struct commit_array {
size_t nr, alloc;
};
-static int add_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int add_ref(const struct reference *ref, void *cb_data)
{
struct commit_array *ca = cb_data;
ALLOC_GROW(ca->commits, ca->nr + 1, ca->alloc);
ca->commits[ca->nr] = lookup_commit_reference_gently(the_repository,
- oid, 1);
+ ref->oid, 1);
if (ca->commits[ca->nr])
ca->nr++;
return 0;
diff --git a/submodule.c b/submodule.c
index 35c55155f7b..40a5c6fb9d1 100644
--- a/submodule.c
+++ b/submodule.c
@@ -934,10 +934,7 @@ static void free_submodules_data(struct string_list *submodules)
string_list_clear(submodules, 1);
}
-static int has_remote(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED, void *cb_data UNUSED)
+static int has_remote(const struct reference *ref UNUSED, void *cb_data UNUSED)
{
return 1;
}
@@ -1255,13 +1252,10 @@ int push_unpushed_submodules(struct repository *r,
return ret;
}
-static int append_oid_to_array(const char *ref UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *data)
+static int append_oid_to_array(const struct reference *ref, void *data)
{
struct oid_array *array = data;
- oid_array_append(array, oid);
+ oid_array_append(array, ref->oid);
return 0;
}
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 83b06d39a36..b1215947c5e 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -154,10 +154,9 @@ static int cmd_rename_ref(struct ref_store *refs, const char **argv)
return refs_rename_ref(refs, oldref, newref, logmsg);
}
-static int each_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags, void *cb_data UNUSED)
+static int each_ref(const struct reference *ref, void *cb_data UNUSED)
{
- printf("%s %s 0x%x\n", oid_to_hex(oid), refname, flags);
+ printf("%s %s 0x%x\n", oid_to_hex(ref->oid), ref->name, ref->flags);
return 0;
}
diff --git a/upload-pack.c b/upload-pack.c
index 1e87ae95593..0d563ae74e9 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -870,8 +870,8 @@ static void send_unshallow(struct upload_pack_data *data)
}
}
-static int check_ref(const char *refname_full, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data);
+static int check_ref(const struct reference *ref, void *cb_data);
+
static void deepen(struct upload_pack_data *data, int depth)
{
if (depth == INFINITE_DEPTH && !is_repository_shallow(the_repository)) {
@@ -1224,13 +1224,12 @@ static int mark_our_ref(const char *refname, const char *refname_full,
return 0;
}
-static int check_ref(const char *refname_full, const char *referent UNUSED,const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int check_ref(const struct reference *ref, void *cb_data)
{
- const char *refname = strip_namespace(refname_full);
+ const char *refname = strip_namespace(ref->name);
struct upload_pack_data *data = cb_data;
- mark_our_ref(refname, refname_full, oid, &data->hidden_refs);
+ mark_our_ref(refname, ref->name, ref->oid, &data->hidden_refs);
return 0;
}
@@ -1292,27 +1291,25 @@ static void write_v0_ref(struct upload_pack_data *data,
return;
}
-static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int send_ref(const struct reference *ref, void *cb_data)
{
- write_v0_ref(cb_data, refname, strip_namespace(refname), oid);
+ write_v0_ref(cb_data, ref->name, strip_namespace(ref->name), ref->oid);
return 0;
}
-static int find_symref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag, void *cb_data)
+static int find_symref(const struct reference *ref, void *cb_data)
{
const char *symref_target;
struct string_list_item *item;
+ int flag;
- if ((flag & REF_ISSYMREF) == 0)
+ if ((ref->flags & REF_ISSYMREF) == 0)
return 0;
symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- refname, 0, NULL, &flag);
+ ref->name, 0, NULL, &flag);
if (!symref_target || (flag & REF_ISSYMREF) == 0)
- die("'%s' is a symref but it is not?", refname);
- item = string_list_append(cb_data, strip_namespace(refname));
+ die("'%s' is a symref but it is not?", ref->name);
+ item = string_list_append(cb_data, strip_namespace(ref->name));
item->util = xstrdup(strip_namespace(symref_target));
return 0;
}
diff --git a/walker.c b/walker.c
index 80737545172..409b646578a 100644
--- a/walker.c
+++ b/walker.c
@@ -226,14 +226,10 @@ static int interpret_target(struct walker *walker, char *target, struct object_i
return -1;
}
-static int mark_complete(const char *path UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED,
- void *cb_data UNUSED)
+static int mark_complete(const struct reference *ref, void *cb_data UNUSED)
{
struct commit *commit = lookup_commit_reference_gently(the_repository,
- oid, 1);
+ ref->oid, 1);
if (commit) {
commit->object.flags |= COMPLETE;
diff --git a/worktree.c b/worktree.c
index a2a5f51f29f..9308389cb6f 100644
--- a/worktree.c
+++ b/worktree.c
@@ -595,8 +595,15 @@ int other_head_refs(each_ref_fn fn, void *cb_data)
if (refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
refname.buf,
RESOLVE_REF_READING,
- &oid, &flag))
- ret = fn(refname.buf, NULL, &oid, flag, cb_data);
+ &oid, &flag)) {
+ struct reference ref = {
+ .name = refname.buf,
+ .oid = &oid,
+ .flags = flag,
+ };
+
+ ret = fn(&ref, cb_data);
+ }
if (ret)
break;
}
--
2.51.1.930.gacf6e81ea2.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v4 02/14] refs: introduce `.ref` field for the base iterator
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 01/14] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
@ 2025-10-23 7:16 ` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 03/14] refs: fully reset `struct ref_iterator::ref` on iteration Patrick Steinhardt
` (12 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 7:16 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
The base iterator has a couple of fields that tracks the name, target,
object ID and flags for the current reference. Due to this design we
have to create a new `struct reference` whenever we want to hand over
that reference to the callback function, which is tedious and not very
efficient.
Convert the structure to instead contain a `struct reference` as member.
This member is expected to be populated by the implementations of the
iterator and is handed over to the callback directly.
While at it, simplify `should_pack_ref()` to take a `struct reference`
directly instead of passing its respective fields.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs.c | 8 ++++----
refs/debug.c | 8 +++-----
refs/files-backend.c | 47 +++++++++++++++++++++--------------------------
refs/iterator.c | 39 ++++++++++++---------------------------
refs/packed-backend.c | 46 +++++++++++++++++++++++-----------------------
refs/ref-cache.c | 10 +++++-----
refs/refs-internal.h | 5 +----
refs/reftable-backend.c | 12 ++++++------
8 files changed, 75 insertions(+), 100 deletions(-)
diff --git a/refs.c b/refs.c
index 25f0579d610..f96cf43b128 100644
--- a/refs.c
+++ b/refs.c
@@ -2327,8 +2327,8 @@ int refs_optimize(struct ref_store *refs, struct pack_refs_opts *opts)
int peel_iterated_oid(struct repository *r, const struct object_id *base, struct object_id *peeled)
{
if (current_ref_iter &&
- (current_ref_iter->oid == base ||
- oideq(current_ref_iter->oid, base)))
+ (current_ref_iter->ref.oid == base ||
+ oideq(current_ref_iter->ref.oid, base)))
return ref_iterator_peel(current_ref_iter, peeled);
return peel_object(r, base, peeled) ? -1 : 0;
@@ -2703,7 +2703,7 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
if (skip &&
- string_list_has_string(skip, iter->refname))
+ string_list_has_string(skip, iter->ref.name))
continue;
if (transaction && ref_transaction_maybe_set_rejected(
@@ -2712,7 +2712,7 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
continue;
strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
- iter->refname, refname);
+ iter->ref.name, refname);
goto cleanup;
}
diff --git a/refs/debug.c b/refs/debug.c
index 697adbd0dc3..67718bd1f49 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -160,11 +160,9 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
trace_printf_key(&trace_refs, "iterator_advance: (%d)\n", res);
else
trace_printf_key(&trace_refs, "iterator_advance: %s (0)\n",
- diter->iter->refname);
+ diter->iter->ref.name);
- diter->base.refname = diter->iter->refname;
- diter->base.oid = diter->iter->oid;
- diter->base.flags = diter->iter->flags;
+ diter->base.ref = diter->iter->ref;
return res;
}
@@ -185,7 +183,7 @@ static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct debug_ref_iterator *diter =
(struct debug_ref_iterator *)ref_iterator;
int res = diter->iter->vtable->peel(diter->iter, peeled);
- trace_printf_key(&trace_refs, "iterator_peel: %s: %d\n", diter->iter->refname, res);
+ trace_printf_key(&trace_refs, "iterator_peel: %s: %d\n", diter->iter->ref.name, res);
return res;
}
diff --git a/refs/files-backend.c b/refs/files-backend.c
index eb3142f8f2d..fac53fa052d 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -961,26 +961,23 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
- parse_worktree_ref(iter->iter0->refname, NULL, NULL,
+ parse_worktree_ref(iter->iter0->ref.name, NULL, NULL,
NULL) != REF_WORKTREE_CURRENT)
continue;
if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) &&
- (iter->iter0->flags & REF_ISSYMREF) &&
- (iter->iter0->flags & REF_ISBROKEN))
+ (iter->iter0->ref.flags & REF_ISSYMREF) &&
+ (iter->iter0->ref.flags & REF_ISBROKEN))
continue;
if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
- !ref_resolves_to_object(iter->iter0->refname,
+ !ref_resolves_to_object(iter->iter0->ref.name,
iter->repo,
- iter->iter0->oid,
- iter->iter0->flags))
+ iter->iter0->ref.oid,
+ iter->iter0->ref.flags))
continue;
- iter->base.refname = iter->iter0->refname;
- iter->base.oid = iter->iter0->oid;
- iter->base.flags = iter->iter0->flags;
- iter->base.referent = iter->iter0->referent;
+ iter->base.ref = iter->iter0->ref;
return ITER_OK;
}
@@ -1367,30 +1364,29 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune **refs_
* Return true if the specified reference should be packed.
*/
static int should_pack_ref(struct files_ref_store *refs,
- const char *refname,
- const struct object_id *oid, unsigned int ref_flags,
+ const struct reference *ref,
struct pack_refs_opts *opts)
{
struct string_list_item *item;
/* Do not pack per-worktree refs: */
- if (parse_worktree_ref(refname, NULL, NULL, NULL) !=
+ if (parse_worktree_ref(ref->name, NULL, NULL, NULL) !=
REF_WORKTREE_SHARED)
return 0;
/* Do not pack symbolic refs: */
- if (ref_flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
return 0;
/* Do not pack broken refs: */
- if (!ref_resolves_to_object(refname, refs->base.repo, oid, ref_flags))
+ if (!ref_resolves_to_object(ref->name, refs->base.repo, ref->oid, ref->flags))
return 0;
- if (ref_excluded(opts->exclusions, refname))
+ if (ref_excluded(opts->exclusions, ref->name))
return 0;
for_each_string_list_item(item, opts->includes)
- if (!wildmatch(item->string, refname, 0))
+ if (!wildmatch(item->string, ref->name, 0))
return 1;
return 0;
@@ -1443,8 +1439,7 @@ static int should_pack_refs(struct files_ref_store *refs,
iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, 0), NULL,
refs->base.repo, 0);
while ((ret = ref_iterator_advance(iter)) == ITER_OK) {
- if (should_pack_ref(refs, iter->refname, iter->oid,
- iter->flags, opts))
+ if (should_pack_ref(refs, &iter->ref, opts))
refcount++;
if (refcount >= limit) {
ref_iterator_free(iter);
@@ -1489,24 +1484,24 @@ static int files_pack_refs(struct ref_store *ref_store,
* in the packed ref cache. If the reference should be
* pruned, also add it to refs_to_prune.
*/
- if (!should_pack_ref(refs, iter->refname, iter->oid, iter->flags, opts))
+ if (!should_pack_ref(refs, &iter->ref, opts))
continue;
/*
* Add a reference creation for this reference to the
* packed-refs transaction:
*/
- if (ref_transaction_update(transaction, iter->refname,
- iter->oid, NULL, NULL, NULL,
+ if (ref_transaction_update(transaction, iter->ref.name,
+ iter->ref.oid, NULL, NULL, NULL,
REF_NO_DEREF, NULL, &err))
die("failure preparing to create packed reference %s: %s",
- iter->refname, err.buf);
+ iter->ref.name, err.buf);
/* Schedule the loose reference for pruning if requested. */
if ((opts->flags & PACK_REFS_PRUNE)) {
struct ref_to_prune *n;
- FLEX_ALLOC_STR(n, name, iter->refname);
- oidcpy(&n->oid, iter->oid);
+ FLEX_ALLOC_STR(n, name, iter->ref.name);
+ oidcpy(&n->oid, iter->ref.oid);
n->next = refs_to_prune;
refs_to_prune = n;
}
@@ -2379,7 +2374,7 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
REFNAME_ALLOW_ONELEVEL))
continue;
- iter->base.refname = diter->relative_path;
+ iter->base.ref.name = diter->relative_path;
return ITER_OK;
}
diff --git a/refs/iterator.c b/refs/iterator.c
index 7f2e718f1c9..fe5980e1b6c 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -41,10 +41,7 @@ void base_ref_iterator_init(struct ref_iterator *iter,
struct ref_iterator_vtable *vtable)
{
iter->vtable = vtable;
- iter->refname = NULL;
- iter->referent = NULL;
- iter->oid = NULL;
- iter->flags = 0;
+ memset(&iter->ref, 0, sizeof(iter->ref));
}
struct empty_ref_iterator {
@@ -127,8 +124,8 @@ enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
* latter.
*/
if (iter_worktree) {
- int cmp = strcmp(iter_worktree->refname,
- iter_common->refname);
+ int cmp = strcmp(iter_worktree->ref.name,
+ iter_common->ref.name);
if (cmp < 0)
return ITER_SELECT_0;
else if (!cmp)
@@ -139,7 +136,7 @@ enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
* We now know that the lexicographically-next ref is a common
* ref. When the common ref is a shared one we return it.
*/
- if (parse_worktree_ref(iter_common->refname, NULL, NULL,
+ if (parse_worktree_ref(iter_common->ref.name, NULL, NULL,
NULL) == REF_WORKTREE_SHARED)
return ITER_SELECT_1;
@@ -212,10 +209,7 @@ static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
}
if (selection & ITER_YIELD_CURRENT) {
- iter->base.referent = (*iter->current)->referent;
- iter->base.refname = (*iter->current)->refname;
- iter->base.oid = (*iter->current)->oid;
- iter->base.flags = (*iter->current)->flags;
+ iter->base.ref = (*iter->current)->ref;
return ITER_OK;
}
}
@@ -313,7 +307,7 @@ static enum iterator_selection overlay_iterator_select(
else if (!front)
return ITER_SELECT_1;
- cmp = strcmp(front->refname, back->refname);
+ cmp = strcmp(front->ref.name, back->ref.name);
if (cmp < 0)
return ITER_SELECT_0;
@@ -371,7 +365,7 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
int ok;
while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
- int cmp = compare_prefix(iter->iter0->refname, iter->prefix);
+ int cmp = compare_prefix(iter->iter0->ref.name, iter->prefix);
if (cmp < 0)
continue;
/*
@@ -382,6 +376,8 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
if (cmp > 0)
return ITER_DONE;
+ iter->base.ref = iter->iter0->ref;
+
if (iter->trim) {
/*
* It is nonsense to trim off characters that
@@ -392,15 +388,11 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
* one character left in the refname after
* trimming, report it as a bug:
*/
- if (strlen(iter->iter0->refname) <= iter->trim)
+ if (strlen(iter->base.ref.name) <= iter->trim)
BUG("attempt to trim too many characters");
- iter->base.refname = iter->iter0->refname + iter->trim;
- } else {
- iter->base.refname = iter->iter0->refname;
+ iter->base.ref.name += iter->trim;
}
- iter->base.oid = iter->iter0->oid;
- iter->base.flags = iter->iter0->flags;
return ITER_OK;
}
@@ -476,14 +468,7 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
- struct reference ref = {
- .name = iter->refname,
- .target = iter->referent,
- .oid = iter->oid,
- .flags = iter->flags,
- };
-
- retval = fn(&ref, cb_data);
+ retval = fn(&iter->ref, cb_data);
if (retval)
goto out;
}
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index a8c22a0a7ff..7987acdc96a 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -908,7 +908,7 @@ static int next_record(struct packed_ref_iterator *iter)
if (iter->pos == iter->eof)
return ITER_DONE;
- iter->base.flags = REF_ISPACKED;
+ iter->base.ref.flags = REF_ISPACKED;
p = iter->pos;
if (iter->eof - p < snapshot_hexsz(iter->snapshot) + 2 ||
@@ -923,22 +923,22 @@ static int next_record(struct packed_ref_iterator *iter)
iter->pos, iter->eof - iter->pos);
strbuf_add(&iter->refname_buf, p, eol - p);
- iter->base.refname = iter->refname_buf.buf;
+ iter->base.ref.name = iter->refname_buf.buf;
if (refname_contains_nul(&iter->refname_buf))
- die("packed refname contains embedded NULL: %s", iter->base.refname);
+ die("packed refname contains embedded NULL: %s", iter->base.ref.name);
- if (check_refname_format(iter->base.refname, REFNAME_ALLOW_ONELEVEL)) {
- if (!refname_is_safe(iter->base.refname))
+ if (check_refname_format(iter->base.ref.name, REFNAME_ALLOW_ONELEVEL)) {
+ if (!refname_is_safe(iter->base.ref.name))
die("packed refname is dangerous: %s",
- iter->base.refname);
+ iter->base.ref.name);
oidclr(&iter->oid, iter->repo->hash_algo);
- iter->base.flags |= REF_BAD_NAME | REF_ISBROKEN;
+ iter->base.ref.flags |= REF_BAD_NAME | REF_ISBROKEN;
}
if (iter->snapshot->peeled == PEELED_FULLY ||
(iter->snapshot->peeled == PEELED_TAGS &&
- starts_with(iter->base.refname, "refs/tags/")))
- iter->base.flags |= REF_KNOWS_PEELED;
+ starts_with(iter->base.ref.name, "refs/tags/")))
+ iter->base.ref.flags |= REF_KNOWS_PEELED;
iter->pos = eol + 1;
@@ -956,11 +956,11 @@ static int next_record(struct packed_ref_iterator *iter)
* definitely know the value of *this* reference. But
* we suppress it if the reference is broken:
*/
- if ((iter->base.flags & REF_ISBROKEN)) {
+ if ((iter->base.ref.flags & REF_ISBROKEN)) {
oidclr(&iter->peeled, iter->repo->hash_algo);
- iter->base.flags &= ~REF_KNOWS_PEELED;
+ iter->base.ref.flags &= ~REF_KNOWS_PEELED;
} else {
- iter->base.flags |= REF_KNOWS_PEELED;
+ iter->base.ref.flags |= REF_KNOWS_PEELED;
}
} else {
oidclr(&iter->peeled, iter->repo->hash_algo);
@@ -976,15 +976,15 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
int ok;
while ((ok = next_record(iter)) == ITER_OK) {
- const char *refname = iter->base.refname;
+ const char *refname = iter->base.ref.name;
const char *prefix = iter->prefix;
if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
- !is_per_worktree_ref(iter->base.refname))
+ !is_per_worktree_ref(iter->base.ref.name))
continue;
if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
- !ref_resolves_to_object(iter->base.refname, iter->repo,
+ !ref_resolves_to_object(iter->base.ref.name, iter->repo,
&iter->oid, iter->flags))
continue;
@@ -1033,10 +1033,10 @@ static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct packed_ref_iterator *iter =
(struct packed_ref_iterator *)ref_iterator;
- if ((iter->base.flags & REF_KNOWS_PEELED)) {
+ if ((iter->base.ref.flags & REF_KNOWS_PEELED)) {
oidcpy(peeled, &iter->peeled);
return is_null_oid(&iter->peeled) ? -1 : 0;
- } else if ((iter->base.flags & (REF_ISBROKEN | REF_ISSYMREF))) {
+ } else if ((iter->base.ref.flags & (REF_ISBROKEN | REF_ISSYMREF))) {
return -1;
} else {
return peel_object(iter->repo, &iter->oid, peeled) ? -1 : 0;
@@ -1194,7 +1194,7 @@ static struct ref_iterator *packed_ref_iterator_begin(
iter->snapshot = snapshot;
acquire_snapshot(snapshot);
strbuf_init(&iter->refname_buf, 0);
- iter->base.oid = &iter->oid;
+ iter->base.ref.oid = &iter->oid;
iter->repo = ref_store->repo;
iter->flags = flags;
@@ -1436,7 +1436,7 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
if (!iter)
cmp = +1;
else
- cmp = strcmp(iter->refname, update->refname);
+ cmp = strcmp(iter->ref.name, update->refname);
}
if (!cmp) {
@@ -1459,11 +1459,11 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
}
goto error;
- } else if (!oideq(&update->old_oid, iter->oid)) {
+ } else if (!oideq(&update->old_oid, iter->ref.oid)) {
strbuf_addf(err, "cannot update ref '%s': "
"is at %s but expected %s",
update->refname,
- oid_to_hex(iter->oid),
+ oid_to_hex(iter->ref.oid),
oid_to_hex(&update->old_oid));
ret = REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE;
@@ -1527,8 +1527,8 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
struct object_id peeled;
int peel_error = ref_iterator_peel(iter, &peeled);
- if (write_packed_entry(out, iter->refname,
- iter->oid,
+ if (write_packed_entry(out, iter->ref.name,
+ iter->ref.oid,
peel_error ? NULL : &peeled))
goto write_error;
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index e5e5df16d85..f1abc396241 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -425,10 +425,10 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
level->prefix_state = entry_prefix_state;
level->index = -1;
} else {
- iter->base.refname = entry->name;
- iter->base.referent = entry->u.value.referent;
- iter->base.oid = &entry->u.value.oid;
- iter->base.flags = entry->flag;
+ iter->base.ref.name = entry->name;
+ iter->base.ref.target = entry->u.value.referent;
+ iter->base.ref.oid = &entry->u.value.oid;
+ iter->base.ref.flags = entry->flag;
return ITER_OK;
}
}
@@ -550,7 +550,7 @@ static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
{
struct cache_ref_iterator *iter =
(struct cache_ref_iterator *)ref_iterator;
- return peel_object(iter->repo, ref_iterator->oid, peeled) ? -1 : 0;
+ return peel_object(iter->repo, ref_iterator->ref.oid, peeled) ? -1 : 0;
}
static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 4ef3bd75c6a..ed749d16572 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -249,10 +249,7 @@ const char *find_descendant_ref(const char *dirname,
*/
struct ref_iterator {
struct ref_iterator_vtable *vtable;
- const char *refname;
- const char *referent;
- const struct object_id *oid;
- unsigned int flags;
+ struct reference ref;
};
/*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index d4b79286202..0e47986cb5b 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -704,10 +704,10 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
&iter->oid, flags))
continue;
- iter->base.refname = iter->ref.refname;
- iter->base.referent = referent;
- iter->base.oid = &iter->oid;
- iter->base.flags = flags;
+ iter->base.ref.name = iter->ref.refname;
+ iter->base.ref.target = referent;
+ iter->base.ref.oid = &iter->oid;
+ iter->base.ref.flags = flags;
break;
}
@@ -828,7 +828,7 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
iter = xcalloc(1, sizeof(*iter));
base_ref_iterator_init(&iter->base, &reftable_ref_iterator_vtable);
- iter->base.oid = &iter->oid;
+ iter->base.ref.oid = &iter->oid;
iter->flags = flags;
iter->refs = refs;
iter->exclude_patterns = filter_exclude_patterns(exclude_patterns);
@@ -2072,7 +2072,7 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
strbuf_reset(&iter->last_name);
strbuf_addstr(&iter->last_name, iter->log.refname);
- iter->base.refname = iter->log.refname;
+ iter->base.ref.name = iter->log.refname;
break;
}
--
2.51.1.930.gacf6e81ea2.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v4 03/14] refs: fully reset `struct ref_iterator::ref` on iteration
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 01/14] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 02/14] refs: introduce `.ref` field for the base iterator Patrick Steinhardt
@ 2025-10-23 7:16 ` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 04/14] refs: refactor reference status flags Patrick Steinhardt
` (11 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 7:16 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
With the introduction of the `struct ref_iterator::ref` field it now is
a whole lot easier to introduce new fields that become accessible to the
caller without having to adapt every single callsite. But there's a
downside: when a new field is introduced we always have to adapt all
backends to set that field.
This isn't something we can avoid in the general case: when the new
field is expected to be populated by all backends we of course cannot
avoid doing so. But new fields may be entirely optional, in which case
we'd still have such churn. And furthermore, it is very easy right now
to leak state from a previous iteration into the next iteration.
Address this issue by ensuring that the reference backends all fully
reset the field on every single iteration. This ensures that no state
from previous iterations can leak into the next one. And it ensures that
any newly introduced fields will be zeroed out by default.
Note that we don't have to explicitly adapt the "files" backend, as it
uses the `cache_ref_iterator` internally. Furthermore, other "wrapping"
iterators like for example the `prefix_ref_iterator` copy around the
whole reference, so these don't need to be adapted either.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs/packed-backend.c | 3 ++-
refs/ref-cache.c | 1 +
refs/reftable-backend.c | 1 +
3 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 7987acdc96a..711e07f8326 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -882,6 +882,7 @@ static int next_record(struct packed_ref_iterator *iter)
{
const char *p, *eol;
+ memset(&iter->base.ref, 0, sizeof(iter->base.ref));
strbuf_reset(&iter->refname_buf);
/*
@@ -916,6 +917,7 @@ static int next_record(struct packed_ref_iterator *iter)
!isspace(*p++))
die_invalid_line(iter->snapshot->refs->path,
iter->pos, iter->eof - iter->pos);
+ iter->base.ref.oid = &iter->oid;
eol = memchr(p, '\n', iter->eof - p);
if (!eol)
@@ -1194,7 +1196,6 @@ static struct ref_iterator *packed_ref_iterator_begin(
iter->snapshot = snapshot;
acquire_snapshot(snapshot);
strbuf_init(&iter->refname_buf, 0);
- iter->base.ref.oid = &iter->oid;
iter->repo = ref_store->repo;
iter->flags = flags;
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index f1abc396241..e427848879d 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -425,6 +425,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
level->prefix_state = entry_prefix_state;
level->index = -1;
} else {
+ memset(&iter->base.ref, 0, sizeof(iter->base.ref));
iter->base.ref.name = entry->name;
iter->base.ref.target = entry->u.value.referent;
iter->base.ref.oid = &entry->u.value.oid;
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 0e47986cb5b..728886eafd3 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -704,6 +704,7 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
&iter->oid, flags))
continue;
+ memset(&iter->base.ref, 0, sizeof(iter->base.ref));
iter->base.ref.name = iter->ref.refname;
iter->base.ref.target = referent;
iter->base.ref.oid = &iter->oid;
--
2.51.1.930.gacf6e81ea2.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v4 04/14] refs: refactor reference status flags
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
` (2 preceding siblings ...)
2025-10-23 7:16 ` [PATCH v4 03/14] refs: fully reset `struct ref_iterator::ref` on iteration Patrick Steinhardt
@ 2025-10-23 7:16 ` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 05/14] refs: expose peeled object ID via the iterator Patrick Steinhardt
` (10 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 7:16 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
The reference flags encode information like whether or not a reference
is a symbolic reference or whether it may be broken. This information is
stored in a `int flags` bitfield, which is in conflict with our modern
best practices; we tend to use an unsigned integer to store flags.
Change the type of the field to be `unsigned`. While at it, refactor the
individual flags to be part of an `enum` instead of using preprocessor
defines.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs.h | 41 +++++++++++++++++++++--------------------
1 file changed, 21 insertions(+), 20 deletions(-)
diff --git a/refs.h b/refs.h
index 68d235438c2..4f0a685714f 100644
--- a/refs.h
+++ b/refs.h
@@ -333,27 +333,28 @@ struct ref_transaction;
* stored in ref_iterator::flags. Other bits are for internal use
* only:
*/
+enum reference_status {
+ /* Reference is a symbolic reference. */
+ REF_ISSYMREF = (1 << 0),
-/* Reference is a symbolic reference. */
-#define REF_ISSYMREF 0x01
+ /* Reference is a packed reference. */
+ REF_ISPACKED = (1 << 1),
-/* Reference is a packed reference. */
-#define REF_ISPACKED 0x02
-
-/*
- * Reference cannot be resolved to an object name: dangling symbolic
- * reference (directly or indirectly), corrupt reference file,
- * reference exists but name is bad, or symbolic reference refers to
- * ill-formatted reference name.
- */
-#define REF_ISBROKEN 0x04
+ /*
+ * Reference cannot be resolved to an object name: dangling symbolic
+ * reference (directly or indirectly), corrupt reference file,
+ * reference exists but name is bad, or symbolic reference refers to
+ * ill-formatted reference name.
+ */
+ REF_ISBROKEN = (1 << 2),
-/*
- * Reference name is not well formed.
- *
- * See git-check-ref-format(1) for the definition of well formed ref names.
- */
-#define REF_BAD_NAME 0x08
+ /*
+ * Reference name is not well formed.
+ *
+ * See git-check-ref-format(1) for the definition of well formed ref names.
+ */
+ REF_BAD_NAME = (1 << 3),
+};
/* A reference passed to `for_each_ref()`-style callbacks. */
struct reference {
@@ -370,8 +371,8 @@ struct reference {
*/
const struct object_id *oid;
- /* A bitfield of `REF_` flags. */
- int flags;
+ /* A bitfield of `enum reference_status` flags. */
+ unsigned flags;
};
/*
--
2.51.1.930.gacf6e81ea2.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v4 05/14] refs: expose peeled object ID via the iterator
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
` (3 preceding siblings ...)
2025-10-23 7:16 ` [PATCH v4 04/14] refs: refactor reference status flags Patrick Steinhardt
@ 2025-10-23 7:16 ` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 06/14] upload-pack: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
` (9 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 7:16 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
Both the "files" and "reftable" backend are able to store peeled values
for tags in the respective formats. This allows for a more efficient
lookup of the target object of such a tag without having to manually
peel via the object database.
The infrastructure to access these peeled object IDs is somewhat funky
though. When iterating through objects, we store a pointer reference to
the current iterator in a global variable. The callbacks invoked by that
iterator are then expected to call `peel_iterated_oid()`, which checks
whether the globally-stored iterator's current reference refers to the
one handed into that function. If so, we ask the iterator to peel the
object, otherwise we manually peel the object via the object database.
Depending on global state like this is somewhat weird and also quite
fragile.
Introduce a new `struct reference::peeled_oid` field that can be
populated by the reference backends. This field can be accessed via a
new function `reference_get_peeled_oid()` that either uses that value,
if set, or alternatively peels via the ODB. With this change we don't
have to rely on global state anymore, but make the peeled object ID
available to the callback functions directly.
Adjust trivial callers that already have a `struct reference` available.
Remaining callers will be adjusted in subsequent commits.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/describe.c | 2 +-
builtin/gc.c | 2 +-
builtin/pack-objects.c | 7 ++++---
commit-graph.c | 2 +-
ls-refs.c | 2 +-
midx-write.c | 2 +-
pseudo-merge.c | 2 +-
refs.c | 12 ++++++++++++
refs.h | 19 +++++++++++++++++++
refs/packed-backend.c | 1 +
refs/reftable-backend.c | 5 +++++
repack-midx.c | 2 +-
12 files changed, 48 insertions(+), 10 deletions(-)
diff --git a/builtin/describe.c b/builtin/describe.c
index 79545350443..443546aaac9 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -208,7 +208,7 @@ static int get_name(const struct reference *ref, void *cb_data UNUSED)
}
/* Is it annotated? */
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled)) {
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
is_annotated = !oideq(ref->oid, &peeled);
} else {
oidcpy(&peeled, ref->oid);
diff --git a/builtin/gc.c b/builtin/gc.c
index 9de5de175f6..f0cf20d4238 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -1109,7 +1109,7 @@ static int dfs_on_ref(const struct reference *ref, void *cb_data)
struct commit_list *stack = NULL;
struct commit *commit;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
if (odb_read_object_info(the_repository->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 39633a0158e..1613fecb669 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -838,7 +838,7 @@ static int mark_tagged(const struct reference *ref, void *cb_data UNUSED)
if (entry)
entry->tagged = 1;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled)) {
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
entry = packlist_find(&to_pack, &peeled);
if (entry)
entry->tagged = 1;
@@ -3309,7 +3309,8 @@ static int add_ref_tag(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled) && obj_is_packed(&peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled) &&
+ obj_is_packed(&peeled))
add_tag_chain(ref->oid);
return 0;
}
@@ -4537,7 +4538,7 @@ static int mark_bitmap_preferred_tip(const struct reference *ref, void *data UNU
struct object_id peeled;
struct object *object;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
object = parse_object_or_die(the_repository, maybe_peeled, ref->name);
diff --git a/commit-graph.c b/commit-graph.c
index f91af416259..80be2ff2c39 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1857,7 +1857,7 @@ static int add_ref_to_set(const struct reference *ref, void *cb_data)
struct object_id peeled;
struct refs_cb_data *data = (struct refs_cb_data *)cb_data;
- if (!peel_iterated_oid(data->repo, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(data->repo, ref, &peeled))
maybe_peeled = &peeled;
if (odb_read_object_info(data->repo->objects, maybe_peeled, NULL) == OBJ_COMMIT)
oidset_insert(data->commits, maybe_peeled);
diff --git a/ls-refs.c b/ls-refs.c
index 64d02723691..8641281b86c 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -110,7 +110,7 @@ static int send_ref(const struct reference *ref, void *cb_data)
if (data->peel && ref->oid) {
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
strbuf_addf(&data->buf, " peeled:%s", oid_to_hex(&peeled));
}
diff --git a/midx-write.c b/midx-write.c
index f4dd875747a..23e61cb0001 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -709,7 +709,7 @@ static int add_ref_to_pending(const struct reference *ref, void *cb_data)
return 0;
}
- if (!peel_iterated_oid(revs->repo, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(revs->repo, ref, &peeled))
maybe_peeled = &peeled;
object = parse_object_or_die(revs->repo, maybe_peeled, ref->name);
diff --git a/pseudo-merge.c b/pseudo-merge.c
index 0abd51b42c1..a2d5bd85f95 100644
--- a/pseudo-merge.c
+++ b/pseudo-merge.c
@@ -230,7 +230,7 @@ static int find_pseudo_merge_group_for_ref(const struct reference *ref, void *_d
uint32_t i;
int has_bitmap;
- if (!peel_iterated_oid(the_repository, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
c = lookup_commit(the_repository, maybe_peeled);
diff --git a/refs.c b/refs.c
index f96cf43b128..1b1551f9814 100644
--- a/refs.c
+++ b/refs.c
@@ -2334,6 +2334,18 @@ int peel_iterated_oid(struct repository *r, const struct object_id *base, struct
return peel_object(r, base, peeled) ? -1 : 0;
}
+int reference_get_peeled_oid(struct repository *repo,
+ const struct reference *ref,
+ struct object_id *peeled_oid)
+{
+ if (ref->peeled_oid) {
+ oidcpy(peeled_oid, ref->peeled_oid);
+ return 0;
+ }
+
+ return peel_object(repo, ref->oid, peeled_oid) ? -1 : 0;
+}
+
int refs_update_symref(struct ref_store *refs, const char *ref,
const char *target, const char *logmsg)
{
diff --git a/refs.h b/refs.h
index 4f0a685714f..886ed2c0f43 100644
--- a/refs.h
+++ b/refs.h
@@ -371,10 +371,29 @@ struct reference {
*/
const struct object_id *oid;
+ /*
+ * An optional peeled object ID. This field _may_ be set for tags in
+ * case the peeled value is present in the backend. Please refer to
+ * `reference_get_peeled_oid()`.
+ */
+ const struct object_id *peeled_oid;
+
/* A bitfield of `enum reference_status` flags. */
unsigned flags;
};
+/*
+ * Peel the tag to a non-tag commit. If present, this uses the peeled object ID
+ * exposed by the reference backend. Otherwise, the object is peeled via the
+ * object database, which is less efficient.
+ *
+ * Return `0` if the reference could be peeled, a negative error code
+ * otherwise.
+ */
+int reference_get_peeled_oid(struct repository *repo,
+ const struct reference *ref,
+ struct object_id *peeled_oid);
+
/*
* The signature for the callback function for the for_each_*()
* functions below. The memory pointed to by the `struct reference`
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 711e07f8326..1fefefd54ed 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -963,6 +963,7 @@ static int next_record(struct packed_ref_iterator *iter)
iter->base.ref.flags &= ~REF_KNOWS_PEELED;
} else {
iter->base.ref.flags |= REF_KNOWS_PEELED;
+ iter->base.ref.peeled_oid = &iter->peeled;
}
} else {
oidclr(&iter->peeled, iter->repo->hash_algo);
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 728886eafd3..e214e120d77 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -547,6 +547,7 @@ struct reftable_ref_iterator {
struct reftable_iterator iter;
struct reftable_ref_record ref;
struct object_id oid;
+ struct object_id peeled_oid;
char *prefix;
size_t prefix_len;
@@ -671,6 +672,8 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
case REFTABLE_REF_VAL2:
oidread(&iter->oid, iter->ref.value.val2.value,
refs->base.repo->hash_algo);
+ oidread(&iter->peeled_oid, iter->ref.value.val2.target_value,
+ refs->base.repo->hash_algo);
break;
case REFTABLE_REF_SYMREF:
referent = refs_resolve_ref_unsafe(&iter->refs->base,
@@ -708,6 +711,8 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
iter->base.ref.name = iter->ref.refname;
iter->base.ref.target = referent;
iter->base.ref.oid = &iter->oid;
+ if (iter->ref.value_type == REFTABLE_REF_VAL2)
+ iter->base.ref.peeled_oid = &iter->peeled_oid;
iter->base.ref.flags = flags;
break;
diff --git a/repack-midx.c b/repack-midx.c
index 349f7e20b53..74bdfa3a6e9 100644
--- a/repack-midx.c
+++ b/repack-midx.c
@@ -22,7 +22,7 @@ static int midx_snapshot_ref_one(const struct reference *ref, void *_data)
const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
- if (!peel_iterated_oid(data->repo, ref->oid, &peeled))
+ if (!reference_get_peeled_oid(data->repo, ref, &peeled))
maybe_peeled = &peeled;
if (oidset_insert(&data->seen, maybe_peeled))
--
2.51.1.930.gacf6e81ea2.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v4 06/14] upload-pack: convert to use `reference_get_peeled_oid()`
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
` (4 preceding siblings ...)
2025-10-23 7:16 ` [PATCH v4 05/14] refs: expose peeled object ID via the iterator Patrick Steinhardt
@ 2025-10-23 7:16 ` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 07/14] ref-filter: propagate peeled object ID Patrick Steinhardt
` (8 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 7:16 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
The `write_v0_ref()` callback is invoked from two callsites:
- Once via `send_ref()` which is a callback passed to
`for_each_namespaced_ref_1()` and `refs_head_ref_namespaced()`.
- Once manually to announce capabilities.
When sending references to the client we also send the peeled value of
tags. As we don't have a `struct reference` available in the second
case, we cannot easily peel by calling `reference_get_peeled_oid()`, but
we instead have to depend on on global state via `peel_iterated_oid()`.
We do have a reference available though in the first case, it's only the
second case that keeps us from using `reference_get_peeled_oid()`. But
that second case only announces capabilities anyway, so we're not really
handling a reference at all here.
Adapt that case to construct a reference manually and pass that to
`write_v0_ref()`. Start to use `reference_get_peeled_oid()` now that we
always have a `struct reference` available.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
upload-pack.c | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/upload-pack.c b/upload-pack.c
index 0d563ae74e9..2d2b70cbf2d 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -1249,15 +1249,15 @@ static void format_session_id(struct strbuf *buf, struct upload_pack_data *d) {
}
static void write_v0_ref(struct upload_pack_data *data,
- const char *refname, const char *refname_nons,
- const struct object_id *oid)
+ const struct reference *ref,
+ const char *refname_nons)
{
static const char *capabilities = "multi_ack thin-pack side-band"
" side-band-64k ofs-delta shallow deepen-since deepen-not"
" deepen-relative no-progress include-tag multi_ack_detailed";
struct object_id peeled;
- if (mark_our_ref(refname_nons, refname, oid, &data->hidden_refs))
+ if (mark_our_ref(refname_nons, ref->name, ref->oid, &data->hidden_refs))
return;
if (capabilities) {
@@ -1267,7 +1267,7 @@ static void write_v0_ref(struct upload_pack_data *data,
format_symref_info(&symref_info, &data->symref);
format_session_id(&session_id, data);
packet_fwrite_fmt(stdout, "%s %s%c%s%s%s%s%s%s%s object-format=%s agent=%s\n",
- oid_to_hex(oid), refname_nons,
+ oid_to_hex(ref->oid), refname_nons,
0, capabilities,
(data->allow_uor & ALLOW_TIP_SHA1) ?
" allow-tip-sha1-in-want" : "",
@@ -1283,17 +1283,17 @@ static void write_v0_ref(struct upload_pack_data *data,
strbuf_release(&session_id);
data->sent_capabilities = 1;
} else {
- packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(oid), refname_nons);
+ packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(ref->oid), refname_nons);
}
capabilities = NULL;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
packet_fwrite_fmt(stdout, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
return;
}
static int send_ref(const struct reference *ref, void *cb_data)
{
- write_v0_ref(cb_data, ref->name, strip_namespace(ref->name), ref->oid);
+ write_v0_ref(cb_data, ref, strip_namespace(ref->name));
return 0;
}
@@ -1442,8 +1442,12 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
send_ref, &data);
for_each_namespaced_ref_1(send_ref, &data);
if (!data.sent_capabilities) {
- const char *refname = "capabilities^{}";
- write_v0_ref(&data, refname, refname, null_oid(the_hash_algo));
+ struct reference ref = {
+ .name = "capabilities^{}",
+ .oid = null_oid(the_hash_algo),
+ };
+
+ write_v0_ref(&data, &ref, ref.name);
}
/*
* fflush stdout before calling advertise_shallow_grafts because send_ref
--
2.51.1.930.gacf6e81ea2.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v4 07/14] ref-filter: propagate peeled object ID
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
` (5 preceding siblings ...)
2025-10-23 7:16 ` [PATCH v4 06/14] upload-pack: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
@ 2025-10-23 7:16 ` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 08/14] builtin/show-ref: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
` (7 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 7:16 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
When queueing a reference in the "ref-filter" subsystem we end up
creating a new ref array item that contains the reference's info. One
bit of info that we always discard though is the peeled object ID, and
because of that we are forced to use `peel_iterated_oid()`.
Refactor the code to propagate the peeled object ID via the ref array,
if available. This allows us to manually peel tags without having to go
through the object database.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/ls-remote.c | 2 +-
builtin/tag.c | 2 +-
builtin/verify-tag.c | 2 +-
ref-filter.c | 66 ++++++++++++++++++++++++++++++----------------------
ref-filter.h | 5 +++-
5 files changed, 45 insertions(+), 32 deletions(-)
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index df09000b30d..fe77829557f 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -156,7 +156,7 @@ int cmd_ls_remote(int argc,
continue;
if (!tail_match(&pattern, ref->name))
continue;
- item = ref_array_push(&ref_array, ref->name, &ref->old_oid);
+ item = ref_array_push(&ref_array, ref->name, &ref->old_oid, NULL);
item->symref = xstrdup_or_null(ref->symref);
}
diff --git a/builtin/tag.c b/builtin/tag.c
index f0665af3acd..01eba90c5c7 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -153,7 +153,7 @@ static int verify_tag(const char *name, const char *ref UNUSED,
return -1;
if (format->format)
- pretty_print_ref(name, oid, format);
+ pretty_print_ref(name, oid, NULL, format);
return 0;
}
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index cd6bc11095d..558121eaa16 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -67,7 +67,7 @@ int cmd_verify_tag(int argc,
}
if (format.format)
- pretty_print_ref(name, &oid, &format);
+ pretty_print_ref(name, &oid, NULL, &format);
}
return had_error;
}
diff --git a/ref-filter.c b/ref-filter.c
index 6837fa60a9b..7fd8babec8f 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2578,8 +2578,15 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
* If it is a tag object, see if we use the peeled value. If we do,
* grab the peeled OID.
*/
- if (need_tagged && peel_iterated_oid(the_repository, &obj->oid, &oi_deref.oid))
- die("bad tag");
+ if (need_tagged) {
+ if (!is_null_oid(&ref->peeled_oid)) {
+ oidcpy(&oi_deref.oid, &ref->peeled_oid);
+ } else if (!peel_object(the_repository, &obj->oid, &oi_deref.oid)) {
+ /* We managed to peel the object ourselves. */
+ } else {
+ die("bad tag");
+ }
+ }
return get_object(ref, 1, &obj, &oi_deref, err);
}
@@ -2807,12 +2814,15 @@ static int match_points_at(struct oid_array *points_at,
* Callers can then fill in other struct members at their leisure.
*/
static struct ref_array_item *new_ref_array_item(const char *refname,
- const struct object_id *oid)
+ const struct object_id *oid,
+ const struct object_id *peeled_oid)
{
struct ref_array_item *ref;
FLEX_ALLOC_STR(ref, refname, refname);
oidcpy(&ref->objectname, oid);
+ if (peeled_oid)
+ oidcpy(&ref->peeled_oid, peeled_oid);
ref->rest = NULL;
return ref;
@@ -2826,9 +2836,10 @@ static void ref_array_append(struct ref_array *array, struct ref_array_item *ref
struct ref_array_item *ref_array_push(struct ref_array *array,
const char *refname,
- const struct object_id *oid)
+ const struct object_id *oid,
+ const struct object_id *peeled_oid)
{
- struct ref_array_item *ref = new_ref_array_item(refname, oid);
+ struct ref_array_item *ref = new_ref_array_item(refname, oid, peeled_oid);
ref_array_append(array, ref);
return ref;
}
@@ -2871,25 +2882,25 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)
return ref_kind_from_refname(refname);
}
-static struct ref_array_item *apply_ref_filter(const char *refname, const char *referent, const struct object_id *oid,
- int flag, struct ref_filter *filter)
+static struct ref_array_item *apply_ref_filter(const struct reference *ref,
+ struct ref_filter *filter)
{
- struct ref_array_item *ref;
+ struct ref_array_item *item;
struct commit *commit = NULL;
unsigned int kind;
- if (flag & REF_BAD_NAME) {
- warning(_("ignoring ref with broken name %s"), refname);
+ if (ref->flags & REF_BAD_NAME) {
+ warning(_("ignoring ref with broken name %s"), ref->name);
return NULL;
}
- if (flag & REF_ISBROKEN) {
- warning(_("ignoring broken ref %s"), refname);
+ if (ref->flags & REF_ISBROKEN) {
+ warning(_("ignoring broken ref %s"), ref->name);
return NULL;
}
/* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */
- kind = filter_ref_kind(filter, refname);
+ kind = filter_ref_kind(filter, ref->name);
/*
* Generally HEAD refs are printed with special description denoting a rebase,
@@ -2902,13 +2913,13 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const char *
else if (!(kind & filter->kind))
return NULL;
- if (!filter_pattern_match(filter, refname))
+ if (!filter_pattern_match(filter, ref->name))
return NULL;
- if (filter_exclude_match(filter, refname))
+ if (filter_exclude_match(filter, ref->name))
return NULL;
- if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname))
+ if (filter->points_at.nr && !match_points_at(&filter->points_at, ref->oid, ref->name))
return NULL;
/*
@@ -2918,7 +2929,7 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const char *
*/
if (filter->reachable_from || filter->unreachable_from ||
filter->with_commit || filter->no_commit || filter->verbose) {
- commit = lookup_commit_reference_gently(the_repository, oid, 1);
+ commit = lookup_commit_reference_gently(the_repository, ref->oid, 1);
if (!commit)
return NULL;
/* We perform the filtering for the '--contains' option... */
@@ -2936,13 +2947,13 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const char *
* to do its job and the resulting list may yet to be pruned
* by maxcount logic.
*/
- ref = new_ref_array_item(refname, oid);
- ref->commit = commit;
- ref->flag = flag;
- ref->kind = kind;
- ref->symref = xstrdup_or_null(referent);
+ item = new_ref_array_item(ref->name, ref->oid, ref->peeled_oid);
+ item->commit = commit;
+ item->flag = ref->flags;
+ item->kind = kind;
+ item->symref = xstrdup_or_null(ref->target);
- return ref;
+ return item;
}
struct ref_filter_cbdata {
@@ -2959,8 +2970,7 @@ static int filter_one(const struct reference *ref, void *cb_data)
struct ref_filter_cbdata *ref_cbdata = cb_data;
struct ref_array_item *item;
- item = apply_ref_filter(ref->name, ref->target, ref->oid,
- ref->flags, ref_cbdata->filter);
+ item = apply_ref_filter(ref, ref_cbdata->filter);
if (item)
ref_array_append(ref_cbdata->array, item);
@@ -2997,8 +3007,7 @@ static int filter_and_format_one(const struct reference *ref, void *cb_data)
struct ref_array_item *item;
struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
- item = apply_ref_filter(ref->name, ref->target, ref->oid,
- ref->flags, ref_cbdata->filter);
+ item = apply_ref_filter(ref, ref_cbdata->filter);
if (!item)
return 0;
@@ -3585,13 +3594,14 @@ void print_formatted_ref_array(struct ref_array *array, struct ref_format *forma
}
void pretty_print_ref(const char *name, const struct object_id *oid,
+ const struct object_id *peeled_oid,
struct ref_format *format)
{
struct ref_array_item *ref_item;
struct strbuf output = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
- ref_item = new_ref_array_item(name, oid);
+ ref_item = new_ref_array_item(name, oid, peeled_oid);
ref_item->kind = ref_kind_from_refname(name);
if (format_ref_array_item(ref_item, format, &output, &err))
die("%s", err.buf);
diff --git a/ref-filter.h b/ref-filter.h
index 235c60f79c9..120221b47fa 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -41,6 +41,7 @@ enum ref_sorting_order {
struct ref_array_item {
struct object_id objectname;
+ struct object_id peeled_oid;
const char *rest;
int flag;
unsigned int kind;
@@ -187,6 +188,7 @@ void print_formatted_ref_array(struct ref_array *array, struct ref_format *forma
* name must be a fully qualified refname.
*/
void pretty_print_ref(const char *name, const struct object_id *oid,
+ const struct object_id *peeled_oid,
struct ref_format *format);
/*
@@ -195,7 +197,8 @@ void pretty_print_ref(const char *name, const struct object_id *oid,
*/
struct ref_array_item *ref_array_push(struct ref_array *array,
const char *refname,
- const struct object_id *oid);
+ const struct object_id *oid,
+ const struct object_id *peeled_oid);
/*
* If the provided format includes ahead-behind atoms, then compute the
--
2.51.1.930.gacf6e81ea2.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v4 08/14] builtin/show-ref: convert to use `reference_get_peeled_oid()`
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
` (6 preceding siblings ...)
2025-10-23 7:16 ` [PATCH v4 07/14] ref-filter: propagate peeled object ID Patrick Steinhardt
@ 2025-10-23 7:16 ` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 09/14] refs: drop `current_ref_iter` hack Patrick Steinhardt
` (6 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 7:16 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
The git-show-ref(1) command has multiple different modes:
- It knows to show all references matching a pattern.
- It knows to list all references that are an exact match to whatever
the user has provided.
- It knows to check for reference existence.
The first two commands use mostly the same infrastructure to print the
references via `show_one()`. But while the former mode uses a proper
iterator and thus has a `struct reference` available in its context, the
latter calls `refs_read_ref()` and thus doesn't. Consequently, we cannot
easily use `reference_get_peeled_oid()` to print the peeled value.
Adapt the code so that we manually construct a `struct reference` when
verifying refs. We wouldn't ever have the peeled value available anyway
as we're not using an iterator here, so we can simply plug in the values
we _do_ have.
With this change we now have a `struct reference` available at both
callsites of `show_one()` and can thus pass it, which allows us to use
`reference_get_peeled_oid()` instead of `peel_iterated_oid()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/show-ref.c | 32 +++++++++++++++++++-------------
1 file changed, 19 insertions(+), 13 deletions(-)
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 4803b5e5986..4d4984e4e0c 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -31,31 +31,31 @@ struct show_one_options {
};
static void show_one(const struct show_one_options *opts,
- const char *refname, const struct object_id *oid)
+ const struct reference *ref)
{
const char *hex;
struct object_id peeled;
- if (!odb_has_object(the_repository->objects, oid,
+ if (!odb_has_object(the_repository->objects, ref->oid,
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
- die("git show-ref: bad ref %s (%s)", refname,
- oid_to_hex(oid));
+ die("git show-ref: bad ref %s (%s)", ref->name,
+ oid_to_hex(ref->oid));
if (opts->quiet)
return;
- hex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev);
+ hex = repo_find_unique_abbrev(the_repository, ref->oid, opts->abbrev);
if (opts->hash_only)
printf("%s\n", hex);
else
- printf("%s %s\n", hex, refname);
+ printf("%s %s\n", hex, ref->name);
if (!opts->deref_tags)
return;
- if (!peel_iterated_oid(the_repository, oid, &peeled)) {
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
hex = repo_find_unique_abbrev(the_repository, &peeled, opts->abbrev);
- printf("%s %s^{}\n", hex, refname);
+ printf("%s %s^{}\n", hex, ref->name);
}
}
@@ -93,7 +93,7 @@ static int show_ref(const struct reference *ref, void *cbdata)
match:
data->found_match++;
- show_one(data->show_one_opts, ref->name, ref->oid);
+ show_one(data->show_one_opts, ref);
return 0;
}
@@ -175,12 +175,18 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) &&
!refs_read_ref(get_main_ref_store(the_repository), *refs, &oid)) {
- show_one(show_one_opts, *refs, &oid);
- }
- else if (!show_one_opts->quiet)
+ struct reference ref = {
+ .name = *refs,
+ .oid = &oid,
+ };
+
+ show_one(show_one_opts, &ref);
+ } else if (!show_one_opts->quiet) {
die("'%s' - not a valid ref", *refs);
- else
+ } else {
return 1;
+ }
+
refs++;
}
--
2.51.1.930.gacf6e81ea2.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v4 09/14] refs: drop `current_ref_iter` hack
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
` (7 preceding siblings ...)
2025-10-23 7:16 ` [PATCH v4 08/14] builtin/show-ref: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
@ 2025-10-23 7:16 ` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 10/14] refs: drop infrastructure to peel via iterators Patrick Steinhardt
` (5 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 7:16 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
In preceding commits we have refactored all callers of
`peel_iterated_oid()` to instead use `reference_get_peeled_oid()`. This
allows us to thus get rid of the former function.
Getting rid of that function is nice, but even nicer is that this also
allows us to get rid of the `current_ref_iter` hack. This global
variable tracked the currently-active ref iterator so that we can use it
to peel an object ID. Now that the peeled object ID is propagated via
`struct reference` though we don't have to depend on this hack anymore,
which makes for a more robust and easier-to-understand infrastructure.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs.c | 10 ----------
refs/iterator.c | 5 -----
refs/refs-internal.h | 13 -------------
3 files changed, 28 deletions(-)
diff --git a/refs.c b/refs.c
index 1b1551f9814..9d8f0a9ca4a 100644
--- a/refs.c
+++ b/refs.c
@@ -2324,16 +2324,6 @@ int refs_optimize(struct ref_store *refs, struct pack_refs_opts *opts)
return refs->be->optimize(refs, opts);
}
-int peel_iterated_oid(struct repository *r, const struct object_id *base, struct object_id *peeled)
-{
- if (current_ref_iter &&
- (current_ref_iter->ref.oid == base ||
- oideq(current_ref_iter->ref.oid, base)))
- return ref_iterator_peel(current_ref_iter, peeled);
-
- return peel_object(r, base, peeled) ? -1 : 0;
-}
-
int reference_get_peeled_oid(struct repository *repo,
const struct reference *ref,
struct object_id *peeled_oid)
diff --git a/refs/iterator.c b/refs/iterator.c
index fe5980e1b6c..072c6aacdb0 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -458,15 +458,11 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
return ref_iterator;
}
-struct ref_iterator *current_ref_iter = NULL;
-
int do_for_each_ref_iterator(struct ref_iterator *iter,
each_ref_fn fn, void *cb_data)
{
int retval = 0, ok;
- struct ref_iterator *old_ref_iter = current_ref_iter;
- current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
retval = fn(&iter->ref, cb_data);
if (retval)
@@ -474,7 +470,6 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
}
out:
- current_ref_iter = old_ref_iter;
if (ok == ITER_ERROR)
retval = -1;
ref_iterator_free(iter);
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index ed749d16572..f4f845bbeaf 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -376,19 +376,6 @@ struct ref_iterator_vtable {
ref_iterator_release_fn *release;
};
-/*
- * current_ref_iter is a performance hack: when iterating over
- * references using the for_each_ref*() functions, current_ref_iter is
- * set to the reference iterator before calling the callback function.
- * If the callback function calls peel_ref(), then peel_ref() first
- * checks whether the reference to be peeled is the one referred to by
- * the iterator (it usually is) and if so, asks the iterator for the
- * peeled version of the reference if it is available. This avoids a
- * refname lookup in a common case. current_ref_iter is set to NULL
- * when the iteration is over.
- */
-extern struct ref_iterator *current_ref_iter;
-
struct ref_store;
/* refs backends */
--
2.51.1.930.gacf6e81ea2.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v4 10/14] refs: drop infrastructure to peel via iterators
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
` (8 preceding siblings ...)
2025-10-23 7:16 ` [PATCH v4 09/14] refs: drop `current_ref_iter` hack Patrick Steinhardt
@ 2025-10-23 7:16 ` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 11/14] object: add flag to `peel_object()` to verify object type Patrick Steinhardt
` (4 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 7:16 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
Now that the peeled object ID gets propagated via the `struct reference`
there is no need anymore to call into the reference iterator itself to
dereference an object. Remove this infrastructure.
Most of the changes are straight-forward deletions of code. There is one
exception though in `refs/packed-backend.c::write_with_updates()`. Here
we stop peeling the iterator and instead just pass the peeled object ID
of that iterator directly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs.h | 14 --------------
refs/debug.c | 11 -----------
refs/files-backend.c | 17 -----------------
refs/iterator.c | 36 ------------------------------------
refs/packed-backend.c | 24 +-----------------------
refs/ref-cache.c | 9 ---------
refs/refs-internal.h | 7 -------
refs/reftable-backend.c | 24 ------------------------
8 files changed, 1 insertion(+), 141 deletions(-)
diff --git a/refs.h b/refs.h
index 886ed2c0f43..2dd7ac1a16a 100644
--- a/refs.h
+++ b/refs.h
@@ -1289,10 +1289,6 @@ int repo_migrate_ref_storage_format(struct repository *repo,
* to the next entry, ref_iterator_advance() aborts the iteration,
* frees the ref_iterator, and returns ITER_ERROR.
*
- * The reference currently being looked at can be peeled by calling
- * ref_iterator_peel(). This function is often faster than peel_ref(),
- * so it should be preferred when iterating over references.
- *
* Putting it all together, a typical iteration looks like this:
*
* int ok;
@@ -1307,9 +1303,6 @@ int repo_migrate_ref_storage_format(struct repository *repo,
* // Access information about the current reference:
* if (!(iter->flags & REF_ISSYMREF))
* printf("%s is %s\n", iter->refname, oid_to_hex(iter->oid));
- *
- * // If you need to peel the reference:
- * ref_iterator_peel(iter, &oid);
* }
*
* if (ok != ITER_DONE)
@@ -1400,13 +1393,6 @@ enum ref_iterator_seek_flag {
int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname,
unsigned int flags);
-/*
- * If possible, peel the reference currently being viewed by the
- * iterator. Return 0 on success.
- */
-int ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled);
-
/* Free the reference iterator and any associated resources. */
void ref_iterator_free(struct ref_iterator *ref_iterator);
diff --git a/refs/debug.c b/refs/debug.c
index 67718bd1f49..01499b9033c 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -177,16 +177,6 @@ static int debug_ref_iterator_seek(struct ref_iterator *ref_iterator,
return res;
}
-static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct debug_ref_iterator *diter =
- (struct debug_ref_iterator *)ref_iterator;
- int res = diter->iter->vtable->peel(diter->iter, peeled);
- trace_printf_key(&trace_refs, "iterator_peel: %s: %d\n", diter->iter->ref.name, res);
- return res;
-}
-
static void debug_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct debug_ref_iterator *diter =
@@ -198,7 +188,6 @@ static void debug_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable debug_ref_iterator_vtable = {
.advance = debug_ref_iterator_advance,
.seek = debug_ref_iterator_seek,
- .peel = debug_ref_iterator_peel,
.release = debug_ref_iterator_release,
};
diff --git a/refs/files-backend.c b/refs/files-backend.c
index fac53fa052d..5aeb454fb47 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -993,15 +993,6 @@ static int files_ref_iterator_seek(struct ref_iterator *ref_iterator,
return ref_iterator_seek(iter->iter0, refname, flags);
}
-static int files_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct files_ref_iterator *iter =
- (struct files_ref_iterator *)ref_iterator;
-
- return ref_iterator_peel(iter->iter0, peeled);
-}
-
static void files_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct files_ref_iterator *iter =
@@ -1012,7 +1003,6 @@ static void files_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable files_ref_iterator_vtable = {
.advance = files_ref_iterator_advance,
.seek = files_ref_iterator_seek,
- .peel = files_ref_iterator_peel,
.release = files_ref_iterator_release,
};
@@ -2388,12 +2378,6 @@ static int files_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
BUG("ref_iterator_seek() called for reflog_iterator");
}
-static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
- struct object_id *peeled UNUSED)
-{
- BUG("ref_iterator_peel() called for reflog_iterator");
-}
-
static void files_reflog_iterator_release(struct ref_iterator *ref_iterator)
{
struct files_reflog_iterator *iter =
@@ -2404,7 +2388,6 @@ static void files_reflog_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable files_reflog_iterator_vtable = {
.advance = files_reflog_iterator_advance,
.seek = files_reflog_iterator_seek,
- .peel = files_reflog_iterator_peel,
.release = files_reflog_iterator_release,
};
diff --git a/refs/iterator.c b/refs/iterator.c
index 072c6aacdb0..d79aa5ec82d 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -21,12 +21,6 @@ int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname,
return ref_iterator->vtable->seek(ref_iterator, refname, flags);
}
-int ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- return ref_iterator->vtable->peel(ref_iterator, peeled);
-}
-
void ref_iterator_free(struct ref_iterator *ref_iterator)
{
if (ref_iterator) {
@@ -60,12 +54,6 @@ static int empty_ref_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
return 0;
}
-static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
- struct object_id *peeled UNUSED)
-{
- BUG("peel called for empty iterator");
-}
-
static void empty_ref_iterator_release(struct ref_iterator *ref_iterator UNUSED)
{
}
@@ -73,7 +61,6 @@ static void empty_ref_iterator_release(struct ref_iterator *ref_iterator UNUSED)
static struct ref_iterator_vtable empty_ref_iterator_vtable = {
.advance = empty_ref_iterator_advance,
.seek = empty_ref_iterator_seek,
- .peel = empty_ref_iterator_peel,
.release = empty_ref_iterator_release,
};
@@ -240,18 +227,6 @@ static int merge_ref_iterator_seek(struct ref_iterator *ref_iterator,
return 0;
}
-static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct merge_ref_iterator *iter =
- (struct merge_ref_iterator *)ref_iterator;
-
- if (!iter->current) {
- BUG("peel called before advance for merge iterator");
- }
- return ref_iterator_peel(*iter->current, peeled);
-}
-
static void merge_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct merge_ref_iterator *iter =
@@ -263,7 +238,6 @@ static void merge_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable merge_ref_iterator_vtable = {
.advance = merge_ref_iterator_advance,
.seek = merge_ref_iterator_seek,
- .peel = merge_ref_iterator_peel,
.release = merge_ref_iterator_release,
};
@@ -412,15 +386,6 @@ static int prefix_ref_iterator_seek(struct ref_iterator *ref_iterator,
return ref_iterator_seek(iter->iter0, refname, flags);
}
-static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct prefix_ref_iterator *iter =
- (struct prefix_ref_iterator *)ref_iterator;
-
- return ref_iterator_peel(iter->iter0, peeled);
-}
-
static void prefix_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct prefix_ref_iterator *iter =
@@ -432,7 +397,6 @@ static void prefix_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable prefix_ref_iterator_vtable = {
.advance = prefix_ref_iterator_advance,
.seek = prefix_ref_iterator_seek,
- .peel = prefix_ref_iterator_peel,
.release = prefix_ref_iterator_release,
};
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 1fefefd54ed..6fa229edd0f 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1030,22 +1030,6 @@ static int packed_ref_iterator_seek(struct ref_iterator *ref_iterator,
return 0;
}
-static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct packed_ref_iterator *iter =
- (struct packed_ref_iterator *)ref_iterator;
-
- if ((iter->base.ref.flags & REF_KNOWS_PEELED)) {
- oidcpy(peeled, &iter->peeled);
- return is_null_oid(&iter->peeled) ? -1 : 0;
- } else if ((iter->base.ref.flags & (REF_ISBROKEN | REF_ISSYMREF))) {
- return -1;
- } else {
- return peel_object(iter->repo, &iter->oid, peeled) ? -1 : 0;
- }
-}
-
static void packed_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct packed_ref_iterator *iter =
@@ -1059,7 +1043,6 @@ static void packed_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable packed_ref_iterator_vtable = {
.advance = packed_ref_iterator_advance,
.seek = packed_ref_iterator_seek,
- .peel = packed_ref_iterator_peel,
.release = packed_ref_iterator_release,
};
@@ -1525,13 +1508,8 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
if (cmp < 0) {
/* Pass the old reference through. */
-
- struct object_id peeled;
- int peel_error = ref_iterator_peel(iter, &peeled);
-
if (write_packed_entry(out, iter->ref.name,
- iter->ref.oid,
- peel_error ? NULL : &peeled))
+ iter->ref.oid, iter->ref.peeled_oid))
goto write_error;
if ((ok = ref_iterator_advance(iter)) != ITER_OK) {
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index e427848879d..ffef01a5975 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -546,14 +546,6 @@ static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator,
return 0;
}
-static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct cache_ref_iterator *iter =
- (struct cache_ref_iterator *)ref_iterator;
- return peel_object(iter->repo, ref_iterator->ref.oid, peeled) ? -1 : 0;
-}
-
static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct cache_ref_iterator *iter =
@@ -565,7 +557,6 @@ static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable cache_ref_iterator_vtable = {
.advance = cache_ref_iterator_advance,
.seek = cache_ref_iterator_seek,
- .peel = cache_ref_iterator_peel,
.release = cache_ref_iterator_release,
};
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index f4f845bbeaf..4671517dade 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -357,12 +357,6 @@ typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator);
typedef int ref_iterator_seek_fn(struct ref_iterator *ref_iterator,
const char *refname, unsigned int flags);
-/*
- * Peels the current ref, returning 0 for success or -1 for failure.
- */
-typedef int ref_iterator_peel_fn(struct ref_iterator *ref_iterator,
- struct object_id *peeled);
-
/*
* Implementations of this function should free any resources specific
* to the derived class.
@@ -372,7 +366,6 @@ typedef void ref_iterator_release_fn(struct ref_iterator *ref_iterator);
struct ref_iterator_vtable {
ref_iterator_advance_fn *advance;
ref_iterator_seek_fn *seek;
- ref_iterator_peel_fn *peel;
ref_iterator_release_fn *release;
};
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index e214e120d77..e329d4a423a 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -744,21 +744,6 @@ static int reftable_ref_iterator_seek(struct ref_iterator *ref_iterator,
return iter->err;
}
-static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct reftable_ref_iterator *iter =
- (struct reftable_ref_iterator *)ref_iterator;
-
- if (iter->ref.value_type == REFTABLE_REF_VAL2) {
- oidread(peeled, iter->ref.value.val2.target_value,
- iter->refs->base.repo->hash_algo);
- return 0;
- }
-
- return -1;
-}
-
static void reftable_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct reftable_ref_iterator *iter =
@@ -776,7 +761,6 @@ static void reftable_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable reftable_ref_iterator_vtable = {
.advance = reftable_ref_iterator_advance,
.seek = reftable_ref_iterator_seek,
- .peel = reftable_ref_iterator_peel,
.release = reftable_ref_iterator_release,
};
@@ -2098,13 +2082,6 @@ static int reftable_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSE
return -1;
}
-static int reftable_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
- struct object_id *peeled UNUSED)
-{
- BUG("reftable reflog iterator cannot be peeled");
- return -1;
-}
-
static void reftable_reflog_iterator_release(struct ref_iterator *ref_iterator)
{
struct reftable_reflog_iterator *iter =
@@ -2117,7 +2094,6 @@ static void reftable_reflog_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable reftable_reflog_iterator_vtable = {
.advance = reftable_reflog_iterator_advance,
.seek = reftable_reflog_iterator_seek,
- .peel = reftable_reflog_iterator_peel,
.release = reftable_reflog_iterator_release,
};
--
2.51.1.930.gacf6e81ea2.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v4 11/14] object: add flag to `peel_object()` to verify object type
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
` (9 preceding siblings ...)
2025-10-23 7:16 ` [PATCH v4 10/14] refs: drop infrastructure to peel via iterators Patrick Steinhardt
@ 2025-10-23 7:16 ` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 12/14] refs: don't store peeled object IDs for invalid tags Patrick Steinhardt
` (3 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 7:16 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
When peeling a tag to a non-tag object we repeatedly call
`parse_object()` on the tagged object until we find the first object
that isn't a tag. While this feels sensible at first, there is a big
catch here: `parse_object()` doesn't actually verify the type of the
tagged object.
The relevant code path here eventually ends up in `parse_tag_buffer()`.
Here, we parse the various fields of the tag, including the "type". Once
we've figured out the type and the tagged object ID, we call one of the
`lookup_${type}()` functions for whatever type we have found. There is
two possible outcomes in the successful case:
1. The object is already part of our cached objects. In that case we
double-check whether the type we're trying to look up matches the
type that was cached.
2. The object is _not_ part of our cached objects. In that case, we
simply create a new object with the expected type, but we don't
parse that object.
In the first case we might notice type mismatches, but only in the case
where our cache has the object with the correct type. In the second
case, we'll blindly assume that the type is correct and then go with it.
We'll only notice that the type might be wrong when we try to parse the
object at a later point.
Now arguably, we could change `parse_tag_buffer()` to verify the tagged
object's type for us. But that would have the effect that such a tag
cannot be parsed at all anymore, and we have a small bunch of tests for
exactly this case that assert we still can open such tags. So this
change does not feel like something we can retroactively tighten, even
though one shouldn't ever hit such corrupted tags.
Instead, add a new `flags` field to `peel_object()` that allows the
caller to opt in to strict object verification. This will be wired up at
a subset of callsites over the next few commits.
Note that this change also inlines `deref_tag_noverify()`. There's only
been two callsites of that function, the one we're changing and one in
our test helpers. The latter callsite can trivially use `deref_tag()`
instead, so by inlining the function we avoid having to pass down the
flag.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
object.c | 20 +++++++++++++++++---
object.h | 15 ++++++++++++++-
ref-filter.c | 2 +-
refs.c | 2 +-
refs/packed-backend.c | 5 ++---
refs/reftable-backend.c | 4 ++--
t/helper/test-reach.c | 2 +-
tag.c | 12 ------------
tag.h | 1 -
9 files changed, 38 insertions(+), 25 deletions(-)
diff --git a/object.c b/object.c
index 986114a6dba..e72b0ed4360 100644
--- a/object.c
+++ b/object.c
@@ -209,11 +209,12 @@ struct object *lookup_object_by_type(struct repository *r,
enum peel_status peel_object(struct repository *r,
const struct object_id *name,
- struct object_id *oid)
+ struct object_id *oid,
+ unsigned flags)
{
struct object *o = lookup_unknown_object(r, name);
- if (o->type == OBJ_NONE) {
+ if (o->type == OBJ_NONE || flags & PEEL_OBJECT_VERIFY_OBJECT_TYPE) {
int type = odb_read_object_info(r->objects, name, NULL);
if (type < 0 || !object_as_type(o, type, 0))
return PEEL_INVALID;
@@ -222,7 +223,20 @@ enum peel_status peel_object(struct repository *r,
if (o->type != OBJ_TAG)
return PEEL_NON_TAG;
- o = deref_tag_noverify(r, o);
+ while (o && o->type == OBJ_TAG) {
+ o = parse_object(r, &o->oid);
+ if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged) {
+ o = ((struct tag *)o)->tagged;
+
+ if (flags & PEEL_OBJECT_VERIFY_OBJECT_TYPE) {
+ int type = odb_read_object_info(r->objects, &o->oid, NULL);
+ if (type < 0 || !object_as_type(o, type, 0))
+ return PEEL_INVALID;
+ }
+ } else {
+ o = NULL;
+ }
+ }
if (!o)
return PEEL_INVALID;
diff --git a/object.h b/object.h
index 8c3c1c46e1b..1499f63d507 100644
--- a/object.h
+++ b/object.h
@@ -287,6 +287,17 @@ enum peel_status {
PEEL_BROKEN = -4
};
+enum peel_object_flags {
+ /*
+ * Always verify the object type, even in the case where the looked-up
+ * object already has an object type. This can be useful when the
+ * stored object type may be invalid. One such case is when looking up
+ * objects via tags, where we blindly trust the object type declared by
+ * the tag.
+ */
+ PEEL_OBJECT_VERIFY_OBJECT_TYPE = (1 << 0),
+};
+
/*
* Peel the named object; i.e., if the object is a tag, resolve the
* tag recursively until a non-tag is found. If successful, store the
@@ -295,7 +306,9 @@ enum peel_status {
* and leave oid unchanged.
*/
enum peel_status peel_object(struct repository *r,
- const struct object_id *name, struct object_id *oid);
+ const struct object_id *name,
+ struct object_id *oid,
+ unsigned flags);
struct object_list *object_list_insert(struct object *item,
struct object_list **list_p);
diff --git a/ref-filter.c b/ref-filter.c
index 7fd8babec8f..9a8ed8c8fc1 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2581,7 +2581,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
if (need_tagged) {
if (!is_null_oid(&ref->peeled_oid)) {
oidcpy(&oi_deref.oid, &ref->peeled_oid);
- } else if (!peel_object(the_repository, &obj->oid, &oi_deref.oid)) {
+ } else if (!peel_object(the_repository, &oi.oid, &oi_deref.oid, 0)) {
/* We managed to peel the object ourselves. */
} else {
die("bad tag");
diff --git a/refs.c b/refs.c
index 9d8f0a9ca4a..a41a94ae55b 100644
--- a/refs.c
+++ b/refs.c
@@ -2333,7 +2333,7 @@ int reference_get_peeled_oid(struct repository *repo,
return 0;
}
- return peel_object(repo, ref->oid, peeled_oid) ? -1 : 0;
+ return peel_object(repo, ref->oid, peeled_oid, 0) ? -1 : 0;
}
int refs_update_symref(struct ref_store *refs, const char *ref,
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 6fa229edd0f..4752d3f3981 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1527,9 +1527,8 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
i++;
} else {
struct object_id peeled;
- int peel_error = peel_object(refs->base.repo,
- &update->new_oid,
- &peeled);
+ int peel_error = peel_object(refs->base.repo, &update->new_oid,
+ &peeled, 0);
if (write_packed_entry(out, update->refname,
&update->new_oid,
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index e329d4a423a..9febb2322c3 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1632,7 +1632,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
ref.refname = (char *)u->refname;
ref.update_index = ts;
- peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled);
+ peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled, 0);
if (!peel_error) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
@@ -2497,7 +2497,7 @@ static int write_reflog_expiry_table(struct reftable_writer *writer, void *cb_da
ref.refname = (char *)arg->refname;
ref.update_index = ts;
- if (!peel_object(arg->refs->base.repo, &arg->update_oid, &peeled)) {
+ if (!peel_object(arg->refs->base.repo, &arg->update_oid, &peeled, 0)) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
memcpy(ref.value.val2.value, arg->update_oid.hash, GIT_MAX_RAWSZ);
diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c
index 028ec003067..c58c93800f3 100644
--- a/t/helper/test-reach.c
+++ b/t/helper/test-reach.c
@@ -63,7 +63,7 @@ int cmd__reach(int ac, const char **av)
die("failed to resolve %s", buf.buf + 2);
orig = parse_object(r, &oid);
- peeled = deref_tag_noverify(the_repository, orig);
+ peeled = deref_tag(the_repository, orig, NULL, 0);
if (!peeled)
die("failed to load commit for input %s resulting in oid %s",
diff --git a/tag.c b/tag.c
index 1d52686ee10..f5c232d2f1f 100644
--- a/tag.c
+++ b/tag.c
@@ -94,18 +94,6 @@ struct object *deref_tag(struct repository *r, struct object *o, const char *war
return o;
}
-struct object *deref_tag_noverify(struct repository *r, struct object *o)
-{
- while (o && o->type == OBJ_TAG) {
- o = parse_object(r, &o->oid);
- if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged)
- o = ((struct tag *)o)->tagged;
- else
- o = NULL;
- }
- return o;
-}
-
struct tag *lookup_tag(struct repository *r, const struct object_id *oid)
{
struct object *obj = lookup_object(r, oid);
diff --git a/tag.h b/tag.h
index c49d7c19ad3..ef12a610372 100644
--- a/tag.h
+++ b/tag.h
@@ -16,7 +16,6 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
int parse_tag(struct tag *item);
void release_tag_memory(struct tag *t);
struct object *deref_tag(struct repository *r, struct object *, const char *, int);
-struct object *deref_tag_noverify(struct repository *r, struct object *);
int gpg_verify_tag(const struct object_id *oid,
const char *name_to_report, unsigned flags);
struct object_id *get_tagged_oid(struct tag *tag);
--
2.51.1.930.gacf6e81ea2.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v4 12/14] refs: don't store peeled object IDs for invalid tags
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
` (10 preceding siblings ...)
2025-10-23 7:16 ` [PATCH v4 11/14] object: add flag to `peel_object()` to verify object type Patrick Steinhardt
@ 2025-10-23 7:16 ` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 13/14] ref-filter: detect broken tags when dereferencing them Patrick Steinhardt
` (2 subsequent siblings)
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 7:16 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
Both the "files" and "reftable" backend store peeled object IDs for
references that point to tags:
- The "files" backend stores the value when packing refs, where each
peeled object ID is prefixed with "^".
- The "reftable" backend stores the value whenever writing a new
reference that points to a tag via a special ref record type.
Both of these backends use `peel_object()` to find the peeled object ID.
But as explained in the preceding commit, that function does not detect
the case where the tag's tagged object and its claimed type mismatch.
The consequence of storing these bogus peeled object IDs is that we're
less likely to detect such corruption in other parts of Git.
git-for-each-ref(1) for example does not notice anymore that the tag is
broken when using "--format=%(*objectname)" to dereference tags.
One could claim that this is good, because it still allows us to mostly
use the tag as intended. But the biggest problem here is that we now
have different behaviour for such a broken tag depending on whether or
not we have its peeled value in the refdb.
Fix the issue by verifying the object type when peeling the object. If
that verification fails we simply skip storing the peeled value in
either of the reference formats.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs/packed-backend.c | 2 +-
refs/reftable-backend.c | 3 ++-
t/pack-refs-tests.sh | 32 ++++++++++++++++++++++++++++++++
t/t0610-reftable-basics.sh | 28 ++++++++++++++++++++++++++++
4 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 4752d3f3981..1ab0c503930 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1528,7 +1528,7 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
} else {
struct object_id peeled;
int peel_error = peel_object(refs->base.repo, &update->new_oid,
- &peeled, 0);
+ &peeled, PEEL_OBJECT_VERIFY_OBJECT_TYPE);
if (write_packed_entry(out, update->refname,
&update->new_oid,
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 9febb2322c3..6bbfd5618da 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1632,7 +1632,8 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
ref.refname = (char *)u->refname;
ref.update_index = ts;
- peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled, 0);
+ peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled,
+ PEEL_OBJECT_VERIFY_OBJECT_TYPE);
if (!peel_error) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
diff --git a/t/pack-refs-tests.sh b/t/pack-refs-tests.sh
index 3dbcc01718e..095823d915f 100644
--- a/t/pack-refs-tests.sh
+++ b/t/pack-refs-tests.sh
@@ -428,4 +428,36 @@ do
'
done
+test_expect_success 'pack-refs does not store invalid peeled tag value' '
+ test_when_finished rm -rf repo &&
+ git init repo &&
+ (
+ cd repo &&
+ git commit --allow-empty --message initial &&
+
+ echo garbage >blob-content &&
+ blob_id=$(git hash-object -w -t blob blob-content) &&
+
+ # Write an invalid tag into the object database. The tag itself
+ # is well-formed, but the tagged object is a blob while we
+ # claim that it is a commit.
+ cat >tag-content <<-EOF &&
+ object $blob_id
+ type commit
+ tag bad-tag
+ tagger C O Mitter <committer@example.com> 1112354055 +0200
+
+ annotated
+ EOF
+ tag_id=$(git hash-object -w -t tag tag-content) &&
+ git update-ref refs/tags/bad-tag "$tag_id" &&
+
+ # The packed-refs file should not contain the peeled object ID.
+ # If it did this would cause commands that use the peeled value
+ # to not notice this corrupted tag.
+ git pack-refs --all &&
+ test_grep ! "^\^" .git/packed-refs
+ )
+'
+
test_done
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
index 3ea5d51532a..6575528f212 100755
--- a/t/t0610-reftable-basics.sh
+++ b/t/t0610-reftable-basics.sh
@@ -1135,4 +1135,32 @@ test_expect_success 'fetch: accessing FETCH_HEAD special ref works' '
test_cmp expect actual
'
+test_expect_success 'writes do not persist peeled value for invalid tags' '
+ test_when_finished rm -rf repo &&
+ git init repo &&
+ (
+ cd repo &&
+ git commit --allow-empty --message initial &&
+
+ # We cannot easily verify that the peeled value is not stored
+ # in the tables. Instead, we test this indirectly: we create
+ # two tags that both point to the same object, but they claim
+ # different object types. If we parse both tags we notice that
+ # the parsed tagged object has a mismatch between the two tags
+ # and bail out.
+ #
+ # If we instead use the persisted peeled value we would not
+ # even parse the tags. As such, we would not notice the
+ # discrepancy either and thus listing these tags would succeed.
+ git tag tag-1 -m "tag 1" &&
+ git cat-file tag tag-1 >raw-tag &&
+ sed "s/^type commit$/type blob/" <raw-tag >broken-tag &&
+ broken_tag_id=$(git hash-object -w -t tag broken-tag) &&
+ git update-ref refs/tags/tag-2 $broken_tag_id &&
+
+ test_must_fail git for-each-ref --format="%(*objectname)" refs/tags/ 2>err &&
+ test_grep "bad tag pointer" err
+ )
+'
+
test_done
--
2.51.1.930.gacf6e81ea2.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v4 13/14] ref-filter: detect broken tags when dereferencing them
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
` (11 preceding siblings ...)
2025-10-23 7:16 ` [PATCH v4 12/14] refs: don't store peeled object IDs for invalid tags Patrick Steinhardt
@ 2025-10-23 7:16 ` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 14/14] ref-filter: parse objects on demand Patrick Steinhardt
2025-10-23 23:06 ` [PATCH v4 00/14] refs: improvements and fixes for peeling tags Junio C Hamano
14 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 7:16 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
Users can ask git-for-each-ref(1) to peel tags and return information of
the tagged object by adding an asterisk to the format, like for example
"%(*$objectname)". If so, git-for-each-ref(1) peels that object to the
first non-tag object and then returns its values.
As mentioned in preceding commits, it can happen that the tagged object
type and the claimed object type differ, effectively resulting in a
corrupt tag. git-for-each-ref(1) would notice this mismatch, print an
error and then bail out when trying to peel the tag.
But we only notice this corruption in some very specific edge cases!
While we have a test in "t/for-each-ref-tests.sh" that verifies the
above scenario, this test is specifically crafted to detect the issue at
hand. Namely, we create two tags:
- One tag points to a specific object with the correct type.
- The other tag points to the *same* object with a different type.
The fact that both tags point to the same object is important here:
`peel_object()` wouldn't notice the corruption if the tagged objects
were different.
The root cause is that `peel_object()` calls `lookup_${type}()`
eventually, where the type is the same type declared in the tag object.
Consequently, when we have two tags pointing to the same object but with
different declared types we'll call two different lookup functions. The
first lookup will store the object with an unverified type A, whereas
the second lookup will try to look up the object with a different
unverified type B. And it is only now that we notice the discrepancy in
object types, even though type A could've already been the wrong type.
Fix the issue by verifying the object type in `populate_value()`. With
this change we'll also notice type mismatches when only dereferencing a
tag once.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
ref-filter.c | 3 ++-
t/for-each-ref-tests.sh | 4 +++-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/ref-filter.c b/ref-filter.c
index 9a8ed8c8fc1..c54025d6b4c 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2581,7 +2581,8 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
if (need_tagged) {
if (!is_null_oid(&ref->peeled_oid)) {
oidcpy(&oi_deref.oid, &ref->peeled_oid);
- } else if (!peel_object(the_repository, &oi.oid, &oi_deref.oid, 0)) {
+ } else if (!peel_object(the_repository, &oi.oid, &oi_deref.oid,
+ PEEL_OBJECT_VERIFY_OBJECT_TYPE)) {
/* We managed to peel the object ourselves. */
} else {
die("bad tag");
diff --git a/t/for-each-ref-tests.sh b/t/for-each-ref-tests.sh
index e3ad19298ac..4593be5fd54 100644
--- a/t/for-each-ref-tests.sh
+++ b/t/for-each-ref-tests.sh
@@ -1809,7 +1809,9 @@ test_expect_success "${git_for_each_ref} reports broken tags" '
bad=$(git hash-object -w -t tag bad) &&
git update-ref refs/tags/broken-tag-bad $bad &&
test_must_fail ${git_for_each_ref} --format="%(*objectname)" \
- refs/tags/broken-tag-*
+ refs/tags/broken-tag-* &&
+ test_must_fail ${git_for_each_ref} --format="%(*objectname)" \
+ refs/tags/broken-tag-bad
'
test_expect_success 'set up tag with signature and no blank lines' '
--
2.51.1.930.gacf6e81ea2.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v4 14/14] ref-filter: parse objects on demand
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
` (12 preceding siblings ...)
2025-10-23 7:16 ` [PATCH v4 13/14] ref-filter: detect broken tags when dereferencing them Patrick Steinhardt
@ 2025-10-23 7:16 ` Patrick Steinhardt
2025-11-04 22:07 ` Jeff King
2025-10-23 23:06 ` [PATCH v4 00/14] refs: improvements and fixes for peeling tags Junio C Hamano
14 siblings, 1 reply; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-23 7:16 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau, Junio C Hamano,
Justin Tobler
When formatting an arbitrary object we parse that object regardless of
whether or not we actually need any parsed data. In fact, many of the
atoms we have don't require any.
Refactor the code so that we parse the data on demand when we see an
atom that wants to access the objects. This leads to a small speedup,
for example in the Chromium repository with around 40000 refs:
Benchmark 1: for-each-ref --format='%(raw)' (HEAD~)
Time (mean ± σ): 388.7 ms ± 1.1 ms [User: 322.2 ms, System: 65.0 ms]
Range (min … max): 387.3 ms … 390.8 ms 10 runs
Benchmark 2: for-each-ref --format='%(raw)' (HEAD)
Time (mean ± σ): 344.7 ms ± 0.7 ms [User: 287.8 ms, System: 55.1 ms]
Range (min … max): 343.9 ms … 345.7 ms 10 runs
Summary
for-each-ref --format='%(raw)' (HEAD) ran
1.13 ± 0.00 times faster than for-each-ref --format='%(raw)' (HEAD~)
With this change, we now spend ~90% of the time decompressing objects,
which is almost as good as it gets regarding git-for-each-ref(1)'s own
infrastructure.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
ref-filter.c | 142 ++++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 106 insertions(+), 36 deletions(-)
diff --git a/ref-filter.c b/ref-filter.c
index c54025d6b4c..7cfcd5c3554 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -91,6 +91,7 @@ static struct expand_data {
struct object_id delta_base_oid;
void *content;
+ struct object *maybe_object;
struct object_info info;
} oi, oi_deref;
@@ -1475,11 +1476,29 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_
}
}
+static struct object *get_or_parse_object(struct expand_data *data, const char *refname,
+ struct strbuf *err, int *eaten)
+{
+ if (!data->maybe_object) {
+ data->maybe_object = parse_object_buffer(the_repository, &data->oid, data->type,
+ data->size, data->content, eaten);
+ if (!data->maybe_object) {
+ strbuf_addf(err, _("parse_object_buffer failed on %s for %s"),
+ oid_to_hex(&data->oid), refname);
+ return NULL;
+ }
+ }
+
+ return data->maybe_object;
+}
+
/* See grab_values */
-static void grab_tag_values(struct atom_value *val, int deref, struct object *obj)
+static int grab_tag_values(struct atom_value *val, int deref,
+ struct expand_data *data, const char *refname,
+ struct strbuf *err, int *eaten)
{
+ struct tag *tag = NULL;
int i;
- struct tag *tag = (struct tag *) obj;
for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i].name;
@@ -1487,6 +1506,14 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
struct atom_value *v = &val[i];
if (!!deref != (*name == '*'))
continue;
+
+ if (!tag) {
+ tag = (struct tag *) get_or_parse_object(data, refname,
+ err, eaten);
+ if (!tag)
+ return -1;
+ }
+
if (deref)
name++;
if (atom_type == ATOM_TAG)
@@ -1496,22 +1523,35 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
else if (atom_type == ATOM_OBJECT && tag->tagged)
v->s = xstrdup(oid_to_hex(&tag->tagged->oid));
}
+
+ return 0;
}
/* See grab_values */
-static void grab_commit_values(struct atom_value *val, int deref, struct object *obj)
+static int grab_commit_values(struct atom_value *val, int deref,
+ struct expand_data *data, const char *refname,
+ struct strbuf *err, int *eaten)
{
int i;
- struct commit *commit = (struct commit *) obj;
+ struct commit *commit = NULL;
for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i].name;
enum atom_type atom_type = used_atom[i].atom_type;
struct atom_value *v = &val[i];
+
if (!!deref != (*name == '*'))
continue;
if (deref)
name++;
+
+ if (!commit) {
+ commit = (struct commit *) get_or_parse_object(data, refname,
+ err, eaten);
+ if (!commit)
+ return -1;
+ }
+
if (atom_type == ATOM_TREE &&
grab_oid(name, "tree", get_commit_tree_oid(commit), v, &used_atom[i]))
continue;
@@ -1531,6 +1571,8 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
v->s = strbuf_detach(&s, NULL);
}
}
+
+ return 0;
}
static const char *find_wholine(const char *who, int wholen, const char *buf)
@@ -1759,10 +1801,12 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void
}
}
-static void grab_signature(struct atom_value *val, int deref, struct object *obj)
+static int grab_signature(struct atom_value *val, int deref,
+ struct expand_data *data, const char *refname,
+ struct strbuf *err, int *eaten)
{
int i;
- struct commit *commit = (struct commit *) obj;
+ struct commit *commit = NULL;
struct signature_check sigc = { 0 };
int signature_checked = 0;
@@ -1790,6 +1834,13 @@ static void grab_signature(struct atom_value *val, int deref, struct object *obj
continue;
if (!signature_checked) {
+ if (!commit) {
+ commit = (struct commit *) get_or_parse_object(data, refname,
+ err, eaten);
+ if (!commit)
+ return -1;
+ }
+
check_commit_signature(commit, &sigc);
signature_checked = 1;
}
@@ -1843,6 +1894,8 @@ static void grab_signature(struct atom_value *val, int deref, struct object *obj
if (signature_checked)
signature_check_clear(&sigc);
+
+ return 0;
}
static void find_subpos(const char *buf,
@@ -1920,9 +1973,8 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size
}
static void grab_describe_values(struct atom_value *val, int deref,
- struct object *obj)
+ struct expand_data *data)
{
- struct commit *commit = (struct commit *)obj;
int i;
for (i = 0; i < used_atom_cnt; i++) {
@@ -1944,7 +1996,7 @@ static void grab_describe_values(struct atom_value *val, int deref,
cmd.git_cmd = 1;
strvec_push(&cmd.args, "describe");
strvec_pushv(&cmd.args, atom->u.describe_args.v);
- strvec_push(&cmd.args, oid_to_hex(&commit->object.oid));
+ strvec_push(&cmd.args, oid_to_hex(&data->oid));
if (pipe_command(&cmd, NULL, 0, &out, 0, &err, 0) < 0) {
error(_("failed to run 'describe'"));
v->s = xstrdup("");
@@ -2066,24 +2118,36 @@ static void fill_missing_values(struct atom_value *val)
* pointed at by the ref itself; otherwise it is the object the
* ref (which is a tag) refers to.
*/
-static void grab_values(struct atom_value *val, int deref, struct object *obj, struct expand_data *data)
+static int grab_values(struct atom_value *val, int deref, struct expand_data *data,
+ const char *refname, struct strbuf *err, int *eaten)
{
void *buf = data->content;
+ int ret;
- switch (obj->type) {
+ switch (data->type) {
case OBJ_TAG:
- grab_tag_values(val, deref, obj);
+ ret = grab_tag_values(val, deref, data, refname, err, eaten);
+ if (ret < 0)
+ goto out;
+
grab_sub_body_contents(val, deref, data);
grab_person("tagger", val, deref, buf);
- grab_describe_values(val, deref, obj);
+ grab_describe_values(val, deref, data);
break;
case OBJ_COMMIT:
- grab_commit_values(val, deref, obj);
+ ret = grab_commit_values(val, deref, data, refname, err, eaten);
+ if (ret < 0)
+ goto out;
+
grab_sub_body_contents(val, deref, data);
grab_person("author", val, deref, buf);
grab_person("committer", val, deref, buf);
- grab_signature(val, deref, obj);
- grab_describe_values(val, deref, obj);
+
+ ret = grab_signature(val, deref, data, refname, err, eaten);
+ if (ret < 0)
+ goto out;
+
+ grab_describe_values(val, deref, data);
break;
case OBJ_TREE:
/* grab_tree_values(val, deref, obj, buf, sz); */
@@ -2094,8 +2158,12 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s
grab_sub_body_contents(val, deref, data);
break;
default:
- die("Eh? Object of type %d?", obj->type);
+ die("Eh? Object of type %d?", data->type);
}
+
+ ret = 0;
+out:
+ return ret;
}
static inline char *copy_advance(char *dst, const char *src)
@@ -2292,38 +2360,41 @@ static const char *get_refname(struct used_atom *atom, struct ref_array_item *re
return show_ref(&atom->u.refname, ref->refname);
}
-static int get_object(struct ref_array_item *ref, int deref, struct object **obj,
+static int get_object(struct ref_array_item *ref, int deref,
struct expand_data *oi, struct strbuf *err)
{
- /* parse_object_buffer() will set eaten to 0 if free() will be needed */
- int eaten = 1;
+ /* parse_object_buffer() will set eaten to 1 if free() will be needed */
+ int eaten = 0;
+ int ret;
+
if (oi->info.contentp) {
/* We need to know that to use parse_object_buffer properly */
oi->info.sizep = &oi->size;
oi->info.typep = &oi->type;
}
+
if (odb_read_object_info_extended(the_repository->objects, &oi->oid, &oi->info,
- OBJECT_INFO_LOOKUP_REPLACE))
- return strbuf_addf_ret(err, -1, _("missing object %s for %s"),
- oid_to_hex(&oi->oid), ref->refname);
+ OBJECT_INFO_LOOKUP_REPLACE)) {
+ ret = strbuf_addf_ret(err, -1, _("missing object %s for %s"),
+ oid_to_hex(&oi->oid), ref->refname);
+ goto out;
+ }
if (oi->info.disk_sizep && oi->disk_size < 0)
BUG("Object size is less than zero.");
if (oi->info.contentp) {
- *obj = parse_object_buffer(the_repository, &oi->oid, oi->type, oi->size, oi->content, &eaten);
- if (!*obj) {
- if (!eaten)
- free(oi->content);
- return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
- oid_to_hex(&oi->oid), ref->refname);
- }
- grab_values(ref->value, deref, *obj, oi);
+ ret = grab_values(ref->value, deref, oi, ref->refname, err, &eaten);
+ if (ret < 0)
+ goto out;
}
grab_common_values(ref->value, deref, oi);
+ ret = 0;
+
+out:
if (!eaten)
free(oi->content);
- return 0;
+ return ret;
}
static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees)
@@ -2376,7 +2447,6 @@ static char *get_worktree_path(const struct ref_array_item *ref)
*/
static int populate_value(struct ref_array_item *ref, struct strbuf *err)
{
- struct object *obj;
int i;
struct object_info empty = OBJECT_INFO_INIT;
int ahead_behind_atoms = 0;
@@ -2564,14 +2634,14 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
oi.oid = ref->objectname;
- if (get_object(ref, 0, &obj, &oi, err))
+ if (get_object(ref, 0, &oi, err))
return -1;
/*
* If there is no atom that wants to know about tagged
* object, we are done.
*/
- if (!need_tagged || (obj->type != OBJ_TAG))
+ if (!need_tagged || (oi.type != OBJ_TAG))
return 0;
/*
@@ -2589,7 +2659,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
}
}
- return get_object(ref, 1, &obj, &oi_deref, err);
+ return get_object(ref, 1, &oi_deref, err);
}
/*
--
2.51.1.930.gacf6e81ea2.dirty
^ permalink raw reply related [flat|nested] 106+ messages in thread
* Re: [PATCH v4 00/14] refs: improvements and fixes for peeling tags
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
` (13 preceding siblings ...)
2025-10-23 7:16 ` [PATCH v4 14/14] ref-filter: parse objects on demand Patrick Steinhardt
@ 2025-10-23 23:06 ` Junio C Hamano
2025-10-24 5:12 ` Patrick Steinhardt
14 siblings, 1 reply; 106+ messages in thread
From: Junio C Hamano @ 2025-10-23 23:06 UTC (permalink / raw)
To: Patrick Steinhardt
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Justin Tobler
Patrick Steinhardt <ps@pks.im> writes:
> originally, all I wanted to do was the last patch: a small performance
> optimization that stops parsing objects in git-for-each-ref(1) unless we
> really need to parse them. But that fix cause one specific test to fail,
> and only with the reftable backend. So this led me down the rabbit hole
> of tag peeling, ending up with this patch series.
>
> The series is structured like follows:
>
> - Patches 1 to 8 refactor our codebase so that we don't have the
> `peel_iterated_object()` hack anymore. I just found it hard to
> follow and thought it shouldn't be too hard to get rid of it.
>
> - Patches 9 and 10 remove infrastructure that we don't need anymore
> after the first couple of patches.
>
> - Patches 11 to 13 fix a couple of issues with peeled tags that I
> found. The underlying issue is that tags store both the tagged
> object and their type, but this information may not match. We never
> verify the actual object type though when allocating the tagged
> object, so this only blows up much later.
>
> - Patch 14 was my original motivation, a small performance
> optimization.
>
> I'm not particularly fond of the patches 11 to 13. It feels more like
> playing whack-a-mole, and I very much assume that there still are edge
> cases where we should properly verify the tagged object type. But
> changing it in `parse_tag_buffer()` itself causes a bunch of tests to
> fail where we intentionally create such corrupted tags. So I didn't
> really dare to touch that part, to be honest.
>
> If anybody has suggestions for an alternative approach I'd be very open
> to it.
Are you still ;-), or are we ready to declare victory for now and
mark the topic for 'next'? It seems that another topic depends on
this and the topic itself is a good shape enough to advance.
Thanks.
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v4 00/14] refs: improvements and fixes for peeling tags
2025-10-23 23:06 ` [PATCH v4 00/14] refs: improvements and fixes for peeling tags Junio C Hamano
@ 2025-10-24 5:12 ` Patrick Steinhardt
0 siblings, 0 replies; 106+ messages in thread
From: Patrick Steinhardt @ 2025-10-24 5:12 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Justin Tobler
On Thu, Oct 23, 2025 at 04:06:01PM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> > I'm not particularly fond of the patches 11 to 13. It feels more like
> > playing whack-a-mole, and I very much assume that there still are edge
> > cases where we should properly verify the tagged object type. But
> > changing it in `parse_tag_buffer()` itself causes a bunch of tests to
> > fail where we intentionally create such corrupted tags. So I didn't
> > really dare to touch that part, to be honest.
> >
> > If anybody has suggestions for an alternative approach I'd be very open
> > to it.
>
> Are you still ;-), or are we ready to declare victory for now and
> mark the topic for 'next'? It seems that another topic depends on
> this and the topic itself is a good shape enough to advance.
Yes and no. I still think that this is playing whack-a-mole, and I'm not
particularly happy about that. But nobody else had a better suggestion,
and I don't see any downside with merging the current approach. So let's
merge it down.
Thanks!
Patrick
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v4 14/14] ref-filter: parse objects on demand
2025-10-23 7:16 ` [PATCH v4 14/14] ref-filter: parse objects on demand Patrick Steinhardt
@ 2025-11-04 22:07 ` Jeff King
2025-11-04 23:40 ` Junio C Hamano
0 siblings, 1 reply; 106+ messages in thread
From: Jeff King @ 2025-11-04 22:07 UTC (permalink / raw)
To: Patrick Steinhardt
Cc: git, Kristoffer Haugsbakk, Karthik Nayak, Taylor Blau,
Junio C Hamano, Justin Tobler
On Thu, Oct 23, 2025 at 09:16:23AM +0200, Patrick Steinhardt wrote:
> -static int get_object(struct ref_array_item *ref, int deref, struct object **obj,
> +static int get_object(struct ref_array_item *ref, int deref,
> struct expand_data *oi, struct strbuf *err)
> {
> - /* parse_object_buffer() will set eaten to 0 if free() will be needed */
> - int eaten = 1;
> + /* parse_object_buffer() will set eaten to 1 if free() will be needed */
> + int eaten = 0;
This comment is surely wrong now, isn't it? It will be set to 1 if
free() is _not_ needed:
> +out:
> if (!eaten)
> free(oi->content);
-Peff
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v4 14/14] ref-filter: parse objects on demand
2025-11-04 22:07 ` Jeff King
@ 2025-11-04 23:40 ` Junio C Hamano
2025-11-04 23:54 ` Jeff King
0 siblings, 1 reply; 106+ messages in thread
From: Junio C Hamano @ 2025-11-04 23:40 UTC (permalink / raw)
To: Jeff King
Cc: Patrick Steinhardt, git, Kristoffer Haugsbakk, Karthik Nayak,
Taylor Blau, Justin Tobler
Jeff King <peff@peff.net> writes:
> On Thu, Oct 23, 2025 at 09:16:23AM +0200, Patrick Steinhardt wrote:
>
>> -static int get_object(struct ref_array_item *ref, int deref, struct object **obj,
>> +static int get_object(struct ref_array_item *ref, int deref,
>> struct expand_data *oi, struct strbuf *err)
>> {
>> - /* parse_object_buffer() will set eaten to 0 if free() will be needed */
>> - int eaten = 1;
>> + /* parse_object_buffer() will set eaten to 1 if free() will be needed */
>> + int eaten = 0;
>
> This comment is surely wrong now, isn't it? It will be set to 1 if
> free() is _not_ needed:
>
>> +out:
>> if (!eaten)
>> free(oi->content);
>
> -Peff
Wow. Is it just the comment or the updated logic is upside down,
too?
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v4 14/14] ref-filter: parse objects on demand
2025-11-04 23:40 ` Junio C Hamano
@ 2025-11-04 23:54 ` Jeff King
0 siblings, 0 replies; 106+ messages in thread
From: Jeff King @ 2025-11-04 23:54 UTC (permalink / raw)
To: Junio C Hamano
Cc: Patrick Steinhardt, git, Kristoffer Haugsbakk, Karthik Nayak,
Taylor Blau, Justin Tobler
On Tue, Nov 04, 2025 at 03:40:53PM -0800, Junio C Hamano wrote:
> Jeff King <peff@peff.net> writes:
>
> > On Thu, Oct 23, 2025 at 09:16:23AM +0200, Patrick Steinhardt wrote:
> >
> >> -static int get_object(struct ref_array_item *ref, int deref, struct object **obj,
> >> +static int get_object(struct ref_array_item *ref, int deref,
> >> struct expand_data *oi, struct strbuf *err)
> >> {
> >> - /* parse_object_buffer() will set eaten to 0 if free() will be needed */
> >> - int eaten = 1;
> >> + /* parse_object_buffer() will set eaten to 1 if free() will be needed */
> >> + int eaten = 0;
> >
> > This comment is surely wrong now, isn't it? It will be set to 1 if
> > free() is _not_ needed:
> >
> >> +out:
> >> if (!eaten)
> >> free(oi->content);
> >
> > -Peff
>
> Wow. Is it just the comment or the updated logic is upside down,
> too?
I think the logic is fine. The meaning of "eaten" did not change. It's
just that some code paths will not bother calling parse_object_buffer()
now (if no atoms need it). So we need to default to "0" (the buffer must
be freed) for those cases. And if parse_object_buffer() is called, it
will always correctly set the value to 1.
The new code does mean that if contentp is NULL, we will always call
free(oi->content), even though nobody would ever have set it. But
presumably it was initialized to NULL in that case and the free is a
noop.
-Peff
^ permalink raw reply [flat|nested] 106+ messages in thread
end of thread, other threads:[~2025-11-04 23:54 UTC | newest]
Thread overview: 106+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-07 10:58 [PATCH 00/13] refs: improvements and fixes for peeling tags Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 01/13] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
2025-10-07 18:05 ` Justin Tobler
2025-10-08 13:42 ` Patrick Steinhardt
2025-10-07 21:56 ` Taylor Blau
2025-10-08 15:52 ` shejialuo
2025-10-09 6:03 ` Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 02/13] refs: introduce `.ref` field for the base iterator Patrick Steinhardt
2025-10-07 14:24 ` Karthik Nayak
2025-10-08 13:44 ` Patrick Steinhardt
2025-10-08 15:03 ` Patrick Steinhardt
2025-10-07 20:19 ` Justin Tobler
2025-10-07 21:57 ` Taylor Blau
2025-10-07 10:58 ` [PATCH 03/13] refs: refactor reference status flags Patrick Steinhardt
2025-10-07 14:27 ` Karthik Nayak
2025-10-08 13:44 ` Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 04/13] refs: expose peeled object ID via the iterator Patrick Steinhardt
2025-10-07 14:52 ` Karthik Nayak
2025-10-08 13:45 ` Patrick Steinhardt
2025-10-15 8:28 ` Karthik Nayak
2025-10-07 10:58 ` [PATCH 05/13] upload-pack: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
2025-10-07 16:18 ` Karthik Nayak
2025-10-08 13:45 ` Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 06/13] ref-filter: propagate peeled object ID Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 07/13] builtin/show-ref: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 08/13] refs: drop `current_ref_iter` hack Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 09/13] refs: drop infrastructure to peel via iterators Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 10/13] object: add flag to `peel_object()` to verify object type Patrick Steinhardt
2025-10-08 11:04 ` Kristoffer Haugsbakk
2025-10-07 10:58 ` [PATCH 11/13] refs: don't store peeled object IDs for invalid tags Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 12/13] ref-filter: detect broken tags when dereferencing them Patrick Steinhardt
2025-10-07 10:58 ` [PATCH 13/13] ref-filter: parse objects on demand Patrick Steinhardt
2025-10-08 11:05 ` Kristoffer Haugsbakk
2025-10-08 13:45 ` Patrick Steinhardt
2025-10-07 21:00 ` [PATCH 00/13] refs: improvements and fixes for peeling tags Junio C Hamano
2025-10-07 21:49 ` Taylor Blau
2025-10-07 23:01 ` Junio C Hamano
2025-10-08 15:50 ` [PATCH v2 00/14] " Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 01/14] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 02/14] refs: introduce `.ref` field for the base iterator Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 03/14] refs: fully reset `struct ref_iterator::ref` on iteration Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 04/14] refs: refactor reference status flags Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 05/14] refs: expose peeled object ID via the iterator Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 06/14] upload-pack: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 07/14] ref-filter: propagate peeled object ID Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 08/14] builtin/show-ref: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 09/14] refs: drop `current_ref_iter` hack Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 10/14] refs: drop infrastructure to peel via iterators Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 11/14] object: add flag to `peel_object()` to verify object type Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 12/14] refs: don't store peeled object IDs for invalid tags Patrick Steinhardt
2025-10-08 16:27 ` shejialuo
2025-10-09 5:22 ` Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 13/14] ref-filter: detect broken tags when dereferencing them Patrick Steinhardt
2025-10-08 15:50 ` [PATCH v2 14/14] ref-filter: parse objects on demand Patrick Steinhardt
2025-10-09 5:38 ` [PATCH v2 00/14] refs: improvements and fixes for peeling tags Jeff King
2025-10-09 6:09 ` Patrick Steinhardt
2025-10-09 6:39 ` Jeff King
2025-10-09 7:24 ` Patrick Steinhardt
2025-10-10 5:12 ` Jeff King
2025-10-10 5:22 ` Patrick Steinhardt
2025-10-10 6:26 ` Jeff King
2025-10-10 15:29 ` Junio C Hamano
2025-10-14 6:31 ` Patrick Steinhardt
2025-10-14 16:52 ` Junio C Hamano
2025-10-09 10:11 ` Toon Claes
2025-10-09 19:37 ` Junio C Hamano
2025-10-22 6:41 ` [PATCH v3 " Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 01/14] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 02/14] refs: introduce `.ref` field for the base iterator Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 03/14] refs: fully reset `struct ref_iterator::ref` on iteration Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 04/14] refs: refactor reference status flags Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 05/14] refs: expose peeled object ID via the iterator Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 06/14] upload-pack: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 07/14] ref-filter: propagate peeled object ID Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 08/14] builtin/show-ref: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 09/14] refs: drop `current_ref_iter` hack Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 10/14] refs: drop infrastructure to peel via iterators Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 11/14] object: add flag to `peel_object()` to verify object type Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 12/14] refs: don't store peeled object IDs for invalid tags Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 13/14] ref-filter: detect broken tags when dereferencing them Patrick Steinhardt
2025-10-22 6:41 ` [PATCH v3 14/14] ref-filter: parse objects on demand Patrick Steinhardt
2025-10-22 15:27 ` Junio C Hamano
2025-10-23 6:00 ` Patrick Steinhardt
2025-10-22 10:57 ` [PATCH v3 00/14] refs: improvements and fixes for peeling tags Karthik Nayak
2025-10-22 14:47 ` Junio C Hamano
2025-10-23 5:52 ` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 " Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 01/14] refs: introduce wrapper struct for `each_ref_fn` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 02/14] refs: introduce `.ref` field for the base iterator Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 03/14] refs: fully reset `struct ref_iterator::ref` on iteration Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 04/14] refs: refactor reference status flags Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 05/14] refs: expose peeled object ID via the iterator Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 06/14] upload-pack: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 07/14] ref-filter: propagate peeled object ID Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 08/14] builtin/show-ref: convert to use `reference_get_peeled_oid()` Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 09/14] refs: drop `current_ref_iter` hack Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 10/14] refs: drop infrastructure to peel via iterators Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 11/14] object: add flag to `peel_object()` to verify object type Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 12/14] refs: don't store peeled object IDs for invalid tags Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 13/14] ref-filter: detect broken tags when dereferencing them Patrick Steinhardt
2025-10-23 7:16 ` [PATCH v4 14/14] ref-filter: parse objects on demand Patrick Steinhardt
2025-11-04 22:07 ` Jeff King
2025-11-04 23:40 ` Junio C Hamano
2025-11-04 23:54 ` Jeff King
2025-10-23 23:06 ` [PATCH v4 00/14] refs: improvements and fixes for peeling tags Junio C Hamano
2025-10-24 5:12 ` Patrick Steinhardt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).