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]);
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;
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"))
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]);
}
/* 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.
* 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)
{
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);
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);
+}
#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);
{
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)) {
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;