Skip to content

Commit 60858f3

Browse files
committed
Merge branch 'jk/merge-subtree-heuristics'
The automatic tree-matching in "git merge -s subtree" was broken 5 years ago and nobody has noticed since then, which is now fixed. * jk/merge-subtree-heuristics: score_trees(): fix iteration over trees with missing entries
2 parents 28bdd99 + 2ec4150 commit 60858f3

File tree

2 files changed

+54
-17
lines changed

2 files changed

+54
-17
lines changed

match-trees.c

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -83,34 +83,43 @@ static int score_trees(const struct object_id *hash1, const struct object_id *ha
8383
int score = 0;
8484

8585
for (;;) {
86-
struct name_entry e1, e2;
87-
int got_entry_from_one = tree_entry(&one, &e1);
88-
int got_entry_from_two = tree_entry(&two, &e2);
8986
int cmp;
9087

91-
if (got_entry_from_one && got_entry_from_two)
92-
cmp = base_name_entries_compare(&e1, &e2);
93-
else if (got_entry_from_one)
88+
if (one.size && two.size)
89+
cmp = base_name_entries_compare(&one.entry, &two.entry);
90+
else if (one.size)
9491
/* two lacks this entry */
9592
cmp = -1;
96-
else if (got_entry_from_two)
93+
else if (two.size)
9794
/* two has more entries */
9895
cmp = 1;
9996
else
10097
break;
10198

102-
if (cmp < 0)
99+
if (cmp < 0) {
103100
/* path1 does not appear in two */
104-
score += score_missing(e1.mode, e1.path);
105-
else if (cmp > 0)
101+
score += score_missing(one.entry.mode, one.entry.path);
102+
update_tree_entry(&one);
103+
} else if (cmp > 0) {
106104
/* path2 does not appear in one */
107-
score += score_missing(e2.mode, e2.path);
108-
else if (oidcmp(e1.oid, e2.oid))
109-
/* they are different */
110-
score += score_differs(e1.mode, e2.mode, e1.path);
111-
else
112-
/* same subtree or blob */
113-
score += score_matches(e1.mode, e2.mode, e1.path);
105+
score += score_missing(two.entry.mode, two.entry.path);
106+
update_tree_entry(&two);
107+
} else {
108+
/* path appears in both */
109+
if (oidcmp(one.entry.oid, two.entry.oid)) {
110+
/* they are different */
111+
score += score_differs(one.entry.mode,
112+
two.entry.mode,
113+
one.entry.path);
114+
} else {
115+
/* same subtree or blob */
116+
score += score_matches(one.entry.mode,
117+
two.entry.mode,
118+
one.entry.path);
119+
}
120+
update_tree_entry(&one);
121+
update_tree_entry(&two);
122+
}
114123
}
115124
free(one_buf);
116125
free(two_buf);

t/t6029-merge-subtree.sh

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,34 @@ test_expect_success 'subtree available and works like recursive' '
2929
3030
'
3131

32+
test_expect_success 'setup branch sub' '
33+
git checkout --orphan sub &&
34+
git rm -rf . &&
35+
test_commit foo
36+
'
37+
38+
test_expect_success 'setup branch main' '
39+
git checkout -b main master &&
40+
git merge -s ours --no-commit --allow-unrelated-histories sub &&
41+
git read-tree --prefix=dir/ -u sub &&
42+
git commit -m "initial merge of sub into main" &&
43+
test_path_is_file dir/foo.t &&
44+
test_path_is_file hello
45+
'
46+
47+
test_expect_success 'update branch sub' '
48+
git checkout sub &&
49+
test_commit bar
50+
'
51+
52+
test_expect_success 'update branch main' '
53+
git checkout main &&
54+
git merge -s subtree sub -m "second merge of sub into main" &&
55+
test_path_is_file dir/bar.t &&
56+
test_path_is_file dir/foo.t &&
57+
test_path_is_file hello
58+
'
59+
3260
test_expect_success 'setup' '
3361
mkdir git-gui &&
3462
cd git-gui &&

0 commit comments

Comments
 (0)