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;