From e691395365b871608551bfbe20982b53140a50f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 9 Jan 2026 22:30:12 +0100 Subject: [PATCH 01/20] environment: move access to core.maxTreeDepth into repo settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The config setting core.maxTreeDepth is stored in a global variable and populated by the function git_default_core_config. This won't work if we need to access multiple repositories with different values of that setting in the same process. Store the setting in struct repo_settings instead and track it separately for each repository. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- environment.c | 29 ----------------------------- environment.h | 1 - git-compat-util.h | 24 ++++++++++++++++++++++++ list-objects.c | 2 +- repo-settings.c | 3 +++ repo-settings.h | 3 +++ tree-diff.c | 2 +- tree-walk.c | 4 +++- tree.c | 2 +- 9 files changed, 36 insertions(+), 34 deletions(-) diff --git a/environment.c b/environment.c index a770b5921d9546..c6e5b65abac19b 100644 --- a/environment.c +++ b/environment.c @@ -80,30 +80,6 @@ int core_sparse_checkout_cone; int sparse_expect_files_outside_of_patterns; int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */ unsigned long pack_size_limit_cfg; -int max_allowed_tree_depth = -#ifdef _MSC_VER - /* - * When traversing into too-deep trees, Visual C-compiled Git seems to - * run into some internal stack overflow detection in the - * `RtlpAllocateHeap()` function that is called from within - * `git_inflate_init()`'s call tree. The following value seems to be - * low enough to avoid that by letting Git exit with an error before - * the stack overflow can occur. - */ - 512; -#elif defined(GIT_WINDOWS_NATIVE) && defined(__clang__) && defined(__aarch64__) - /* - * Similar to Visual C, it seems that on Windows/ARM64 the clang-based - * builds have a smaller stack space available. When running out of - * that stack space, a `STATUS_STACK_OVERFLOW` is produced. When the - * Git command was run from an MSYS2 Bash, this unfortunately results - * in an exit code 127. Let's prevent that by lowering the maximal - * tree depth; This value seems to be low enough. - */ - 1280; -#else - 2048; -#endif #ifndef PROTECT_HFS_DEFAULT #define PROTECT_HFS_DEFAULT 0 @@ -569,11 +545,6 @@ static int git_default_core_config(const char *var, const char *value, return 0; } - if (!strcmp(var, "core.maxtreedepth")) { - max_allowed_tree_depth = git_config_int(var, value, ctx->kvi); - return 0; - } - /* Add other config variables here and to Documentation/config.adoc. */ return platform_core_config(var, value, ctx, cb); } diff --git a/environment.h b/environment.h index 51898c99cd1e45..9efe0b30fb3d8b 100644 --- a/environment.h +++ b/environment.h @@ -156,7 +156,6 @@ extern char *git_attributes_file; extern int zlib_compression_level; extern int pack_compression_level; extern unsigned long pack_size_limit_cfg; -extern int max_allowed_tree_depth; extern int precomposed_unicode; extern int protect_hfs; diff --git a/git-compat-util.h b/git-compat-util.h index b0673d1a450db5..bebcf9f698cd39 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -578,6 +578,30 @@ static inline bool strip_suffix(const char *str, const char *suffix, #define DEFAULT_PACKED_GIT_LIMIT \ ((1024L * 1024L) * (size_t)(sizeof(void*) >= 8 ? (32 * 1024L * 1024L) : 256)) +#ifdef _MSC_VER + /* + * When traversing into too-deep trees, Visual C-compiled Git seems to + * run into some internal stack overflow detection in the + * `RtlpAllocateHeap()` function that is called from within + * `git_inflate_init()`'s call tree. The following value seems to be + * low enough to avoid that by letting Git exit with an error before + * the stack overflow can occur. + */ +#define DEFAULT_MAX_ALLOWED_TREE_DEPTH 512 +#elif defined(GIT_WINDOWS_NATIVE) && defined(__clang__) && defined(__aarch64__) + /* + * Similar to Visual C, it seems that on Windows/ARM64 the clang-based + * builds have a smaller stack space available. When running out of + * that stack space, a `STATUS_STACK_OVERFLOW` is produced. When the + * Git command was run from an MSYS2 Bash, this unfortunately results + * in an exit code 127. Let's prevent that by lowering the maximal + * tree depth; This value seems to be low enough. + */ +#define DEFAULT_MAX_ALLOWED_TREE_DEPTH 1280 +#else +#define DEFAULT_MAX_ALLOWED_TREE_DEPTH 2048 +#endif + int git_open_cloexec(const char *name, int flags); #define git_open(name) git_open_cloexec(name, O_RDONLY) diff --git a/list-objects.c b/list-objects.c index 42c17d9573910b..1279676ddca4df 100644 --- a/list-objects.c +++ b/list-objects.c @@ -167,7 +167,7 @@ static void process_tree(struct traversal_context *ctx, !revs->include_check_obj(&tree->object, revs->include_check_data)) return; - if (ctx->depth > max_allowed_tree_depth) + if (ctx->depth > revs->repo->settings.max_allowed_tree_depth) die("exceeded maximum allowed tree depth"); failed_parse = parse_tree_gently(tree, 1); diff --git a/repo-settings.c b/repo-settings.c index 195c24e9c07606..208e09ff17fcee 100644 --- a/repo-settings.c +++ b/repo-settings.c @@ -100,6 +100,9 @@ void prepare_repo_settings(struct repository *r) */ if (!repo_config_get_int(r, "index.version", &value)) r->settings.index_version = value; + repo_cfg_int(r, "core.maxtreedepth", + &r->settings.max_allowed_tree_depth, + DEFAULT_MAX_ALLOWED_TREE_DEPTH); if (!repo_config_get_string_tmp(r, "core.untrackedcache", &strval)) { int v = git_parse_maybe_bool(strval); diff --git a/repo-settings.h b/repo-settings.h index d4778855614497..cad9c3f0cc15f3 100644 --- a/repo-settings.h +++ b/repo-settings.h @@ -67,6 +67,8 @@ struct repo_settings { size_t packed_git_limit; unsigned long big_file_threshold; + int max_allowed_tree_depth; + char *hooks_path; }; #define REPO_SETTINGS_INIT { \ @@ -78,6 +80,7 @@ struct repo_settings { .delta_base_cache_limit = DEFAULT_DELTA_BASE_CACHE_LIMIT, \ .packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE, \ .packed_git_limit = DEFAULT_PACKED_GIT_LIMIT, \ + .max_allowed_tree_depth = DEFAULT_MAX_ALLOWED_TREE_DEPTH, \ } void prepare_repo_settings(struct repository *r); diff --git a/tree-diff.c b/tree-diff.c index 5988148b602536..631ea868124256 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -439,7 +439,7 @@ static void ll_diff_tree_paths( void *ttree, **tptree; int i; - if (depth > max_allowed_tree_depth) + if (depth > opt->repo->settings.max_allowed_tree_depth) die("exceeded maximum allowed tree depth"); FAST_ARRAY_ALLOC(tp, nparent); diff --git a/tree-walk.c b/tree-walk.c index e449a1320e55a6..7e1b956f278164 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -12,6 +12,7 @@ #include "pathspec.h" #include "json-writer.h" #include "environment.h" +#include "read-cache-ll.h" static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size, struct strbuf *err) { @@ -441,8 +442,9 @@ int traverse_trees(struct index_state *istate, struct strbuf base = STRBUF_INIT; int interesting = 1; char *traverse_path; + struct repository *r = istate ? istate->repo : the_repository; - if (traverse_trees_cur_depth > max_allowed_tree_depth) + if (traverse_trees_cur_depth > r->settings.max_allowed_tree_depth) return error("exceeded maximum allowed tree depth"); traverse_trees_count++; diff --git a/tree.c b/tree.c index 1ef743d90f4bad..2a677234d60aa1 100644 --- a/tree.c +++ b/tree.c @@ -25,7 +25,7 @@ int read_tree_at(struct repository *r, int len, oldlen = base->len; enum interesting retval = entry_not_interesting; - if (depth > max_allowed_tree_depth) + if (depth > r->settings.max_allowed_tree_depth) return error("exceeded maximum allowed tree depth"); if (parse_tree(tree)) From 2b74f68ca0553afcab3bed4b6971cf3bfd1fa7b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 9 Jan 2026 22:30:13 +0100 Subject: [PATCH 02/20] tree: add repo_parse_tree*() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add variants of parse_tree(), parse_tree_gently() and parse_tree_indirect() that allow using an arbitrary repository. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- tree.c | 16 +++++++++++++--- tree.h | 8 ++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/tree.c b/tree.c index 2a677234d60aa1..036f56ca29bb14 100644 --- a/tree.c +++ b/tree.c @@ -186,6 +186,12 @@ int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size) } int parse_tree_gently(struct tree *item, int quiet_on_missing) +{ + return repo_parse_tree_gently(the_repository, item, quiet_on_missing); +} + +int repo_parse_tree_gently(struct repository *r, struct tree *item, + int quiet_on_missing) { enum object_type type; void *buffer; @@ -193,8 +199,7 @@ int parse_tree_gently(struct tree *item, int quiet_on_missing) if (item->object.parsed) return 0; - buffer = odb_read_object(the_repository->objects, &item->object.oid, - &type, &size); + buffer = odb_read_object(r->objects, &item->object.oid, &type, &size); if (!buffer) return quiet_on_missing ? -1 : error("Could not read %s", @@ -216,7 +221,12 @@ void free_tree_buffer(struct tree *tree) struct tree *parse_tree_indirect(const struct object_id *oid) { - struct repository *r = the_repository; + return repo_parse_tree_indirect(the_repository, oid); +} + +struct tree *repo_parse_tree_indirect(struct repository *r, + const struct object_id *oid) +{ struct object *obj = parse_object(r, oid); return (struct tree *)repo_peel_to_type(r, NULL, 0, obj, OBJ_TREE); } diff --git a/tree.h b/tree.h index cc6ddf51b3273c..9037891d30fbb4 100644 --- a/tree.h +++ b/tree.h @@ -20,14 +20,22 @@ struct tree *lookup_tree(struct repository *r, const struct object_id *oid); int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size); int parse_tree_gently(struct tree *tree, int quiet_on_missing); +int repo_parse_tree_gently(struct repository *r, struct tree *item, + int quiet_on_missing); static inline int parse_tree(struct tree *tree) { return parse_tree_gently(tree, 0); } +static inline int repo_parse_tree(struct repository *r, struct tree *item) +{ + return repo_parse_tree_gently(r, item, 0); +} void free_tree_buffer(struct tree *tree); /* Parses and returns the tree in the given ent, chasing tags and commits. */ struct tree *parse_tree_indirect(const struct object_id *oid); +struct tree *repo_parse_tree_indirect(struct repository *r, + const struct object_id *oid); /* * Functions for comparing pathnames From 548aaf9d06002e8c7acef383c810a398da9917fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 9 Jan 2026 22:30:14 +0100 Subject: [PATCH 03/20] add-interactive: use repo_parse_tree_indirect() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1b374ad71f (add-interactive: stop using `the_repository`, 2024-12-17) replaced explicit uses of the_repository. parse_tree_indirect() uses it internally, though, so call repo_parse_tree_indirect() instead and hand it the correct repository. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- add-interactive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/add-interactive.c b/add-interactive.c index 68fc09547dd02e..95ec5a89f8c3d3 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -840,7 +840,7 @@ static int run_revert(struct add_i_state *s, const struct pathspec *ps, if (is_initial) oidcpy(&oid, s->r->hash_algo->empty_tree); else { - tree = parse_tree_indirect(&oid); + tree = repo_parse_tree_indirect(s->r, &oid); if (!tree) { res = error(_("Could not parse HEAD^{tree}")); goto finish_revert; From 83131ed25c01ae1862d5e99ad349cb8eead454c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 9 Jan 2026 22:30:15 +0100 Subject: [PATCH 04/20] bloom: use repo_parse_tree() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the passed in repository instead of the implicit the_repository when parsing the tree. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- bloom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bloom.c b/bloom.c index 2d7b951e5bf245..77a6fddf720912 100644 --- a/bloom.c +++ b/bloom.c @@ -354,7 +354,7 @@ static void init_truncated_large_filter(struct bloom_filter *filter, static int has_entries_with_high_bit(struct repository *r, struct tree *t) { - if (parse_tree(t)) + if (repo_parse_tree(r, t)) return 1; if (!(t->object.flags & VISITED)) { From d54eb12910978a4c87a63e0ff5889c5acb39d6c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 9 Jan 2026 22:30:16 +0100 Subject: [PATCH 05/20] delta-islands: use repo_parse_tree() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 19be71db9c (delta-islands: stop depending on `the_repository`, 2025-03-10) replaced explicit uses of the_repository. parse_tree() uses it internally, though, so call repo_parse_tree() instead and hand it the correct repository. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- delta-islands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/delta-islands.c b/delta-islands.c index 7cfebc4162b0e0..f4d2468790ce4f 100644 --- a/delta-islands.c +++ b/delta-islands.c @@ -283,7 +283,7 @@ void resolve_tree_islands(struct repository *r, root_marks = kh_value(island_marks, pos); tree = lookup_tree(r, &ent->idx.oid); - if (!tree || parse_tree(tree) < 0) + if (!tree || repo_parse_tree(r, tree) < 0) die(_("bad tree object %s"), oid_to_hex(&ent->idx.oid)); init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size); From fd64c6fbcb29a37e9d1ed8028ef52b886208866f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 9 Jan 2026 22:30:17 +0100 Subject: [PATCH 06/20] pack-bitmap-write: use repo_parse_tree() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1a6768d1dd (pack-bitmap-write: stop depending on `the_repository`, 2025-03-10) replaced explicit uses of the_repository. parse_tree() uses it internally, though, so call repo_parse_tree() instead and hand it the correct repository. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- pack-bitmap-write.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c index 4404921521ca34..d38de772c6c93d 100644 --- a/pack-bitmap-write.c +++ b/pack-bitmap-write.c @@ -478,7 +478,7 @@ static int fill_bitmap_tree(struct bitmap_writer *writer, return 0; bitmap_set(bitmap, pos); - if (parse_tree(tree) < 0) + if (repo_parse_tree(writer->repo, tree) < 0) die("unable to load tree object %s", oid_to_hex(&tree->object.oid)); init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size); From d473865154ff12d398ee86d2c9093419aedfbf58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 9 Jan 2026 22:30:18 +0100 Subject: [PATCH 07/20] path-walk: use repo_parse_tree_gently() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the passed in repository instead of the implicit the_repository when parsing the tree. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- path-walk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path-walk.c b/path-walk.c index f1ceed99e94ca9..364e4cfa19b2e4 100644 --- a/path-walk.c +++ b/path-walk.c @@ -137,7 +137,7 @@ static int add_tree_entries(struct path_walk_context *ctx, error(_("failed to walk children of tree %s: not found"), oid_to_hex(oid)); return -1; - } else if (parse_tree_gently(tree, 1)) { + } else if (repo_parse_tree_gently(ctx->repo, tree, 1)) { error("bad tree object %s", oid_to_hex(oid)); return -1; } From e61c387a1be8feec8bcb0cf1793893d206eaa2f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 9 Jan 2026 22:30:19 +0100 Subject: [PATCH 08/20] tree: use repo_parse_tree() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit e092073d64 (tree.c: make read_tree*() take 'struct repository *', 2018-11-18) replaced explicit uses of the_repository. parse_tree() uses it internally, though, so call repo_parse_tree() instead and hand it the correct repository. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tree.c b/tree.c index 036f56ca29bb14..edcf6a284cbb17 100644 --- a/tree.c +++ b/tree.c @@ -28,7 +28,7 @@ int read_tree_at(struct repository *r, if (depth > r->settings.max_allowed_tree_depth) return error("exceeded maximum allowed tree depth"); - if (parse_tree(tree)) + if (repo_parse_tree(r, tree)) return -1; init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size); From a8a50f29aeab6cf3cd848caaa8229ab26d542b80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 9 Jan 2026 22:30:20 +0100 Subject: [PATCH 09/20] tree: stop using the_repository MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Push the use of the_repository to the remaining callers by turning the compatibility wrappers into macros, whose use still requires USE_THE_REPOSITORY_VARIABLE to be defined. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- tree.c | 12 ------------ tree.h | 9 +++------ 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/tree.c b/tree.c index edcf6a284cbb17..d703ab97c8303a 100644 --- a/tree.c +++ b/tree.c @@ -1,5 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE - #include "git-compat-util.h" #include "hex.h" #include "tree.h" @@ -185,11 +183,6 @@ int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size) return 0; } -int parse_tree_gently(struct tree *item, int quiet_on_missing) -{ - return repo_parse_tree_gently(the_repository, item, quiet_on_missing); -} - int repo_parse_tree_gently(struct repository *r, struct tree *item, int quiet_on_missing) { @@ -219,11 +212,6 @@ void free_tree_buffer(struct tree *tree) tree->object.parsed = 0; } -struct tree *parse_tree_indirect(const struct object_id *oid) -{ - return repo_parse_tree_indirect(the_repository, oid); -} - struct tree *repo_parse_tree_indirect(struct repository *r, const struct object_id *oid) { diff --git a/tree.h b/tree.h index 9037891d30fbb4..677382eed86c59 100644 --- a/tree.h +++ b/tree.h @@ -19,13 +19,10 @@ struct tree *lookup_tree(struct repository *r, const struct object_id *oid); int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size); -int parse_tree_gently(struct tree *tree, int quiet_on_missing); +#define parse_tree_gently(t, q) repo_parse_tree_gently(the_repository, t, q) int repo_parse_tree_gently(struct repository *r, struct tree *item, int quiet_on_missing); -static inline int parse_tree(struct tree *tree) -{ - return parse_tree_gently(tree, 0); -} +#define parse_tree(t) repo_parse_tree(the_repository, t) static inline int repo_parse_tree(struct repository *r, struct tree *item) { return repo_parse_tree_gently(r, item, 0); @@ -33,7 +30,7 @@ static inline int repo_parse_tree(struct repository *r, struct tree *item) void free_tree_buffer(struct tree *tree); /* Parses and returns the tree in the given ent, chasing tags and commits. */ -struct tree *parse_tree_indirect(const struct object_id *oid); +#define parse_tree_indirect(o) repo_parse_tree_indirect(the_repository, o) struct tree *repo_parse_tree_indirect(struct repository *r, const struct object_id *oid); From ec7a16b14551fed736ecfe0a9d4f6d6f9e03be79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 9 Jan 2026 22:30:21 +0100 Subject: [PATCH 10/20] cocci: convert parse_tree functions to repo_ variants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add and apply a semantic patch to convert calls to parse_tree() and friends to the corresponding variant that takes a repository argument, to allow the functions that implicitly use the_repository to be retired once all potential in-flight topics are settled and converted as well. The changes in .c files were generated by Coccinelle, but I fixed a whitespace bug it would have introduced to builtin/commit.c. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- archive.c | 2 +- builtin/am.c | 10 +++++----- builtin/checkout.c | 12 +++++++----- builtin/clone.c | 4 ++-- builtin/commit.c | 5 +++-- builtin/diff-tree.c | 2 +- builtin/ls-tree.c | 2 +- builtin/merge-tree.c | 9 ++++++--- builtin/merge.c | 8 ++++---- builtin/read-tree.c | 4 ++-- builtin/reset.c | 4 ++-- builtin/stash.c | 8 ++++---- cache-tree.c | 2 +- contrib/coccinelle/the_repository.cocci | 10 ++++++++++ diff-lib.c | 2 +- fsck.c | 2 +- http-push.c | 2 +- list-objects.c | 2 +- merge-ort.c | 13 +++++++------ merge.c | 6 +++--- read-cache.c | 2 +- reset.c | 2 +- revision.c | 4 ++-- sequencer.c | 4 ++-- t/helper/test-cache-tree.c | 2 +- t/helper/test-match-trees.c | 4 ++-- walker.c | 2 +- 27 files changed, 73 insertions(+), 56 deletions(-) diff --git a/archive.c b/archive.c index 310672b479a19f..fcd474c682ffe5 100644 --- a/archive.c +++ b/archive.c @@ -519,7 +519,7 @@ static void parse_treeish_arg(const char **argv, if (ar_args->mtime_option) archive_time = approxidate(ar_args->mtime_option); - tree = parse_tree_indirect(&oid); + tree = repo_parse_tree_indirect(the_repository, &oid); if (!tree) die(_("not a tree object: %s"), oid_to_hex(&oid)); diff --git a/builtin/am.c b/builtin/am.c index 277c2e7937dcc1..b66a33d8a8899c 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1998,7 +1998,7 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset) struct unpack_trees_options opts; struct tree_desc t[2]; - if (parse_tree(head) || parse_tree(remote)) + if (repo_parse_tree(the_repository, head) || repo_parse_tree(the_repository, remote)) return -1; repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); @@ -2038,7 +2038,7 @@ static int merge_tree(struct tree *tree) struct unpack_trees_options opts; struct tree_desc t[1]; - if (parse_tree(tree)) + if (repo_parse_tree(the_repository, tree)) return -1; repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); @@ -2071,11 +2071,11 @@ static int clean_index(const struct object_id *head, const struct object_id *rem struct tree *head_tree, *remote_tree, *index_tree; struct object_id index; - head_tree = parse_tree_indirect(head); + head_tree = repo_parse_tree_indirect(the_repository, head); if (!head_tree) return error(_("Could not parse object '%s'."), oid_to_hex(head)); - remote_tree = parse_tree_indirect(remote); + remote_tree = repo_parse_tree_indirect(the_repository, remote); if (!remote_tree) return error(_("Could not parse object '%s'."), oid_to_hex(remote)); @@ -2089,7 +2089,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem 0, NULL)) return -1; - index_tree = parse_tree_indirect(&index); + index_tree = repo_parse_tree_indirect(the_repository, &index); if (!index_tree) return error(_("Could not parse object '%s'."), oid_to_hex(&index)); diff --git a/builtin/checkout.c b/builtin/checkout.c index 261699e2f5fc97..0ba4f03f2ee064 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -724,7 +724,7 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o, init_checkout_metadata(&opts.meta, info->refname, info->commit ? &info->commit->object.oid : null_oid(the_hash_algo), NULL); - if (parse_tree(tree) < 0) + if (repo_parse_tree(the_repository, tree) < 0) return 128; init_tree_desc(&tree_desc, &tree->object.oid, tree->buffer, tree->size); switch (unpack_trees(1, &tree_desc, &opts)) { @@ -803,7 +803,8 @@ static int merge_working_tree(const struct checkout_opts *opts, if (opts->new_orphan_branch && opts->orphan_from_empty_tree) { if (new_branch_info->commit) BUG("'switch --orphan' should never accept a commit as starting point"); - new_tree = parse_tree_indirect(the_hash_algo->empty_tree); + new_tree = repo_parse_tree_indirect(the_repository, + the_hash_algo->empty_tree); if (!new_tree) BUG("unable to read empty tree"); } else { @@ -841,14 +842,15 @@ static int merge_working_tree(const struct checkout_opts *opts, old_commit_oid = old_branch_info->commit ? &old_branch_info->commit->object.oid : the_hash_algo->empty_tree; - tree = parse_tree_indirect(old_commit_oid); + tree = repo_parse_tree_indirect(the_repository, + old_commit_oid); if (!tree) die(_("unable to parse commit %s"), oid_to_hex(old_commit_oid)); init_tree_desc(&trees[0], &tree->object.oid, tree->buffer, tree->size); - if (parse_tree(new_tree) < 0) + if (repo_parse_tree(the_repository, new_tree) < 0) die(NULL); tree = new_tree; init_tree_desc(&trees[1], &tree->object.oid, @@ -1278,7 +1280,7 @@ static void setup_new_branch_info_and_source_tree( new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1); if (!new_branch_info->commit) { /* not a commit */ - *source_tree = parse_tree_indirect(rev); + *source_tree = repo_parse_tree_indirect(the_repository, rev); if (!*source_tree) die(_("unable to read tree (%s)"), oid_to_hex(rev)); } else { diff --git a/builtin/clone.c b/builtin/clone.c index b19b302b065467..b40cee596804f5 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -680,10 +680,10 @@ static int checkout(int submodule_progress, int filter_submodules, opts.dst_index = the_repository->index; init_checkout_metadata(&opts.meta, head, &oid, NULL); - tree = parse_tree_indirect(&oid); + tree = repo_parse_tree_indirect(the_repository, &oid); if (!tree) die(_("unable to parse commit %s"), oid_to_hex(&oid)); - if (parse_tree(tree) < 0) + if (repo_parse_tree(the_repository, tree) < 0) exit(128); init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size); if (unpack_trees(1, &t, &opts) < 0) diff --git a/builtin/commit.c b/builtin/commit.c index 0243f17d53c97c..8e901fe8db7942 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -327,10 +327,11 @@ static void create_base_index(const struct commit *current_head) opts.dst_index = the_repository->index; opts.fn = oneway_merge; - tree = parse_tree_indirect(¤t_head->object.oid); + tree = repo_parse_tree_indirect(the_repository, + ¤t_head->object.oid); if (!tree) die(_("failed to unpack HEAD tree object")); - if (parse_tree(tree) < 0) + if (repo_parse_tree(the_repository, tree) < 0) exit(128); init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size); if (unpack_trees(1, &t, &opts)) diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index 49dd4d00ebf1bc..740d9a791c9c1d 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -52,7 +52,7 @@ static int stdin_diff_trees(struct tree *tree1, const char *p) if (!isspace(*p++) || parse_oid_hex(p, &oid, &p) || *p) return error("Need exactly two trees, separated by a space"); tree2 = lookup_tree(the_repository, &oid); - if (!tree2 || parse_tree(tree2)) + if (!tree2 || repo_parse_tree(the_repository, tree2)) return -1; printf("%s %s\n", oid_to_hex(&tree1->object.oid), oid_to_hex(&tree2->object.oid)); diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index ec6940fc7c4b9c..113e4a960dc7dd 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -421,7 +421,7 @@ int cmd_ls_tree(int argc, for (i = 0; i < options.pathspec.nr; i++) options.pathspec.items[i].nowildcard_len = options.pathspec.items[i].len; options.pathspec.has_wildcard = 0; - tree = parse_tree_indirect(&oid); + tree = repo_parse_tree_indirect(the_repository, &oid); if (!tree) die("not a tree object"); /* diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 1c063d9a41a695..a6e6d5b555f7db 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -447,17 +447,20 @@ static int real_merge(struct merge_tree_options *o, if (repo_get_oid_treeish(the_repository, merge_base, &base_oid)) die(_("could not parse as tree '%s'"), merge_base); - base_tree = parse_tree_indirect(&base_oid); + base_tree = repo_parse_tree_indirect(the_repository, + &base_oid); if (!base_tree) die(_("unable to read tree (%s)"), oid_to_hex(&base_oid)); if (repo_get_oid_treeish(the_repository, branch1, &head_oid)) die(_("could not parse as tree '%s'"), branch1); - parent1_tree = parse_tree_indirect(&head_oid); + parent1_tree = repo_parse_tree_indirect(the_repository, + &head_oid); if (!parent1_tree) die(_("unable to read tree (%s)"), oid_to_hex(&head_oid)); if (repo_get_oid_treeish(the_repository, branch2, &merge_oid)) die(_("could not parse as tree '%s'"), branch2); - parent2_tree = parse_tree_indirect(&merge_oid); + parent2_tree = repo_parse_tree_indirect(the_repository, + &merge_oid); if (!parent2_tree) die(_("unable to read tree (%s)"), oid_to_hex(&merge_oid)); diff --git a/builtin/merge.c b/builtin/merge.c index c421a11b0b69df..50001b4c59f083 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -756,19 +756,19 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head, opts.trivial_merges_only = 1; opts.merge = 1; opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */ - trees[nr_trees] = parse_tree_indirect(common); + trees[nr_trees] = repo_parse_tree_indirect(the_repository, common); if (!trees[nr_trees++]) return -1; - trees[nr_trees] = parse_tree_indirect(head); + trees[nr_trees] = repo_parse_tree_indirect(the_repository, head); if (!trees[nr_trees++]) return -1; - trees[nr_trees] = parse_tree_indirect(one); + trees[nr_trees] = repo_parse_tree_indirect(the_repository, one); if (!trees[nr_trees++]) return -1; opts.fn = threeway_merge; cache_tree_free(&the_repository->index->cache_tree); for (i = 0; i < nr_trees; i++) { - parse_tree(trees[i]); + repo_parse_tree(the_repository, trees[i]); init_tree_desc(t+i, &trees[i]->object.oid, trees[i]->buffer, trees[i]->size); } diff --git a/builtin/read-tree.c b/builtin/read-tree.c index 34f7a59f38ee22..460b21e40ac914 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -32,7 +32,7 @@ static int list_tree(struct object_id *oid) if (nr_trees >= MAX_UNPACK_TREES) die("I cannot read more than %d trees", MAX_UNPACK_TREES); - tree = parse_tree_indirect(oid); + tree = repo_parse_tree_indirect(the_repository, oid); if (!tree) return -1; trees[nr_trees++] = tree; @@ -268,7 +268,7 @@ int cmd_read_tree(int argc, cache_tree_free(&the_repository->index->cache_tree); for (i = 0; i < nr_trees; i++) { struct tree *tree = trees[i]; - if (parse_tree(tree) < 0) + if (repo_parse_tree(the_repository, tree) < 0) return 128; init_tree_desc(t+i, &tree->object.oid, tree->buffer, tree->size); } diff --git a/builtin/reset.c b/builtin/reset.c index ed35802af15c94..c48d9845f8425e 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -118,7 +118,7 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t goto out; if (reset_type == MIXED || reset_type == HARD) { - tree = parse_tree_indirect(oid); + tree = repo_parse_tree_indirect(the_repository, oid); if (!tree) { error(_("unable to read tree (%s)"), oid_to_hex(oid)); goto out; @@ -417,7 +417,7 @@ int cmd_reset(int argc, struct tree *tree; if (repo_get_oid_treeish(the_repository, rev, &oid)) die(_("Failed to resolve '%s' as a valid tree."), rev); - tree = parse_tree_indirect(&oid); + tree = repo_parse_tree_indirect(the_repository, &oid); if (!tree) die(_("Could not parse object '%s'."), rev); oidcpy(&oid, &tree->object.oid); diff --git a/builtin/stash.c b/builtin/stash.c index 948eba06fbccb3..193e3ea47a17c8 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -347,8 +347,8 @@ static int reset_tree(struct object_id *i_tree, int update, int reset) memset(&opts, 0, sizeof(opts)); - tree = parse_tree_indirect(i_tree); - if (parse_tree(tree)) + tree = repo_parse_tree_indirect(the_repository, i_tree); + if (repo_parse_tree(the_repository, tree)) return -1; init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size); @@ -940,8 +940,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op struct unpack_trees_options unpack_tree_opt = { 0 }; for (size_t i = 0; i < ARRAY_SIZE(oid); i++) { - tree[i] = parse_tree_indirect(oid[i]); - if (parse_tree(tree[i]) < 0) + tree[i] = repo_parse_tree_indirect(the_repository, oid[i]); + if (repo_parse_tree(the_repository, tree[i]) < 0) die(_("failed to parse tree")); init_tree_desc(&tree_desc[i], &tree[i]->object.oid, tree[i]->buffer, tree[i]->size); diff --git a/cache-tree.c b/cache-tree.c index 2d8947b5187840..16c3a36b48267b 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -813,7 +813,7 @@ static void prime_cache_tree_rec(struct repository *r, struct cache_tree_sub *sub; struct tree *subtree = lookup_tree(r, &entry.oid); - if (parse_tree(subtree) < 0) + if (repo_parse_tree(the_repository, subtree) < 0) exit(128); sub = cache_tree_sub(it, entry.path); sub->cache_tree = cache_tree(); diff --git a/contrib/coccinelle/the_repository.cocci b/contrib/coccinelle/the_repository.cocci index ea7fe1c8db78f3..a1f6f5ac06c253 100644 --- a/contrib/coccinelle/the_repository.cocci +++ b/contrib/coccinelle/the_repository.cocci @@ -117,6 +117,16 @@ | - init_revisions + repo_init_revisions +// tree.h +| +- parse_tree ++ repo_parse_tree +| +- parse_tree_gently ++ repo_parse_tree_gently +| +- parse_tree_indirect ++ repo_parse_tree_indirect ) ( + the_repository, diff --git a/diff-lib.c b/diff-lib.c index 5307390ff3db7b..506000761d4db5 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -552,7 +552,7 @@ static int diff_cache(struct rev_info *revs, struct tree_desc t; struct unpack_trees_options opts; - tree = parse_tree_indirect(tree_oid); + tree = repo_parse_tree_indirect(the_repository, tree_oid); if (!tree) return error("bad tree object %s", tree_name ? tree_name : oid_to_hex(tree_oid)); diff --git a/fsck.c b/fsck.c index fae18d8561e067..5532f5ae9f938d 100644 --- a/fsck.c +++ b/fsck.c @@ -360,7 +360,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op int res = 0; const char *name; - if (parse_tree(tree)) + if (repo_parse_tree(the_repository, tree)) return -1; name = fsck_get_object_name(options, &tree->object.oid); diff --git a/http-push.c b/http-push.c index 60a9b756209071..cc0f80934615ba 100644 --- a/http-push.c +++ b/http-push.c @@ -1311,7 +1311,7 @@ static struct object_list **process_tree(struct tree *tree, if (obj->flags & (UNINTERESTING | SEEN)) return p; - if (parse_tree(tree) < 0) + if (repo_parse_tree(the_repository, tree) < 0) die("bad tree object %s", oid_to_hex(&obj->oid)); obj->flags |= SEEN; diff --git a/list-objects.c b/list-objects.c index 1279676ddca4df..91b23e22f71aac 100644 --- a/list-objects.c +++ b/list-objects.c @@ -170,7 +170,7 @@ static void process_tree(struct traversal_context *ctx, if (ctx->depth > revs->repo->settings.max_allowed_tree_depth) die("exceeded maximum allowed tree depth"); - failed_parse = parse_tree_gently(tree, 1); + failed_parse = repo_parse_tree_gently(the_repository, tree, 1); if (failed_parse) { if (revs->ignore_missing_links) return; diff --git a/merge-ort.c b/merge-ort.c index 2b837a58c3a6f8..e80e4f735a60f0 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -1732,9 +1732,9 @@ static int collect_merge_info(struct merge_options *opt, info.data = opt; info.show_all_errors = 1; - if (parse_tree(merge_base) < 0 || - parse_tree(side1) < 0 || - parse_tree(side2) < 0) + if (repo_parse_tree(the_repository, merge_base) < 0 || + repo_parse_tree(the_repository, side1) < 0 || + repo_parse_tree(the_repository, side2) < 0) return -1; init_tree_desc(t + 0, &merge_base->object.oid, merge_base->buffer, merge_base->size); @@ -4619,10 +4619,10 @@ static int checkout(struct merge_options *opt, unpack_opts.verbose_update = (opt->verbosity > 2); unpack_opts.fn = twoway_merge; unpack_opts.preserve_ignored = 0; /* FIXME: !opts->overwrite_ignore */ - if (parse_tree(prev) < 0) + if (repo_parse_tree(the_repository, prev) < 0) return -1; init_tree_desc(&trees[0], &prev->object.oid, prev->buffer, prev->size); - if (parse_tree(next) < 0) + if (repo_parse_tree(the_repository, next) < 0) return -1; init_tree_desc(&trees[1], &next->object.oid, next->buffer, next->size); @@ -5280,7 +5280,8 @@ static void merge_ort_nonrecursive_internal(struct merge_options *opt, if (result->clean >= 0) { if (!opt->mergeability_only) { - result->tree = parse_tree_indirect(&working_tree_oid); + result->tree = repo_parse_tree_indirect(the_repository, + &working_tree_oid); if (!result->tree) die(_("unable to read tree (%s)"), oid_to_hex(&working_tree_oid)); diff --git a/merge.c b/merge.c index 5ecaf508e4cb98..0f5e823e63ed5f 100644 --- a/merge.c +++ b/merge.c @@ -68,18 +68,18 @@ int checkout_fast_forward(struct repository *r, memset(&trees, 0, sizeof(trees)); memset(&t, 0, sizeof(t)); - trees[nr_trees] = parse_tree_indirect(head); + trees[nr_trees] = repo_parse_tree_indirect(the_repository, head); if (!trees[nr_trees++]) { rollback_lock_file(&lock_file); return -1; } - trees[nr_trees] = parse_tree_indirect(remote); + trees[nr_trees] = repo_parse_tree_indirect(the_repository, remote); if (!trees[nr_trees++]) { rollback_lock_file(&lock_file); return -1; } for (i = 0; i < nr_trees; i++) { - if (parse_tree(trees[i]) < 0) { + if (repo_parse_tree(the_repository, trees[i]) < 0) { rollback_lock_file(&lock_file); return -1; } diff --git a/read-cache.c b/read-cache.c index 990d4ead0d8ae4..e9c1b23e48437a 100644 --- a/read-cache.c +++ b/read-cache.c @@ -3807,7 +3807,7 @@ void overlay_tree_on_index(struct index_state *istate, if (repo_get_oid(the_repository, tree_name, &oid)) die("tree-ish %s not found.", tree_name); - tree = parse_tree_indirect(&oid); + tree = repo_parse_tree_indirect(the_repository, &oid); if (!tree) die("bad tree-ish %s", tree_name); diff --git a/reset.c b/reset.c index bb590271811ec2..46e30e639458c5 100644 --- a/reset.c +++ b/reset.c @@ -163,7 +163,7 @@ int reset_head(struct repository *r, const struct reset_head_opts *opts) goto leave_reset_head; } - tree = parse_tree_indirect(oid); + tree = repo_parse_tree_indirect(the_repository, oid); if (!tree) { ret = error(_("unable to read tree (%s)"), oid_to_hex(oid)); goto leave_reset_head; diff --git a/revision.c b/revision.c index 5f0850ae5c9c1a..426d19a2c6c8c3 100644 --- a/revision.c +++ b/revision.c @@ -72,7 +72,7 @@ static void mark_tree_contents_uninteresting(struct repository *r, struct tree_desc desc; struct name_entry entry; - if (parse_tree_gently(tree, 1) < 0) + if (repo_parse_tree_gently(the_repository, tree, 1) < 0) return; init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size); @@ -179,7 +179,7 @@ static void add_children_by_path(struct repository *r, if (!tree) return; - if (parse_tree_gently(tree, 1) < 0) + if (repo_parse_tree_gently(the_repository, tree, 1) < 0) return; init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size); diff --git a/sequencer.c b/sequencer.c index 71ed31c7740688..cccde58bee96fb 100644 --- a/sequencer.c +++ b/sequencer.c @@ -767,7 +767,7 @@ static int do_recursive_merge(struct repository *r, o.buffer_output = 2; o.show_rename_progress = 1; - head_tree = parse_tree_indirect(head); + head_tree = repo_parse_tree_indirect(the_repository, head); if (!head_tree) return error(_("unable to read tree (%s)"), oid_to_hex(head)); next_tree = next ? repo_get_commit_tree(r, next) : empty_tree(r); @@ -4052,7 +4052,7 @@ static int do_reset(struct repository *r, goto cleanup; } - tree = parse_tree_indirect(&oid); + tree = repo_parse_tree_indirect(the_repository, &oid); if (!tree) return error(_("unable to read tree (%s)"), oid_to_hex(&oid)); prime_cache_tree(r, r->index, tree); diff --git a/t/helper/test-cache-tree.c b/t/helper/test-cache-tree.c index 3ae45cec3be639..ff61d0ca7e2fe3 100644 --- a/t/helper/test-cache-tree.c +++ b/t/helper/test-cache-tree.c @@ -41,7 +41,7 @@ int cmd__cache_tree(int argc, const char **argv) die(_("unable to read index file")); oidcpy(&oid, &the_repository->index->cache_tree->oid); - tree = parse_tree_indirect(&oid); + tree = repo_parse_tree_indirect(the_repository, &oid); if (!tree) die(_("not a tree object: %s"), oid_to_hex(&oid)); diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c index e0e2048320d583..2ed064b9716ac8 100644 --- a/t/helper/test-match-trees.c +++ b/t/helper/test-match-trees.c @@ -19,10 +19,10 @@ int cmd__match_trees(int ac UNUSED, const char **av) die("cannot parse %s as an object name", av[1]); if (repo_get_oid(the_repository, av[2], &hash2)) die("cannot parse %s as an object name", av[2]); - one = parse_tree_indirect(&hash1); + one = repo_parse_tree_indirect(the_repository, &hash1); if (!one) die("not a tree-ish %s", av[1]); - two = parse_tree_indirect(&hash2); + two = repo_parse_tree_indirect(the_repository, &hash2); if (!two) die("not a tree-ish %s", av[2]); diff --git a/walker.c b/walker.c index 2891563b03620b..91332539d3a286 100644 --- a/walker.c +++ b/walker.c @@ -45,7 +45,7 @@ static int process_tree(struct walker *walker, struct tree *tree) struct tree_desc desc; struct name_entry entry; - if (parse_tree(tree)) + if (repo_parse_tree(the_repository, tree)) return -1; init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size); From e16ac6ca0db59da32969d5f44b5dc4494439bbeb Mon Sep 17 00:00:00 2001 From: Taylor Blau Date: Mon, 12 Jan 2026 18:45:03 -0500 Subject: [PATCH 11/20] t/t5319-multi-pack-index.sh: drop early 'test_done' In 6ce9d558ced (midx-write: skip rewriting MIDX with `--stdin-packs` unless needed, 2025-12-10), an extra 'test_done' was added, causing the test script to finish before having run all of its tests. Dropping this extraneous 'test_done' exposes a bug from commit 6ce9d558ced that causes a subsequent test to fail. Mark that test with a 'test_expect_failure' for now, and the subsequent commit will explain and fix the bug. Signed-off-by: Taylor Blau Signed-off-by: Junio C Hamano --- t/t5319-multi-pack-index.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh index 794f8b5ab4e136..b6622849db7149 100755 --- a/t/t5319-multi-pack-index.sh +++ b/t/t5319-multi-pack-index.sh @@ -415,8 +415,6 @@ test_expect_success 'up-to-date multi-pack-index is retained' ' ) ' -test_done - test_expect_success 'verify multi-pack-index success' ' git multi-pack-index verify --object-dir=$objdir ' @@ -565,7 +563,7 @@ test_expect_success 'git fsck suppresses MIDX output with --no-progress' ' ! grep "Verifying object offsets" err ' -test_expect_success 'corrupt MIDX is not reused' ' +test_expect_failure 'corrupt MIDX is not reused' ' corrupt_midx_and_verify $MIDX_BYTE_OFFSET "\377" $objdir \ "incorrect object offset" && git multi-pack-index write 2>err && From 38b72e581513dfbef784a1b808d282df1e0504d2 Mon Sep 17 00:00:00 2001 From: Taylor Blau Date: Mon, 12 Jan 2026 18:45:06 -0500 Subject: [PATCH 12/20] midx-write.c: assume checksum-invalid MIDXs require an update In 6ce9d558ced (midx-write: skip rewriting MIDX with `--stdin-packs` unless needed, 2025-12-10), the MIDX machinery learned how to optimize out unnecessary writes with "--stdin-packs". In order to do this, it compares the contents of the in-progress write against a MIDX loaded directly from the object store. We load a separate MIDX (as opposed to checking our update relative to "ctx.m") because the MIDX code does not reuse an existing MIDX with --stdin-packs, and always leaves "ctx.m" as NULL. See commit 0c5a62f14bc (midx-write.c: do not read existing MIDX with `packs_to_include`, 2024-06-11) for details on why. If "ctx.m" is non-NULL, however, it is guaranteed to be checksum-valid, since we only assign "ctx.m" when "midx_checksum_valid()" returns true. Since the same guard does not exist for the MIDX we pass to "midx_needs_update()", we may ignore on-disk corruption when determining whether or not we can optimize out the write. Add a similar guard within "midx_needs_update()" to prevent such an issue. A more robust fix would involve revising 0c5a62f14bc and teaching the MIDX generation code how to reuse an existing MIDX even when invoked with "--stdin-packs", such that we could avoid side-loading the MIDX directly from the object store in order to call "midx_needs_update()". For now, pursue the minimal fix. Signed-off-by: Taylor Blau Signed-off-by: Junio C Hamano --- midx-write.c | 14 ++++++++++++++ t/t5319-multi-pack-index.sh | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/midx-write.c b/midx-write.c index 40abe3868c4d6f..51e13901640445 100644 --- a/midx-write.c +++ b/midx-write.c @@ -1021,6 +1021,20 @@ static bool midx_needs_update(struct multi_pack_index *midx, struct write_midx_c struct strbuf buf = STRBUF_INIT; bool needed = true; + /* + * Ensure that we have a valid checksum before consulting the + * exisiting MIDX in order to determine if we can avoid an + * update. + * + * This is necessary because the given MIDX is loaded directly + * from the object store (because we still compare our proposed + * update to any on-disk MIDX regardless of whether or not we + * have assigned "ctx.m") and is thus not guaranteed to have a + * valid checksum. + */ + if (!midx_checksum_valid(midx)) + goto out; + /* * Ignore incremental updates for now. The assumption is that any * incremental update would be either empty (in which case we will bail diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh index b6622849db7149..faae98c7e76a20 100755 --- a/t/t5319-multi-pack-index.sh +++ b/t/t5319-multi-pack-index.sh @@ -563,7 +563,7 @@ test_expect_success 'git fsck suppresses MIDX output with --no-progress' ' ! grep "Verifying object offsets" err ' -test_expect_failure 'corrupt MIDX is not reused' ' +test_expect_success 'corrupt MIDX is not reused' ' corrupt_midx_and_verify $MIDX_BYTE_OFFSET "\377" $objdir \ "incorrect object offset" && git multi-pack-index write 2>err && From 0cd306ebc8c7a9faeced0a2fa7bddeea434bf285 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 5 Jan 2026 14:16:41 +0100 Subject: [PATCH 13/20] builtin/pack-objects: exclude promisor objects with "--stdin-packs" It is currently not possible to combine "--exclude-promisor-objects" with "--stdin-packs" because both flags want to set up a revision walk to enumerate the objects to pack. In a subsequent commit though we want to extend geometric repacks to support promisor objects, and for that we need to handle the combination of both flags. There are two cases we have to think about here: - "--stdin-packs" asks us to pack exactly the objects part of the specified packfiles. It is somewhat questionable what to do in the case where the user asks us to exclude promisor objects, but at the same time explicitly passes a promisor pack to us. For now, we simply abort the request as it is self-contradicting. As we have also been dying before this commit there is no regression here. - "--stdin-packs=follow" does the same as the first flag, but it also asks us to include all objects transitively reachable from any object in the packs we are about to repack. This is done by doing the revision walk mentioned further up. Luckily, fixing this case is trivial: we only need to modify the revision walk to also set the `exclude_promisor_objects` field. Note that we do not support the "--exclude-promisor-objects-best-effort" flag for now as we don't need it to support geometric repacking with promisor objects. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/pack-objects.c | 14 ++++++++++--- t/t5331-pack-objects-stdin.sh | 39 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index ca44b7894fc064..c53e0c91b992f7 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -3857,8 +3857,11 @@ static void read_packs_list_from_stdin(struct rev_info *revs) repo_for_each_pack(the_repository, p) { const char *pack_name = pack_basename(p); - if ((item = string_list_lookup(&include_packs, pack_name))) + if ((item = string_list_lookup(&include_packs, pack_name))) { + if (exclude_promisor_objects && p->pack_promisor) + die(_("packfile %s is a promisor but --exclude-promisor-objects was given"), p->pack_name); item->util = p; + } if ((item = string_list_lookup(&exclude_packs, pack_name))) item->util = p; } @@ -3936,6 +3939,7 @@ static void read_stdin_packs(enum stdin_packs_mode mode, int rev_list_unpacked) revs.tree_objects = 1; revs.tag_objects = 1; revs.ignore_missing_links = 1; + revs.exclude_promisor_objects = exclude_promisor_objects; /* avoids adding objects in excluded packs */ ignore_packed_keep_in_core = 1; @@ -5092,9 +5096,13 @@ int cmd_pack_objects(int argc, exclude_promisor_objects_best_effort, "--exclude-promisor-objects-best-effort"); if (exclude_promisor_objects) { - use_internal_rev_list = 1; fetch_if_missing = 0; - strvec_push(&rp, "--exclude-promisor-objects"); + + /* --stdin-packs handles promisor objects separately. */ + if (!stdin_packs) { + use_internal_rev_list = 1; + strvec_push(&rp, "--exclude-promisor-objects"); + } } else if (exclude_promisor_objects_best_effort) { use_internal_rev_list = 1; fetch_if_missing = 0; diff --git a/t/t5331-pack-objects-stdin.sh b/t/t5331-pack-objects-stdin.sh index 4a8df5a389d29f..cd949025b982a4 100755 --- a/t/t5331-pack-objects-stdin.sh +++ b/t/t5331-pack-objects-stdin.sh @@ -319,6 +319,45 @@ test_expect_success '--stdin-packs=follow walks into unknown packs' ' ) ' +test_expect_success '--stdin-packs with promisors' ' + test_when_finished "rm -fr repo" && + git init repo && + ( + cd repo && + git config set maintenance.auto false && + git remote add promisor garbage && + git config set remote.promisor.promisor true && + + for c in A B C D + do + echo "$c" >file && + git add file && + git commit --message "$c" && + git tag "$c" || return 1 + done && + + A="$(echo A | git pack-objects --revs $packdir/pack)" && + B="$(echo A..B | git pack-objects --revs $packdir/pack --filter=blob:none)" && + C="$(echo B..C | git pack-objects --revs $packdir/pack)" && + D="$(echo C..D | git pack-objects --revs $packdir/pack)" && + touch $packdir/pack-$B.promisor && + + test_must_fail git pack-objects --stdin-packs --exclude-promisor-objects pack- 2>err <<-EOF && + pack-$B.pack + EOF + test_grep "is a promisor but --exclude-promisor-objects was given" err && + + PACK=$(git pack-objects --stdin-packs=follow --exclude-promisor-objects $packdir/pack <<-EOF + pack-$D.pack + EOF + ) && + objects_in_packs $C $D >expect && + objects_in_packs $PACK >actual && + test_cmp expect actual && + rm -f $packdir/pack-$PACK.* + ) +' + stdin_packs__follow_with_only () { rm -fr stdin_packs__follow_with_only && git init stdin_packs__follow_with_only && From 861248b946b68822375d2fe3cdffa174bf73104c Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 5 Jan 2026 14:16:42 +0100 Subject: [PATCH 14/20] repack-geometry: extract function to compute repacking split We're about to add a second caller that wants to compute the repacking split for a set of packfiles. Split out the function that computes this split to prepare for that. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- repack-geometry.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/repack-geometry.c b/repack-geometry.c index b3e32cd07ec119..17e6652a91c723 100644 --- a/repack-geometry.c +++ b/repack-geometry.c @@ -78,33 +78,32 @@ void pack_geometry_init(struct pack_geometry *geometry, strbuf_release(&buf); } -void pack_geometry_split(struct pack_geometry *geometry) +static uint32_t compute_pack_geometry_split(struct packed_git **pack, size_t pack_nr, + int split_factor) { uint32_t i; uint32_t split; off_t total_size = 0; - if (!geometry->pack_nr) { - geometry->split = geometry->pack_nr; - return; - } + if (!pack_nr) + return 0; /* * First, count the number of packs (in descending order of size) which * already form a geometric progression. */ - for (i = geometry->pack_nr - 1; i > 0; i--) { - struct packed_git *ours = geometry->pack[i]; - struct packed_git *prev = geometry->pack[i - 1]; + for (i = pack_nr - 1; i > 0; i--) { + struct packed_git *ours = pack[i]; + struct packed_git *prev = pack[i - 1]; - if (unsigned_mult_overflows(geometry->split_factor, + if (unsigned_mult_overflows(split_factor, pack_geometry_weight(prev))) die(_("pack %s too large to consider in geometric " "progression"), prev->pack_name); if (pack_geometry_weight(ours) < - geometry->split_factor * pack_geometry_weight(prev)) + split_factor * pack_geometry_weight(prev)) break; } @@ -130,21 +129,19 @@ void pack_geometry_split(struct pack_geometry *geometry) * the geometric progression. */ for (i = 0; i < split; i++) { - struct packed_git *p = geometry->pack[i]; + struct packed_git *p = pack[i]; if (unsigned_add_overflows(total_size, pack_geometry_weight(p))) die(_("pack %s too large to roll up"), p->pack_name); total_size += pack_geometry_weight(p); } - for (i = split; i < geometry->pack_nr; i++) { - struct packed_git *ours = geometry->pack[i]; + for (i = split; i < pack_nr; i++) { + struct packed_git *ours = pack[i]; - if (unsigned_mult_overflows(geometry->split_factor, - total_size)) + if (unsigned_mult_overflows(split_factor, total_size)) die(_("pack %s too large to roll up"), ours->pack_name); - if (pack_geometry_weight(ours) < - geometry->split_factor * total_size) { + if (pack_geometry_weight(ours) < split_factor * total_size) { if (unsigned_add_overflows(total_size, pack_geometry_weight(ours))) die(_("pack %s too large to roll up"), @@ -156,7 +153,13 @@ void pack_geometry_split(struct pack_geometry *geometry) break; } - geometry->split = split; + return split; +} + +void pack_geometry_split(struct pack_geometry *geometry) +{ + geometry->split = compute_pack_geometry_split(geometry->pack, geometry->pack_nr, + geometry->split_factor); } struct packed_git *pack_geometry_preferred_pack(struct pack_geometry *geometry) From dd8c4e12c2da850d67849ccfc221ee1dbf87ce55 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 5 Jan 2026 14:16:43 +0100 Subject: [PATCH 15/20] repack-promisor: extract function to finalize repacking We're about to add a second caller that wants to finalize repacking of promisor objects. Split out the function which does this to prepare for that. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- repack-promisor.c | 69 ++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/repack-promisor.c b/repack-promisor.c index ee6e0669f65602..125038d92e2565 100644 --- a/repack-promisor.c +++ b/repack-promisor.c @@ -34,39 +34,17 @@ static int write_oid(const struct object_id *oid, return 0; } -void repack_promisor_objects(struct repository *repo, - const struct pack_objects_args *args, - struct string_list *names, const char *packtmp) +static void finish_repacking_promisor_objects(struct repository *repo, + struct child_process *cmd, + struct string_list *names, + const char *packtmp) { - struct write_oid_context ctx; - struct child_process cmd = CHILD_PROCESS_INIT; - FILE *out; struct strbuf line = STRBUF_INIT; + FILE *out; - prepare_pack_objects(&cmd, args, packtmp); - cmd.in = -1; - - /* - * NEEDSWORK: Giving pack-objects only the OIDs without any ordering - * hints may result in suboptimal deltas in the resulting pack. See if - * the OIDs can be sent with fake paths such that pack-objects can use a - * {type -> existing pack order} ordering when computing deltas instead - * of a {type -> size} ordering, which may produce better deltas. - */ - ctx.cmd = &cmd; - ctx.algop = repo->hash_algo; - for_each_packed_object(repo, write_oid, &ctx, - FOR_EACH_OBJECT_PROMISOR_ONLY); - - if (cmd.in == -1) { - /* No packed objects; cmd was never started */ - child_process_clear(&cmd); - return; - } - - close(cmd.in); + close(cmd->in); - out = xfdopen(cmd.out, "r"); + out = xfdopen(cmd->out, "r"); while (strbuf_getline_lf(&line, out) != EOF) { struct string_list_item *item; char *promisor_name; @@ -96,7 +74,38 @@ void repack_promisor_objects(struct repository *repo, } fclose(out); - if (finish_command(&cmd)) + if (finish_command(cmd)) die(_("could not finish pack-objects to repack promisor objects")); strbuf_release(&line); } + +void repack_promisor_objects(struct repository *repo, + const struct pack_objects_args *args, + struct string_list *names, const char *packtmp) +{ + struct write_oid_context ctx; + struct child_process cmd = CHILD_PROCESS_INIT; + + prepare_pack_objects(&cmd, args, packtmp); + cmd.in = -1; + + /* + * NEEDSWORK: Giving pack-objects only the OIDs without any ordering + * hints may result in suboptimal deltas in the resulting pack. See if + * the OIDs can be sent with fake paths such that pack-objects can use a + * {type -> existing pack order} ordering when computing deltas instead + * of a {type -> size} ordering, which may produce better deltas. + */ + ctx.cmd = &cmd; + ctx.algop = repo->hash_algo; + for_each_packed_object(repo, write_oid, &ctx, + FOR_EACH_OBJECT_PROMISOR_ONLY); + + if (cmd.in == -1) { + /* No packed objects; cmd was never started */ + child_process_clear(&cmd); + return; + } + + finish_repacking_promisor_objects(repo, &cmd, names, packtmp); +} From fa7b91247b42bc9ffab423d42f0e9e12e86769fa Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 5 Jan 2026 14:16:44 +0100 Subject: [PATCH 16/20] repack-promisor: extract function to remove redundant packs We're about to add a second caller that wants to remove redundant packs after a geometric repack. Split out the function which does this to prepare for that. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- repack-geometry.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/repack-geometry.c b/repack-geometry.c index 17e6652a91c723..0daf545a815c0f 100644 --- a/repack-geometry.c +++ b/repack-geometry.c @@ -197,17 +197,18 @@ struct packed_git *pack_geometry_preferred_pack(struct pack_geometry *geometry) return NULL; } -void pack_geometry_remove_redundant(struct pack_geometry *geometry, - struct string_list *names, - struct existing_packs *existing, - const char *packdir) +static void remove_redundant_packs(struct packed_git **pack, + uint32_t pack_nr, + struct string_list *names, + struct existing_packs *existing, + const char *packdir) { const struct git_hash_algo *algop = existing->repo->hash_algo; struct strbuf buf = STRBUF_INIT; uint32_t i; - for (i = 0; i < geometry->split; i++) { - struct packed_git *p = geometry->pack[i]; + for (i = 0; i < pack_nr; i++) { + struct packed_git *p = pack[i]; if (string_list_has_string(names, hash_to_hex_algop(p->hash, algop))) continue; @@ -226,6 +227,15 @@ void pack_geometry_remove_redundant(struct pack_geometry *geometry, strbuf_release(&buf); } +void pack_geometry_remove_redundant(struct pack_geometry *geometry, + struct string_list *names, + struct existing_packs *existing, + const char *packdir) +{ + remove_redundant_packs(geometry->pack, geometry->split, + names, existing, packdir); +} + void pack_geometry_release(struct pack_geometry *geometry) { if (!geometry) From dcc9c7ef47d8755f1448116cdf0a421129999cd4 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 5 Jan 2026 14:16:45 +0100 Subject: [PATCH 17/20] builtin/repack: handle promisor packs with geometric repacking When performing a fetch with an object filter, we mark the resulting packfile as a promisor pack. An object part of such a pack may miss any of its referenced objects, and Git knows to handle this case by fetching any such missing objects from the promisor remote. The "promisor" property needs to be retained going forward. So every time we pack a promisor object, the resulting pack must be marked as a promisor pack. git-repack(1) does this already: when a repository has a promisor remote, it knows to pass "--exclude-promisor-objects" to the git-pack-objects(1) child process. Promisor packs are written separately when doing an all-into-one repack via `repack_promisor_objects()`. But we don't support promisor objects when doing a geometric repack yet. Promisor packs do not get any special treatment there, as we simply merge promisor and non-promisor packs. The resulting pack is not even marked as a promisor pack, which essentially corrupts the repository. This corruption couldn't happen in the real world though: we pass both "--exclude-promisor-objects" and "--stdin-packs" to git-pack-objects(1) if a repository has a promisor remote, but as those options are mutually exclusive we always end up dying. And while we made those flags compatible with one another in a preceding commit, we still end up dying in case git-pack-objects(1) is asked to repack a promisor pack. There's multiple ways to fix this: - We can exclude promisor packs from the geometric progression altogether. This would have the consequence that we never repack promisor packs at all. But in a partial clone it is quite likely that the user generates a bunch of promisor packs over time, as every backfill fetch would create another one. So this doesn't really feel like a sensible option. - We can adapt git-pack-objects(1) to support repacking promisor packs and include them in the normal geometric progression. But this would mean that the set of promisor objects expands over time as the packs are merged with normal packs. - We can use a separate geometric progression to repack promisor packs. The first two options both have significant downsides, so they aren't really feasible. But the third option fixes both of these downsides: we make sure that promisor packs get merged, and at the same time we never expand the set of promisor objects beyond the set of objects that are already marked as promisor objects. Implement this strategy so that geometric repacking works in partial clones. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/repack.c | 3 ++ repack-geometry.c | 28 +++++++++++++---- repack-promisor.c | 28 +++++++++++++++++ repack.h | 10 ++++++ t/t7703-repack-geometric.sh | 61 +++++++++++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+), 6 deletions(-) diff --git a/builtin/repack.c b/builtin/repack.c index d9012141f699c9..f6bb04bef7264e 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -332,6 +332,9 @@ int cmd_repack(int argc, !(pack_everything & PACK_CRUFT)) strvec_push(&cmd.args, "--pack-loose-unreachable"); } else if (geometry.split_factor) { + pack_geometry_repack_promisors(repo, &po_args, &geometry, + &names, packtmp); + if (midx_must_contain_cruft) strvec_push(&cmd.args, "--stdin-packs"); else diff --git a/repack-geometry.c b/repack-geometry.c index 0daf545a815c0f..7cebd0cb45f0ea 100644 --- a/repack-geometry.c +++ b/repack-geometry.c @@ -66,15 +66,25 @@ void pack_geometry_init(struct pack_geometry *geometry, if (p->is_cruft) continue; - ALLOC_GROW(geometry->pack, - geometry->pack_nr + 1, - geometry->pack_alloc); - - geometry->pack[geometry->pack_nr] = p; - geometry->pack_nr++; + if (p->pack_promisor) { + ALLOC_GROW(geometry->promisor_pack, + geometry->promisor_pack_nr + 1, + geometry->promisor_pack_alloc); + + geometry->promisor_pack[geometry->promisor_pack_nr] = p; + geometry->promisor_pack_nr++; + } else { + ALLOC_GROW(geometry->pack, + geometry->pack_nr + 1, + geometry->pack_alloc); + + geometry->pack[geometry->pack_nr] = p; + geometry->pack_nr++; + } } QSORT(geometry->pack, geometry->pack_nr, pack_geometry_cmp); + QSORT(geometry->promisor_pack, geometry->promisor_pack_nr, pack_geometry_cmp); strbuf_release(&buf); } @@ -160,6 +170,9 @@ void pack_geometry_split(struct pack_geometry *geometry) { geometry->split = compute_pack_geometry_split(geometry->pack, geometry->pack_nr, geometry->split_factor); + geometry->promisor_split = compute_pack_geometry_split(geometry->promisor_pack, + geometry->promisor_pack_nr, + geometry->split_factor); } struct packed_git *pack_geometry_preferred_pack(struct pack_geometry *geometry) @@ -234,6 +247,8 @@ void pack_geometry_remove_redundant(struct pack_geometry *geometry, { remove_redundant_packs(geometry->pack, geometry->split, names, existing, packdir); + remove_redundant_packs(geometry->promisor_pack, geometry->promisor_split, + names, existing, packdir); } void pack_geometry_release(struct pack_geometry *geometry) @@ -242,4 +257,5 @@ void pack_geometry_release(struct pack_geometry *geometry) return; free(geometry->pack); + free(geometry->promisor_pack); } diff --git a/repack-promisor.c b/repack-promisor.c index 125038d92e2565..73af57bce31376 100644 --- a/repack-promisor.c +++ b/repack-promisor.c @@ -109,3 +109,31 @@ void repack_promisor_objects(struct repository *repo, finish_repacking_promisor_objects(repo, &cmd, names, packtmp); } + +void pack_geometry_repack_promisors(struct repository *repo, + const struct pack_objects_args *args, + const struct pack_geometry *geometry, + struct string_list *names, + const char *packtmp) +{ + struct child_process cmd = CHILD_PROCESS_INIT; + FILE *in; + + if (!geometry->promisor_split) + return; + + prepare_pack_objects(&cmd, args, packtmp); + strvec_push(&cmd.args, "--stdin-packs"); + cmd.in = -1; + if (start_command(&cmd)) + die(_("could not start pack-objects to repack promisor packs")); + + in = xfdopen(cmd.in, "w"); + for (size_t i = 0; i < geometry->promisor_split; i++) + fprintf(in, "%s\n", pack_basename(geometry->promisor_pack[i])); + for (size_t i = geometry->promisor_split; i < geometry->promisor_pack_nr; i++) + fprintf(in, "^%s\n", pack_basename(geometry->promisor_pack[i])); + fclose(in); + + finish_repacking_promisor_objects(repo, &cmd, names, packtmp); +} diff --git a/repack.h b/repack.h index 3a688a12eeb7aa..bc9f2e1a5de984 100644 --- a/repack.h +++ b/repack.h @@ -103,9 +103,19 @@ struct pack_geometry { uint32_t pack_nr, pack_alloc; uint32_t split; + struct packed_git **promisor_pack; + uint32_t promisor_pack_nr, promisor_pack_alloc; + uint32_t promisor_split; + int split_factor; }; +void pack_geometry_repack_promisors(struct repository *repo, + const struct pack_objects_args *args, + const struct pack_geometry *geometry, + struct string_list *names, + const char *packtmp); + void pack_geometry_init(struct pack_geometry *geometry, struct existing_packs *existing, const struct pack_objects_args *args); diff --git a/t/t7703-repack-geometric.sh b/t/t7703-repack-geometric.sh index 98806cdb6fe9b7..04d5d8fc33e9e5 100755 --- a/t/t7703-repack-geometric.sh +++ b/t/t7703-repack-geometric.sh @@ -480,4 +480,65 @@ test_expect_success '--geometric -l disables writing bitmaps with non-local pack test_path_is_file member/.git/objects/pack/multi-pack-index-*.bitmap ' +write_packfile () { + NR="$1" + PREFIX="$2" + + printf "blob\ndata <objects-expect && + + ls .git/objects/pack/*.pack >packs-before && + test_line_count = 8 packs-before && + git repack --geometric=2 -d && + ls .git/objects/pack/*.pack >packs-after && + test_line_count = 5 packs-after && + test_grep ! "$NORMAL_A" packs-after && + test_grep ! "$NORMAL_B" packs-after && + test_grep "$NORMAL_C" packs-after && + test_grep ! "$PROMISOR_A" packs-after && + test_grep ! "$PROMISOR_B" packs-after && + test_grep ! "$PROMISOR_C" packs-after && + test_grep "$PROMISOR_D" packs-after && + test_grep "$PROMISOR_E" packs-after && + + ls .git/objects/pack/*.promisor >promisors && + test_line_count = 3 promisors && + + git cat-file --batch-all-objects --batch-check="%(objectname)" >objects-actual && + test_cmp objects-expect objects-actual + ) +' + test_done From 5814b04c02a983b3810f7b25acb024608c5246bb Mon Sep 17 00:00:00 2001 From: Pushkar Singh Date: Thu, 15 Jan 2026 11:08:05 +0000 Subject: [PATCH 18/20] Documentation/config: fix replacement for --get-urlmatch The documentation claims that --get-urlmatch is replaced by git config get --all --show-names --url= However, --url cannot be combined with --all, and this command fails in practice. Update the replacement to use only --url, which matches the actual behavior of --get-urlmatch. Signed-off-by: Pushkar Singh Signed-off-by: Junio C Hamano --- Documentation/git-config.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-config.adoc b/Documentation/git-config.adoc index 936e0c5130fe7d..4602be75508e4f 100644 --- a/Documentation/git-config.adoc +++ b/Documentation/git-config.adoc @@ -317,7 +317,7 @@ recommended to migrate to the new syntax. Replaced by `git config get --all --show-names --regexp `. --get-urlmatch :: - Replaced by `git config get --all --show-names --url= `. + Replaced by `git config get --url= `. --get-color []:: Replaced by `git config get --type=color [--default=] `. From 4eb105c11970747e8b78bd0275ff9ae431d58d1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Thu, 15 Jan 2026 23:01:25 +0100 Subject: [PATCH 19/20] cocci: remove obsolete the_repository rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 035c7de9e9e (cocci: apply the "revision.h" part of "the_repository.pending", 2023-03-28) removed the last of the repo-less functions and macros mentioned in the_repository.cocci at the time. No stragglers appeared since then. Remove the applied rules now that they have outlived their usefulness. Also add a reminder to eventually remove the just added rules for tree.h. Suggested-by: Patrick Steinhardt Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- contrib/coccinelle/the_repository.cocci | 119 +----------------------- 1 file changed, 2 insertions(+), 117 deletions(-) diff --git a/contrib/coccinelle/the_repository.cocci b/contrib/coccinelle/the_repository.cocci index a1f6f5ac06c253..f1129f79859cee 100644 --- a/contrib/coccinelle/the_repository.cocci +++ b/contrib/coccinelle/the_repository.cocci @@ -2,123 +2,8 @@ @@ @@ ( -// cache.h -- get_oid -+ repo_get_oid -| -- get_oid_commit -+ repo_get_oid_commit -| -- get_oid_committish -+ repo_get_oid_committish -| -- get_oid_tree -+ repo_get_oid_tree -| -- get_oid_treeish -+ repo_get_oid_treeish -| -- get_oid_blob -+ repo_get_oid_blob -| -- get_oid_mb -+ repo_get_oid_mb -| -- find_unique_abbrev -+ repo_find_unique_abbrev -| -- find_unique_abbrev_r -+ repo_find_unique_abbrev_r -| -- for_each_abbrev -+ repo_for_each_abbrev -| -- interpret_branch_name -+ repo_interpret_branch_name -| -- peel_to_type -+ repo_peel_to_type -// commit-reach.h -| -- get_merge_bases -+ repo_get_merge_bases -| -- get_merge_bases_many -+ repo_get_merge_bases_many -| -- get_merge_bases_many_dirty -+ repo_get_merge_bases_many_dirty -| -- in_merge_bases -+ repo_in_merge_bases -| -- in_merge_bases_many -+ repo_in_merge_bases_many -// commit.h -| -- parse_commit_internal -+ repo_parse_commit_internal -| -- parse_commit -+ repo_parse_commit -| -- get_commit_buffer -+ repo_get_commit_buffer -| -- unuse_commit_buffer -+ repo_unuse_commit_buffer -| -- logmsg_reencode -+ repo_logmsg_reencode -| -- get_commit_tree -+ repo_get_commit_tree -// diff.h -| -- diff_setup -+ repo_diff_setup -// odb.h -| -- read_object_file -+ repo_read_object_file -| -- has_object_file -+ repo_has_object_file -| -- has_object_file_with_flags -+ repo_has_object_file_with_flags -// pretty.h -| -- format_commit_message -+ repo_format_commit_message -// packfile.h -| -- approximate_object_count -+ repo_approximate_object_count -// promisor-remote.h -| -- promisor_remote_reinit -+ repo_promisor_remote_reinit -| -- promisor_remote_find -+ repo_promisor_remote_find -| -- has_promisor_remote -+ repo_has_promisor_remote -// refs.h -| -- dwim_ref -+ repo_dwim_ref -// rerere.h -| -- rerere -+ repo_rerere -// revision.h -| -- init_revisions -+ repo_init_revisions -// tree.h -| +// TODO: remove the rules below and the macros from tree.h after the +// next Git release. - parse_tree + repo_parse_tree | From 1faf5b085a171f9ba9a6d7a446e0de16acccb1dc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 21 Jan 2026 13:58:08 -0800 Subject: [PATCH 20/20] A few on top of -rc1 Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.53.0.adoc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/RelNotes/2.53.0.adoc b/Documentation/RelNotes/2.53.0.adoc index 7d9ca507f1730b..be8df254774caa 100644 --- a/Documentation/RelNotes/2.53.0.adoc +++ b/Documentation/RelNotes/2.53.0.adoc @@ -110,6 +110,10 @@ Performance, Internal Implementation, Development Support etc. * Further preparation to upstream symbolic link support on Windows. + * Remove implicit reliance on the_repository global in the APIs + around tree objects and make it explicit which repository to work + in. + Fixes since v2.52 ----------------- @@ -280,6 +284,12 @@ Fixes since v2.52 terminating newline, which has been corrected. (merge a8227ae8d5 kt/http-backend-errors later to maint). + * "git repack --geometric" did not work with promisor packs, which + has been corrected. + + * The logic that avoids reusing MIDX files with a wrong checksum was + broken, which has been corrected. + * Other code cleanup, docfix, build fix, etc. (merge 46207a54cc qj/doc-http-bad-want-response later to maint). (merge df90eccd93 kh/doc-commit-extra-references later to maint). @@ -311,3 +321,4 @@ Fixes since v2.52 (merge 3f051fc9c9 kh/doc-patch-id later to maint). (merge 555c8464e5 je/doc-reset later to maint). (merge 220f888d7e ps/t1410-cleanup later to maint). + (merge 5814b04c02 ps/config-doc-get-urlmatch-fix later to maint).