Skip to content

Commit 2842c0f

Browse files
committed
traverse_trees(): allow pruning with pathspec
The traverse_trees() machinery is primarily meant for merging two (or more) trees, and because a merge is a full tree operation, it doesn't support any pruning with pathspec. Since d1f2d7e (Make run_diff_index() use unpack_trees(), not read_tree(), 2008-01-19), however, we use unpack_trees() to traverse_trees() callchain to perform "diff-index", which could waste a lot of work traversing trees outside the user-supplied pathspec, only to discard at the blob comparison level in diff-lib.c::oneway_diff() which is way too late. Signed-off-by: Junio C Hamano <[email protected]>
1 parent f696543 commit 2842c0f

File tree

2 files changed

+34
-6
lines changed

2 files changed

+34
-6
lines changed

tree-walk.c

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -309,17 +309,37 @@ static void free_extended_entry(struct tree_desc_x *t)
309309
}
310310
}
311311

312+
static inline int prune_traversal(struct name_entry *e,
313+
struct traverse_info *info,
314+
struct strbuf *base,
315+
int still_interesting)
316+
{
317+
if (!info->pathspec || still_interesting == 2)
318+
return 2;
319+
if (still_interesting < 0)
320+
return still_interesting;
321+
return tree_entry_interesting(e, base, 0, info->pathspec);
322+
}
323+
312324
int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
313325
{
314326
int ret = 0;
315327
int error = 0;
316328
struct name_entry *entry = xmalloc(n*sizeof(*entry));
317329
int i;
318330
struct tree_desc_x *tx = xcalloc(n, sizeof(*tx));
331+
struct strbuf base = STRBUF_INIT;
332+
int interesting = 1;
319333

320334
for (i = 0; i < n; i++)
321335
tx[i].d = t[i];
322336

337+
if (info->prev) {
338+
strbuf_grow(&base, info->pathlen);
339+
make_traverse_path(base.buf, info->prev, &info->name);
340+
base.buf[info->pathlen-1] = '/';
341+
strbuf_setlen(&base, info->pathlen);
342+
}
323343
for (;;) {
324344
unsigned long mask, dirmask;
325345
const char *first = NULL;
@@ -376,16 +396,22 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
376396
mask |= 1ul << i;
377397
if (S_ISDIR(entry[i].mode))
378398
dirmask |= 1ul << i;
399+
e = &entry[i];
379400
}
380401
if (!mask)
381402
break;
382-
ret = info->fn(n, mask, dirmask, entry, info);
383-
if (ret < 0) {
384-
error = ret;
385-
if (!info->show_all_errors)
386-
break;
403+
interesting = prune_traversal(e, info, &base, interesting);
404+
if (interesting < 0)
405+
break;
406+
if (interesting) {
407+
ret = info->fn(n, mask, dirmask, entry, info);
408+
if (ret < 0) {
409+
error = ret;
410+
if (!info->show_all_errors)
411+
break;
412+
}
413+
mask &= ret;
387414
}
388-
mask &= ret;
389415
ret = 0;
390416
for (i = 0; i < n; i++)
391417
if (mask & (1ul << i))
@@ -395,6 +421,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
395421
for (i = 0; i < n; i++)
396422
free_extended_entry(tx + i);
397423
free(tx);
424+
strbuf_release(&base);
398425
return error;
399426
}
400427

tree-walk.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ struct traverse_info {
4444
struct traverse_info *prev;
4545
struct name_entry name;
4646
int pathlen;
47+
struct pathspec *pathspec;
4748

4849
unsigned long conflicts;
4950
traverse_callback_t fn;

0 commit comments

Comments
 (0)