summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builtin-merge-recursive.c10
-rw-r--r--builtin-merge.c6
-rw-r--r--cache.h1
-rw-r--r--match-trees.c69
-rw-r--r--merge-recursive.c20
-rw-r--r--merge-recursive.h3
6 files changed, 91 insertions, 18 deletions
diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
index 1a160d8127..d8875d5892 100644
--- a/builtin-merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -25,10 +25,8 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
struct commit *result;
init_merge_options(&o);
- if (argv[0]) {
- if (!suffixcmp(argv[0], "-subtree"))
- o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
- }
+ if (argv[0] && !suffixcmp(argv[0], "-subtree"))
+ o.subtree_shift = "";
if (argc < 4)
usagef("%s <base>... -- <head> <remote> ...", argv[0]);
@@ -44,7 +42,9 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
else if (!strcmp(arg+2, "theirs"))
o.recursive_variant = MERGE_RECURSIVE_THEIRS;
else if (!strcmp(arg+2, "subtree"))
- o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
+ o.subtree_shift = "";
+ else if (!prefixcmp(arg+2, "subtree="))
+ o.subtree_shift = arg + 10;
else
die("Unknown option %s", arg);
continue;
diff --git a/builtin-merge.c b/builtin-merge.c
index 42f19e1fd3..3aa7ea4052 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -578,7 +578,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
init_merge_options(&o);
if (!strcmp(strategy, "subtree"))
- o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
+ o.subtree_shift = "";
for (x = 0; x < xopts_nr; x++) {
if (!strcmp(xopts[x], "ours"))
@@ -586,7 +586,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
else if (!strcmp(xopts[x], "theirs"))
o.recursive_variant = MERGE_RECURSIVE_THEIRS;
else if (!strcmp(xopts[x], "subtree"))
- o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
+ o.subtree_shift = "";
+ else if (!prefixcmp(xopts[x], "subtree="))
+ o.subtree_shift = xopts[x]+8;
else
die("Unknown option for merge-recursive: -X%s", xopts[x]);
}
diff --git a/cache.h b/cache.h
index bf468e5235..c6902d2c22 100644
--- a/cache.h
+++ b/cache.h
@@ -993,6 +993,7 @@ extern int diff_auto_refresh_index;
/* match-trees.c */
void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
+void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *, const char *);
/*
* whitespace rules.
diff --git a/match-trees.c b/match-trees.c
index 0fd6df7d6e..26f7ed143e 100644
--- a/match-trees.c
+++ b/match-trees.c
@@ -185,7 +185,7 @@ static void match_trees(const unsigned char *hash1,
* tree object by replacing it with another tree "hash2".
*/
static int splice_tree(const unsigned char *hash1,
- char *prefix,
+ const char *prefix,
const unsigned char *hash2,
unsigned char *result)
{
@@ -264,6 +264,13 @@ void shift_tree(const unsigned char *hash1,
char *del_prefix;
int add_score, del_score;
+ /*
+ * NEEDSWORK: this limits the recursion depth to hardcoded
+ * value '2' to avoid excessive overhead.
+ */
+ if (!depth_limit)
+ depth_limit = 2;
+
add_score = del_score = score_trees(hash1, hash2);
add_prefix = xcalloc(1, 1);
del_prefix = xcalloc(1, 1);
@@ -301,3 +308,63 @@ void shift_tree(const unsigned char *hash1,
splice_tree(hash1, add_prefix, hash2, shifted);
}
+
+/*
+ * The user says the trees will be shifted by this much.
+ * Unfortunately we cannot fundamentally tell which one to
+ * be prefixed, as recursive merge can work in either direction.
+ */
+void shift_tree_by(const unsigned char *hash1,
+ const unsigned char *hash2,
+ unsigned char *shifted,
+ const char *shift_prefix)
+{
+ unsigned char sub1[20], sub2[20];
+ unsigned mode1, mode2;
+ unsigned candidate = 0;
+
+ /* Can hash2 be a tree at shift_prefix in tree hash1? */
+ if (!get_tree_entry(hash1, shift_prefix, sub1, &mode1) &&
+ S_ISDIR(mode1))
+ candidate |= 1;
+
+ /* Can hash1 be a tree at shift_prefix in tree hash2? */
+ if (!get_tree_entry(hash2, shift_prefix, sub2, &mode2) &&
+ S_ISDIR(mode2))
+ candidate |= 2;
+
+ if (candidate == 3) {
+ /* Both are plausible -- we need to evaluate the score */
+ int best_score = score_trees(hash1, hash2);
+ int score;
+
+ candidate = 0;
+ score = score_trees(sub1, hash2);
+ if (score > best_score) {
+ candidate = 1;
+ best_score = score;
+ }
+ score = score_trees(sub2, hash1);
+ if (score > best_score)
+ candidate = 2;
+ }
+
+ if (!candidate) {
+ /* Neither is plausible -- do not shift */
+ hashcpy(shifted, hash2);
+ return;
+ }
+
+ if (candidate == 1)
+ /*
+ * shift tree2 down by adding shift_prefix above it
+ * to match tree1.
+ */
+ splice_tree(hash1, shift_prefix, hash2, shifted);
+ else
+ /*
+ * shift tree2 up by removing shift_prefix from it
+ * to match tree1.
+ */
+ hashcpy(shifted, sub2);
+}
diff --git a/merge-recursive.c b/merge-recursive.c
index 82f24ac16e..0af77cfa56 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -21,15 +21,17 @@
#include "merge-recursive.h"
#include "dir.h"
-static struct tree *shift_tree_object(struct tree *one, struct tree *two)
+static struct tree *shift_tree_object(struct tree *one, struct tree *two,
+ const char *subtree_shift)
{
unsigned char shifted[20];
- /*
- * NEEDSWORK: this limits the recursion depth to hardcoded
- * value '2' to avoid excessive overhead.
- */
- shift_tree(one->object.sha1, two->object.sha1, shifted, 2);
+ if (!*subtree_shift) {
+ shift_tree(one->object.sha1, two->object.sha1, shifted, 0);
+ } else {
+ shift_tree_by(one->object.sha1, two->object.sha1, shifted,
+ subtree_shift);
+ }
if (!hashcmp(two->object.sha1, shifted))
return two;
return lookup_tree(shifted);
@@ -1213,9 +1215,9 @@ int merge_trees(struct merge_options *o,
{
int code, clean;
- if (o->recursive_variant == MERGE_RECURSIVE_SUBTREE) {
- merge = shift_tree_object(head, merge);
- common = shift_tree_object(head, common);
+ if (o->subtree_shift) {
+ merge = shift_tree_object(head, merge, o->subtree_shift);
+ common = shift_tree_object(head, common, o->subtree_shift);
}
if (sha_eq(common->object.sha1, merge->object.sha1)) {
diff --git a/merge-recursive.h b/merge-recursive.h
index 39de100cd5..e63df9d63b 100644
--- a/merge-recursive.h
+++ b/merge-recursive.h
@@ -7,10 +7,11 @@ struct merge_options {
const char *branch1;
const char *branch2;
enum {
- MERGE_RECURSIVE_SUBTREE = 1,
+ MERGE_RECURSIVE_NORMAL = 0,
MERGE_RECURSIVE_OURS,
MERGE_RECURSIVE_THEIRS,
} recursive_variant;
+ const char *subtree_shift;
unsigned buffer_output : 1;
int verbosity;
int diff_rename_limit;