Skip to content

Commit 83c9031

Browse files
committed
unpack_trees(): skip trees that are the same in all input
unpack_trees() merges two trees (the current HEAD and the destination commit) when switching to another branch, checking and updating the index entry where the destination differs from the current HEAD. It merges three trees (the common ancestor, the current HEAD and the other commit) when performing a three-way merge, checking and updating the index entry when the merge result differs from the current HEAD. It does so by walking the input trees in parallel all the way down to the leaves. One common special case is a directory is identical across the trees involved in the merge. In such a case, we do not have to descend into the directory at all---we know that the end result is to keep the entries in the current index. This optimization cannot be applied in a few special cases in unpack_trees(), though. We need to descend into the directory and update the index entries from the target tree in the following cases: - When resetting (e.g. "git reset --hard"); and - When checking out a tree for the first time into an empty working tree (e.g. "git read-tree -m -u HEAD HEAD" with missing .git/index). Signed-off-by: Junio C Hamano <[email protected]>
1 parent 84563a6 commit 83c9031

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

unpack-trees.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,10 @@ static int switch_cache_bottom(struct traverse_info *info)
436436
return ret;
437437
}
438438

439+
static int fast_forward_merge(int n, unsigned long dirmask,
440+
struct name_entry *names,
441+
struct traverse_info *info);
442+
439443
static int traverse_trees_recursive(int n, unsigned long dirmask,
440444
unsigned long df_conflicts,
441445
struct name_entry *names,
@@ -447,6 +451,11 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
447451
struct traverse_info newinfo;
448452
struct name_entry *p;
449453

454+
if (!df_conflicts) {
455+
int status = fast_forward_merge(n, dirmask, names, info);
456+
if (status)
457+
return status;
458+
}
450459
p = names;
451460
while (!p->mode)
452461
p++;
@@ -694,6 +703,53 @@ static struct cache_entry *find_cache_entry(struct traverse_info *info,
694703
return NULL;
695704
}
696705

706+
static int fast_forward_merge(int n, unsigned long dirmask,
707+
struct name_entry *names,
708+
struct traverse_info *info)
709+
{
710+
int i;
711+
struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
712+
struct unpack_trees_options *o = info->data;
713+
714+
/* merging two or more trees with an identical subdirectory? */
715+
if ((n < 2) || ((1UL << n) - 1) != dirmask ||
716+
!o->merge || o->reset || o->initial_checkout)
717+
return 0;
718+
for (i = 1; i < n; i++)
719+
if (hashcmp(names[i-1].sha1, names[i].sha1))
720+
return 0;
721+
722+
/*
723+
* Instead of descending into the directory, keep the contents
724+
* of the current index.
725+
*/
726+
while (1) {
727+
struct cache_entry *ce;
728+
ce = next_cache_entry(o);
729+
if (!ce)
730+
break;
731+
/* Is the entry still in that directory? */
732+
if (do_compare_entry(ce, info, names))
733+
break;
734+
/*
735+
* Note: we do not just run unpack_index_entry() here,
736+
* as the callback may want to compare what is in the
737+
* index with what are from the HEAD and the other tree
738+
* and reject the merge. We pretend that ancestors, the
739+
* HEAD and the other tree all have the same contents as
740+
* the current index, which is a lie, but it works.
741+
*/
742+
for (i = 0; i < n + 1; i++)
743+
src[i] = ce;
744+
mark_ce_used(ce, o);
745+
if (call_unpack_fn(src, o) < 0)
746+
return unpack_failed(o, NULL);
747+
if (ce_stage(ce))
748+
mark_ce_used_same_name(ce, o);
749+
}
750+
return dirmask;
751+
}
752+
697753
static void debug_path(struct traverse_info *info)
698754
{
699755
if (info->prev) {

0 commit comments

Comments
 (0)