3 test_description="limiting blob downloads when merging with partial clones"
4 # Uses a methodology similar to
5 # t6042: corner cases with renames but not criss-cross merges
6 # t6036: corner cases with both renames and criss-cross merges
7 # t6423: directory rename detection
9 # The setup for all of them, pictorially, is:
19 # To help make it easier to follow the flow of tests, they have been
20 # divided into sections and each test will start with a quick explanation
21 # of what commits O, A, and B contain.
24 # z/{b,c} means files z/b and z/c both exist
25 # x/d_1 means file x/d exists with content d1. (Purpose of the
26 # underscore notation is to differentiate different
27 # files that might be renamed into each other's paths.)
30 . "$TEST_DIRECTORY"/lib-merge.sh
33 test -d server && return
34 test_create_repo server &&
38 git config uploadpack.allowfilter 1 &&
39 git config uploadpack.allowanysha1inwant 1 &&
42 test_seq 2 9 >general/leap1 &&
43 cp general/leap1 general/leap2 &&
44 echo leap2 >>general/leap2 &&
47 cp general/leap1 basename/numbers &&
48 cp general/leap1 basename/sequence &&
49 cp general/leap1 basename/values &&
50 echo numbers >>basename/numbers &&
51 echo sequence >>basename/sequence &&
52 echo values >>basename/values &&
54 mkdir -p dir/unchanged &&
55 mkdir -p dir/subdir/tweaked &&
56 echo a >dir/subdir/a &&
57 echo b >dir/subdir/b &&
58 echo c >dir/subdir/c &&
59 echo d >dir/subdir/d &&
60 echo e >dir/subdir/e &&
61 cp general/leap1 dir/subdir/Makefile &&
62 echo toplevel makefile >>dir/subdir/Makefile &&
63 echo f >dir/subdir/tweaked/f &&
64 echo g >dir/subdir/tweaked/g &&
65 echo h >dir/subdir/tweaked/h &&
66 echo subdirectory makefile >dir/subdir/tweaked/Makefile &&
67 for i in `test_seq 1 88`; do
68 echo content $i >dir/unchanged/file_$i
75 git branch B-single &&
81 git rm general/leap* &&
83 test_seq 1 9 >general/jump1 &&
84 cp general/jump1 general/jump2 &&
85 echo leap2 >>general/jump2 &&
87 rm basename/numbers basename/sequence basename/values &&
88 mkdir -p basename/subdir/
89 cp general/jump1 basename/subdir/numbers &&
90 cp general/jump1 basename/subdir/sequence &&
91 cp general/jump1 basename/subdir/values &&
92 echo numbers >>basename/subdir/numbers &&
93 echo sequence >>basename/subdir/sequence &&
94 echo values >>basename/subdir/values &&
96 git rm dir/subdir/tweaked/f &&
97 echo more >>dir/subdir/e &&
98 echo more >>dir/subdir/Makefile &&
99 echo more >>dir/subdir/tweaked/Makefile &&
100 mkdir dir/subdir/newsubdir &&
101 echo rust code >dir/subdir/newsubdir/newfile.rs &&
102 git mv dir/subdir/e dir/subdir/newsubdir/ &&
107 git switch B-single &&
108 echo new first line >dir/subdir/Makefile &&
109 cat general/leap1 >>dir/subdir/Makefile &&
110 echo toplevel makefile >>dir/subdir/Makefile &&
111 echo perl code >general/newfile.pl &&
113 git commit -m "B-single" &&
116 echo java code >dir/subdir/newfile.java &&
117 echo scala code >dir/subdir/newfile.scala &&
118 echo groovy code >dir/subdir/newfile.groovy &&
120 git commit -m "B-dir" &&
123 test_seq 2 10 >general/leap1 &&
125 cp general/leap1 general/leap2 &&
126 echo leap2 >>general/leap2 &&
128 rm basename/numbers basename/sequence basename/values &&
129 mkdir -p basename/subdir/
130 cp general/leap1 basename/subdir/numbers &&
131 cp general/leap1 basename/subdir/sequence &&
132 cp general/leap1 basename/subdir/values &&
133 echo numbers >>basename/subdir/numbers &&
134 echo sequence >>basename/subdir/sequence &&
135 echo values >>basename/subdir/values &&
137 mkdir dir/subdir/newsubdir/ &&
138 echo c code >dir/subdir/newfile.c &&
139 echo python code >dir/subdir/newsubdir/newfile.py &&
141 git commit -m "B-many" &&
147 # Testcase: Objects downloaded for single relevant rename
149 # general/{leap1_O, leap2_O}
150 # basename/{numbers_O, sequence_O, values_O}
151 # dir/subdir/{a,b,c,d,e_O,Makefile_TOP_O}
152 # dir/subdir/tweaked/{f,g,h,Makefile_SUB_O}
153 # dir/unchanged/<LOTS OF FILES>
155 # (Rename leap->jump, rename basename/ -> basename/subdir/, rename dir/
156 # -> folder/, move e into newsubdir, add newfile.rs, remove f, modify
157 # both both Makefiles and jumps)
158 # general/{jump1_A, jump2_A}
159 # basename/subdir/{numbers_A, sequence_A, values_A}
160 # folder/subdir/{a,b,c,d,Makefile_TOP_A}
161 # folder/subdir/newsubdir/{e_A,newfile.rs}
162 # folder/subdir/tweaked/{g,h,Makefile_SUB_A}
163 # folder/unchanged/<LOTS OF FILES>
165 # (add newfile.pl, tweak Makefile_TOP)
166 # general/{leap1_O, leap2_O,newfile.pl}
167 # basename/{numbers_O, sequence_O, values_O}
168 # dir/{a,b,c,d,e_O,Makefile_TOP_B}
169 # dir/tweaked/{f,g,h,Makefile_SUB_O}
170 # dir/unchanged/<LOTS OF FILES>
172 # general/{jump1_A, jump2_A,newfile.pl}
173 # basename/subdir/{numbers_A, sequence_A, values_A}
174 # folder/subdir/{a,b,c,d,Makefile_TOP_Merged}
175 # folder/subdir/newsubdir/{e_A,newfile.rs}
176 # folder/subdir/tweaked/{g,h,Makefile_SUB_A}
177 # folder/unchanged/<LOTS OF FILES>
179 # Objects that need to be fetched:
182 # Basename-matches rename detection only needs to fetch these objects:
183 # Makefile_TOP_O, Makefile_TOP_A
184 # (Despite many renames, all others are content irrelevant. They
185 # are also location irrelevant because newfile.rs was added on
186 # the side doing the directory rename, and newfile.pl was added to
187 # a directory that was not renamed on either side.)
188 # General rename detection only needs to fetch these objects:
190 # (Even though newfile.rs, jump[12], basename/subdir/*, and e
191 # could all be used as destinations in rename detection, the
192 # basename detection for Makefile matches up all relevant
193 # sources, so these other files never end up needing to be
196 # Basename-matches rename detection only needs to fetch these objects:
198 # (there are no deleted files, so no possible sources)
199 # General rename detection only needs to fetch these objects:
201 # (there are no deleted files, so no possible sources)
203 # 3-way content merge needs to grab these objects:
205 # Nothing else needs to fetch objects
207 # Summary: 2 fetches (1 for 2 objects, 1 for 1 object)
209 test_expect_merge_algorithm failure success 'Objects downloaded for single relevant rename' '
211 git clone --sparse --filter=blob:none "file://$(pwd)/server" objects-single &&
215 git rev-list --objects --all --missing=print |
216 grep '\?' >missing-objects-before &&
218 git checkout -q origin/A &&
220 GIT_TRACE2_PERF="$(pwd)/trace.output" git \
221 -c merge.directoryRenames=true merge --no-stat \
222 --no-progress origin/B-single &&
224 # Check the number of objects we reported we would fetch
225 cat >expect <<-EOF &&
229 grep fetch_count trace.output | cut -d "|" -f 9 | tr -d " ." >actual &&
230 test_cmp expect actual &&
232 # Check the number of fetch commands exec-ed
233 grep d0.*fetch.negotiationAlgorithm trace.output >fetches &&
234 test_line_count = 2 fetches &&
236 git rev-list --objects --all --missing=print |
237 grep ^? >missing-objects-after &&
238 test_cmp missing-objects-before missing-objects-after |
239 grep "^[-+]?" >found-and-new-objects &&
240 # We should not have any NEW missing objects
241 ! grep ^+ found-and-new-objects &&
242 # Fetched 2+1=3 objects, so should have 3 fewer missing objects
243 test_line_count = 3 found-and-new-objects
247 # Testcase: Objects downloaded for directory rename
249 # general/{leap1_O, leap2_O}
250 # basename/{numbers_O, sequence_O, values_O}
251 # dir/subdir/{a,b,c,d,e_O,Makefile_TOP_O}
252 # dir/subdir/tweaked/{f,g,h,Makefile_SUB_O}
253 # dir/unchanged/<LOTS OF FILES>
255 # (Rename leap->jump, rename basename/ -> basename/subdir/, rename dir/ ->
256 # folder/, move e into newsubdir, add newfile.rs, remove f, modify
257 # both Makefiles and jumps)
258 # general/{jump1_A, jump2_A}
259 # basename/subdir/{numbers_A, sequence_A, values_A}
260 # folder/subdir/{a,b,c,d,Makefile_TOP_A}
261 # folder/subdir/newsubdir/{e_A,newfile.rs}
262 # folder/subdir/tweaked/{g,h,Makefile_SUB_A}
263 # folder/unchanged/<LOTS OF FILES>
265 # (add dir/subdir/newfile.{java,scala,groovy}
266 # general/{leap1_O, leap2_O}
267 # basename/{numbers_O, sequence_O, values_O}
268 # dir/subdir/{a,b,c,d,e_O,Makefile_TOP_O,
269 # newfile.java,newfile.scala,newfile.groovy}
270 # dir/subdir/tweaked/{f,g,h,Makefile_SUB_O}
271 # dir/unchanged/<LOTS OF FILES>
273 # general/{jump1_A, jump2_A}
274 # basename/subdir/{numbers_A, sequence_A, values_A}
275 # folder/subdir/{a,b,c,d,Makefile_TOP_A,
276 # newfile.java,newfile.scala,newfile.groovy}
277 # folder/subdir/newsubdir/{e_A,newfile.rs}
278 # folder/subdir/tweaked/{g,h,Makefile_SUB_A}
279 # folder/unchanged/<LOTS OF FILES>
281 # Objects that need to be fetched:
282 # Makefile_TOP_O, Makefile_TOP_A
283 # Makefile_SUB_O, Makefile_SUB_A
285 # * Despite A's rename of jump->leap, those renames are irrelevant.
286 # * Despite A's rename of basename/ -> basename/subdir/, those renames are
288 # * Because of A's rename of dir/ -> folder/ and B-dir's addition of
289 # newfile.* into dir/subdir/, we need to determine directory renames.
290 # (Technically, there are enough exact renames to determine directory
291 # rename detection, but the current implementation always does
292 # basename searching before directory rename detection. Running it
293 # also before basename searching would mean doing directory rename
294 # detection twice, but it's a bit expensive to do that and cases like
295 # this are not all that common.)
296 # Summary: 1 fetches for 6 objects
298 test_expect_merge_algorithm failure success 'Objects downloaded when a directory rename triggered' '
300 git clone --sparse --filter=blob:none "file://$(pwd)/server" objects-dir &&
304 git rev-list --objects --all --missing=print |
305 grep '\?' >missing-objects-before &&
307 git checkout -q origin/A &&
309 GIT_TRACE2_PERF="$(pwd)/trace.output" git \
310 -c merge.directoryRenames=true merge --no-stat \
311 --no-progress origin/B-dir &&
313 # Check the number of objects we reported we would fetch
314 cat >expect <<-EOF &&
317 grep fetch_count trace.output | cut -d "|" -f 9 | tr -d " ." >actual &&
318 test_cmp expect actual &&
320 # Check the number of fetch commands exec-ed
321 grep d0.*fetch.negotiationAlgorithm trace.output >fetches &&
322 test_line_count = 1 fetches &&
324 git rev-list --objects --all --missing=print |
325 grep ^? >missing-objects-after &&
326 test_cmp missing-objects-before missing-objects-after |
327 grep "^[-+]?" >found-and-new-objects &&
328 # We should not have any NEW missing objects
329 ! grep ^+ found-and-new-objects &&
330 # Fetched 6 objects, so should have 6 fewer missing objects
331 test_line_count = 6 found-and-new-objects
335 # Testcase: Objects downloaded with lots of renames and modifications
337 # general/{leap1_O, leap2_O}
338 # basename/{numbers_O, sequence_O, values_O}
339 # dir/subdir/{a,b,c,d,e_O,Makefile_TOP_O}
340 # dir/subdir/tweaked/{f,g,h,Makefile_SUB_O}
341 # dir/unchanged/<LOTS OF FILES>
343 # (Rename leap->jump, rename basename/ -> basename/subdir/, rename dir/
344 # -> folder/, move e into newsubdir, add newfile.rs, remove f, modify
345 # both both Makefiles and jumps)
346 # general/{jump1_A, jump2_A}
347 # basename/subdir/{numbers_A, sequence_A, values_A}
348 # folder/subdir/{a,b,c,d,Makefile_TOP_A}
349 # folder/subdir/newsubdir/{e_A,newfile.rs}
350 # folder/subdir/tweaked/{g,h,Makefile_SUB_A}
351 # folder/unchanged/<LOTS OF FILES>
352 # Commit B(-minimal):
353 # (modify both leaps, rename basename/ -> basename/subdir/, add
355 # general/{leap1_B, leap2_B}
356 # basename/subdir/{numbers_B, sequence_B, values_B}
357 # dir/{a,b,c,d,e_O,Makefile_TOP_O,newfile.c}
358 # dir/tweaked/{f,g,h,Makefile_SUB_O,newfile.py}
359 # dir/unchanged/<LOTS OF FILES>
361 # general/{jump1_Merged, jump2_Merged}
362 # basename/subdir/{numbers_Merged, sequence_Merged, values_Merged}
363 # folder/subdir/{a,b,c,d,Makefile_TOP_A,newfile.c}
364 # folder/subdir/newsubdir/e_A
365 # folder/subdir/tweaked/{g,h,Makefile_SUB_A,newfile.py}
366 # folder/unchanged/<LOTS OF FILES>
368 # Objects that need to be fetched:
371 # Basename-matches rename detection only needs to fetch these objects:
372 # numbers_O, numbers_A
373 # sequence_O, sequence_A
375 # Makefile_TOP_O, Makefile_TOP_A
376 # Makefile_SUB_O, Makefile_SUB_A
378 # General rename detection only needs to fetch these objects:
380 # jump1_A, jump2_A, newfile.rs
381 # (only need remaining relevant sources, but any relevant sources need
382 # to be matched against all possible unpaired destinations)
384 # Basename-matches rename detection only needs to fetch these objects:
388 # (because numbers_O, sequence_O, and values_O already fetched above)
389 # General rename detection only needs to fetch these objects:
392 # 3-way content merge needs to grab these objects:
395 # Nothing else needs to fetch objects
397 # Summary: 4 fetches (1 for 6 objects, 1 for 8, 1 for 3, 1 for 2)
399 test_expect_merge_algorithm failure success 'Objects downloaded with lots of renames and modifications' '
401 git clone --sparse --filter=blob:none "file://$(pwd)/server" objects-many &&
405 git rev-list --objects --all --missing=print |
406 grep '\?' >missing-objects-before &&
408 git checkout -q origin/A &&
410 GIT_TRACE2_PERF="$(pwd)/trace.output" git \
411 -c merge.directoryRenames=true merge --no-stat \
412 --no-progress origin/B-many &&
414 # Check the number of objects we reported we would fetch
415 cat >expect <<-EOF &&
421 grep fetch_count trace.output | cut -d "|" -f 9 | tr -d " ." >actual &&
422 test_cmp expect actual &&
424 # Check the number of fetch commands exec-ed
425 grep d0.*fetch.negotiationAlgorithm trace.output >fetches &&
426 test_line_count = 4 fetches &&
428 git rev-list --objects --all --missing=print |
429 grep ^? >missing-objects-after &&
430 test_cmp missing-objects-before missing-objects-after |
431 grep "^[-+]?" >found-and-new-objects &&
432 # We should not have any NEW missing objects
433 ! grep ^+ found-and-new-objects &&
434 # Fetched 12 + 5 + 3 + 2 == 22 objects
435 test_line_count = 22 found-and-new-objects