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 <gitster@pobox.com>
Junio C Hamano committed
Aug 29, 2011 at 12:26 UTC
2842c0f914f7c05401c449db9d01276ac5a743f0
2 files changed
+34
-6
tree-walk.c
+33
-6
@@ -309,6 +309,18 @@ static void free_extended_entry(struct tree_desc_x *t)
309
}
310
}
311
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
+
324
int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
325
{
326
int ret = 0;
@@ -316,10 +328,18 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
328
struct name_entry *entry = xmalloc(n*sizeof(*entry));
329
int i;
330
struct tree_desc_x *tx = xcalloc(n, sizeof(*tx));
331
+ struct strbuf base = STRBUF_INIT;
332
+ int interesting = 1;
333
334
for (i = 0; i < n; i++)
335
tx[i].d = t[i];
336
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
+ }
343
for (;;) {
344
unsigned long mask, dirmask;
345
const char *first = NULL;
@@ -376,16 +396,22 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
396
mask |= 1ul << i;
397
if (S_ISDIR(entry[i].mode))
398
dirmask |= 1ul << i;
399
+ e = &entry[i];
400
}
401
if (!mask)
402
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;
414
}
388
- mask &= ret;
415
ret = 0;
416
for (i = 0; i < n; i++)
417
if (mask & (1ul << i))
@@ -395,6 +421,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
421
for (i = 0; i < n; i++)
422
free_extended_entry(tx + i);
423
free(tx);
424
+ strbuf_release(&base);
425
return error;
426
}
427
tree-walk.h
+1
@@ -44,6 +44,7 @@ struct traverse_info {
44
struct traverse_info *prev;
45
struct name_entry name;
46
int pathlen;
47
+ struct pathspec *pathspec;
48
49
unsigned long conflicts;
50
traverse_callback_t fn;