Merge branch 'jk/tree-name-and-depth-limit'

We now limit depth of the tree objects and maximum length of
pathnames recorded in tree objects.

* jk/tree-name-and-depth-limit:
  lower core.maxTreeDepth default to 2048
  tree-diff: respect max_allowed_tree_depth
  list-objects: respect max_allowed_tree_depth
  read_tree(): respect max_allowed_tree_depth
  traverse_trees(): respect max_allowed_tree_depth
  add core.maxTreeDepth config
  fsck: detect very large tree pathnames
  tree-walk: rename "error" variable
  tree-walk: drop MAX_TRAVERSE_TREES macro
  tree-walk: reduce stack size for recursive functions
This commit is contained in:
Junio C Hamano
2023-09-14 11:16:59 -07:00
19 changed files with 201 additions and 25 deletions

View File

@ -736,3 +736,9 @@ core.abbrev::
If set to "no", no abbreviation is made and the object names If set to "no", no abbreviation is made and the object names
are shown in their full length. are shown in their full length.
The minimum length is 4. The minimum length is 4.
core.maxTreeDepth::
The maximum depth Git is willing to recurse while traversing a
tree (e.g., "a/b/cde/f" has a depth of 4). This is a fail-safe
to allow Git to abort cleanly, and should not generally need to
be adjusted. The default is 4096.

View File

@ -103,6 +103,13 @@
`hasDotgit`:: `hasDotgit`::
(WARN) A tree contains an entry named `.git`. (WARN) A tree contains an entry named `.git`.
`largePathname`::
(WARN) A tree contains an entry with a very long path name. If
the value of `fsck.largePathname` contains a colon, that value
is used as the maximum allowable length (e.g., "warn:10" would
complain about any path component of 11 or more bytes). The
default value is 4096.
`mailmapSymlink`:: `mailmapSymlink`::
(INFO) `.mailmap` is a symlink. (INFO) `.mailmap` is a symlink.

View File

@ -1801,6 +1801,11 @@ static int git_default_core_config(const char *var, const char *value,
return 0; 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.txt. */ /* Add other config variables here and to Documentation/config.txt. */
return platform_core_config(var, value, ctx, cb); return platform_core_config(var, value, ctx, cb);
} }

View File

@ -81,6 +81,7 @@ int merge_log_config = -1;
int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */ int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
unsigned long pack_size_limit_cfg; unsigned long pack_size_limit_cfg;
enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET; enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET;
int max_allowed_tree_depth = 2048;
#ifndef PROTECT_HFS_DEFAULT #ifndef PROTECT_HFS_DEFAULT
#define PROTECT_HFS_DEFAULT 0 #define PROTECT_HFS_DEFAULT 0

View File

@ -132,6 +132,7 @@ extern size_t packed_git_limit;
extern size_t delta_base_cache_limit; extern size_t delta_base_cache_limit;
extern unsigned long big_file_threshold; extern unsigned long big_file_threshold;
extern unsigned long pack_size_limit_cfg; extern unsigned long pack_size_limit_cfg;
extern int max_allowed_tree_depth;
/* /*
* Accessors for the core.sharedrepository config which lazy-load the value * Accessors for the core.sharedrepository config which lazy-load the value

24
fsck.c
View File

@ -24,6 +24,8 @@
#include "credential.h" #include "credential.h"
#include "help.h" #include "help.h"
static ssize_t max_tree_entry_len = 4096;
#define STR(x) #x #define STR(x) #x
#define MSG_ID(id, msg_type) { STR(id), NULL, NULL, FSCK_##msg_type }, #define MSG_ID(id, msg_type) { STR(id), NULL, NULL, FSCK_##msg_type },
static struct { static struct {
@ -154,15 +156,29 @@ void fsck_set_msg_type(struct fsck_options *options,
const char *msg_id_str, const char *msg_type_str) const char *msg_id_str, const char *msg_type_str)
{ {
int msg_id = parse_msg_id(msg_id_str); int msg_id = parse_msg_id(msg_id_str);
enum fsck_msg_type msg_type = parse_msg_type(msg_type_str); char *to_free = NULL;
enum fsck_msg_type msg_type;
if (msg_id < 0) if (msg_id < 0)
die("Unhandled message id: %s", msg_id_str); die("Unhandled message id: %s", msg_id_str);
if (msg_id == FSCK_MSG_LARGE_PATHNAME) {
const char *colon = strchr(msg_type_str, ':');
if (colon) {
msg_type_str = to_free =
xmemdupz(msg_type_str, colon - msg_type_str);
colon++;
if (!git_parse_ssize_t(colon, &max_tree_entry_len))
die("unable to parse max tree entry len: %s", colon);
}
}
msg_type = parse_msg_type(msg_type_str);
if (msg_type != FSCK_ERROR && msg_id_info[msg_id].msg_type == FSCK_FATAL) if (msg_type != FSCK_ERROR && msg_id_info[msg_id].msg_type == FSCK_FATAL)
die("Cannot demote %s to %s", msg_id_str, msg_type_str); die("Cannot demote %s to %s", msg_id_str, msg_type_str);
fsck_set_msg_type_from_ids(options, msg_id, msg_type); fsck_set_msg_type_from_ids(options, msg_id, msg_type);
free(to_free);
} }
void fsck_set_msg_types(struct fsck_options *options, const char *values) void fsck_set_msg_types(struct fsck_options *options, const char *values)
@ -578,6 +594,7 @@ static int fsck_tree(const struct object_id *tree_oid,
int has_bad_modes = 0; int has_bad_modes = 0;
int has_dup_entries = 0; int has_dup_entries = 0;
int not_properly_sorted = 0; int not_properly_sorted = 0;
int has_large_name = 0;
struct tree_desc desc; struct tree_desc desc;
unsigned o_mode; unsigned o_mode;
const char *o_name; const char *o_name;
@ -607,6 +624,7 @@ static int fsck_tree(const struct object_id *tree_oid,
has_dotdot |= !strcmp(name, ".."); has_dotdot |= !strcmp(name, "..");
has_dotgit |= is_hfs_dotgit(name) || is_ntfs_dotgit(name); has_dotgit |= is_hfs_dotgit(name) || is_ntfs_dotgit(name);
has_zero_pad |= *(char *)desc.buffer == '0'; has_zero_pad |= *(char *)desc.buffer == '0';
has_large_name |= tree_entry_len(&desc.entry) > max_tree_entry_len;
if (is_hfs_dotgitmodules(name) || is_ntfs_dotgitmodules(name)) { if (is_hfs_dotgitmodules(name) || is_ntfs_dotgitmodules(name)) {
if (!S_ISLNK(mode)) if (!S_ISLNK(mode))
@ -749,6 +767,10 @@ static int fsck_tree(const struct object_id *tree_oid,
retval += report(options, tree_oid, OBJ_TREE, retval += report(options, tree_oid, OBJ_TREE,
FSCK_MSG_TREE_NOT_SORTED, FSCK_MSG_TREE_NOT_SORTED,
"not properly sorted"); "not properly sorted");
if (has_large_name)
retval += report(options, tree_oid, OBJ_TREE,
FSCK_MSG_LARGE_PATHNAME,
"contains excessively large pathname");
return retval; return retval;
} }

1
fsck.h
View File

@ -73,6 +73,7 @@ enum fsck_msg_type {
FUNC(NULL_SHA1, WARN) \ FUNC(NULL_SHA1, WARN) \
FUNC(ZERO_PADDED_FILEMODE, WARN) \ FUNC(ZERO_PADDED_FILEMODE, WARN) \
FUNC(NUL_IN_COMMIT, WARN) \ FUNC(NUL_IN_COMMIT, WARN) \
FUNC(LARGE_PATHNAME, WARN) \
/* infos (reported as warnings, but ignored by default) */ \ /* infos (reported as warnings, but ignored by default) */ \
FUNC(BAD_FILEMODE, INFO) \ FUNC(BAD_FILEMODE, INFO) \
FUNC(GITMODULES_PARSE, INFO) \ FUNC(GITMODULES_PARSE, INFO) \

View File

@ -14,6 +14,7 @@
#include "packfile.h" #include "packfile.h"
#include "object-store-ll.h" #include "object-store-ll.h"
#include "trace.h" #include "trace.h"
#include "environment.h"
struct traversal_context { struct traversal_context {
struct rev_info *revs; struct rev_info *revs;
@ -21,6 +22,7 @@ struct traversal_context {
show_commit_fn show_commit; show_commit_fn show_commit;
void *show_data; void *show_data;
struct filter *filter; struct filter *filter;
int depth;
}; };
static void show_commit(struct traversal_context *ctx, static void show_commit(struct traversal_context *ctx,
@ -118,7 +120,9 @@ static void process_tree_contents(struct traversal_context *ctx,
entry.path, oid_to_hex(&tree->object.oid)); entry.path, oid_to_hex(&tree->object.oid));
} }
t->object.flags |= NOT_USER_GIVEN; t->object.flags |= NOT_USER_GIVEN;
ctx->depth++;
process_tree(ctx, t, base, entry.path); process_tree(ctx, t, base, entry.path);
ctx->depth--;
} }
else if (S_ISGITLINK(entry.mode)) else if (S_ISGITLINK(entry.mode))
; /* ignore gitlink */ ; /* ignore gitlink */
@ -156,6 +160,9 @@ static void process_tree(struct traversal_context *ctx,
!revs->include_check_obj(&tree->object, revs->include_check_data)) !revs->include_check_obj(&tree->object, revs->include_check_data))
return; return;
if (ctx->depth > max_allowed_tree_depth)
die("exceeded maximum allowed tree depth");
failed_parse = parse_tree_gently(tree, 1); failed_parse = parse_tree_gently(tree, 1);
if (failed_parse) { if (failed_parse) {
if (revs->ignore_missing_links) if (revs->ignore_missing_links)
@ -349,6 +356,7 @@ static void traverse_non_commits(struct traversal_context *ctx,
if (!path) if (!path)
path = ""; path = "";
if (obj->type == OBJ_TREE) { if (obj->type == OBJ_TREE) {
ctx->depth = 0;
process_tree(ctx, (struct tree *)obj, base, path); process_tree(ctx, (struct tree *)obj, base, path);
continue; continue;
} }

View File

@ -391,7 +391,7 @@ void expand_index(struct index_state *istate, struct pattern_list *pl)
strbuf_setlen(&base, 0); strbuf_setlen(&base, 0);
strbuf_add(&base, ce->name, strlen(ce->name)); strbuf_add(&base, ce->name, strlen(ce->name));
read_tree_at(istate->repo, tree, &base, &ps, read_tree_at(istate->repo, tree, &base, 0, &ps,
add_path_to_index, &ctx); add_path_to_index, &ctx);
/* free directory entries. full entries are re-used */ /* free directory entries. full entries are re-used */

View File

@ -589,6 +589,16 @@ test_expect_success 'fsck notices submodule entry pointing to null sha1' '
) )
' '
test_expect_success 'fsck notices excessively large tree entry name' '
git init large-name &&
(
cd large-name &&
test_commit a-long-name &&
git -c fsck.largePathname=warn:10 fsck 2>out &&
grep "warning.*large pathname" out
)
'
while read name path pretty; do while read name path pretty; do
while read mode type; do while read mode type; do
: ${pretty:=$path} : ${pretty:=$path}

93
t/t6700-tree-depth.sh Executable file
View File

@ -0,0 +1,93 @@
#!/bin/sh
test_description='handling of deep trees in various commands'
. ./test-lib.sh
# We'll test against two depths here: a small one that will let us check the
# behavior of the config setting easily, and a large one that should be
# forbidden by default. Testing the default depth will let us know whether our
# default is enough to prevent segfaults on systems that run the tests.
small_depth=50
big_depth=4100
small_ok="-c core.maxtreedepth=$small_depth"
small_no="-c core.maxtreedepth=$((small_depth-1))"
# usage: mkdeep <name> <depth>
# Create a tag <name> containing a file whose path has depth <depth>.
#
# We'll use fast-import here for two reasons:
#
# 1. It's faster than creating $big_depth tree objects.
#
# 2. As we tighten tree limits, it's more likely to allow large sizes
# than trying to stuff a deep path into the index.
mkdeep () {
{
echo "commit refs/tags/$1" &&
echo "committer foo <foo@example.com> 1234 -0000" &&
echo "data <<EOF" &&
echo "the commit message" &&
echo "EOF" &&
printf 'M 100644 inline ' &&
i=0 &&
while test $i -lt $2
do
printf 'a/'
i=$((i+1))
done &&
echo "file" &&
echo "data <<EOF" &&
echo "the file contents" &&
echo "EOF" &&
echo
} | git fast-import
}
test_expect_success 'create small tree' '
mkdeep small $small_depth
'
test_expect_success 'create big tree' '
mkdeep big $big_depth
'
test_expect_success 'limit recursion of git-archive' '
git $small_ok archive small >/dev/null &&
test_must_fail git $small_no archive small >/dev/null
'
test_expect_success 'default limit for git-archive fails gracefully' '
test_must_fail git archive big >/dev/null
'
test_expect_success 'limit recursion of ls-tree -r' '
git $small_ok ls-tree -r small &&
test_must_fail git $small_no ls-tree -r small
'
test_expect_success 'default limit for ls-tree fails gracefully' '
test_must_fail git ls-tree -r big >/dev/null
'
test_expect_success 'limit recursion of rev-list --objects' '
git $small_ok rev-list --objects small >/dev/null &&
test_must_fail git $small_no rev-list --objects small >/dev/null
'
test_expect_success 'default limit for rev-list fails gracefully' '
test_must_fail git rev-list --objects big >/dev/null
'
test_expect_success 'limit recursion of diff-tree -r' '
git $small_ok diff-tree -r $EMPTY_TREE small &&
test_must_fail git $small_no diff-tree -r $EMPTY_TREE small
'
test_expect_success 'default limit for diff-tree fails gracefully' '
test_must_fail git diff-tree -r $EMPTY_TREE big
'
test_done

View File

@ -7,6 +7,7 @@
#include "hash.h" #include "hash.h"
#include "tree.h" #include "tree.h"
#include "tree-walk.h" #include "tree-walk.h"
#include "environment.h"
/* /*
* Some mode bits are also used internally for computations. * Some mode bits are also used internally for computations.
@ -45,7 +46,8 @@
static struct combine_diff_path *ll_diff_tree_paths( static struct combine_diff_path *ll_diff_tree_paths(
struct combine_diff_path *p, const struct object_id *oid, struct combine_diff_path *p, const struct object_id *oid,
const struct object_id **parents_oid, int nparent, const struct object_id **parents_oid, int nparent,
struct strbuf *base, struct diff_options *opt); struct strbuf *base, struct diff_options *opt,
int depth);
static void ll_diff_tree_oid(const struct object_id *old_oid, static void ll_diff_tree_oid(const struct object_id *old_oid,
const struct object_id *new_oid, const struct object_id *new_oid,
struct strbuf *base, struct diff_options *opt); struct strbuf *base, struct diff_options *opt);
@ -196,7 +198,7 @@ static struct combine_diff_path *path_appendnew(struct combine_diff_path *last,
static struct combine_diff_path *emit_path(struct combine_diff_path *p, static struct combine_diff_path *emit_path(struct combine_diff_path *p,
struct strbuf *base, struct diff_options *opt, int nparent, struct strbuf *base, struct diff_options *opt, int nparent,
struct tree_desc *t, struct tree_desc *tp, struct tree_desc *t, struct tree_desc *tp,
int imin) int imin, int depth)
{ {
unsigned short mode; unsigned short mode;
const char *path; const char *path;
@ -302,7 +304,8 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,
strbuf_add(base, path, pathlen); strbuf_add(base, path, pathlen);
strbuf_addch(base, '/'); strbuf_addch(base, '/');
p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt); p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt,
depth + 1);
FAST_ARRAY_FREE(parents_oid, nparent); FAST_ARRAY_FREE(parents_oid, nparent);
} }
@ -423,12 +426,16 @@ static inline void update_tp_entries(struct tree_desc *tp, int nparent)
static struct combine_diff_path *ll_diff_tree_paths( static struct combine_diff_path *ll_diff_tree_paths(
struct combine_diff_path *p, const struct object_id *oid, struct combine_diff_path *p, const struct object_id *oid,
const struct object_id **parents_oid, int nparent, const struct object_id **parents_oid, int nparent,
struct strbuf *base, struct diff_options *opt) struct strbuf *base, struct diff_options *opt,
int depth)
{ {
struct tree_desc t, *tp; struct tree_desc t, *tp;
void *ttree, **tptree; void *ttree, **tptree;
int i; int i;
if (depth > max_allowed_tree_depth)
die("exceeded maximum allowed tree depth");
FAST_ARRAY_ALLOC(tp, nparent); FAST_ARRAY_ALLOC(tp, nparent);
FAST_ARRAY_ALLOC(tptree, nparent); FAST_ARRAY_ALLOC(tptree, nparent);
@ -522,7 +529,7 @@ static struct combine_diff_path *ll_diff_tree_paths(
/* D += {δ(t,pi) if pi=p[imin]; "+a" if pi > p[imin]} */ /* D += {δ(t,pi) if pi=p[imin]; "+a" if pi > p[imin]} */
p = emit_path(p, base, opt, nparent, p = emit_path(p, base, opt, nparent,
&t, tp, imin); &t, tp, imin, depth);
skip_emit_t_tp: skip_emit_t_tp:
/* t↓, ∀ pi=p[imin] pi↓ */ /* t↓, ∀ pi=p[imin] pi↓ */
@ -534,7 +541,7 @@ static struct combine_diff_path *ll_diff_tree_paths(
else if (cmp < 0) { else if (cmp < 0) {
/* D += "+t" */ /* D += "+t" */
p = emit_path(p, base, opt, nparent, p = emit_path(p, base, opt, nparent,
&t, /*tp=*/NULL, -1); &t, /*tp=*/NULL, -1, depth);
/* t↓ */ /* t↓ */
update_tree_entry(&t); update_tree_entry(&t);
@ -550,7 +557,7 @@ static struct combine_diff_path *ll_diff_tree_paths(
} }
p = emit_path(p, base, opt, nparent, p = emit_path(p, base, opt, nparent,
/*t=*/NULL, tp, imin); /*t=*/NULL, tp, imin, depth);
skip_emit_tp: skip_emit_tp:
/* ∀ pi=p[imin] pi↓ */ /* ∀ pi=p[imin] pi↓ */
@ -572,7 +579,7 @@ struct combine_diff_path *diff_tree_paths(
const struct object_id **parents_oid, int nparent, const struct object_id **parents_oid, int nparent,
struct strbuf *base, struct diff_options *opt) struct strbuf *base, struct diff_options *opt)
{ {
p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt); p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt, 0);
/* /*
* free pre-allocated last element, if any * free pre-allocated last element, if any

View File

@ -9,6 +9,7 @@
#include "tree.h" #include "tree.h"
#include "pathspec.h" #include "pathspec.h"
#include "json-writer.h" #include "json-writer.h"
#include "environment.h"
static const char *get_mode(const char *str, unsigned int *modep) static const char *get_mode(const char *str, unsigned int *modep)
{ {
@ -441,22 +442,25 @@ int traverse_trees(struct index_state *istate,
int n, struct tree_desc *t, int n, struct tree_desc *t,
struct traverse_info *info) struct traverse_info *info)
{ {
int error = 0; int ret = 0;
struct name_entry entry[MAX_TRAVERSE_TREES]; struct name_entry *entry;
int i; int i;
struct tree_desc_x tx[ARRAY_SIZE(entry)]; struct tree_desc_x *tx;
struct strbuf base = STRBUF_INIT; struct strbuf base = STRBUF_INIT;
int interesting = 1; int interesting = 1;
char *traverse_path; char *traverse_path;
if (traverse_trees_cur_depth > max_allowed_tree_depth)
return error("exceeded maximum allowed tree depth");
traverse_trees_count++; traverse_trees_count++;
traverse_trees_cur_depth++; traverse_trees_cur_depth++;
if (traverse_trees_cur_depth > traverse_trees_max_depth) if (traverse_trees_cur_depth > traverse_trees_max_depth)
traverse_trees_max_depth = traverse_trees_cur_depth; traverse_trees_max_depth = traverse_trees_cur_depth;
if (n >= ARRAY_SIZE(entry)) ALLOC_ARRAY(entry, n);
BUG("traverse_trees() called with too many trees (%d)", n); ALLOC_ARRAY(tx, n);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
tx[i].d = t[i]; tx[i].d = t[i];
@ -539,7 +543,7 @@ int traverse_trees(struct index_state *istate,
if (interesting) { if (interesting) {
trees_used = info->fn(n, mask, dirmask, entry, info); trees_used = info->fn(n, mask, dirmask, entry, info);
if (trees_used < 0) { if (trees_used < 0) {
error = trees_used; ret = trees_used;
if (!info->show_all_errors) if (!info->show_all_errors)
break; break;
} }
@ -551,12 +555,14 @@ int traverse_trees(struct index_state *istate,
} }
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
free_extended_entry(tx + i); free_extended_entry(tx + i);
free(tx);
free(entry);
free(traverse_path); free(traverse_path);
info->traverse_path = NULL; info->traverse_path = NULL;
strbuf_release(&base); strbuf_release(&base);
traverse_trees_cur_depth--; traverse_trees_cur_depth--;
return error; return ret;
} }
struct dir_state { struct dir_state {

View File

@ -6,8 +6,6 @@
struct index_state; struct index_state;
struct repository; struct repository;
#define MAX_TRAVERSE_TREES 8
/** /**
* The tree walking API is used to traverse and inspect trees. * The tree walking API is used to traverse and inspect trees.
*/ */

9
tree.c
View File

@ -10,11 +10,13 @@
#include "alloc.h" #include "alloc.h"
#include "tree-walk.h" #include "tree-walk.h"
#include "repository.h" #include "repository.h"
#include "environment.h"
const char *tree_type = "tree"; const char *tree_type = "tree";
int read_tree_at(struct repository *r, int read_tree_at(struct repository *r,
struct tree *tree, struct strbuf *base, struct tree *tree, struct strbuf *base,
int depth,
const struct pathspec *pathspec, const struct pathspec *pathspec,
read_tree_fn_t fn, void *context) read_tree_fn_t fn, void *context)
{ {
@ -24,6 +26,9 @@ int read_tree_at(struct repository *r,
int len, oldlen = base->len; int len, oldlen = base->len;
enum interesting retval = entry_not_interesting; enum interesting retval = entry_not_interesting;
if (depth > max_allowed_tree_depth)
return error("exceeded maximum allowed tree depth");
if (parse_tree(tree)) if (parse_tree(tree))
return -1; return -1;
@ -74,7 +79,7 @@ int read_tree_at(struct repository *r,
strbuf_add(base, entry.path, len); strbuf_add(base, entry.path, len);
strbuf_addch(base, '/'); strbuf_addch(base, '/');
retval = read_tree_at(r, lookup_tree(r, &oid), retval = read_tree_at(r, lookup_tree(r, &oid),
base, pathspec, base, depth + 1, pathspec,
fn, context); fn, context);
strbuf_setlen(base, oldlen); strbuf_setlen(base, oldlen);
if (retval) if (retval)
@ -89,7 +94,7 @@ int read_tree(struct repository *r,
read_tree_fn_t fn, void *context) read_tree_fn_t fn, void *context)
{ {
struct strbuf sb = STRBUF_INIT; struct strbuf sb = STRBUF_INIT;
int ret = read_tree_at(r, tree, &sb, pathspec, fn, context); int ret = read_tree_at(r, tree, &sb, 0, pathspec, fn, context);
strbuf_release(&sb); strbuf_release(&sb);
return ret; return ret;
} }

1
tree.h
View File

@ -44,6 +44,7 @@ typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const c
int read_tree_at(struct repository *r, int read_tree_at(struct repository *r,
struct tree *tree, struct strbuf *base, struct tree *tree, struct strbuf *base,
int depth,
const struct pathspec *pathspec, const struct pathspec *pathspec,
read_tree_fn_t fn, void *context); read_tree_fn_t fn, void *context);

View File

@ -864,8 +864,8 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
struct unpack_trees_options *o = info->data; struct unpack_trees_options *o = info->data;
int i, ret, bottom; int i, ret, bottom;
int nr_buf = 0; int nr_buf = 0;
struct tree_desc t[MAX_UNPACK_TREES]; struct tree_desc *t;
void *buf[MAX_UNPACK_TREES]; void **buf;
struct traverse_info newinfo; struct traverse_info newinfo;
struct name_entry *p; struct name_entry *p;
int nr_entries; int nr_entries;
@ -902,6 +902,9 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
newinfo.pathlen = st_add3(newinfo.pathlen, tree_entry_len(p), 1); newinfo.pathlen = st_add3(newinfo.pathlen, tree_entry_len(p), 1);
newinfo.df_conflicts |= df_conflicts; newinfo.df_conflicts |= df_conflicts;
ALLOC_ARRAY(t, n);
ALLOC_ARRAY(buf, n);
/* /*
* Fetch the tree from the ODB for each peer directory in the * Fetch the tree from the ODB for each peer directory in the
* n commits. * n commits.
@ -937,6 +940,8 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
for (i = 0; i < nr_buf; i++) for (i = 0; i < nr_buf; i++)
free(buf[i]); free(buf[i]);
free(buf);
free(t);
return ret; return ret;
} }

View File

@ -7,7 +7,7 @@
#include "string-list.h" #include "string-list.h"
#include "tree-walk.h" #include "tree-walk.h"
#define MAX_UNPACK_TREES MAX_TRAVERSE_TREES #define MAX_UNPACK_TREES 8
struct cache_entry; struct cache_entry;
struct unpack_trees_options; struct unpack_trees_options;

View File

@ -739,7 +739,7 @@ static void wt_status_collect_changes_initial(struct wt_status *s)
ps.max_depth = -1; ps.max_depth = -1;
strbuf_add(&base, ce->name, ce->ce_namelen); strbuf_add(&base, ce->name, ce->ce_namelen);
read_tree_at(istate->repo, tree, &base, &ps, read_tree_at(istate->repo, tree, &base, 0, &ps,
add_file_to_list, s); add_file_to_list, s);
continue; continue;
} }