From ff9fb2cfe6efb26b9d25dc5c114ab56126f9003e Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 15 Jan 2026 10:35:32 +0100 Subject: [PATCH 01/23] commit: rename `copy_commit_list()` to conform to coding guidelines Our coding guidelines say that: Functions that operate on `struct S` are named `S_()` and should generally receive a pointer to `struct S` as first parameter. While most of the functions related to `struct commit_list` already follow that naming schema, `copy_commit_list()` doesn't. Rename the function to address this and adjust all of its callers. Add a compatibility wrapper for the old function name to ease the transition and avoid any semantic conflicts with in-flight patch series. This wrapper will be removed once Git 2.53 has been released. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/commit.c | 2 +- commit.c | 2 +- commit.h | 11 ++++++++++- merge-ort.c | 2 +- revision.c | 4 ++-- sequencer.c | 2 +- 6 files changed, 16 insertions(+), 7 deletions(-) diff --git a/builtin/commit.c b/builtin/commit.c index 0243f17d53c97c..0aa3690b04b955 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1849,7 +1849,7 @@ int cmd_commit(int argc, } else if (amend) { if (!reflog_msg) reflog_msg = "commit (amend)"; - parents = copy_commit_list(current_head->parents); + parents = commit_list_copy(current_head->parents); } else if (whence == FROM_MERGE) { struct strbuf m = STRBUF_INIT; FILE *fp; diff --git a/commit.c b/commit.c index efd0c026831e6b..c5c66d3a6b2b78 100644 --- a/commit.c +++ b/commit.c @@ -680,7 +680,7 @@ unsigned commit_list_count(const struct commit_list *l) return c; } -struct commit_list *copy_commit_list(const struct commit_list *list) +struct commit_list *commit_list_copy(const struct commit_list *list) { struct commit_list *head = NULL; struct commit_list **pp = &head; diff --git a/commit.h b/commit.h index 79a761c37df023..2faf08cd1863d5 100644 --- a/commit.h +++ b/commit.h @@ -186,13 +186,22 @@ struct commit_list *commit_list_insert_by_date(struct commit *item, void commit_list_sort_by_date(struct commit_list **list); /* Shallow copy of the input list */ -struct commit_list *copy_commit_list(const struct commit_list *list); +struct commit_list *commit_list_copy(const struct commit_list *list); /* Modify list in-place to reverse it, returning new head; list will be tail */ struct commit_list *reverse_commit_list(struct commit_list *list); void free_commit_list(struct commit_list *list); +/* + * Deprecated compatibility functions for `struct commit_list`, to be removed + * once Git 2.53 is released. + */ +static inline struct commit_list *copy_commit_list(struct commit_list *l) +{ + return commit_list_copy(l); +} + struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */ const char *repo_logmsg_reencode(struct repository *r, diff --git a/merge-ort.c b/merge-ort.c index 2b837a58c3a6f8..f31754c3611c43 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -5301,7 +5301,7 @@ static void merge_ort_internal(struct merge_options *opt, struct commit *h2, struct merge_result *result) { - struct commit_list *merge_bases = copy_commit_list(_merge_bases); + struct commit_list *merge_bases = commit_list_copy(_merge_bases); struct commit *next; struct commit *merged_merge_bases; const char *ancestor_name; diff --git a/revision.c b/revision.c index 1858e093eeeb89..9f5baceb85f2b3 100644 --- a/revision.c +++ b/revision.c @@ -4224,7 +4224,7 @@ static void save_parents(struct rev_info *revs, struct commit *commit) if (*pp) return; if (commit->parents) - *pp = copy_commit_list(commit->parents); + *pp = commit_list_copy(commit->parents); else *pp = EMPTY_PARENT_LIST; } @@ -4294,7 +4294,7 @@ static void track_linear(struct rev_info *revs, struct commit *commit) commit->object.flags |= TRACK_LINEAR; } free_commit_list(revs->previous_parents); - revs->previous_parents = copy_commit_list(commit->parents); + revs->previous_parents = commit_list_copy(commit->parents); } static struct commit *get_revision_1(struct rev_info *revs) diff --git a/sequencer.c b/sequencer.c index 71ed31c7740688..f38d247b1099d1 100644 --- a/sequencer.c +++ b/sequencer.c @@ -1566,7 +1566,7 @@ static int try_to_commit(struct repository *r, res = error(_("unable to parse commit author")); goto out; } - parents = copy_commit_list(current_head->parents); + parents = commit_list_copy(current_head->parents); extra = read_commit_extra_headers(current_head, exclude_gpgsig); } else if (current_head && (!(flags & CREATE_ROOT_COMMIT) || (flags & AMEND_MSG))) { From a468f3cefab32eed7d9a12bd6b93719d38ec67a6 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 15 Jan 2026 10:35:33 +0100 Subject: [PATCH 02/23] commit: rename `reverse_commit_list()` to conform to coding guidelines Our coding guidelines say that: Functions that operate on `struct S` are named `S_()` and should generally receive a pointer to `struct S` as first parameter. While most of the functions related to `struct commit_list` already follow that naming schema, `reverse_commit_list()` doesn't. Rename the function to address this and adjust all of its callers. Add a compatibility wrapper for the old function name to ease the transition and avoid any semantic conflicts with in-flight patch series. This wrapper will be removed once Git 2.53 has been released. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/merge-tree.c | 2 +- builtin/stash.c | 2 +- commit.c | 2 +- commit.h | 7 ++++++- merge-ort.c | 2 +- sequencer.c | 2 +- 6 files changed, 11 insertions(+), 6 deletions(-) diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 1c063d9a41a695..979a55d3b2983f 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -483,7 +483,7 @@ static int real_merge(struct merge_tree_options *o, exit(128); if (!merge_bases && !o->allow_unrelated_histories) die(_("refusing to merge unrelated histories")); - merge_bases = reverse_commit_list(merge_bases); + merge_bases = commit_list_reverse(merge_bases); merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result); free_commit_list(merge_bases); } diff --git a/builtin/stash.c b/builtin/stash.c index 948eba06fbccb3..4cb2351787c33d 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -2308,7 +2308,7 @@ static int do_export_stash(struct repository *r, * but where their first parents form a chain to our original empty * base commit. */ - items = reverse_commit_list(items); + items = commit_list_reverse(items); for (cur = items; cur; cur = cur->next) { struct commit_list *parents = NULL; struct commit_list **next = &parents; diff --git a/commit.c b/commit.c index c5c66d3a6b2b78..36f02c96aabb9d 100644 --- a/commit.c +++ b/commit.c @@ -691,7 +691,7 @@ struct commit_list *commit_list_copy(const struct commit_list *list) return head; } -struct commit_list *reverse_commit_list(struct commit_list *list) +struct commit_list *commit_list_reverse(struct commit_list *list) { struct commit_list *next = NULL, *current, *backup; for (current = list; current; current = backup) { diff --git a/commit.h b/commit.h index 2faf08cd1863d5..f50d9e5a4abe91 100644 --- a/commit.h +++ b/commit.h @@ -189,7 +189,7 @@ void commit_list_sort_by_date(struct commit_list **list); struct commit_list *commit_list_copy(const struct commit_list *list); /* Modify list in-place to reverse it, returning new head; list will be tail */ -struct commit_list *reverse_commit_list(struct commit_list *list); +struct commit_list *commit_list_reverse(struct commit_list *list); void free_commit_list(struct commit_list *list); @@ -202,6 +202,11 @@ static inline struct commit_list *copy_commit_list(struct commit_list *l) return commit_list_copy(l); } +static inline struct commit_list *reverse_commit_list(struct commit_list *l) +{ + return commit_list_reverse(l); +} + struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */ const char *repo_logmsg_reencode(struct repository *r, diff --git a/merge-ort.c b/merge-ort.c index f31754c3611c43..2ddaaffc263d46 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -5314,7 +5314,7 @@ static void merge_ort_internal(struct merge_options *opt, goto out; } /* See merge-ort.h:merge_incore_recursive() declaration NOTE */ - merge_bases = reverse_commit_list(merge_bases); + merge_bases = commit_list_reverse(merge_bases); } merged_merge_bases = pop_commit(&merge_bases); diff --git a/sequencer.c b/sequencer.c index f38d247b1099d1..e09f8eed551425 100644 --- a/sequencer.c +++ b/sequencer.c @@ -4317,7 +4317,7 @@ static int do_merge(struct repository *r, git_path_merge_head(r), 0); write_message("no-ff", 5, git_path_merge_mode(r), 0); - bases = reverse_commit_list(bases); + bases = commit_list_reverse(bases); repo_read_index(r); init_ui_merge_options(&o, r); From 9f18d089c51fba2776fe1fece877a359c47417f7 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 15 Jan 2026 10:35:34 +0100 Subject: [PATCH 03/23] commit: rename `free_commit_list()` to conform to coding guidelines Our coding guidelines say that: Functions that operate on `struct S` are named `S_()` and should generally receive a pointer to `struct S` as first parameter. While most of the functions related to `struct commit_list` already follow that naming schema, `free_commit_list()` doesn't. Rename the function to address this and adjust all of its callers. Add a compatibility wrapper for the old function name to ease the transition and avoid any semantic conflicts with in-flight patch series. This wrapper will be removed once Git 2.53 has been released. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- bisect.c | 12 ++++++------ blame.c | 2 +- builtin/am.c | 2 +- builtin/commit-tree.c | 2 +- builtin/commit.c | 2 +- builtin/describe.c | 2 +- builtin/diff-tree.c | 2 +- builtin/gc.c | 2 +- builtin/log.c | 12 ++++++------ builtin/merge-base.c | 14 +++++++------- builtin/merge-tree.c | 2 +- builtin/merge.c | 16 ++++++++-------- builtin/pull.c | 8 ++++---- builtin/rebase.c | 4 ++-- builtin/rev-list.c | 2 +- builtin/show-branch.c | 2 +- builtin/stash.c | 10 +++++----- commit-graph.c | 2 +- commit-reach.c | 30 +++++++++++++++--------------- commit.c | 12 ++++++------ commit.h | 7 ++++++- contrib/coccinelle/free.cocci | 8 ++++---- diff-lib.c | 2 +- fmt-merge-msg.c | 2 +- line-log.c | 2 +- log-tree.c | 2 +- merge-ort-wrappers.c | 2 +- merge-ort.c | 2 +- notes-merge.c | 4 ++-- notes-utils.c | 2 +- object-name.c | 8 ++++---- pack-bitmap-write.c | 6 +++--- ref-filter.c | 8 ++++---- reflog.c | 4 ++-- remote.c | 2 +- revision.c | 32 ++++++++++++++++---------------- sequencer.c | 16 ++++++++-------- shallow.c | 4 ++-- submodule.c | 4 ++-- t/helper/test-reach.c | 10 +++++----- 40 files changed, 136 insertions(+), 131 deletions(-) diff --git a/bisect.c b/bisect.c index 326b59c0dc70e7..b313f1324009b6 100644 --- a/bisect.c +++ b/bisect.c @@ -257,7 +257,7 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n p = p->next; } if (p) { - free_commit_list(p->next); + commit_list_free(p->next); p->next = NULL; } strbuf_release(&buf); @@ -438,7 +438,7 @@ void find_bisection(struct commit_list **commit_list, int *reaches, if (best) { if (!(bisect_flags & FIND_BISECTION_ALL)) { list->item = best->item; - free_commit_list(list->next); + commit_list_free(list->next); best = list; best->next = NULL; } @@ -559,8 +559,8 @@ struct commit_list *filter_skipped(struct commit_list *list, } else { if (!show_all) { if (!skipped_first || !*skipped_first) { - free_commit_list(next); - free_commit_list(filtered); + commit_list_free(next); + commit_list_free(filtered); return list; } } else if (skipped_first && !*skipped_first) { @@ -879,7 +879,7 @@ static enum bisect_error check_merge_bases(size_t rev_nr, struct commit **rev, i } } - free_commit_list(result); + commit_list_free(result); return res; } @@ -1142,7 +1142,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix) res = bisect_checkout(bisect_rev, no_checkout); cleanup: - free_commit_list(tried); + commit_list_free(tried); release_revisions(&revs); strvec_clear(&rev_argv); return res; diff --git a/blame.c b/blame.c index cb0b08342308ef..a3c49d132e4ae1 100644 --- a/blame.c +++ b/blame.c @@ -2368,7 +2368,7 @@ static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit if (revs->first_parent_only && commit->parents && commit->parents->next) { - free_commit_list(commit->parents->next); + commit_list_free(commit->parents->next); commit->parents->next = NULL; } return commit->parents; diff --git a/builtin/am.c b/builtin/am.c index 277c2e7937dcc1..97a7b1d46a2892 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1726,7 +1726,7 @@ static void do_commit(const struct am_state *state) run_hooks(the_repository, "post-applypatch"); - free_commit_list(parents); + commit_list_free(parents); strbuf_release(&sb); } diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index 5189e685a7eccd..30535db131eaa6 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -154,7 +154,7 @@ int cmd_commit_tree(int argc, ret = 0; out: - free_commit_list(parents); + commit_list_free(parents); strbuf_release(&buffer); return ret; } diff --git a/builtin/commit.c b/builtin/commit.c index 0aa3690b04b955..b1315b512bd12d 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1978,7 +1978,7 @@ int cmd_commit(int argc, cleanup: free_commit_extra_headers(extra); - free_commit_list(parents); + commit_list_free(parents); strbuf_release(&author_ident); strbuf_release(&err); strbuf_release(&sb); diff --git a/builtin/describe.c b/builtin/describe.c index 989a78d715d525..abfe3525a5385b 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -558,7 +558,7 @@ static void process_object(struct object *obj, const char *path, void *data) describe_commit(pcd->current_commit, pcd->dst); strbuf_addf(pcd->dst, ":%s", path); } - free_commit_list(pcd->revs->commits); + commit_list_free(pcd->revs->commits); pcd->revs->commits = NULL; } } diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index 49dd4d00ebf1bc..cd35d1c91575b2 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -33,7 +33,7 @@ static int stdin_diff_commit(struct commit *commit, const char *p) struct commit *parent = lookup_commit(the_repository, &oid); if (!pptr) { /* Free the real parent list */ - free_commit_list(commit->parents); + commit_list_free(commit->parents); commit->parents = NULL; pptr = &(commit->parents); } diff --git a/builtin/gc.c b/builtin/gc.c index 92c6e7b954faff..6c529c429e7660 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -1166,7 +1166,7 @@ static int dfs_on_ref(const struct reference *ref, void *cb_data) } } - free_commit_list(stack); + commit_list_free(stack); return result; } diff --git a/builtin/log.c b/builtin/log.c index 5c9a8ef3632906..d43ca693bf7a8a 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -424,7 +424,7 @@ static int cmd_log_walk_no_free(struct rev_info *rev) */ free_commit_buffer(the_repository->parsed_objects, commit); - free_commit_list(commit->parents); + commit_list_free(commit->parents); commit->parents = NULL; } if (saved_nrl < rev->diffopt.needed_rename_limit) @@ -1697,12 +1697,12 @@ static struct commit *get_base_commit(const struct format_config *cfg, if (die_on_failure) { die(_("could not find exact merge base")); } else { - free_commit_list(base_list); + commit_list_free(base_list); return NULL; } } base = base_list->item; - free_commit_list(base_list); + commit_list_free(base_list); } else { if (die_on_failure) die(_("failed to get upstream, if you want to record base commit automatically,\n" @@ -1732,14 +1732,14 @@ static struct commit *get_base_commit(const struct format_config *cfg, if (die_on_failure) { die(_("failed to find exact merge base")); } else { - free_commit_list(merge_base); + commit_list_free(merge_base); free(rev); return NULL; } } rev[i] = merge_base->item; - free_commit_list(merge_base); + commit_list_free(merge_base); } if (rev_nr % 2) @@ -2610,7 +2610,7 @@ int cmd_cherry(int argc, print_commit(sign, commit, verbose, abbrev, revs.diffopt.file); } - free_commit_list(list); + commit_list_free(list); free_patch_ids(&ids); return 0; } diff --git a/builtin/merge-base.c b/builtin/merge-base.c index 3f82781245bd27..c7ee97fa6ac62a 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -15,7 +15,7 @@ static int show_merge_base(struct commit **rev, size_t rev_nr, int show_all) if (repo_get_merge_bases_many_dirty(the_repository, rev[0], rev_nr - 1, rev + 1, &result) < 0) { - free_commit_list(result); + commit_list_free(result); return -1; } @@ -28,7 +28,7 @@ static int show_merge_base(struct commit **rev, size_t rev_nr, int show_all) break; } - free_commit_list(result); + commit_list_free(result); return 0; } @@ -71,7 +71,7 @@ static int handle_independent(int count, const char **args) for (rev = revs; rev; rev = rev->next) printf("%s\n", oid_to_hex(&rev->item->object.oid)); - free_commit_list(revs); + commit_list_free(revs); return 0; } @@ -85,11 +85,11 @@ static int handle_octopus(int count, const char **args, int show_all) commit_list_insert(get_commit_reference(args[i]), &revs); if (get_octopus_merge_bases(revs, &result) < 0) { - free_commit_list(revs); - free_commit_list(result); + commit_list_free(revs); + commit_list_free(result); return 128; } - free_commit_list(revs); + commit_list_free(revs); reduce_heads_replace(&result); if (!result) @@ -101,7 +101,7 @@ static int handle_octopus(int count, const char **args, int show_all) break; } - free_commit_list(result); + commit_list_free(result); return 0; } diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 979a55d3b2983f..e141fef3ce6148 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -485,7 +485,7 @@ static int real_merge(struct merge_tree_options *o, die(_("refusing to merge unrelated histories")); merge_bases = commit_list_reverse(merge_bases); merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result); - free_commit_list(merge_bases); + commit_list_free(merge_bases); } if (result.clean < 0) diff --git a/builtin/merge.c b/builtin/merge.c index c421a11b0b69df..6a0831a6588701 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -831,7 +831,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, LOCK_DIE_ON_ERROR); clean = merge_ort_recursive(&o, head, remoteheads->item, reversed, &result); - free_commit_list(reversed); + commit_list_free(reversed); strbuf_release(&o.obuf); if (clean < 0) { @@ -1006,7 +1006,7 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads) finish(head, remoteheads, &result_commit, "In-index merge"); remove_merge_branch_state(the_repository); - free_commit_list(parents); + commit_list_free(parents); return 0; } @@ -1022,7 +1022,7 @@ static int finish_automerge(struct commit *head, struct object_id result_commit; write_tree_trivial(result_tree); - free_commit_list(common); + commit_list_free(common); parents = remoteheads; if (!head_subsumed || fast_forward == FF_NO) commit_list_insert(head, &parents); @@ -1035,7 +1035,7 @@ static int finish_automerge(struct commit *head, strbuf_release(&buf); remove_merge_branch_state(the_repository); - free_commit_list(parents); + commit_list_free(parents); return 0; } @@ -1197,7 +1197,7 @@ static struct commit_list *reduce_parents(struct commit *head_commit, /* Find what parents to record by checking independent ones. */ parents = reduce_heads(remoteheads); - free_commit_list(remoteheads); + commit_list_free(remoteheads); remoteheads = NULL; remotes = &remoteheads; @@ -1748,7 +1748,7 @@ int cmd_merge(int argc, exit(128); common_item = common_one->item; - free_commit_list(common_one); + commit_list_free(common_one); if (!oideq(&common_item->object.oid, &j->item->object.oid)) { up_to_date = 0; break; @@ -1880,8 +1880,8 @@ int cmd_merge(int argc, done: if (!automerge_was_ok) { - free_commit_list(common); - free_commit_list(remoteheads); + commit_list_free(common); + commit_list_free(remoteheads); } strbuf_release(&buf); free(branch_to_free); diff --git a/builtin/pull.c b/builtin/pull.c index 3ff748e0b3ea60..6ad420ce6f9b41 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -704,14 +704,14 @@ static int get_octopus_merge_base(struct object_id *merge_base, if (get_octopus_merge_bases(revs, &result) < 0) exit(128); - free_commit_list(revs); + commit_list_free(revs); reduce_heads_replace(&result); if (!result) return 1; oidcpy(merge_base, &result->item->object.oid); - free_commit_list(result); + commit_list_free(result); return 0; } @@ -803,7 +803,7 @@ static int get_can_ff(struct object_id *orig_head, commit_list_insert(head, &list); merge_head = lookup_commit_reference(the_repository, orig_merge_head); ret = repo_is_descendant_of(the_repository, merge_head, list); - free_commit_list(list); + commit_list_free(list); if (ret < 0) exit(128); return ret; @@ -828,7 +828,7 @@ static int already_up_to_date(struct object_id *orig_head, theirs = lookup_commit_reference(the_repository, &merge_heads->oid[i]); commit_list_insert(theirs, &list); ok = repo_is_descendant_of(the_repository, ours, list); - free_commit_list(list); + commit_list_free(list); if (ok < 0) exit(128); if (!ok) diff --git a/builtin/rebase.c b/builtin/rebase.c index c46882818982aa..c487e1090779c2 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -912,7 +912,7 @@ static int can_fast_forward(struct commit *onto, struct commit *upstream, res = 1; done: - free_commit_list(merge_bases); + commit_list_free(merge_bases); return res && is_linear_history(onto, head); } @@ -929,7 +929,7 @@ static void fill_branch_base(struct rebase_options *options, else oidcpy(branch_base, &merge_bases->item->object.oid); - free_commit_list(merge_bases); + commit_list_free(merge_bases); } static int parse_opt_am(const struct option *opt, const char *arg, int unset) diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 99f876ba857579..ddea8aa251a361 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -216,7 +216,7 @@ static inline void finish_object__ma(struct object *obj, const char *name) static void finish_commit(struct commit *commit) { - free_commit_list(commit->parents); + commit_list_free(commit->parents); commit->parents = NULL; free_commit_buffer(the_repository->parsed_objects, commit); diff --git a/builtin/show-branch.c b/builtin/show-branch.c index f3ebc1d4eaf14b..f02831b08500c4 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -1008,7 +1008,7 @@ int cmd_show_branch(int ac, out: for (size_t i = 0; i < ARRAY_SIZE(reflog_msg); i++) free(reflog_msg[i]); - free_commit_list(seen); + commit_list_free(seen); clear_prio_queue(&queue); free(args_copy); free(head); diff --git a/builtin/stash.c b/builtin/stash.c index 4cb2351787c33d..aea68a16aab27e 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -1495,7 +1495,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b goto done; } - free_commit_list(parents); + commit_list_free(parents); parents = NULL; if (include_untracked) { @@ -1564,7 +1564,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b strbuf_release(&commit_tree_label); strbuf_release(&msg); strbuf_release(&untracked_files); - free_commit_list(parents); + commit_list_free(parents); free(branch_name_buf); return ret; } @@ -2184,7 +2184,7 @@ static int do_import_stash(struct repository *r, const char *rev) out: if (this && buffer) repo_unuse_commit_buffer(r, this, buffer); - free_commit_list(items); + commit_list_free(items); free(msg); return res; @@ -2318,7 +2318,7 @@ static int do_export_stash(struct repository *r, next = commit_list_append(prev, next); next = commit_list_append(stash, next); res = write_commit_with_parents(r, &out, &stash->object.oid, parents); - free_commit_list(parents); + commit_list_free(parents); if (res) goto out; prev = lookup_commit_reference(r, &out); @@ -2330,7 +2330,7 @@ static int do_export_stash(struct repository *r, puts(oid_to_hex(&prev->object.oid)); out: strbuf_release(&revision); - free_commit_list(items); + commit_list_free(items); return res; } diff --git a/commit-graph.c b/commit-graph.c index 00e8193adcab81..ed480c05379154 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -965,7 +965,7 @@ static int fill_commit_in_graph(struct commit *item, do { if (g->chunk_extra_edges_size / sizeof(uint32_t) <= parent_data_pos) { error(_("commit-graph extra-edges pointer out of bounds")); - free_commit_list(item->parents); + commit_list_free(item->parents); item->parents = NULL; item->object.parsed = 0; return 0; diff --git a/commit-reach.c b/commit-reach.c index e7d9b3208fabc4..9604bbdcce2f35 100644 --- a/commit-reach.c +++ b/commit-reach.c @@ -109,7 +109,7 @@ static int paint_down_to_common(struct repository *r, continue; if (repo_parse_commit(r, p)) { clear_prio_queue(&queue); - free_commit_list(*result); + commit_list_free(*result); *result = NULL; /* * At this stage, we know that the commit is @@ -166,7 +166,7 @@ static int merge_bases_many(struct repository *r, } if (paint_down_to_common(r, one, n, twos, 0, 0, &list)) { - free_commit_list(list); + commit_list_free(list); return -1; } @@ -195,8 +195,8 @@ int get_octopus_merge_bases(struct commit_list *in, struct commit_list **result) struct commit_list *bases = NULL; if (repo_get_merge_bases(the_repository, i->item, j->item, &bases) < 0) { - free_commit_list(bases); - free_commit_list(*result); + commit_list_free(bases); + commit_list_free(*result); *result = NULL; return -1; } @@ -207,7 +207,7 @@ int get_octopus_merge_bases(struct commit_list *in, struct commit_list **result) for (k = bases; k; k = k->next) end = k; } - free_commit_list(*result); + commit_list_free(*result); *result = new_commits; } return 0; @@ -249,7 +249,7 @@ static int remove_redundant_no_gen(struct repository *r, work, min_generation, 0, &common)) { clear_commit_marks(array[i], all_flags); clear_commit_marks_many(filled, work, all_flags); - free_commit_list(common); + commit_list_free(common); free(work); free(redundant); free(filled_index); @@ -262,7 +262,7 @@ static int remove_redundant_no_gen(struct repository *r, redundant[filled_index[j]] = 1; clear_commit_marks(array[i], all_flags); clear_commit_marks_many(filled, work, all_flags); - free_commit_list(common); + commit_list_free(common); } /* Now collect the result */ @@ -374,7 +374,7 @@ static int remove_redundant_with_gen(struct repository *r, if (!parents) pop_commit(&stack); } - free_commit_list(stack); + commit_list_free(stack); } free(sorted); @@ -451,7 +451,7 @@ static int get_merge_bases_many_0(struct repository *r, CALLOC_ARRAY(rslt, cnt); for (list = *result, i = 0; list; list = list->next) rslt[i++] = list->item; - free_commit_list(*result); + commit_list_free(*result); *result = NULL; clear_commit_marks(one, all_flags); @@ -510,7 +510,7 @@ int repo_is_descendant_of(struct repository *r, int result; commit_list_insert(commit, &from_list); result = can_all_from_reach(from_list, with_commit, 0); - free_commit_list(from_list); + commit_list_free(from_list); return result; } else { while (with_commit) { @@ -561,7 +561,7 @@ int repo_in_merge_bases_many(struct repository *r, struct commit *commit, ret = 1; clear_commit_marks(commit, all_flags); clear_commit_marks_many(nr_reference, reference, all_flags); - free_commit_list(bases); + commit_list_free(bases); return ret; } @@ -578,7 +578,7 @@ int repo_in_merge_bases(struct repository *r, next = commit_list_append(commit, next); res = repo_is_descendant_of(r, reference, list); - free_commit_list(list); + commit_list_free(list); return res; } @@ -626,7 +626,7 @@ struct commit_list *reduce_heads(struct commit_list *heads) void reduce_heads_replace(struct commit_list **heads) { struct commit_list *result = reduce_heads(*heads); - free_commit_list(*heads); + commit_list_free(*heads); *heads = result; } @@ -661,7 +661,7 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid) new_commit, old_commit_list); if (ret < 0) exit(128); - free_commit_list(old_commit_list); + commit_list_free(old_commit_list); return ret; } @@ -1236,7 +1236,7 @@ void tips_reachable_from_bases(struct repository *r, done: free(commits); repo_clear_commit_marks(r, SEEN); - free_commit_list(stack); + commit_list_free(stack); } /* diff --git a/commit.c b/commit.c index 36f02c96aabb9d..ddda9ee19d6538 100644 --- a/commit.c +++ b/commit.c @@ -191,7 +191,7 @@ void unparse_commit(struct repository *r, const struct object_id *oid) if (!c->object.parsed) return; - free_commit_list(c->parents); + commit_list_free(c->parents); c->parents = NULL; c->object.parsed = 0; } @@ -436,7 +436,7 @@ void release_commit_memory(struct parsed_object_pool *pool, struct commit *c) set_commit_tree(c, NULL); free_commit_buffer(pool, c); c->index = 0; - free_commit_list(c->parents); + commit_list_free(c->parents); c->object.parsed = 0; } @@ -480,7 +480,7 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b * same error, but that's good, since it lets our caller know * the result cannot be trusted. */ - free_commit_list(item->parents); + commit_list_free(item->parents); item->parents = NULL; tail += size; @@ -702,7 +702,7 @@ struct commit_list *commit_list_reverse(struct commit_list *list) return next; } -void free_commit_list(struct commit_list *list) +void commit_list_free(struct commit_list *list) { while (list) pop_commit(&list); @@ -977,7 +977,7 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so prio_queue_reverse(&queue); /* We no longer need the commit list */ - free_commit_list(orig); + commit_list_free(orig); pptr = list; *list = NULL; @@ -1107,7 +1107,7 @@ struct commit *get_fork_point(const char *refname, struct commit *commit) cleanup_return: free(revs.commit); - free_commit_list(bases); + commit_list_free(bases); free(full_refname); return ret; } diff --git a/commit.h b/commit.h index f50d9e5a4abe91..1635de418b59e0 100644 --- a/commit.h +++ b/commit.h @@ -191,7 +191,7 @@ struct commit_list *commit_list_copy(const struct commit_list *list); /* Modify list in-place to reverse it, returning new head; list will be tail */ struct commit_list *commit_list_reverse(struct commit_list *list); -void free_commit_list(struct commit_list *list); +void commit_list_free(struct commit_list *list); /* * Deprecated compatibility functions for `struct commit_list`, to be removed @@ -207,6 +207,11 @@ static inline struct commit_list *reverse_commit_list(struct commit_list *l) return commit_list_reverse(l); } +static inline void free_commit_list(struct commit_list *l) +{ + commit_list_free(l); +} + struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */ const char *repo_logmsg_reencode(struct repository *r, diff --git a/contrib/coccinelle/free.cocci b/contrib/coccinelle/free.cocci index 6fb9eb6e88379a..03799e190828fa 100644 --- a/contrib/coccinelle/free.cocci +++ b/contrib/coccinelle/free.cocci @@ -5,7 +5,7 @@ expression E; ( free(E); | - free_commit_list(E); + commit_list_free(E); ) @@ @@ -15,7 +15,7 @@ expression E; ( free(E); | - free_commit_list(E); + commit_list_free(E); ) @@ @@ -30,7 +30,7 @@ expression E; @@ - if (E) - { - free_commit_list(E); + commit_list_free(E); E = NULL; - } @@ -41,5 +41,5 @@ statement S; - if (E) { + if (E) S - free_commit_list(E); + commit_list_free(E); - } diff --git a/diff-lib.c b/diff-lib.c index 5307390ff3db7b..4772e5a561717a 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -615,7 +615,7 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb) oidcpy(mb, &merge_bases->item->object.oid); - free_commit_list(merge_bases); + commit_list_free(merge_bases); } void run_diff_index(struct rev_info *revs, unsigned int option) diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c index c9085edc40e934..877a7daed5c268 100644 --- a/fmt-merge-msg.c +++ b/fmt-merge-msg.c @@ -421,7 +421,7 @@ static void shortlog(const char *name, clear_commit_marks((struct commit *)branch, flags); clear_commit_marks(head, flags); - free_commit_list(rev->commits); + commit_list_free(rev->commits); rev->commits = NULL; rev->pending.nr = 0; diff --git a/line-log.c b/line-log.c index 8bd422148dd492..eeaf68454e2246 100644 --- a/line-log.c +++ b/line-log.c @@ -1239,7 +1239,7 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm * don't follow any other path in history */ add_line_range(rev, parent, cand[i]); - free_commit_list(commit->parents); + commit_list_free(commit->parents); commit_list_append(parent, &commit->parents); ret = 0; diff --git a/log-tree.c b/log-tree.c index 1729b0c201271b..7e048701d0c5b4 100644 --- a/log-tree.c +++ b/log-tree.c @@ -1077,7 +1077,7 @@ static int do_remerge_diff(struct rev_info *opt, log_tree_diff_flush(opt); /* Cleanup */ - free_commit_list(bases); + commit_list_free(bases); cleanup_additional_headers(&opt->diffopt); strbuf_release(&parent1_desc); strbuf_release(&parent2_desc); diff --git a/merge-ort-wrappers.c b/merge-ort-wrappers.c index c54d56b34465bf..2110844f5331c1 100644 --- a/merge-ort-wrappers.c +++ b/merge-ort-wrappers.c @@ -120,7 +120,7 @@ int merge_ort_generic(struct merge_options *opt, repo_hold_locked_index(opt->repo, &lock, LOCK_DIE_ON_ERROR); clean = merge_ort_recursive(opt, head_commit, next_commit, ca, result); - free_commit_list(ca); + commit_list_free(ca); if (clean < 0) { rollback_lock_file(&lock); return clean; diff --git a/merge-ort.c b/merge-ort.c index 2ddaaffc263d46..0c755361cdb9ee 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -5382,7 +5382,7 @@ static void merge_ort_internal(struct merge_options *opt, opt->ancestor = NULL; /* avoid accidental re-use of opt->ancestor */ out: - free_commit_list(merge_bases); + commit_list_free(merge_bases); } void merge_incore_nonrecursive(struct merge_options *opt, diff --git a/notes-merge.c b/notes-merge.c index 586939939f2451..49d0dadd32e427 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -668,11 +668,11 @@ int notes_merge(struct notes_merge_options *o, commit_list_insert(local, &parents); create_notes_commit(o->repo, local_tree, parents, o->commit_msg.buf, o->commit_msg.len, result_oid); - free_commit_list(parents); + commit_list_free(parents); } found_result: - free_commit_list(bases); + commit_list_free(bases); strbuf_release(&(o->commit_msg)); trace_printf("notes_merge(): result = %i, result_oid = %.7s\n", result, oid_to_hex(result_oid)); diff --git a/notes-utils.c b/notes-utils.c index 6a50c6d56466d5..5c1c75d5b8099a 100644 --- a/notes-utils.c +++ b/notes-utils.c @@ -40,7 +40,7 @@ void create_notes_commit(struct repository *r, NULL)) die("Failed to commit notes tree to database"); - free_commit_list(parents_to_free); + commit_list_free(parents_to_free); } void commit_notes(struct repository *r, struct notes_tree *t, const char *msg) diff --git a/object-name.c b/object-name.c index 8b862c124e05a9..e697566423a075 100644 --- a/object-name.c +++ b/object-name.c @@ -1281,7 +1281,7 @@ static int peel_onion(struct repository *r, const char *name, int len, commit_list_insert((struct commit *)o, &list); ret = get_oid_oneline(r, prefix, oid, list); - free_commit_list(list); + commit_list_free(list); free(prefix); return ret; } @@ -1623,7 +1623,7 @@ int repo_get_oid_mb(struct repository *r, if (!two) return -1; if (repo_get_merge_bases(r, one, two, &mbs) < 0) { - free_commit_list(mbs); + commit_list_free(mbs); return -1; } if (!mbs || mbs->next) @@ -1632,7 +1632,7 @@ int repo_get_oid_mb(struct repository *r, st = 0; oidcpy(oid, &mbs->item->object.oid); } - free_commit_list(mbs); + commit_list_free(mbs); return st; } @@ -2052,7 +2052,7 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo, refs_head_ref(get_main_ref_store(repo), handle_one_ref, &cb); ret = get_oid_oneline(repo, name + 2, oid, list); - free_commit_list(list); + commit_list_free(list); return ret; } if (namelen < 3 || diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c index bf73ce5710abcc..2e3f1c1530bad5 100644 --- a/pack-bitmap-write.c +++ b/pack-bitmap-write.c @@ -306,7 +306,7 @@ struct bb_commit { static void clear_bb_commit(struct bb_commit *commit) { - free_commit_list(commit->reverse_edges); + commit_list_free(commit->reverse_edges); bitmap_free(commit->commit_mask); bitmap_free(commit->bitmap); } @@ -414,7 +414,7 @@ static void bitmap_builder_init(struct bitmap_builder *bb, p_ent->maximal = 1; else { p_ent->maximal = 0; - free_commit_list(p_ent->reverse_edges); + commit_list_free(p_ent->reverse_edges); p_ent->reverse_edges = NULL; } @@ -445,7 +445,7 @@ static void bitmap_builder_init(struct bitmap_builder *bb, "num_maximal_commits", num_maximal); release_revisions(&revs); - free_commit_list(reusable); + commit_list_free(reusable); } static void bitmap_builder_clear(struct bitmap_builder *bb) diff --git a/ref-filter.c b/ref-filter.c index c318f9ca0ec8dd..3917c4ccd9f73a 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -3782,9 +3782,9 @@ void ref_filter_clear(struct ref_filter *filter) { strvec_clear(&filter->exclude); oid_array_clear(&filter->points_at); - free_commit_list(filter->with_commit); - free_commit_list(filter->no_commit); - free_commit_list(filter->reachable_from); - free_commit_list(filter->unreachable_from); + commit_list_free(filter->with_commit); + commit_list_free(filter->no_commit); + commit_list_free(filter->reachable_from); + commit_list_free(filter->unreachable_from); ref_filter_init(filter); } diff --git a/reflog.c b/reflog.c index ac87e20c4f97ff..1460ae9d0dd5f7 100644 --- a/reflog.c +++ b/reflog.c @@ -493,7 +493,7 @@ void reflog_expiry_cleanup(void *cb_data) case UE_HEAD: for (elem = cb->tips; elem; elem = elem->next) clear_commit_marks(elem->item, REACHABLE); - free_commit_list(cb->tips); + commit_list_free(cb->tips); break; case UE_NORMAL: clear_commit_marks(cb->tip_commit, REACHABLE); @@ -501,7 +501,7 @@ void reflog_expiry_cleanup(void *cb_data) } for (elem = cb->mark_list; elem; elem = elem->next) clear_commit_marks(elem->item, REACHABLE); - free_commit_list(cb->mark_list); + commit_list_free(cb->mark_list); } int count_reflog_ent(const char *refname UNUSED, diff --git a/remote.c b/remote.c index b756ff6f1594d9..1c8a9f1a888dd2 100644 --- a/remote.c +++ b/remote.c @@ -1497,7 +1497,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds clear_commit_marks_many(src_commits.nr, src_commits.items, reachable_flag); commit_stack_clear(&src_commits); - free_commit_list(found_commits); + commit_list_free(found_commits); } string_list_clear(&src_tag, 0); diff --git a/revision.c b/revision.c index 9f5baceb85f2b3..6d207c2f23ba62 100644 --- a/revision.c +++ b/revision.c @@ -1048,7 +1048,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) continue; } - free_commit_list(parent->next); + commit_list_free(parent->next); parent->next = NULL; while (commit->parents != parent) pop_commit(&commit->parents); @@ -1083,7 +1083,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) die("cannot simplify commit %s (invalid %s)", oid_to_hex(&commit->object.oid), oid_to_hex(&p->object.oid)); - free_commit_list(p->parents); + commit_list_free(p->parents); p->parents = NULL; } /* fallthrough */ @@ -1405,7 +1405,7 @@ static void limit_to_ancestry(struct commit_list *bottoms, struct commit_list *l p->item->object.flags &= ~(TMP_MARK | ANCESTRY_PATH); for (p = bottoms; p; p = p->next) p->item->object.flags &= ~(TMP_MARK | ANCESTRY_PATH); - free_commit_list(rlist); + commit_list_free(rlist); } /* @@ -1508,7 +1508,7 @@ static int limit_list(struct rev_info *revs) } } - free_commit_list(original_list); + commit_list_free(original_list); revs->commits = newlist; return 0; } @@ -2011,7 +2011,7 @@ static void prepare_show_merge(struct rev_info *revs) exit(128); add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM); add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM); - free_commit_list(bases); + commit_list_free(bases); head->object.flags |= SYMMETRIC_LEFT; if (!istate->cache_nr) @@ -2105,13 +2105,13 @@ static int handle_dotdot_1(const char *arg, char *dotdot, return dotdot_missing(arg, dotdot, revs, symmetric); if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0) { - free_commit_list(exclude); + commit_list_free(exclude); return -1; } add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE, flags_exclude); add_pending_commit_list(revs, exclude, flags_exclude); - free_commit_list(exclude); + commit_list_free(exclude); b_flags = flags; a_flags = flags | SYMMETRIC_LEFT; @@ -3221,13 +3221,13 @@ static void release_revisions_bloom_keyvecs(struct rev_info *revs) static void free_void_commit_list(void *list) { - free_commit_list(list); + commit_list_free(list); } void release_revisions(struct rev_info *revs) { - free_commit_list(revs->commits); - free_commit_list(revs->ancestry_path_bottoms); + commit_list_free(revs->commits); + commit_list_free(revs->ancestry_path_bottoms); release_display_notes(&revs->notes_opt); object_array_clear(&revs->pending); object_array_clear(&revs->boundary_commits); @@ -3335,7 +3335,7 @@ static int mark_redundant_parents(struct commit *commit) if (i != cnt || cnt+marked != orig_cnt) die("mark_redundant_parents %d %d %d %d", orig_cnt, cnt, i, marked); - free_commit_list(h); + commit_list_free(h); return marked; } @@ -4232,7 +4232,7 @@ static void save_parents(struct rev_info *revs, struct commit *commit) static void free_saved_parent(struct commit_list **parents) { if (*parents != EMPTY_PARENT_LIST) - free_commit_list(*parents); + commit_list_free(*parents); } static void free_saved_parents(struct rev_info *revs) @@ -4293,7 +4293,7 @@ static void track_linear(struct rev_info *revs, struct commit *commit) if (revs->linear) commit->object.flags |= TRACK_LINEAR; } - free_commit_list(revs->previous_parents); + commit_list_free(revs->previous_parents); revs->previous_parents = commit_list_copy(commit->parents); } @@ -4382,7 +4382,7 @@ static void create_boundary_commit_list(struct rev_info *revs) * boundary commits anyway. (This is what the code has always * done.) */ - free_commit_list(revs->commits); + commit_list_free(revs->commits); revs->commits = NULL; /* @@ -4504,7 +4504,7 @@ struct commit *get_revision(struct rev_info *revs) reversed = NULL; while ((c = get_revision_internal(revs))) commit_list_insert(c, &reversed); - free_commit_list(revs->commits); + commit_list_free(revs->commits); revs->commits = reversed; revs->reverse = 0; revs->reverse_output_stage = 1; @@ -4522,7 +4522,7 @@ struct commit *get_revision(struct rev_info *revs) graph_update(revs->graph, c); if (!c) { free_saved_parents(revs); - free_commit_list(revs->previous_parents); + commit_list_free(revs->previous_parents); revs->previous_parents = NULL; } return c; diff --git a/sequencer.c b/sequencer.c index e09f8eed551425..f5a6496937a893 100644 --- a/sequencer.c +++ b/sequencer.c @@ -1698,7 +1698,7 @@ static int try_to_commit(struct repository *r, out: free_commit_extra_headers(extra); - free_commit_list(parents); + commit_list_free(parents); strbuf_release(&err); strbuf_release(&commit_msg); free(amend_author); @@ -2476,8 +2476,8 @@ static int do_pick_commit(struct repository *r, res |= try_merge_command(r, opts->strategy, opts->xopts.nr, opts->xopts.v, common, oid_to_hex(&head), remotes); - free_commit_list(common); - free_commit_list(remotes); + commit_list_free(common); + commit_list_free(remotes); } /* @@ -4381,8 +4381,8 @@ static int do_merge(struct repository *r, leave_merge: strbuf_release(&ref_name); rollback_lock_file(&lock); - free_commit_list(to_merge); - free_commit_list(bases); + commit_list_free(to_merge); + commit_list_free(bases); return ret; } @@ -6039,11 +6039,11 @@ static int make_script_with_merges(struct pretty_print_context *pp, oidset_insert(&shown, oid); } - free_commit_list(list); + commit_list_free(list); } - free_commit_list(commits); - free_commit_list(tips); + commit_list_free(commits); + commit_list_free(tips); strbuf_release(&label_from_message); strbuf_release(&oneline); diff --git a/shallow.c b/shallow.c index c870efcefcac4a..0409b1354cb5f8 100644 --- a/shallow.c +++ b/shallow.c @@ -40,7 +40,7 @@ int register_shallow(struct repository *r, const struct object_id *oid) oidcpy(&graft->oid, oid); graft->nr_parent = -1; if (commit && commit->object.parsed) { - free_commit_list(commit->parents); + commit_list_free(commit->parents); commit->parents = NULL; } return register_commit_graft(r, graft, 0); @@ -267,7 +267,7 @@ struct commit_list *get_shallow_commits_by_rev_list(struct strvec *argv, break; } } - free_commit_list(not_shallow_list); + commit_list_free(not_shallow_list); /* * Now we can clean up NOT_SHALLOW on border commits. Having diff --git a/submodule.c b/submodule.c index 40a5c6fb9d1545..85e9586e669257 100644 --- a/submodule.c +++ b/submodule.c @@ -639,7 +639,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path, print_submodule_diff_summary(sub, &rev, o); out: - free_commit_list(merge_bases); + commit_list_free(merge_bases); release_revisions(&rev); clear_commit_marks(left, ~0); clear_commit_marks(right, ~0); @@ -729,7 +729,7 @@ void show_submodule_inline_diff(struct diff_options *o, const char *path, done: strbuf_release(&sb); - free_commit_list(merge_bases); + commit_list_free(merge_bases); if (left) clear_commit_marks(left, ~0); if (right) diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c index feabeb29c25d89..3131b54a871c1b 100644 --- a/t/helper/test-reach.c +++ b/t/helper/test-reach.c @@ -120,12 +120,12 @@ int cmd__reach(int ac, const char **av) exit(128); printf("%s(A,X):\n", av[1]); print_sorted_commit_ids(list); - free_commit_list(list); + commit_list_free(list); } else if (!strcmp(av[1], "reduce_heads")) { struct commit_list *list = reduce_heads(X); printf("%s(X):\n", av[1]); print_sorted_commit_ids(list); - free_commit_list(list); + commit_list_free(list); } else if (!strcmp(av[1], "can_all_from_reach")) { printf("%s(X,Y):%d\n", av[1], can_all_from_reach(X, Y, 1)); } else if (!strcmp(av[1], "can_all_from_reach_with_flag")) { @@ -172,13 +172,13 @@ int cmd__reach(int ac, const char **av) die(_("too many commits marked reachable")); print_sorted_commit_ids(list); - free_commit_list(list); + commit_list_free(list); } object_array_clear(&X_obj); strbuf_release(&buf); - free_commit_list(X); - free_commit_list(Y); + commit_list_free(X); + commit_list_free(Y); commit_stack_clear(&X_stack); commit_stack_clear(&Y_stack); return 0; From 85329e31dd4c864a5a200d0a0ded886599adc2c5 Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Fri, 30 Jan 2026 15:26:35 +0100 Subject: [PATCH 04/23] last-modified: rewrite error message when more than one commit given When more than one commit is passed to the git-last-modified(1) command, this error message was printed: error: last-modified can only operate on one tree at a time Calling these a "tree" is technically not correct. git-last-modified(1) expects revisions that peel to a commit. Rephrase the error message to: error: last-modified can only operate on one commit at a time While at it, modify the test to ensure the correct error message is printed. Signed-off-by: Toon Claes Signed-off-by: Junio C Hamano --- builtin/last-modified.c | 2 +- t/t8020-last-modified.sh | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/builtin/last-modified.c b/builtin/last-modified.c index c80f0535f6a503..1219f6802e62a9 100644 --- a/builtin/last-modified.c +++ b/builtin/last-modified.c @@ -146,7 +146,7 @@ static int populate_paths_from_revs(struct last_modified *lm) continue; if (num_interesting++) - return error(_("last-modified can only operate on one tree at a time")); + return error(_("last-modified can only operate on one commit at a time")); diff_tree_oid(lm->rev.repo->hash_algo->empty_tree, &obj->item->oid, "", &diffopt); diff --git a/t/t8020-last-modified.sh b/t/t8020-last-modified.sh index 50f4312f715f41..d1aad1231938f6 100755 --- a/t/t8020-last-modified.sh +++ b/t/t8020-last-modified.sh @@ -12,10 +12,6 @@ test_expect_success 'setup' ' test_commit 3 a/b/file ' -test_expect_success 'cannot run last-modified on two trees' ' - test_must_fail git last-modified HEAD HEAD~1 -' - check_last_modified() { local indir= && while test $# != 0 @@ -230,9 +226,14 @@ test_expect_success 'last-modified merge undoes changes' ' EOF ' +test_expect_success 'cannot run last-modified on two commits' ' + test_must_fail git last-modified HEAD HEAD~1 2>err && + test_grep "last-modified can only operate on one commit at a time" err +' + test_expect_success 'last-modified complains about unknown arguments' ' test_must_fail git last-modified --foo 2>err && - grep "unknown last-modified argument: --foo" err + test_grep "unknown last-modified argument: --foo" err ' test_done From e2505ec170bc0861c3b902bc9763416c5f222455 Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Fri, 30 Jan 2026 15:26:36 +0100 Subject: [PATCH 05/23] last-modified: fix memory leak when more than one commit is given When more than one commit is given, the function populate_paths_from_revs() leaks a `struct pathspec`. Plug it. Signed-off-by: Toon Claes Signed-off-by: Junio C Hamano --- builtin/last-modified.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/builtin/last-modified.c b/builtin/last-modified.c index 1219f6802e62a9..31dea975a023a3 100644 --- a/builtin/last-modified.c +++ b/builtin/last-modified.c @@ -123,7 +123,7 @@ static void add_path_from_diff(struct diff_queue_struct *q, static int populate_paths_from_revs(struct last_modified *lm) { - int num_interesting = 0; + int num_interesting = 0, ret = 0; struct diff_options diffopt; /* @@ -145,16 +145,20 @@ static int populate_paths_from_revs(struct last_modified *lm) if (obj->item->flags & UNINTERESTING) continue; - if (num_interesting++) - return error(_("last-modified can only operate on one commit at a time")); + if (num_interesting++) { + ret = error(_("last-modified can only operate on one commit at a time")); + goto out; + } diff_tree_oid(lm->rev.repo->hash_algo->empty_tree, &obj->item->oid, "", &diffopt); diff_flush(&diffopt); } + +out: clear_pathspec(&diffopt.pathspec); - return 0; + return ret; } static void last_modified_emit(struct last_modified *lm, From b768485c4b7ad0a80d0fd24ec941308b57ccb6ed Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Fri, 30 Jan 2026 15:26:37 +0100 Subject: [PATCH 06/23] last-modified: remove double error message When the user passes two revisions, they get the following output: $ git last-modified HEAD HEAD~ error: last-modified can only operate on one revision at a time error: unable to setup last-modified The error message about "unable to setup" is not very informative, remove it. Signed-off-by: Toon Claes Signed-off-by: Junio C Hamano --- builtin/last-modified.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/last-modified.c b/builtin/last-modified.c index 31dea975a023a3..e02ec8428b1c36 100644 --- a/builtin/last-modified.c +++ b/builtin/last-modified.c @@ -495,7 +495,7 @@ static int last_modified_init(struct last_modified *lm, struct repository *r, lm->rev.bloom_filter_settings = get_bloom_filter_settings(lm->rev.repo); if (populate_paths_from_revs(lm) < 0) - return error(_("unable to setup last-modified")); + return -1; CALLOC_ARRAY(lm->all_paths, hashmap_get_size(&lm->paths)); lm->all_paths_nr = 0; From 525ef52301be231c73393da5af4a4071f060eb20 Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Fri, 30 Jan 2026 15:26:38 +0100 Subject: [PATCH 07/23] last-modified: verify revision argument is a commit-ish Passing a non-committish revision to git-last-modified(1) triggers the following BUG: git last-modified HEAD^{tree} BUG: builtin/last-modified.c:456: paths remaining beyond boundary in last-modified Fix this error by ensuring that the given revision peels to a commit. This change also adds a test to verify git-last-modified(1) can operate on an annotated tag. For this an annotated tag is added that points to the second commit. But this causes ambiguous results when calling git-name-rev(1) with `--tags`, because now two tags point to the same commit. To remove this ambiguity, pass `--exclude=` to git-name-rev(1) to exclude the new annotated tag. Reported-by: Gusted Signed-off-by: Toon Claes Signed-off-by: Junio C Hamano --- builtin/last-modified.c | 5 +++++ t/t8020-last-modified.sh | 15 ++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/builtin/last-modified.c b/builtin/last-modified.c index e02ec8428b1c36..d0944673f080f7 100644 --- a/builtin/last-modified.c +++ b/builtin/last-modified.c @@ -150,6 +150,11 @@ static int populate_paths_from_revs(struct last_modified *lm) goto out; } + if (!repo_peel_to_type(lm->rev.repo, obj->path, 0, obj->item, OBJ_COMMIT)) { + ret = error(_("revision argument '%s' is a %s, not a commit-ish"), obj->name, type_name(obj->item->type)); + goto out; + } + diff_tree_oid(lm->rev.repo->hash_algo->empty_tree, &obj->item->oid, "", &diffopt); diff_flush(&diffopt); diff --git a/t/t8020-last-modified.sh b/t/t8020-last-modified.sh index d1aad1231938f6..ec5bdc6aa083a8 100755 --- a/t/t8020-last-modified.sh +++ b/t/t8020-last-modified.sh @@ -8,6 +8,7 @@ test_expect_success 'setup' ' test_commit 1 file && mkdir a && test_commit 2 a/file && + git tag -mA t2 2 && mkdir a/b && test_commit 3 a/b/file ' @@ -30,7 +31,7 @@ check_last_modified() { cat >expect && git ${indir:+-C "$indir"} last-modified "$@" >tmp.1 && - git name-rev --annotate-stdin --name-only --tags \ + git name-rev --annotate-stdin --name-only --tags --exclude=t2 \ tmp.2 && tr '\t' ' ' actual && test_cmp expect actual @@ -51,6 +52,13 @@ test_expect_success 'last-modified recursive' ' EOF ' +test_expect_success 'last-modified on annotated tag' ' + check_last_modified t2 <<-\EOF + 2 a + 1 file + EOF +' + test_expect_success 'last-modified recursive with show-trees' ' check_last_modified -r -t <<-\EOF 3 a/b @@ -236,4 +244,9 @@ test_expect_success 'last-modified complains about unknown arguments' ' test_grep "unknown last-modified argument: --foo" err ' +test_expect_success 'last-modified expects commit-ish' ' + test_must_fail git last-modified HEAD^{tree} 2>err && + test_grep "revision argument ${SQ}HEAD^{tree}${SQ} is a tree, not a commit-ish" err +' + test_done From 585e8dfa27050ce0a69c6e7ead0a3355912d4992 Mon Sep 17 00:00:00 2001 From: Justin Tobler Date: Mon, 2 Feb 2026 18:09:59 -0600 Subject: [PATCH 08/23] odb: store ODB source in `struct odb_transaction` Each `struct odb_transaction` currently stores a reference to the `struct object_database`. Since transactions are handled per object source, instead store a reference to the source. Signed-off-by: Justin Tobler Signed-off-by: Junio C Hamano --- object-file.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/object-file.c b/object-file.c index e7e4c3348f9c1b..196509b252e470 100644 --- a/object-file.c +++ b/object-file.c @@ -711,7 +711,7 @@ struct transaction_packfile { }; struct odb_transaction { - struct object_database *odb; + struct odb_source *source; struct tmp_objdir *objdir; struct transaction_packfile packfile; @@ -728,7 +728,7 @@ static void prepare_loose_object_transaction(struct odb_transaction *transaction if (!transaction || transaction->objdir) return; - transaction->objdir = tmp_objdir_create(transaction->odb->repo, "bulk-fsync"); + transaction->objdir = tmp_objdir_create(transaction->source->odb->repo, "bulk-fsync"); if (transaction->objdir) tmp_objdir_replace_primary_odb(transaction->objdir, 0); } @@ -772,7 +772,7 @@ static void flush_loose_object_transaction(struct odb_transaction *transaction) * the final name is visible. */ strbuf_addf(&temp_path, "%s/bulk_fsync_XXXXXX", - repo_get_object_directory(transaction->odb->repo)); + repo_get_object_directory(transaction->source->odb->repo)); temp = xmks_tempfile(temp_path.buf); fsync_or_die(get_tempfile_fd(temp), get_tempfile_path(temp)); delete_tempfile(&temp); @@ -1344,7 +1344,7 @@ static int already_written(struct odb_transaction *transaction, struct object_id *oid) { /* The object may already exist in the repository */ - if (odb_has_object(transaction->odb, oid, + if (odb_has_object(transaction->source->odb, oid, HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) return 1; @@ -1365,7 +1365,7 @@ static void prepare_packfile_transaction(struct odb_transaction *transaction, if (!(flags & INDEX_WRITE_OBJECT) || state->f) return; - state->f = create_tmp_packfile(transaction->odb->repo, + state->f = create_tmp_packfile(transaction->source->odb->repo, &state->pack_tmp_name); reset_pack_idx_option(&state->pack_idx_opts); @@ -1469,7 +1469,7 @@ static int stream_blob_to_pack(struct transaction_packfile *state, static void flush_packfile_transaction(struct odb_transaction *transaction) { struct transaction_packfile *state = &transaction->packfile; - struct repository *repo = transaction->odb->repo; + struct repository *repo = transaction->source->odb->repo; unsigned char hash[GIT_MAX_RAWSZ]; struct strbuf packname = STRBUF_INIT; char *idx_tmp_name = NULL; @@ -1494,7 +1494,7 @@ static void flush_packfile_transaction(struct odb_transaction *transaction) } strbuf_addf(&packname, "%s/pack/pack-%s.", - repo_get_object_directory(transaction->odb->repo), + repo_get_object_directory(transaction->source->odb->repo), hash_to_hex_algop(hash, repo->hash_algo)); stage_tmp_packfiles(repo, &packname, state->pack_tmp_name, @@ -1553,7 +1553,7 @@ static int index_blob_packfile_transaction(struct odb_transaction *transaction, header_len = format_object_header((char *)obuf, sizeof(obuf), OBJ_BLOB, size); - transaction->odb->repo->hash_algo->init_fn(&ctx); + transaction->source->odb->repo->hash_algo->init_fn(&ctx); git_hash_update(&ctx, obuf, header_len); /* Note: idx is non-NULL when we are writing */ @@ -1993,7 +1993,7 @@ struct odb_transaction *object_file_transaction_begin(struct odb_source *source) return NULL; CALLOC_ARRAY(odb->transaction, 1); - odb->transaction->odb = odb; + odb->transaction->source = source; return odb->transaction; } @@ -2006,11 +2006,11 @@ void object_file_transaction_commit(struct odb_transaction *transaction) /* * Ensure the transaction ending matches the pending transaction. */ - ASSERT(transaction == transaction->odb->transaction); + ASSERT(transaction == transaction->source->odb->transaction); flush_loose_object_transaction(transaction); flush_packfile_transaction(transaction); - transaction->odb->transaction = NULL; + transaction->source->odb->transaction = NULL; free(transaction); } From 8bf06d05a581dfb552cd0e290680f75d3676eb1d Mon Sep 17 00:00:00 2001 From: Justin Tobler Date: Mon, 2 Feb 2026 18:10:00 -0600 Subject: [PATCH 09/23] object-file: rename transaction functions In a subsequent commit, ODB transactions are made more generic to facilitate each ODB source providing its own transaction handling. Rename `object_file_transaction_{begin,commit}()` to `odb_transaction_files_{begin,commit}()` to better match the future source specific transaction implementation. Signed-off-by: Justin Tobler Signed-off-by: Junio C Hamano --- object-file.c | 6 +++--- object-file.h | 6 +++--- odb.c | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/object-file.c b/object-file.c index 196509b252e470..7b34a2b274c8ce 100644 --- a/object-file.c +++ b/object-file.c @@ -723,7 +723,7 @@ static void prepare_loose_object_transaction(struct odb_transaction *transaction * We lazily create the temporary object directory * the first time an object might be added, since * callers may not know whether any objects will be - * added at the time they call object_file_transaction_begin. + * added at the time they call odb_transaction_files_begin. */ if (!transaction || transaction->objdir) return; @@ -1985,7 +1985,7 @@ int read_loose_object(struct repository *repo, return ret; } -struct odb_transaction *object_file_transaction_begin(struct odb_source *source) +struct odb_transaction *odb_transaction_files_begin(struct odb_source *source) { struct object_database *odb = source->odb; @@ -1998,7 +1998,7 @@ struct odb_transaction *object_file_transaction_begin(struct odb_source *source) return odb->transaction; } -void object_file_transaction_commit(struct odb_transaction *transaction) +void odb_transaction_files_commit(struct odb_transaction *transaction) { if (!transaction) return; diff --git a/object-file.h b/object-file.h index 1229d5f675b44a..b4a3341a890dc4 100644 --- a/object-file.h +++ b/object-file.h @@ -202,16 +202,16 @@ struct odb_transaction; /* * Tell the object database to optimize for adding - * multiple objects. object_file_transaction_commit must be called + * multiple objects. odb_transaction_files_commit must be called * to make new objects visible. If a transaction is already * pending, NULL is returned. */ -struct odb_transaction *object_file_transaction_begin(struct odb_source *source); +struct odb_transaction *odb_transaction_files_begin(struct odb_source *source); /* * Tell the object database to make any objects from the * current transaction visible. */ -void object_file_transaction_commit(struct odb_transaction *transaction); +void odb_transaction_files_commit(struct odb_transaction *transaction); #endif /* OBJECT_FILE_H */ diff --git a/odb.c b/odb.c index ac70b6a099f588..a5e6fd01a930af 100644 --- a/odb.c +++ b/odb.c @@ -1153,10 +1153,10 @@ void odb_reprepare(struct object_database *o) struct odb_transaction *odb_transaction_begin(struct object_database *odb) { - return object_file_transaction_begin(odb->sources); + return odb_transaction_files_begin(odb->sources); } void odb_transaction_commit(struct odb_transaction *transaction) { - object_file_transaction_commit(transaction); + odb_transaction_files_commit(transaction); } From fa7d067923a342c298b7723935c60217a5244e4e Mon Sep 17 00:00:00 2001 From: Justin Tobler Date: Mon, 2 Feb 2026 18:10:01 -0600 Subject: [PATCH 10/23] odb: prepare `struct odb_transaction` to become generic An ODB transaction handles how objects are stored temporarily and eventually committed. Due to object storage being implemented differently for a given ODB source, the ODB transactions must be implemented in a manner specific to the source the objects are being written to. To provide generic transactions, `struct odb_transaction` is updated to store a commit callback that can be configured to support a specific ODB source. For now `struct odb_transaction_files` is the only transaction type and what is always returned when starting a transaction. Signed-off-by: Justin Tobler Signed-off-by: Junio C Hamano --- object-file.c | 80 ++++++++++++++++++++++++++++----------------------- object-file.h | 6 ---- odb.c | 5 +++- odb.h | 17 +++++++++++ 4 files changed, 65 insertions(+), 43 deletions(-) diff --git a/object-file.c b/object-file.c index 7b34a2b274c8ce..d7e153c1b9ad04 100644 --- a/object-file.c +++ b/object-file.c @@ -710,15 +710,17 @@ struct transaction_packfile { uint32_t nr_written; }; -struct odb_transaction { - struct odb_source *source; +struct odb_transaction_files { + struct odb_transaction base; struct tmp_objdir *objdir; struct transaction_packfile packfile; }; -static void prepare_loose_object_transaction(struct odb_transaction *transaction) +static void prepare_loose_object_transaction(struct odb_transaction *base) { + struct odb_transaction_files *transaction = (struct odb_transaction_files *)base; + /* * We lazily create the temporary object directory * the first time an object might be added, since @@ -728,14 +730,16 @@ static void prepare_loose_object_transaction(struct odb_transaction *transaction if (!transaction || transaction->objdir) return; - transaction->objdir = tmp_objdir_create(transaction->source->odb->repo, "bulk-fsync"); + transaction->objdir = tmp_objdir_create(base->source->odb->repo, "bulk-fsync"); if (transaction->objdir) tmp_objdir_replace_primary_odb(transaction->objdir, 0); } -static void fsync_loose_object_transaction(struct odb_transaction *transaction, +static void fsync_loose_object_transaction(struct odb_transaction *base, int fd, const char *filename) { + struct odb_transaction_files *transaction = (struct odb_transaction_files *)base; + /* * If we have an active ODB transaction, we issue a call that * cleans the filesystem page cache but avoids a hardware flush @@ -754,7 +758,7 @@ static void fsync_loose_object_transaction(struct odb_transaction *transaction, /* * Cleanup after batch-mode fsync_object_files. */ -static void flush_loose_object_transaction(struct odb_transaction *transaction) +static void flush_loose_object_transaction(struct odb_transaction_files *transaction) { struct strbuf temp_path = STRBUF_INIT; struct tempfile *temp; @@ -772,7 +776,7 @@ static void flush_loose_object_transaction(struct odb_transaction *transaction) * the final name is visible. */ strbuf_addf(&temp_path, "%s/bulk_fsync_XXXXXX", - repo_get_object_directory(transaction->source->odb->repo)); + repo_get_object_directory(transaction->base.source->odb->repo)); temp = xmks_tempfile(temp_path.buf); fsync_or_die(get_tempfile_fd(temp), get_tempfile_path(temp)); delete_tempfile(&temp); @@ -1340,11 +1344,11 @@ static int index_core(struct index_state *istate, return ret; } -static int already_written(struct odb_transaction *transaction, +static int already_written(struct odb_transaction_files *transaction, struct object_id *oid) { /* The object may already exist in the repository */ - if (odb_has_object(transaction->source->odb, oid, + if (odb_has_object(transaction->base.source->odb, oid, HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) return 1; @@ -1358,14 +1362,14 @@ static int already_written(struct odb_transaction *transaction, } /* Lazily create backing packfile for the state */ -static void prepare_packfile_transaction(struct odb_transaction *transaction, +static void prepare_packfile_transaction(struct odb_transaction_files *transaction, unsigned flags) { struct transaction_packfile *state = &transaction->packfile; if (!(flags & INDEX_WRITE_OBJECT) || state->f) return; - state->f = create_tmp_packfile(transaction->source->odb->repo, + state->f = create_tmp_packfile(transaction->base.source->odb->repo, &state->pack_tmp_name); reset_pack_idx_option(&state->pack_idx_opts); @@ -1466,10 +1470,10 @@ static int stream_blob_to_pack(struct transaction_packfile *state, return 0; } -static void flush_packfile_transaction(struct odb_transaction *transaction) +static void flush_packfile_transaction(struct odb_transaction_files *transaction) { struct transaction_packfile *state = &transaction->packfile; - struct repository *repo = transaction->source->odb->repo; + struct repository *repo = transaction->base.source->odb->repo; unsigned char hash[GIT_MAX_RAWSZ]; struct strbuf packname = STRBUF_INIT; char *idx_tmp_name = NULL; @@ -1494,7 +1498,7 @@ static void flush_packfile_transaction(struct odb_transaction *transaction) } strbuf_addf(&packname, "%s/pack/pack-%s.", - repo_get_object_directory(transaction->source->odb->repo), + repo_get_object_directory(transaction->base.source->odb->repo), hash_to_hex_algop(hash, repo->hash_algo)); stage_tmp_packfiles(repo, &packname, state->pack_tmp_name, @@ -1534,7 +1538,7 @@ static void flush_packfile_transaction(struct odb_transaction *transaction) * binary blobs, they generally do not want to get any conversion, and * callers should avoid this code path when filters are requested. */ -static int index_blob_packfile_transaction(struct odb_transaction *transaction, +static int index_blob_packfile_transaction(struct odb_transaction_files *transaction, struct object_id *result_oid, int fd, size_t size, const char *path, unsigned flags) @@ -1553,7 +1557,7 @@ static int index_blob_packfile_transaction(struct odb_transaction *transaction, header_len = format_object_header((char *)obuf, sizeof(obuf), OBJ_BLOB, size); - transaction->source->odb->repo->hash_algo->init_fn(&ctx); + transaction->base.source->odb->repo->hash_algo->init_fn(&ctx); git_hash_update(&ctx, obuf, header_len); /* Note: idx is non-NULL when we are writing */ @@ -1629,10 +1633,11 @@ int index_fd(struct index_state *istate, struct object_id *oid, ret = index_core(istate, oid, fd, xsize_t(st->st_size), type, path, flags); } else { + struct object_database *odb = the_repository->objects; struct odb_transaction *transaction; - transaction = odb_transaction_begin(the_repository->objects); - ret = index_blob_packfile_transaction(the_repository->objects->transaction, + transaction = odb_transaction_begin(odb); + ret = index_blob_packfile_transaction((struct odb_transaction_files *)odb->transaction, oid, fd, xsize_t(st->st_size), path, flags); @@ -1985,35 +1990,38 @@ int read_loose_object(struct repository *repo, return ret; } -struct odb_transaction *odb_transaction_files_begin(struct odb_source *source) +static void odb_transaction_files_commit(struct odb_transaction *base) { - struct object_database *odb = source->odb; - - if (odb->transaction) - return NULL; - - CALLOC_ARRAY(odb->transaction, 1); - odb->transaction->source = source; - - return odb->transaction; -} - -void odb_transaction_files_commit(struct odb_transaction *transaction) -{ - if (!transaction) - return; + struct odb_transaction_files *transaction = (struct odb_transaction_files *)base; /* * Ensure the transaction ending matches the pending transaction. */ - ASSERT(transaction == transaction->source->odb->transaction); + ASSERT(base == base->source->odb->transaction); flush_loose_object_transaction(transaction); flush_packfile_transaction(transaction); - transaction->source->odb->transaction = NULL; + base->source->odb->transaction = NULL; free(transaction); } +struct odb_transaction *odb_transaction_files_begin(struct odb_source *source) +{ + struct odb_transaction_files *transaction; + struct object_database *odb = source->odb; + + if (odb->transaction) + return NULL; + + transaction = xcalloc(1, sizeof(*transaction)); + transaction->base.source = source; + transaction->base.commit = odb_transaction_files_commit; + + odb->transaction = &transaction->base; + + return &transaction->base; +} + struct odb_source_loose *odb_source_loose_new(struct odb_source *source) { struct odb_source_loose *loose; diff --git a/object-file.h b/object-file.h index b4a3341a890dc4..a62d0de39459b1 100644 --- a/object-file.h +++ b/object-file.h @@ -208,10 +208,4 @@ struct odb_transaction; */ struct odb_transaction *odb_transaction_files_begin(struct odb_source *source); -/* - * Tell the object database to make any objects from the - * current transaction visible. - */ -void odb_transaction_files_commit(struct odb_transaction *transaction); - #endif /* OBJECT_FILE_H */ diff --git a/odb.c b/odb.c index a5e6fd01a930af..349b4218a591cf 100644 --- a/odb.c +++ b/odb.c @@ -1158,5 +1158,8 @@ struct odb_transaction *odb_transaction_begin(struct object_database *odb) void odb_transaction_commit(struct odb_transaction *transaction) { - odb_transaction_files_commit(transaction); + if (!transaction) + return; + + transaction->commit(transaction); } diff --git a/odb.h b/odb.h index bab07755f4ec95..83d3a378059802 100644 --- a/odb.h +++ b/odb.h @@ -77,7 +77,24 @@ struct odb_source { struct packed_git; struct packfile_store; struct cached_object_entry; + +/* + * A transaction may be started for an object database prior to writing new + * objects via odb_transaction_begin(). These objects are not committed until + * odb_transaction_commit() is invoked. Only a single transaction may be pending + * at a time. + * + * Each ODB source is expected to implement its own transaction handling. + */ struct odb_transaction; +typedef void (*odb_transaction_commit_fn)(struct odb_transaction *transaction); +struct odb_transaction { + /* The ODB source the transaction is opened against. */ + struct odb_source *source; + + /* The ODB source specific callback invoked to commit a transaction. */ + odb_transaction_commit_fn commit; +}; /* * The object database encapsulates access to objects in a repository. It From 3f67e3d0211dd06d27ee3ee5b23e5f328ff2db12 Mon Sep 17 00:00:00 2001 From: Justin Tobler Date: Mon, 2 Feb 2026 18:10:02 -0600 Subject: [PATCH 11/23] odb: transparently handle common transaction behavior A new ODB transaction is created and returned via `odb_transaction_begin()` and stored in the ODB. Only a single transaction may be pending at a time. If the ODB already has a transaction, the function is expected to return NULL. Similarly, when committing a transaction via `odb_transaction_commit()` the transaction being committed must match the pending transaction and upon commit reset the ODB transaction to NULL. These behaviors apply regardless of the ODB transaction implementation. Move the corresponding logic into `odb_transaction_{begin,commit}()` accordingly. Signed-off-by: Justin Tobler Signed-off-by: Junio C Hamano --- object-file.c | 9 --------- odb.c | 14 +++++++++++++- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/object-file.c b/object-file.c index d7e153c1b9ad04..1b62996ef0a96f 100644 --- a/object-file.c +++ b/object-file.c @@ -1994,15 +1994,8 @@ static void odb_transaction_files_commit(struct odb_transaction *base) { struct odb_transaction_files *transaction = (struct odb_transaction_files *)base; - /* - * Ensure the transaction ending matches the pending transaction. - */ - ASSERT(base == base->source->odb->transaction); - flush_loose_object_transaction(transaction); flush_packfile_transaction(transaction); - base->source->odb->transaction = NULL; - free(transaction); } struct odb_transaction *odb_transaction_files_begin(struct odb_source *source) @@ -2017,8 +2010,6 @@ struct odb_transaction *odb_transaction_files_begin(struct odb_source *source) transaction->base.source = source; transaction->base.commit = odb_transaction_files_commit; - odb->transaction = &transaction->base; - return &transaction->base; } diff --git a/odb.c b/odb.c index 349b4218a591cf..1679cc0465aa68 100644 --- a/odb.c +++ b/odb.c @@ -1153,7 +1153,12 @@ void odb_reprepare(struct object_database *o) struct odb_transaction *odb_transaction_begin(struct object_database *odb) { - return odb_transaction_files_begin(odb->sources); + if (odb->transaction) + return NULL; + + odb->transaction = odb_transaction_files_begin(odb->sources); + + return odb->transaction; } void odb_transaction_commit(struct odb_transaction *transaction) @@ -1161,5 +1166,12 @@ void odb_transaction_commit(struct odb_transaction *transaction) if (!transaction) return; + /* + * Ensure the transaction ending matches the pending transaction. + */ + ASSERT(transaction == transaction->source->odb->transaction); + transaction->commit(transaction); + transaction->source->odb->transaction = NULL; + free(transaction); } From 60614838a44591c1449f939236f396bb7164b5ef Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Tue, 3 Feb 2026 11:29:03 +0100 Subject: [PATCH 12/23] cocci: extend MEMZERO_ARRAY() rules Recently the MEMZERO_ARRAY() macro was introduced. In that commit also coccinelle rules were added to capture cases that can be converted to use that macro. Later a few more cases were manually converted to use the macro, but coccinelle didn't capture those. Extend the rules to capture those as well. In various cases the code could be further beautified by removing parentheses which are no longer needed. Modify the coccinelle rules to optimize those as well and fix them. During conversion indentation also used spaces where tabs should be used, fix that in one go. Signed-off-by: Toon Claes Signed-off-by: Junio C Hamano --- contrib/coccinelle/array.cocci | 36 ++++++++++++++++++++++++++++------ diffcore-delta.c | 2 +- ewah/bitmap.c | 4 ++-- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/contrib/coccinelle/array.cocci b/contrib/coccinelle/array.cocci index d306f6a21efc9e..e71baea00b9b6f 100644 --- a/contrib/coccinelle/array.cocci +++ b/contrib/coccinelle/array.cocci @@ -107,9 +107,32 @@ type T; T *ptr; expression n; @@ -- memset(ptr, \( 0x0 \| 0 \), n * \( sizeof(T) -- \| sizeof(*ptr) -- \) ) +- memset(ptr, \( 0 \| '\0' \), \( (n) \| n \) * \( sizeof(T) +- \| sizeof(ptr[...]) +- \| sizeof(*ptr) +- \) ) ++ MEMZERO_ARRAY(ptr, n) + +@@ +type T; +T *ptr; +expression n; +@@ +- memset(ptr, \( 0 \| '\0' \), \( sizeof(T) +- \| sizeof(ptr[...]) +- \| sizeof(*ptr) +- \) * \( (n) \| n \) ) ++ MEMZERO_ARRAY(ptr, n) + +@@ +type T; +T[] ptr; +expression n; +@@ +- memset(ptr, \( 0 \| '\0' \), \( (n) \| n \) * \( sizeof(T) +- \| sizeof(ptr[...]) +- \| sizeof(*ptr) +- \) ) + MEMZERO_ARRAY(ptr, n) @@ @@ -117,7 +140,8 @@ type T; T[] ptr; expression n; @@ -- memset(ptr, \( 0x0 \| 0 \), n * \( sizeof(T) -- \| sizeof(*ptr) -- \) ) +- memset(ptr, \( 0 \| '\0' \), \( sizeof(T) +- \| sizeof(ptr[...]) +- \| sizeof(*ptr) +- \) * \( (n) \| n \) ) + MEMZERO_ARRAY(ptr, n) diff --git a/diffcore-delta.c b/diffcore-delta.c index 2de9e9ccff321a..2b7db399836e20 100644 --- a/diffcore-delta.c +++ b/diffcore-delta.c @@ -135,7 +135,7 @@ static struct spanhash_top *hash_chars(struct repository *r, st_mult(sizeof(struct spanhash), (size_t)1 << i))); hash->alloc_log2 = i; hash->free = INITIAL_FREE(i); - MEMZERO_ARRAY(hash->data, ((size_t)1 << i)); + MEMZERO_ARRAY(hash->data, (size_t)1 << i); n = 0; accum1 = accum2 = 0; diff --git a/ewah/bitmap.c b/ewah/bitmap.c index bf878bf8768ea0..c378e0ab788360 100644 --- a/ewah/bitmap.c +++ b/ewah/bitmap.c @@ -46,7 +46,7 @@ static void bitmap_grow(struct bitmap *self, size_t word_alloc) { size_t old_size = self->word_alloc; ALLOC_GROW(self->words, word_alloc, self->word_alloc); - MEMZERO_ARRAY(self->words + old_size, (self->word_alloc - old_size)); + MEMZERO_ARRAY(self->words + old_size, self->word_alloc - old_size); } void bitmap_set(struct bitmap *self, size_t pos) @@ -192,7 +192,7 @@ void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other) self->word_alloc = other_final; REALLOC_ARRAY(self->words, self->word_alloc); MEMZERO_ARRAY(self->words + original_size, - (self->word_alloc - original_size)); + self->word_alloc - original_size); } ewah_iterator_init(&it, other); From 0728012c537de800bb6cf2b6faac3f4d746a058e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 3 Feb 2026 13:26:00 -0800 Subject: [PATCH 13/23] diff-highlight: allow testing with Git 3.0 breaking changes The diff-highlight (in contrib/) comes with its own test script, which relies on the initial branch name being 'master'. This is not just encoded in the test logic, but in the illustration in the file that shows the topology of the history. Force the initial branch name to 'master' to allow it pass. Signed-off-by: Junio C Hamano --- contrib/diff-highlight/t/t9400-diff-highlight.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/diff-highlight/t/t9400-diff-highlight.sh b/contrib/diff-highlight/t/t9400-diff-highlight.sh index f6f5195d00f6ca..dee296739cd383 100755 --- a/contrib/diff-highlight/t/t9400-diff-highlight.sh +++ b/contrib/diff-highlight/t/t9400-diff-highlight.sh @@ -10,6 +10,8 @@ DIFF_HIGHLIGHT="$CURR_DIR"/../diff-highlight CW="$(printf "\033[7m")" # white CR="$(printf "\033[27m")" # reset +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME . "$TEST_DIRECTORY"/test-lib.sh if ! test_have_prereq PERL From 486386c687609603a2c1ebc434384d1e380908ae Mon Sep 17 00:00:00 2001 From: Colin Stagner Date: Tue, 3 Feb 2026 22:38:11 -0600 Subject: [PATCH 14/23] contrib/subtree: fix tests with reftable backend One git-subtree test-case relies on git internals to infer the default branch name. This test fails with the new reftable backend. GIT_TEST_DEFAULT_REF_FORMAT=reftable \ meson test t7900-subtree This test script already sets GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main which eliminates the need to infer a branch name at runtime. Hardcode the branch name. Signed-off-by: Colin Stagner Signed-off-by: Junio C Hamano --- contrib/subtree/t/t7900-subtree.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh index 316dc5269e2b6f..e7040718f221f0 100755 --- a/contrib/subtree/t/t7900-subtree.sh +++ b/contrib/subtree/t/t7900-subtree.sh @@ -1597,7 +1597,6 @@ test_expect_success 'push split to subproj' ' test_expect_success 'subtree descendant check' ' subtree_test_create_repo "$test_count" && - defaultBranch=$(sed "s,ref: refs/heads/,," "$test_count/.git/HEAD") && test_create_commit "$test_count" folder_subtree/a && ( cd "$test_count" && @@ -1614,7 +1613,7 @@ test_expect_success 'subtree descendant check' ' ( cd "$test_count" && git cherry-pick $cherry && - git checkout $defaultBranch && + git checkout main && git merge -m "merge should be kept on subtree" branch && git branch no_subtree_work_branch ) && @@ -1626,10 +1625,10 @@ test_expect_success 'subtree descendant check' ' test_create_commit "$test_count" not_a_subtree_change && ( cd "$test_count" && - git checkout $defaultBranch && + git checkout main && git merge -m "merge should be skipped on subtree" no_subtree_work_branch && - git subtree split --prefix folder_subtree/ --branch subtree_tip $defaultBranch && + git subtree split --prefix folder_subtree/ --branch subtree_tip main && git subtree split --prefix folder_subtree/ --branch subtree_branch branch && test $(git rev-list --count subtree_tip..subtree_branch) = 0 ) From 3c6162ea5c137fb90d100865162f460af9f53a0a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 2 Feb 2026 13:07:58 -0800 Subject: [PATCH 15/23] test: optionally test contrib in CI Recently it was reported that a topic merged to 'next' broke build and test for contrib/subtree part of the system. Instead of having those who run 'next' or 'master' to hit the build and test breakage and report to us, make sure we notice breakages in contrib/ area before they hit my tree at all, during their own presubmit testing. Signed-off-by: Junio C Hamano --- Makefile | 6 ++++++ ci/run-build-and-tests.sh | 3 +++ contrib/Makefile | 10 ++++++++++ 3 files changed, 19 insertions(+) create mode 100644 contrib/Makefile diff --git a/Makefile b/Makefile index 8aa489f3b6812f..d0ab8fdb04ee8f 100644 --- a/Makefile +++ b/Makefile @@ -342,6 +342,9 @@ include shared.mak # If it isn't set, fallback to $LC_ALL, $LANG or use the first utf-8 # locale returned by "locale -a". # +# Define TEST_CONTRIB_TOO to make "make test" run tests in contrib/ +# directories. +# # Define HAVE_CLOCK_GETTIME if your platform has clock_gettime. # # Define HAVE_CLOCK_MONOTONIC if your platform has CLOCK_MONOTONIC. @@ -3369,6 +3372,9 @@ export TEST_NO_MALLOC_CHECK test: all $(MAKE) -C t/ all +ifdef TEST_CONTRIB_TOO + $(MAKE) -C contrib/ test +endif perf: all $(MAKE) -C t/perf/ all diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh index 8bda62b921920f..28cfe730ee5aed 100755 --- a/ci/run-build-and-tests.sh +++ b/ci/run-build-and-tests.sh @@ -5,6 +5,8 @@ . ${0%/*}/lib.sh +export TEST_CONTRIB_TOO=yes + case "$jobname" in fedora-breaking-changes-musl|linux-breaking-changes) export WITH_BREAKING_CHANGES=YesPlease @@ -36,6 +38,7 @@ linux-sha256) linux-reftable|linux-reftable-leaks|osx-reftable) export GIT_TEST_DEFAULT_REF_FORMAT=reftable ;; + esac case "$jobname" in diff --git a/contrib/Makefile b/contrib/Makefile new file mode 100644 index 00000000000000..787cd07f52ed03 --- /dev/null +++ b/contrib/Makefile @@ -0,0 +1,10 @@ +all:: + +test:: + $(MAKE) -C diff-highlight $@ + $(MAKE) -C subtree $@ + +clean:: + $(MAKE) -C contacts $@ + $(MAKE) -C diff-highlight $@ + $(MAKE) -C subtree $@ From 831989ef383c85bc32a0b27d51d9e48111f6d4d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Cepl?= Date: Thu, 5 Feb 2026 17:24:01 +0100 Subject: [PATCH 16/23] doc: send-email: correct --no-signed-off-by-cc misspelling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no option --signed-off-cc (without -by) for git send-email. Signed-off-by: Matěj Cepl [kh: rebased and changed subject to house style] Signed-off-by: Kristoffer Haugsbakk [jc: minor copyedit in the commit message] Signed-off-by: Junio C Hamano --- Documentation/git-send-email.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-send-email.adoc b/Documentation/git-send-email.adoc index ebe8853e9f5da7..44ae2ed5bff999 100644 --- a/Documentation/git-send-email.adoc +++ b/Documentation/git-send-email.adoc @@ -435,7 +435,7 @@ Automating + Default is the value of `sendemail.suppressCc` configuration value; if that is unspecified, default to `self` if `--suppress-from` is -specified, as well as `body` if `--no-signed-off-cc` is specified. +specified, as well as `body` if `--no-signed-off-by-cc` is specified. --suppress-from:: --no-suppress-from:: From 4ac4705afa3ab660e206c2b870bfae2ddb647ffa Mon Sep 17 00:00:00 2001 From: Collin Funk Date: Thu, 5 Feb 2026 17:46:09 -0800 Subject: [PATCH 17/23] global: constify some pointers that are not written to MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The recent glibc 2.43 release had the following change listed in its NEWS file: For ISO C23, the functions bsearch, memchr, strchr, strpbrk, strrchr, strstr, wcschr, wcspbrk, wcsrchr, wcsstr and wmemchr that return pointers into their input arrays now have definitions as macros that return a pointer to a const-qualified type when the input argument is a pointer to a const-qualified type. When compiling with GCC 15, which defaults to -std=gnu23, this causes many warnings like this: merge-ort.c: In function ‘apply_directory_rename_modifications’: merge-ort.c:2734:36: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 2734 | char *last_slash = strrchr(cur_path, '/'); | ^~~~~~~ This patch fixes the more obvious ones by making them const when we do not write to the returned pointer. Signed-off-by: Collin Funk Signed-off-by: Junio C Hamano --- add-patch.c | 2 +- apply.c | 2 +- builtin/commit.c | 2 +- builtin/receive-pack.c | 2 +- builtin/remote.c | 2 +- builtin/shortlog.c | 2 +- config.c | 2 +- convert.c | 3 ++- diff.c | 4 ++-- diffcore-rename.c | 2 +- fmt-merge-msg.c | 3 ++- fsck.c | 2 +- gpg-interface.c | 2 +- help.c | 2 +- http-push.c | 2 +- mailinfo.c | 2 +- mem-pool.c | 2 +- merge-ort.c | 2 +- object-name.c | 2 +- pack-revindex.c | 2 +- pkt-line.c | 6 +++--- reflog-walk.c | 3 ++- scalar.c | 2 +- strbuf.c | 2 +- string-list.c | 2 +- t/unit-tests/clar/clar/print.h | 2 +- transport.c | 2 +- wrapper.c | 2 +- 28 files changed, 34 insertions(+), 31 deletions(-) diff --git a/add-patch.c b/add-patch.c index 173a53241ebf07..70242617ef01e5 100644 --- a/add-patch.c +++ b/add-patch.c @@ -342,7 +342,7 @@ static int parse_hunk_header(struct add_p_state *s, struct hunk *hunk) { struct hunk_header *header = &hunk->header; const char *line = s->plain.buf + hunk->start, *p = line; - char *eol = memchr(p, '\n', s->plain.len - hunk->start); + const char *eol = memchr(p, '\n', s->plain.len - hunk->start); if (!eol) eol = s->plain.buf + s->plain.len; diff --git a/apply.c b/apply.c index 3de4aa4d2eaac5..9de2eb953e51f3 100644 --- a/apply.c +++ b/apply.c @@ -4144,7 +4144,7 @@ static int preimage_oid_in_gitlink_patch(struct patch *p, struct object_id *oid) */ struct fragment *hunk = p->fragments; static const char heading[] = "-Subproject commit "; - char *preimage; + const char *preimage; if (/* does the patch have only one hunk? */ hunk && !hunk->next && diff --git a/builtin/commit.c b/builtin/commit.c index 8e901fe8db7942..03265465480e7c 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -816,7 +816,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, logfile); hook_arg1 = "message"; } else if (use_message) { - char *buffer; + const char *buffer; buffer = strstr(use_message_buffer, "\n\n"); if (buffer) strbuf_addstr(&sb, skip_blank_lines(buffer + 2)); diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 9c491746168a6f..e8b6f960faea14 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -393,7 +393,7 @@ struct command { static void proc_receive_ref_append(const char *prefix) { struct proc_receive_ref *ref_pattern; - char *p; + const char *p; int len; CALLOC_ARRAY(ref_pattern, 1); diff --git a/builtin/remote.c b/builtin/remote.c index 7ffc14ba15743a..ace390c671d61c 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -332,7 +332,7 @@ static int config_read_branches(const char *key, const char *value, info->remote_name = xstrdup(value); break; case MERGE: { - char *space = strchr(value, ' '); + const char *space = strchr(value, ' '); value = abbrev_branch(value); while (space) { char *merge; diff --git a/builtin/shortlog.c b/builtin/shortlog.c index b91acf45c8fe59..d80bf1a7d055fc 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -76,7 +76,7 @@ static void insert_one_record(struct shortlog *log, if (!eol) eol = oneline + strlen(oneline); if (starts_with(oneline, "[PATCH")) { - char *eob = strchr(oneline, ']'); + const char *eob = strchr(oneline, ']'); if (eob && (!eol || eob < eol)) oneline = eob + 1; } diff --git a/config.c b/config.c index 7f6d53b4737cd8..156f2a24fa0027 100644 --- a/config.c +++ b/config.c @@ -160,7 +160,7 @@ static int handle_path_include(const struct key_value_info *kvi, * based on the including config file. */ if (!is_absolute_path(path)) { - char *slash; + const char *slash; if (!kvi || kvi->origin_type != CONFIG_ORIGIN_FILE) { ret = error(_("relative config includes must come from files")); diff --git a/convert.c b/convert.c index c7d6a85c226db7..a34ec6ecdc057e 100644 --- a/convert.c +++ b/convert.c @@ -1122,7 +1122,8 @@ static int count_ident(const char *cp, unsigned long size) static int ident_to_git(const char *src, size_t len, struct strbuf *buf, int ident) { - char *dst, *dollar; + char *dst; + const char *dollar; if (!ident || (src && !count_ident(src, len))) return 0; diff --git a/diff.c b/diff.c index a68ddd2168ba1c..2d92665159d540 100644 --- a/diff.c +++ b/diff.c @@ -1961,7 +1961,7 @@ static int fn_out_diff_words_write_helper(struct diff_options *o, struct strbuf sb = STRBUF_INIT; while (count) { - char *p = memchr(buf, '\n', count); + const char *p = memchr(buf, '\n', count); if (print) strbuf_addstr(&sb, diff_line_prefix(o)); @@ -3049,7 +3049,7 @@ static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir, struct dirstat_file *f = dir->files; int namelen = strlen(f->name); unsigned long changes; - char *slash; + const char *slash; if (namelen < baselen) break; diff --git a/diffcore-rename.c b/diffcore-rename.c index 7723bc3334e084..d9476db35acbf7 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -379,7 +379,7 @@ struct dir_rename_info { static char *get_dirname(const char *filename) { - char *slash = strrchr(filename, '/'); + const char *slash = strrchr(filename, '/'); return slash ? xstrndup(filename, slash - filename) : xstrdup(""); } diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c index c9085edc40e934..1626667c0dc5c6 100644 --- a/fmt-merge-msg.c +++ b/fmt-merge-msg.c @@ -246,7 +246,8 @@ static void add_branch_desc(struct strbuf *out, const char *name) static void record_person_from_buf(int which, struct string_list *people, const char *buffer) { - char *name_buf, *name, *name_end; + char *name_buf; + const char *name, *name_end; struct string_list_item *elem; const char *field; diff --git a/fsck.c b/fsck.c index 3afec0d0d3828c..0f02cf8f77bf1a 100644 --- a/fsck.c +++ b/fsck.c @@ -1026,7 +1026,7 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer, int *tagged_type) { int ret = 0; - char *eol; + const char *eol; struct strbuf sb = STRBUF_INIT; const char *buffer_end = buffer + size; const char *p; diff --git a/gpg-interface.c b/gpg-interface.c index 47222bf31b6ef2..377c0cf49f83ad 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -398,7 +398,7 @@ static void parse_ssh_output(struct signature_check *sigc) { const char *line, *principal, *search; char *to_free; - char *key = NULL; + const char *key = NULL; /* * ssh-keygen output should be: diff --git a/help.c b/help.c index 3c36d9c218f824..be334d764284a7 100644 --- a/help.c +++ b/help.c @@ -857,7 +857,7 @@ struct similar_ref_cb { 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(ref->name, '/') + 1; + const char *branch = strrchr(ref->name, '/') + 1; /* A remote branch of the same name is deemed similar */ if (starts_with(ref->name, "refs/remotes/") && diff --git a/http-push.c b/http-push.c index cc0f80934615ba..9ae6062198e14f 100644 --- a/http-push.c +++ b/http-push.c @@ -1768,7 +1768,7 @@ int cmd_main(int argc, const char **argv) usage(http_push_usage); } if (!repo->url) { - char *path = strstr(arg, "//"); + const char *path = strstr(arg, "//"); str_end_url_with_slash(arg, &repo->url); repo->path_len = strlen(repo->url); if (path) { diff --git a/mailinfo.c b/mailinfo.c index 99ac596e096e70..a2f06dbd96ff6f 100644 --- a/mailinfo.c +++ b/mailinfo.c @@ -1141,7 +1141,7 @@ static void output_header_lines(FILE *fout, const char *hdr, const struct strbuf { const char *sp = data->buf; while (1) { - char *ep = strchr(sp, '\n'); + const char *ep = strchr(sp, '\n'); int len; if (!ep) len = strlen(sp); diff --git a/mem-pool.c b/mem-pool.c index 62441dcc71968f..8bc77cb0e80a35 100644 --- a/mem-pool.c +++ b/mem-pool.c @@ -169,7 +169,7 @@ char *mem_pool_strdup(struct mem_pool *pool, const char *str) char *mem_pool_strndup(struct mem_pool *pool, const char *str, size_t len) { - char *p = memchr(str, '\0', len); + const char *p = memchr(str, '\0', len); size_t actual_len = (p ? p - str : len); char *ret = mem_pool_alloc(pool, actual_len+1); diff --git a/merge-ort.c b/merge-ort.c index e80e4f735a60f0..6f30471b49f1ea 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -2731,7 +2731,7 @@ static void apply_directory_rename_modifications(struct merge_options *opt, while (1) { /* Find the parent directory of cur_path */ - char *last_slash = strrchr(cur_path, '/'); + const char *last_slash = strrchr(cur_path, '/'); if (last_slash) { parent_name = mem_pool_strndup(&opt->priv->pool, cur_path, diff --git a/object-name.c b/object-name.c index 8b862c124e05a9..e1b09d823c2f80 100644 --- a/object-name.c +++ b/object-name.c @@ -1756,7 +1756,7 @@ int repo_interpret_branch_name(struct repository *r, struct strbuf *buf, const struct interpret_branch_name_options *options) { - char *at; + const char *at; const char *start; int len; diff --git a/pack-revindex.c b/pack-revindex.c index 8598b941c8c419..56cd803a6798d5 100644 --- a/pack-revindex.c +++ b/pack-revindex.c @@ -544,7 +544,7 @@ static int midx_key_to_pack_pos(struct multi_pack_index *m, struct midx_pack_key *key, uint32_t *pos) { - uint32_t *found; + const uint32_t *found; if (key->pack >= m->num_packs + m->num_packs_in_base) BUG("MIDX pack lookup out of bounds (%"PRIu32" >= %"PRIu32")", diff --git a/pkt-line.c b/pkt-line.c index fc583feb26510d..3fc3e9ea7059be 100644 --- a/pkt-line.c +++ b/pkt-line.c @@ -384,10 +384,10 @@ int packet_length(const char lenbuf_hex[4], size_t size) hexval(lenbuf_hex[3]); } -static char *find_packfile_uri_path(const char *buffer) +static const char *find_packfile_uri_path(const char *buffer) { const char *URI_MARK = "://"; - char *path; + const char *path; int len; /* First char is sideband mark */ @@ -417,7 +417,7 @@ enum packet_read_status packet_read_with_status(int fd, char **src_buffer, { int len; char linelen[4]; - char *uri_path_start; + const char *uri_path_start; if (get_packet_data(fd, src_buffer, src_len, linelen, 4, options) < 0) { *pktlen = -1; diff --git a/reflog-walk.c b/reflog-walk.c index 4f1ce047498116..4dbeaa93a7703f 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -157,7 +157,8 @@ int add_reflog_for_walk(struct reflog_walk_info *info, int recno = -1; struct string_list_item *item; struct complete_reflogs *reflogs; - char *branch, *at = strchr(name, '@'); + char *branch; + const char *at = strchr(name, '@'); struct commit_reflog *commit_reflog; enum selector_type selector = SELECTOR_NONE; diff --git a/scalar.c b/scalar.c index c9df9348ecba46..4efb6ac36d888e 100644 --- a/scalar.c +++ b/scalar.c @@ -393,7 +393,7 @@ static int delete_enlistment(struct strbuf *enlistment) { struct strbuf parent = STRBUF_INIT; size_t offset; - char *path_sep; + const char *path_sep; if (unregister_dir()) return error(_("failed to unregister repository")); diff --git a/strbuf.c b/strbuf.c index 59678bf5b03e0b..3939863cf31ffd 100644 --- a/strbuf.c +++ b/strbuf.c @@ -1119,6 +1119,6 @@ void strbuf_stripspace(struct strbuf *sb, const char *comment_prefix) void strbuf_strip_file_from_path(struct strbuf *sb) { - char *path_sep = find_last_dir_sep(sb->buf); + const char *path_sep = find_last_dir_sep(sb->buf); strbuf_setlen(sb, path_sep ? path_sep - sb->buf + 1 : 0); } diff --git a/string-list.c b/string-list.c index 08dc00984ccbd6..7c34a425da5d0a 100644 --- a/string-list.c +++ b/string-list.c @@ -327,7 +327,7 @@ static int split_string(struct string_list *list, const char *string, const char BUG("string_list_split() called without strdup_strings"); for (;;) { - char *end; + const char *end; if (flags & STRING_LIST_SPLIT_TRIM) { /* ltrim */ diff --git a/t/unit-tests/clar/clar/print.h b/t/unit-tests/clar/clar/print.h index 6a2321b399d192..59b7dc14a104a8 100644 --- a/t/unit-tests/clar/clar/print.h +++ b/t/unit-tests/clar/clar/print.h @@ -127,7 +127,7 @@ static void clar_print_tap_error(int num, const struct clar_report *report, cons static void print_escaped(const char *str) { - char *c; + const char *c; while ((c = strchr(str, '\'')) != NULL) { printf("%.*s", (int)(c - str), str); diff --git a/transport.c b/transport.c index c7f06a7382e605..845fd441bec7fa 100644 --- a/transport.c +++ b/transport.c @@ -1657,7 +1657,7 @@ int transport_disconnect(struct transport *transport) */ char *transport_anonymize_url(const char *url) { - char *scheme_prefix, *anon_part; + const char *scheme_prefix, *anon_part; size_t anon_len, prefix_len = 0; anon_part = strchr(url, '@'); diff --git a/wrapper.c b/wrapper.c index b794fb20e71879..16f5a63fbb614a 100644 --- a/wrapper.c +++ b/wrapper.c @@ -115,7 +115,7 @@ void *xmemdupz(const void *data, size_t len) char *xstrndup(const char *str, size_t len) { - char *p = memchr(str, '\0', len); + const char *p = memchr(str, '\0', len); return xmemdupz(str, p ? p - str : len); } From fc9fd8065c6049243f50e90f00a847054ca15e28 Mon Sep 17 00:00:00 2001 From: Collin Funk Date: Thu, 5 Feb 2026 17:46:10 -0800 Subject: [PATCH 18/23] gpg-interface: remove an unnecessary NULL initialization We assign this variable unconditionally, so we do not need to initialize it to NULL where it is defined. Signed-off-by: Collin Funk Signed-off-by: Junio C Hamano --- gpg-interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpg-interface.c b/gpg-interface.c index 377c0cf49f83ad..87fb6605fba174 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -398,7 +398,7 @@ static void parse_ssh_output(struct signature_check *sigc) { const char *line, *principal, *search; char *to_free; - const char *key = NULL; + const char *key; /* * ssh-keygen output should be: From c44b3f32033c96c2a28b53561729c6eada01c8c0 Mon Sep 17 00:00:00 2001 From: SoutrikDas Date: Fri, 6 Feb 2026 11:51:08 +0530 Subject: [PATCH 19/23] doc: fix repo_config documentation reference In MyFirstContribution.adoc, the link to the repo_config() documentation is invalid because the related documentation was moved to a different file. Replace the path for the repo_config() documentation from 'Documentation/technical/api-config.h' to 'config.h'. Signed-off-by: SoutrikDas Signed-off-by: Junio C Hamano --- Documentation/MyFirstContribution.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/MyFirstContribution.adoc b/Documentation/MyFirstContribution.adoc index f186dfbc898fd4..92de476a7f6989 100644 --- a/Documentation/MyFirstContribution.adoc +++ b/Documentation/MyFirstContribution.adoc @@ -351,7 +351,7 @@ function body: apply standard precedence rules. `repo_config_get_string_tmp()` will look up a specific key ("user.name") and give you the value. There are a number of single-key lookup functions like this one; you can see them all (and more info -about how to use `repo_config()`) in `Documentation/technical/api-config.adoc`. +about how to use `repo_config()`) in `config.h`. You should see that the name printed matches the one you see when you run: From ba447e9cec47ca5b1cc8cfeae8ca90bc962c54fa Mon Sep 17 00:00:00 2001 From: Sam Bostock Date: Fri, 6 Feb 2026 19:16:22 +0000 Subject: [PATCH 20/23] merge-ours: drop USE_THE_REPOSITORY_VARIABLE The merge-ours built-in uses the `the_repository` global to access the repository. The project is moving away from this global in favor of the `repo` parameter that is passed to each built-in command. Since merge-ours is registered with RUN_SETUP, `repo` is guaranteed to be non-NULL and can be used directly. Drop the USE_THE_REPOSITORY_VARIABLE macro and use `repo` throughout. While at it, remove a stray double blank line between the #include block and the usage string. Signed-off-by: Sam Bostock Signed-off-by: Junio C Hamano --- builtin/merge-ours.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/builtin/merge-ours.c b/builtin/merge-ours.c index 97b8a792c77eb7..2312e58ab37068 100644 --- a/builtin/merge-ours.c +++ b/builtin/merge-ours.c @@ -8,20 +8,17 @@ * Pretend we resolved the heads, but declare our tree trumps everybody else. */ -#define USE_THE_REPOSITORY_VARIABLE - #include "git-compat-util.h" #include "builtin.h" #include "diff.h" - static const char builtin_merge_ours_usage[] = "git merge-ours ... -- HEAD ..."; int cmd_merge_ours(int argc, const char **argv, const char *prefix UNUSED, - struct repository *repo UNUSED) + struct repository *repo) { show_usage_if_asked(argc, argv, builtin_merge_ours_usage); @@ -30,9 +27,9 @@ int cmd_merge_ours(int argc, * commit. The index must match HEAD, or this merge cannot go * through. */ - if (repo_read_index(the_repository) < 0) + if (repo_read_index(repo) < 0) die_errno("read_cache failed"); - if (index_differs_from(the_repository, "HEAD", NULL, 0)) + if (index_differs_from(repo, "HEAD", NULL, 0)) return 2; return 0; } From fb1b786ebfab18b851ce1fa94d5063daf11f647f Mon Sep 17 00:00:00 2001 From: Sam Bostock Date: Fri, 6 Feb 2026 19:16:23 +0000 Subject: [PATCH 21/23] merge-ours: integrate with sparse-index The merge-ours built-in opens the index to compare it against HEAD. The machinery used to do this (i.e. run_diff_index()) is capable of working with a sparse index, but the start-up sequence of this command does not take the necessary steps, so we end up expanding the index fully before doing the comparison. In order to convince sparse-index.c:is_sparse_index_allowed() to return true, we need to: - Read basic configuration with git_default_config so that global variables like core_apply_sparse_checkout are populated. merge-ours currently does not read configuration at all. - Set command_requires_full_index to 0. With that, the command can work without expanding the index fully before doing its work. Signed-off-by: Sam Bostock Signed-off-by: Junio C Hamano --- builtin/merge-ours.c | 6 ++++++ t/t1092-sparse-checkout-compatibility.sh | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/builtin/merge-ours.c b/builtin/merge-ours.c index 2312e58ab37068..405b2989f7609d 100644 --- a/builtin/merge-ours.c +++ b/builtin/merge-ours.c @@ -10,6 +10,8 @@ #include "git-compat-util.h" #include "builtin.h" +#include "config.h" +#include "environment.h" #include "diff.h" static const char builtin_merge_ours_usage[] = @@ -22,6 +24,10 @@ int cmd_merge_ours(int argc, { show_usage_if_asked(argc, argv, builtin_merge_ours_usage); + repo_config(repo, git_default_config, NULL); + prepare_repo_settings(repo); + repo->settings.command_requires_full_index = 0; + /* * The contents of the current index becomes the tree we * commit. The index must match HEAD, or this merge cannot go diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh index b0f691c151a7d0..d98cb4ac113c67 100755 --- a/t/t1092-sparse-checkout-compatibility.sh +++ b/t/t1092-sparse-checkout-compatibility.sh @@ -2559,4 +2559,18 @@ test_expect_success 'cat-file --batch' ' ensure_expanded cat-file --batch Date: Fri, 6 Feb 2026 22:27:03 -0600 Subject: [PATCH 22/23] ci: ubuntu: use GNU coreutils for dirname The uutils version of `dirname` has output that is inconsistent with GNU coreutils. Prefer the GNU implementation of this command. Signed-off-by: Colin Stagner Signed-off-by: Junio C Hamano --- ci/install-dependencies.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh index 6ee8216a05e127..c55441d9df91fd 100755 --- a/ci/install-dependencies.sh +++ b/ci/install-dependencies.sh @@ -76,6 +76,17 @@ ubuntu-*|i386/ubuntu-*|debian-*) sudo update-alternatives --set sudo /usr/bin/sudo.ws fi + # on uutils v0.2.2 from rust-coreutils, + # dirname "foo/." + # outputs "." instead of "foo" like it should. + # Use GNU coreutils to provide dirname instead. + # + # See . + if test -x /usr/bin/gnudirname + then + ln -sfT /usr/bin/gnudirname /usr/bin/dirname + fi + case "$distro" in ubuntu-*) mkdir --parents "$CUSTOM_PATH" From 852829b3dd2fe4e7c7fc4d8badde644cf1b66c74 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 13 Feb 2026 12:57:32 -0800 Subject: [PATCH 23/23] The 4th batch Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.54.0.adoc | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Documentation/RelNotes/2.54.0.adoc b/Documentation/RelNotes/2.54.0.adoc index 7a768bc7dda4f9..084b9074c41725 100644 --- a/Documentation/RelNotes/2.54.0.adoc +++ b/Documentation/RelNotes/2.54.0.adoc @@ -33,6 +33,26 @@ Performance, Internal Implementation, Development Support etc. * Improve set-up time of a perf test. + * ISO C23 redefines strchr and friends that tradiotionally took + a const pointer and returned a non-const pointer derived from it to + preserve constness (i.e., if you ask for a substring in a const + string, you get a const pointer to the substring). Update code + paths that used non-const pointer to receive their results that did + not have to be non-const to adjust. + (merge fc9fd8065c cf/c23-const-preserving-strchr-updates-0 later to maint). + + * Rename three functions around the commit_list data structure. + (merge 9f18d089c5 ps/commit-list-functions-renamed later to maint). + + * Transaction to create objects (or not) is currently tied to the + repository, but in the future a repository can have multiple object + sources, which may have different transaction mechanisms. Make the + odb transaction API per object source. + (merge 3f67e3d021 jt/odb-transaction-per-source later to maint). + + * "git merge-ours" is taught to work better in a sparse checkout. + (merge fb1b786ebf sb/merge-ours-sparse later to maint). + Fixes since v2.53 ----------------- @@ -59,6 +79,17 @@ Fixes since v2.53 gets blamed for. (merge d519082d4e rs/blame-ignore-colors-fix later to maint). + * Coccinelle rules update. + (merge 60614838a4 tc/memzero-array later to maint). + + * Giving "git last-modified" a tree (not a commit-ish) died an + uncontrolled death, which has been corrected. + (merge 525ef52301 tc/last-modified-not-a-tree later to maint). + + * Test contrib/ things in CI to catch breakages before they enter the + "next" branch. + (merge c591c3ceff jc/ci-test-contrib-too later to maint). + * Other code cleanup, docfix, build fix, etc. (merge d79fff4a11 jk/remote-tracking-ref-leakfix later to maint). (merge 7a747f972d dd/t5403-modernise later to maint). @@ -68,3 +99,7 @@ Fixes since v2.53 (merge df1c5d7ed7 kh/doc-shortlog-fix later to maint). (merge 2d45507f15 am/doc-github-contributiong-link-to-submittingpatches later to maint). (merge 68060b9262 hs/t9160-test-paths later to maint). + (merge 486386c687 cs/subtree-reftable-testfix later to maint). + (merge 0728012c53 jc/diff-highlight-main-master-testfix later to maint). + (merge 831989ef38 mc/doc-send-email-signed-off-by-cc later to maint). + (merge c44b3f3203 sd/doc-my1c-api-config-reference-fix later to maint).