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:
@ -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.
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
5
config.c
5
config.c
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
24
fsck.c
@ -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
1
fsck.h
@ -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) \
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 */
|
||||||
|
@ -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
93
t/t6700-tree-depth.sh
Executable 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
|
23
tree-diff.c
23
tree-diff.c
@ -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
|
||||||
|
20
tree-walk.c
20
tree-walk.c
@ -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 {
|
||||||
|
@ -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
9
tree.c
@ -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
1
tree.h
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user