Merge branch 'nd/attr-pathspec-in-tree-walk'

The traversal over tree objects has learned to honor
":(attr:label)" pathspec match, which has been implemented only for
enumerating paths on the filesystem.

* nd/attr-pathspec-in-tree-walk:
  tree-walk: support :(attr) matching
  dir.c: move, rename and export match_attrs()
  pathspec.h: clean up "extern" in function declarations
  tree-walk.c: make tree_entry_interesting() take an index
  tree.c: make read_tree*() take 'struct repository *'
This commit is contained in:
Junio C Hamano 2019-01-14 15:29:28 -08:00
commit d6f05a435f
21 changed files with 234 additions and 108 deletions

View File

@ -404,6 +404,8 @@ these forms:
- "`!ATTR`" requires that the attribute `ATTR` be
unspecified.
+
Note that when matching against a tree object, attributes are still
obtained from working tree, not from the given tree object.
exclude;;
After a path matches any non-exclude pathspec, it will be run

View File

@ -285,7 +285,8 @@ int write_archive_entries(struct archiver_args *args,
git_attr_set_direction(GIT_ATTR_INDEX);
}
err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec,
err = read_tree_recursive(args->repo, args->tree, "",
0, 0, &args->pathspec,
queue_or_write_archive_entry,
&context);
if (err == READ_TREE_RECURSIVE)
@ -346,7 +347,8 @@ static int path_exists(struct archiver_args *args, const char *path)
ctx.args = args;
parse_pathspec(&ctx.pathspec, 0, 0, "", paths);
ctx.pathspec.recursive = 1;
ret = read_tree_recursive(args->tree, "", 0, 0, &ctx.pathspec,
ret = read_tree_recursive(args->repo, args->tree, "",
0, 0, &ctx.pathspec,
reject_entry, &ctx);
clear_pathspec(&ctx.pathspec);
return ret != 0;

View File

@ -115,7 +115,8 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)
{
read_tree_recursive(tree, "", 0, 0, pathspec, update_some, NULL);
read_tree_recursive(the_repository, tree, "", 0, 0,
pathspec, update_some, NULL);
/* update the index with the given tree's info
* for all args, expanding wildcards, and exit

View File

@ -553,7 +553,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
if (match != all_entries_interesting) {
strbuf_addstr(&name, base->buf + tn_len);
match = tree_entry_interesting(&entry, &name,
match = tree_entry_interesting(repo->index,
&entry, &name,
0, pathspec);
strbuf_setlen(&name, name_base_len);

View File

@ -641,8 +641,9 @@ int cmd_show(int argc, const char **argv, const char *prefix)
diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
name,
diff_get_color_opt(&rev.diffopt, DIFF_RESET));
read_tree_recursive((struct tree *)o, "", 0, 0, &match_all,
show_tree_object, rev.diffopt.file);
read_tree_recursive(the_repository, (struct tree *)o, "",
0, 0, &match_all, show_tree_object,
rev.diffopt.file);
rev.shown_one = 1;
break;
case OBJ_COMMIT:

View File

@ -441,7 +441,7 @@ void overlay_tree_on_index(struct index_state *istate,
PATHSPEC_PREFER_CWD, prefix, matchbuf);
} else
memset(&pathspec, 0, sizeof(pathspec));
if (read_tree(tree, 1, &pathspec, istate))
if (read_tree(the_repository, tree, 1, &pathspec, istate))
die("unable to read tree entries %s", tree_name);
for (i = 0; i < istate->cache_nr; i++) {

View File

@ -185,5 +185,6 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
tree = parse_tree_indirect(&oid);
if (!tree)
die("not a tree object");
return !!read_tree_recursive(tree, "", 0, 0, &pathspec, show_tree, NULL);
return !!read_tree_recursive(the_repository, tree, "", 0, 0,
&pathspec, show_tree, NULL);
}

View File

@ -346,7 +346,7 @@ static void merge_trees(struct tree_desc t[3], const char *base)
setup_traverse_info(&info, base);
info.fn = threeway_callback;
traverse_trees(3, t, &info);
traverse_trees(&the_index, 3, t, &info);
}
static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)

41
dir.c
View File

@ -276,44 +276,6 @@ static int do_read_blob(const struct object_id *oid, struct oid_stat *oid_stat,
#define DO_MATCH_DIRECTORY (1<<1)
#define DO_MATCH_SUBMODULE (1<<2)
static int match_attrs(const struct index_state *istate,
const char *name, int namelen,
const struct pathspec_item *item)
{
int i;
char *to_free = NULL;
if (name[namelen])
name = to_free = xmemdupz(name, namelen);
git_check_attr(istate, name, item->attr_check);
free(to_free);
for (i = 0; i < item->attr_match_nr; i++) {
const char *value;
int matched;
enum attr_match_mode match_mode;
value = item->attr_check->items[i].value;
match_mode = item->attr_match[i].match_mode;
if (ATTR_TRUE(value))
matched = (match_mode == MATCH_SET);
else if (ATTR_FALSE(value))
matched = (match_mode == MATCH_UNSET);
else if (ATTR_UNSET(value))
matched = (match_mode == MATCH_UNSPECIFIED);
else
matched = (match_mode == MATCH_VALUE &&
!strcmp(item->attr_match[i].value, value));
if (!matched)
return 0;
}
return 1;
}
/*
* Does 'match' match the given name?
* A match is found if
@ -367,7 +329,8 @@ static int match_pathspec_item(const struct index_state *istate,
strncmp(item->match, name - prefix, item->prefix))
return 0;
if (item->attr_match_nr && !match_attrs(istate, name, namelen, item))
if (item->attr_match_nr &&
!match_pathspec_attrs(istate, name, namelen, item))
return 0;
/* If the match was just the prefix, we matched */

View File

@ -114,7 +114,8 @@ static void process_tree_contents(struct traversal_context *ctx,
while (tree_entry(&desc, &entry)) {
if (match != all_entries_interesting) {
match = tree_entry_interesting(&entry, base, 0,
match = tree_entry_interesting(ctx->revs->repo->index,
&entry, base, 0,
&ctx->revs->diffopt.pathspec);
if (match == all_entries_not_interesting)
break;

View File

@ -469,7 +469,8 @@ static void get_files_dirs(struct merge_options *o, struct tree *tree)
{
struct pathspec match_all;
memset(&match_all, 0, sizeof(match_all));
read_tree_recursive(tree, "", 0, 0, &match_all, save_files_dirs, o);
read_tree_recursive(the_repository, tree, "", 0, 0,
&match_all, save_files_dirs, o);
}
static int get_tree_entry_if_blob(const struct object_id *tree,

View File

@ -659,3 +659,41 @@ void clear_pathspec(struct pathspec *pathspec)
FREE_AND_NULL(pathspec->items);
pathspec->nr = 0;
}
int match_pathspec_attrs(const struct index_state *istate,
const char *name, int namelen,
const struct pathspec_item *item)
{
int i;
char *to_free = NULL;
if (name[namelen])
name = to_free = xmemdupz(name, namelen);
git_check_attr(istate, name, item->attr_check);
free(to_free);
for (i = 0; i < item->attr_match_nr; i++) {
const char *value;
int matched;
enum attr_match_mode match_mode;
value = item->attr_check->items[i].value;
match_mode = item->attr_match[i].match_mode;
if (ATTR_TRUE(value))
matched = (match_mode == MATCH_SET);
else if (ATTR_FALSE(value))
matched = (match_mode == MATCH_UNSET);
else if (ATTR_UNSET(value))
matched = (match_mode == MATCH_UNSPECIFIED);
else
matched = (match_mode == MATCH_VALUE &&
!strcmp(item->attr_match[i].value, value));
if (!matched)
return 0;
}
return 1;
}

View File

@ -80,13 +80,13 @@ struct pathspec {
* Any arguments used are copied. It is safe for the caller to modify
* or free 'prefix' and 'args' after calling this function.
*/
extern void parse_pathspec(struct pathspec *pathspec,
unsigned magic_mask,
unsigned flags,
const char *prefix,
const char **args);
extern void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
extern void clear_pathspec(struct pathspec *);
void parse_pathspec(struct pathspec *pathspec,
unsigned magic_mask,
unsigned flags,
const char *prefix,
const char **args);
void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
void clear_pathspec(struct pathspec *);
static inline int ps_strncmp(const struct pathspec_item *item,
const char *s1, const char *s2, size_t n)
@ -106,10 +106,13 @@ static inline int ps_strcmp(const struct pathspec_item *item,
return strcmp(s1, s2);
}
extern void add_pathspec_matches_against_index(const struct pathspec *pathspec,
const struct index_state *istate,
char *seen);
extern char *find_pathspecs_matching_against_index(const struct pathspec *pathspec,
const struct index_state *istate);
void add_pathspec_matches_against_index(const struct pathspec *pathspec,
const struct index_state *istate,
char *seen);
char *find_pathspecs_matching_against_index(const struct pathspec *pathspec,
const struct index_state *istate);
int match_pathspec_attrs(const struct index_state *istate,
const char *name, int namelen,
const struct pathspec_item *item);
#endif /* PATHSPEC_H */

View File

@ -1463,6 +1463,7 @@ void repo_init_revisions(struct repository *r,
revs->abbrev = DEFAULT_ABBREV;
revs->ignore_merges = 1;
revs->simplify_history = 1;
revs->pruning.repo = r;
revs->pruning.flags.recursive = 1;
revs->pruning.flags.quick = 1;
revs->pruning.add_remove = file_add_remove;

View File

@ -31,7 +31,7 @@ test_expect_success 'setup a tree' '
mkdir sub &&
while read path
do
: >$path &&
echo content >$path &&
git add $path || return 1
done <expect &&
git commit -m "initial commit" &&
@ -48,6 +48,10 @@ test_expect_success 'pathspec with labels and non existent .gitattributes' '
test_must_be_empty actual
'
test_expect_success 'pathspec with labels and non existent .gitattributes (2)' '
test_must_fail git grep content HEAD -- ":(attr:label)"
'
test_expect_success 'setup .gitattributes' '
cat <<-\EOF >.gitattributes &&
fileA labelA
@ -74,6 +78,15 @@ test_expect_success 'check specific set attr' '
test_cmp expect actual
'
test_expect_success 'check specific set attr (2)' '
cat <<-\EOF >expect &&
HEAD:fileSetLabel
HEAD:sub/fileSetLabel
EOF
git grep -l content HEAD ":(attr:label)" >actual &&
test_cmp expect actual
'
test_expect_success 'check specific unset attr' '
cat <<-\EOF >expect &&
fileUnsetLabel
@ -83,6 +96,15 @@ test_expect_success 'check specific unset attr' '
test_cmp expect actual
'
test_expect_success 'check specific unset attr (2)' '
cat <<-\EOF >expect &&
HEAD:fileUnsetLabel
HEAD:sub/fileUnsetLabel
EOF
git grep -l content HEAD ":(attr:-label)" >actual &&
test_cmp expect actual
'
test_expect_success 'check specific value attr' '
cat <<-\EOF >expect &&
fileValue
@ -94,6 +116,16 @@ test_expect_success 'check specific value attr' '
test_must_be_empty actual
'
test_expect_success 'check specific value attr (2)' '
cat <<-\EOF >expect &&
HEAD:fileValue
HEAD:sub/fileValue
EOF
git grep -l content HEAD ":(attr:label=foo)" >actual &&
test_cmp expect actual &&
test_must_fail git grep -l content HEAD ":(attr:label=bar)"
'
test_expect_success 'check unspecified attr' '
cat <<-\EOF >expect &&
.gitattributes
@ -118,6 +150,30 @@ test_expect_success 'check unspecified attr' '
test_cmp expect actual
'
test_expect_success 'check unspecified attr (2)' '
cat <<-\EOF >expect &&
HEAD:.gitattributes
HEAD:fileA
HEAD:fileAB
HEAD:fileAC
HEAD:fileB
HEAD:fileBC
HEAD:fileC
HEAD:fileNoLabel
HEAD:fileWrongLabel
HEAD:sub/fileA
HEAD:sub/fileAB
HEAD:sub/fileAC
HEAD:sub/fileB
HEAD:sub/fileBC
HEAD:sub/fileC
HEAD:sub/fileNoLabel
HEAD:sub/fileWrongLabel
EOF
git grep -l ^ HEAD ":(attr:!label)" >actual &&
test_cmp expect actual
'
test_expect_success 'check multiple unspecified attr' '
cat <<-\EOF >expect &&
.gitattributes

View File

@ -299,7 +299,8 @@ static void skip_uninteresting(struct tree_desc *t, struct strbuf *base,
enum interesting match;
while (t->size) {
match = tree_entry_interesting(&t->entry, base, 0, &opt->pathspec);
match = tree_entry_interesting(opt->repo->index, &t->entry,
base, 0, &opt->pathspec);
if (match) {
if (match == all_entries_not_interesting)
t->size = 0;

View File

@ -365,7 +365,8 @@ static void free_extended_entry(struct tree_desc_x *t)
}
}
static inline int prune_traversal(struct name_entry *e,
static inline int prune_traversal(struct index_state *istate,
struct name_entry *e,
struct traverse_info *info,
struct strbuf *base,
int still_interesting)
@ -374,10 +375,13 @@ static inline int prune_traversal(struct name_entry *e,
return 2;
if (still_interesting < 0)
return still_interesting;
return tree_entry_interesting(e, base, 0, info->pathspec);
return tree_entry_interesting(istate, e, base,
0, info->pathspec);
}
int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
int traverse_trees(struct index_state *istate,
int n, struct tree_desc *t,
struct traverse_info *info)
{
int error = 0;
struct name_entry *entry = xmalloc(n*sizeof(*entry));
@ -461,7 +465,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
}
if (!mask)
break;
interesting = prune_traversal(e, info, &base, interesting);
interesting = prune_traversal(istate, e, info, &base, interesting);
if (interesting < 0)
break;
if (interesting) {
@ -928,7 +932,8 @@ static int match_wildcard_base(const struct pathspec_item *item,
* Pre-condition: either baselen == base_offset (i.e. empty path)
* or base[baselen-1] == '/' (i.e. with trailing slash).
*/
static enum interesting do_match(const struct name_entry *entry,
static enum interesting do_match(struct index_state *istate,
const struct name_entry *entry,
struct strbuf *base, int base_offset,
const struct pathspec *ps,
int exclude)
@ -944,7 +949,8 @@ static enum interesting do_match(const struct name_entry *entry,
PATHSPEC_LITERAL |
PATHSPEC_GLOB |
PATHSPEC_ICASE |
PATHSPEC_EXCLUDE);
PATHSPEC_EXCLUDE |
PATHSPEC_ATTR);
if (!ps->nr) {
if (!ps->recursive ||
@ -976,14 +982,20 @@ static enum interesting do_match(const struct name_entry *entry,
if (!ps->recursive ||
!(ps->magic & PATHSPEC_MAXDEPTH) ||
ps->max_depth == -1)
return all_entries_interesting;
ps->max_depth == -1) {
if (!item->attr_match_nr)
return all_entries_interesting;
else
goto interesting;
}
return within_depth(base_str + matchlen + 1,
baselen - matchlen - 1,
!!S_ISDIR(entry->mode),
ps->max_depth) ?
entry_interesting : entry_not_interesting;
if (within_depth(base_str + matchlen + 1,
baselen - matchlen - 1,
!!S_ISDIR(entry->mode),
ps->max_depth))
goto interesting;
else
return entry_not_interesting;
}
/* Either there must be no base, or the base must match. */
@ -991,12 +1003,12 @@ static enum interesting do_match(const struct name_entry *entry,
if (match_entry(item, entry, pathlen,
match + baselen, matchlen - baselen,
&never_interesting))
return entry_interesting;
goto interesting;
if (item->nowildcard_len < item->len) {
if (!git_fnmatch(item, match + baselen, entry->path,
item->nowildcard_len - baselen))
return entry_interesting;
goto interesting;
/*
* Match all directories. We'll try to
@ -1017,7 +1029,7 @@ static enum interesting do_match(const struct name_entry *entry,
!ps_strncmp(item, match + baselen,
entry->path,
item->nowildcard_len - baselen))
return entry_interesting;
goto interesting;
}
continue;
@ -1052,7 +1064,7 @@ match_wildcards:
if (!git_fnmatch(item, match, base->buf + base_offset,
item->nowildcard_len)) {
strbuf_setlen(base, base_offset + baselen);
return entry_interesting;
goto interesting;
}
/*
@ -1066,7 +1078,7 @@ match_wildcards:
!ps_strncmp(item, match, base->buf + base_offset,
item->nowildcard_len)) {
strbuf_setlen(base, base_offset + baselen);
return entry_interesting;
goto interesting;
}
strbuf_setlen(base, base_offset + baselen);
@ -1080,6 +1092,38 @@ match_wildcards:
*/
if (ps->recursive && S_ISDIR(entry->mode))
return entry_interesting;
continue;
interesting:
if (item->attr_match_nr) {
int ret;
/*
* Must not return all_entries_not_interesting
* prematurely. We do not know if all entries do not
* match some attributes with current attr API.
*/
never_interesting = entry_not_interesting;
/*
* Consider all directories interesting (because some
* of those files inside may match some attributes
* even though the parent dir does not)
*
* FIXME: attributes _can_ match directories and we
* can probably return all_entries_interesting or
* all_entries_not_interesting here if matched.
*/
if (S_ISDIR(entry->mode))
return entry_interesting;
strbuf_add(base, entry->path, pathlen);
ret = match_pathspec_attrs(istate, base->buf + base_offset,
base->len - base_offset, item);
strbuf_setlen(base, base_offset + baselen);
if (!ret)
continue;
}
return entry_interesting;
}
return never_interesting; /* No matches */
}
@ -1090,12 +1134,13 @@ match_wildcards:
* Pre-condition: either baselen == base_offset (i.e. empty path)
* or base[baselen-1] == '/' (i.e. with trailing slash).
*/
enum interesting tree_entry_interesting(const struct name_entry *entry,
enum interesting tree_entry_interesting(struct index_state *istate,
const struct name_entry *entry,
struct strbuf *base, int base_offset,
const struct pathspec *ps)
{
enum interesting positive, negative;
positive = do_match(entry, base, base_offset, ps, 0);
positive = do_match(istate, entry, base, base_offset, ps, 0);
/*
* case | entry | positive | negative | result
@ -1132,7 +1177,7 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
positive <= entry_not_interesting) /* #1, #2, #11, #12 */
return positive;
negative = do_match(entry, base, base_offset, ps, 1);
negative = do_match(istate, entry, base, base_offset, ps, 1);
/* #8, #18 */
if (positive == all_entries_interesting &&

View File

@ -1,6 +1,7 @@
#ifndef TREE_WALK_H
#define TREE_WALK_H
struct index_state;
struct strbuf;
struct name_entry {
@ -48,7 +49,7 @@ void *fill_tree_descriptor(struct tree_desc *desc, const struct object_id *oid);
struct traverse_info;
typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info);
int traverse_trees(struct index_state *istate, int n, struct tree_desc *t, struct traverse_info *info);
enum follow_symlinks_result {
FOUND = 0, /* This includes out-of-tree links */
@ -98,8 +99,9 @@ enum interesting {
all_entries_interesting = 2 /* yes, and all subsequent entries will be */
};
extern enum interesting tree_entry_interesting(const struct name_entry *,
struct strbuf *, int,
const struct pathspec *ps);
enum interesting tree_entry_interesting(struct index_state *istate,
const struct name_entry *,
struct strbuf *, int,
const struct pathspec *ps);
#endif

21
tree.c
View File

@ -60,7 +60,8 @@ static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base
ADD_CACHE_JUST_APPEND);
}
static int read_tree_1(struct tree *tree, struct strbuf *base,
static int read_tree_1(struct repository *r,
struct tree *tree, struct strbuf *base,
int stage, const struct pathspec *pathspec,
read_tree_fn_t fn, void *context)
{
@ -77,7 +78,8 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
while (tree_entry(&desc, &entry)) {
if (retval != all_entries_interesting) {
retval = tree_entry_interesting(&entry, base, 0, pathspec);
retval = tree_entry_interesting(r->index, &entry,
base, 0, pathspec);
if (retval == all_entries_not_interesting)
break;
if (retval == entry_not_interesting)
@ -99,7 +101,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
else if (S_ISGITLINK(entry.mode)) {
struct commit *commit;
commit = lookup_commit(the_repository, entry.oid);
commit = lookup_commit(r, entry.oid);
if (!commit)
die("Commit %s in submodule path %s%s not found",
oid_to_hex(entry.oid),
@ -118,7 +120,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
len = tree_entry_len(&entry);
strbuf_add(base, entry.path, len);
strbuf_addch(base, '/');
retval = read_tree_1(lookup_tree(the_repository, &oid),
retval = read_tree_1(r, lookup_tree(r, &oid),
base, stage, pathspec,
fn, context);
strbuf_setlen(base, oldlen);
@ -128,7 +130,8 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
return 0;
}
int read_tree_recursive(struct tree *tree,
int read_tree_recursive(struct repository *r,
struct tree *tree,
const char *base, int baselen,
int stage, const struct pathspec *pathspec,
read_tree_fn_t fn, void *context)
@ -137,7 +140,7 @@ int read_tree_recursive(struct tree *tree,
int ret;
strbuf_add(&sb, base, baselen);
ret = read_tree_1(tree, &sb, stage, pathspec, fn, context);
ret = read_tree_1(r, tree, &sb, stage, pathspec, fn, context);
strbuf_release(&sb);
return ret;
}
@ -152,8 +155,8 @@ static int cmp_cache_name_compare(const void *a_, const void *b_)
ce2->name, ce2->ce_namelen, ce_stage(ce2));
}
int read_tree(struct tree *tree, int stage, struct pathspec *match,
struct index_state *istate)
int read_tree(struct repository *r, struct tree *tree, int stage,
struct pathspec *match, struct index_state *istate)
{
read_tree_fn_t fn = NULL;
int i, err;
@ -181,7 +184,7 @@ int read_tree(struct tree *tree, int stage, struct pathspec *match,
if (!fn)
fn = read_one_entry_quick;
err = read_tree_recursive(tree, "", 0, stage, match, fn, istate);
err = read_tree_recursive(r, tree, "", 0, stage, match, fn, istate);
if (fn == read_one_entry || err)
return err;

18
tree.h
View File

@ -3,7 +3,7 @@
#include "object.h"
extern const char *tree_type;
struct repository;
struct strbuf;
struct tree {
@ -12,6 +12,8 @@ struct tree {
unsigned long size;
};
extern const char *tree_type;
struct tree *lookup_tree(struct repository *r, const struct object_id *oid);
int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size);
@ -29,12 +31,14 @@ struct tree *parse_tree_indirect(const struct object_id *oid);
#define READ_TREE_RECURSIVE 1
typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, int, void *);
extern int read_tree_recursive(struct tree *tree,
const char *base, int baselen,
int stage, const struct pathspec *pathspec,
read_tree_fn_t fn, void *context);
int read_tree_recursive(struct repository *r,
struct tree *tree,
const char *base, int baselen,
int stage, const struct pathspec *pathspec,
read_tree_fn_t fn, void *context);
extern int read_tree(struct tree *tree, int stage, struct pathspec *pathspec,
struct index_state *istate);
int read_tree(struct repository *r, struct tree *tree,
int stage, struct pathspec *pathspec,
struct index_state *istate);
#endif /* TREE_H */

View File

@ -794,6 +794,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
struct name_entry *names,
struct traverse_info *info)
{
struct unpack_trees_options *o = info->data;
int i, ret, bottom;
int nr_buf = 0;
struct tree_desc t[MAX_UNPACK_TREES];
@ -804,7 +805,6 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
nr_entries = all_trees_same_as_cache_tree(n, dirmask, names, info);
if (nr_entries > 0) {
struct unpack_trees_options *o = info->data;
int pos = index_pos_by_traverse_info(names, info);
if (!o->merge || df_conflicts)
@ -863,7 +863,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
}
bottom = switch_cache_bottom(&newinfo);
ret = traverse_trees(n, t, &newinfo);
ret = traverse_trees(o->src_index, n, t, &newinfo);
restore_cache_bottom(&newinfo, bottom);
for (i = 0; i < nr_buf; i++)
@ -1550,7 +1550,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
}
trace_performance_enter();
ret = traverse_trees(len, t, &info);
ret = traverse_trees(o->src_index, len, t, &info);
trace_performance_leave("traverse_trees");
if (ret < 0)
goto return_failed;