summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--unpack-trees.c56
1 files changed, 56 insertions, 0 deletions
diff --git a/unpack-trees.c b/unpack-trees.c
index aa585be1b0..e1b4e996ae 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -436,6 +436,10 @@ static int switch_cache_bottom(struct traverse_info *info)
return ret;
}
+static int fast_forward_merge(int n, unsigned long dirmask,
+ struct name_entry *names,
+ struct traverse_info *info);
+
static int traverse_trees_recursive(int n, unsigned long dirmask,
unsigned long df_conflicts,
struct name_entry *names,
@@ -447,6 +451,11 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
struct traverse_info newinfo;
struct name_entry *p;
+ if (!df_conflicts) {
+ int status = fast_forward_merge(n, dirmask, names, info);
+ if (status)
+ return status;
+ }
p = names;
while (!p->mode)
p++;
@@ -694,6 +703,53 @@ static struct cache_entry *find_cache_entry(struct traverse_info *info,
return NULL;
}
+static int fast_forward_merge(int n, unsigned long dirmask,
+ struct name_entry *names,
+ struct traverse_info *info)
+{
+ int i;
+ struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
+ struct unpack_trees_options *o = info->data;
+
+ /* merging two or more trees with an identical subdirectory? */
+ if ((n < 2) || ((1UL << n) - 1) != dirmask ||
+ !o->merge || o->reset || o->initial_checkout)
+ return 0;
+ for (i = 1; i < n; i++)
+ if (hashcmp(names[i-1].sha1, names[i].sha1))
+ return 0;
+
+ /*
+ * Instead of descending into the directory, keep the contents
+ * of the current index.
+ */
+ while (1) {
+ struct cache_entry *ce;
+ ce = next_cache_entry(o);
+ if (!ce)
+ break;
+ /* Is the entry still in that directory? */
+ if (do_compare_entry(ce, info, names))
+ break;
+ /*
+ * Note: we do not just run unpack_index_entry() here,
+ * as the callback may want to compare what is in the
+ * index with what are from the HEAD and the other tree
+ * and reject the merge. We pretend that ancestors, the
+ * HEAD and the other tree all have the same contents as
+ * the current index, which is a lie, but it works.
+ */
+ for (i = 0; i < n + 1; i++)
+ src[i] = ce;
+ mark_ce_used(ce, o);
+ if (call_unpack_fn(src, o) < 0)
+ return unpack_failed(o, NULL);
+ if (ce_stage(ce))
+ mark_ce_used_same_name(ce, o);
+ }
+ return dirmask;
+}
+
static void debug_path(struct traverse_info *info)
{
if (info->prev) {