Skip to content

Commit ca676b9

Browse files
committed
Merge branch 'en/directory-renames-nothanks'
Recent addition of "directory rename" heuristics to the merge-recursive backend makes the command susceptible to false positives and false negatives. In the context of "git am -3", which does not know about surrounding unmodified paths and thus cannot inform the merge machinery about the full trees involved, this risk is particularly severe. As such, the heuristic is disabled for "git am -3" to keep the machinery "more stupid but predictable". * en/directory-renames-nothanks: am: avoid directory rename detection when calling recursive merge machinery merge-recursive: add ability to turn off directory rename detection t3401: add another directory rename testcase for rebase and am
2 parents 064e0b2 + 6aba117 commit ca676b9

File tree

4 files changed

+124
-6
lines changed

4 files changed

+124
-6
lines changed

builtin/am.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1598,6 +1598,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
15981598
o.branch1 = "HEAD";
15991599
their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
16001600
o.branch2 = their_tree_name;
1601+
o.detect_directory_renames = 0;
16011602

16021603
if (state->quiet)
16031604
o.verbosity = 0;

merge-recursive.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2869,12 +2869,19 @@ static int detect_and_process_renames(struct merge_options *o,
28692869
head_pairs = get_diffpairs(o, common, head);
28702870
merge_pairs = get_diffpairs(o, common, merge);
28712871

2872-
dir_re_head = get_directory_renames(head_pairs, head);
2873-
dir_re_merge = get_directory_renames(merge_pairs, merge);
2872+
if (o->detect_directory_renames) {
2873+
dir_re_head = get_directory_renames(head_pairs, head);
2874+
dir_re_merge = get_directory_renames(merge_pairs, merge);
28742875

2875-
handle_directory_level_conflicts(o,
2876-
dir_re_head, head,
2877-
dir_re_merge, merge);
2876+
handle_directory_level_conflicts(o,
2877+
dir_re_head, head,
2878+
dir_re_merge, merge);
2879+
} else {
2880+
dir_re_head = xmalloc(sizeof(*dir_re_head));
2881+
dir_re_merge = xmalloc(sizeof(*dir_re_merge));
2882+
dir_rename_init(dir_re_head);
2883+
dir_rename_init(dir_re_merge);
2884+
}
28782885

28792886
ri->head_renames = get_renames(o, head_pairs,
28802887
dir_re_merge, dir_re_head, head,
@@ -3586,6 +3593,7 @@ void init_merge_options(struct merge_options *o)
35863593
o->renormalize = 0;
35873594
o->diff_detect_rename = -1;
35883595
o->merge_detect_rename = -1;
3596+
o->detect_directory_renames = 1;
35893597
merge_recursive_config(o);
35903598
merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
35913599
if (merge_verbosity)

merge-recursive.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ struct merge_options {
2020
unsigned renormalize : 1;
2121
long xdl_opts;
2222
int verbosity;
23+
int detect_directory_renames;
2324
int diff_detect_rename;
2425
int merge_detect_rename;
2526
int diff_rename_limit;

t/t3401-rebase-and-am-rename.sh

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ test_description='git rebase + directory rename tests'
55
. ./test-lib.sh
66
. "$TEST_DIRECTORY"/lib-rebase.sh
77

8-
test_expect_success 'setup testcase' '
8+
test_expect_success 'setup testcase where directory rename should be detected' '
99
test_create_repo dir-rename &&
1010
(
1111
cd dir-rename &&
@@ -102,4 +102,112 @@ test_expect_failure 'am: directory rename detected' '
102102
)
103103
'
104104

105+
test_expect_success 'setup testcase where directory rename should NOT be detected' '
106+
test_create_repo no-dir-rename &&
107+
(
108+
cd no-dir-rename &&
109+
110+
mkdir x &&
111+
test_seq 1 10 >x/a &&
112+
test_seq 11 20 >x/b &&
113+
test_seq 21 30 >x/c &&
114+
echo original >project_info &&
115+
git add x project_info &&
116+
git commit -m "Initial" &&
117+
118+
git branch O &&
119+
git branch A &&
120+
git branch B &&
121+
122+
git checkout A &&
123+
echo v2 >project_info &&
124+
git add project_info &&
125+
git commit -m "Modify project_info" &&
126+
127+
git checkout B &&
128+
mkdir y &&
129+
git mv x/c y/c &&
130+
echo v1 >project_info &&
131+
git add project_info &&
132+
git commit -m "Rename x/c to y/c, modify project_info"
133+
)
134+
'
135+
136+
test_expect_success 'rebase --interactive: NO directory rename' '
137+
test_when_finished "git -C no-dir-rename rebase --abort" &&
138+
(
139+
cd no-dir-rename &&
140+
141+
git checkout B^0 &&
142+
143+
set_fake_editor &&
144+
test_must_fail env FAKE_LINES="1" git rebase --interactive A &&
145+
146+
git ls-files -s >out &&
147+
test_line_count = 6 out &&
148+
149+
test_path_is_file x/a &&
150+
test_path_is_file x/b &&
151+
test_path_is_missing x/c
152+
)
153+
'
154+
155+
test_expect_success 'rebase (am): NO directory rename' '
156+
test_when_finished "git -C no-dir-rename rebase --abort" &&
157+
(
158+
cd no-dir-rename &&
159+
160+
git checkout B^0 &&
161+
162+
set_fake_editor &&
163+
test_must_fail git rebase A &&
164+
165+
git ls-files -s >out &&
166+
test_line_count = 6 out &&
167+
168+
test_path_is_file x/a &&
169+
test_path_is_file x/b &&
170+
test_path_is_missing x/c
171+
)
172+
'
173+
174+
test_expect_success 'rebase --merge: NO directory rename' '
175+
test_when_finished "git -C no-dir-rename rebase --abort" &&
176+
(
177+
cd no-dir-rename &&
178+
179+
git checkout B^0 &&
180+
181+
set_fake_editor &&
182+
test_must_fail git rebase --merge A &&
183+
184+
git ls-files -s >out &&
185+
test_line_count = 6 out &&
186+
187+
test_path_is_file x/a &&
188+
test_path_is_file x/b &&
189+
test_path_is_missing x/c
190+
)
191+
'
192+
193+
test_expect_success 'am: NO directory rename' '
194+
test_when_finished "git -C no-dir-rename am --abort" &&
195+
(
196+
cd no-dir-rename &&
197+
198+
git checkout A^0 &&
199+
200+
git format-patch -1 B &&
201+
202+
test_must_fail git am --3way 0001*.patch &&
203+
204+
git ls-files -s >out &&
205+
test_line_count = 6 out &&
206+
207+
test_path_is_file x/a &&
208+
test_path_is_file x/b &&
209+
test_path_is_missing x/c
210+
)
211+
'
212+
105213
test_done

0 commit comments

Comments
 (0)