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
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)
69 echo content
$i >dir
/unchanged
/file_
$i
76 git branch B-single
&&
82 git
rm general
/leap
* &&
84 test_seq
1 9 >general
/jump1
&&
85 cp general
/jump1 general
/jump2
&&
86 echo leap2
>>general
/jump2
&&
88 rm basename
/numbers basename
/sequence basename
/values
&&
89 mkdir
-p basename
/subdir
/
90 cp general
/jump1 basename
/subdir
/numbers
&&
91 cp general
/jump1 basename
/subdir
/sequence
&&
92 cp general
/jump1 basename
/subdir
/values
&&
93 echo numbers
>>basename
/subdir
/numbers
&&
94 echo sequence
>>basename
/subdir
/sequence
&&
95 echo values
>>basename
/subdir
/values
&&
97 git
rm dir
/subdir
/tweaked
/f
&&
98 echo more >>dir
/subdir
/e
&&
99 echo more >>dir
/subdir
/Makefile
&&
100 echo more >>dir
/subdir
/tweaked
/Makefile
&&
101 mkdir dir
/subdir
/newsubdir
&&
102 echo rust code
>dir
/subdir
/newsubdir
/newfile.rs
&&
103 git
mv dir
/subdir
/e dir
/subdir
/newsubdir
/ &&
108 git switch B-single
&&
109 echo new first line
>dir
/subdir
/Makefile
&&
110 cat general
/leap1
>>dir
/subdir
/Makefile
&&
111 echo toplevel makefile
>>dir
/subdir
/Makefile
&&
112 echo perl code
>general
/newfile.pl
&&
114 git commit
-m "B-single" &&
117 echo java code
>dir
/subdir
/newfile.java
&&
118 echo scala code
>dir
/subdir
/newfile.scala
&&
119 echo groovy code
>dir
/subdir
/newfile.groovy
&&
121 git commit
-m "B-dir" &&
124 test_seq
2 10 >general
/leap1
&&
126 cp general
/leap1 general
/leap2
&&
127 echo leap2
>>general
/leap2
&&
129 rm basename
/numbers basename
/sequence basename
/values
&&
130 mkdir
-p basename
/subdir
/
131 cp general
/leap1 basename
/subdir
/numbers
&&
132 cp general
/leap1 basename
/subdir
/sequence
&&
133 cp general
/leap1 basename
/subdir
/values
&&
134 echo numbers
>>basename
/subdir
/numbers
&&
135 echo sequence
>>basename
/subdir
/sequence
&&
136 echo values
>>basename
/subdir
/values
&&
138 mkdir dir
/subdir
/newsubdir
/ &&
139 echo c code
>dir
/subdir
/newfile.c
&&
140 echo python code
>dir
/subdir
/newsubdir
/newfile.py
&&
142 git commit
-m "B-many" &&
148 # Testcase: Objects downloaded for single relevant rename
150 # general/{leap1_O, leap2_O}
151 # basename/{numbers_O, sequence_O, values_O}
152 # dir/subdir/{a,b,c,d,e_O,Makefile_TOP_O}
153 # dir/subdir/tweaked/{f,g,h,Makefile_SUB_O}
154 # dir/unchanged/<LOTS OF FILES>
156 # (Rename leap->jump, rename basename/ -> basename/subdir/, rename dir/
157 # -> folder/, move e into newsubdir, add newfile.rs, remove f, modify
158 # both Makefiles and jumps)
159 # general/{jump1_A, jump2_A}
160 # basename/subdir/{numbers_A, sequence_A, values_A}
161 # folder/subdir/{a,b,c,d,Makefile_TOP_A}
162 # folder/subdir/newsubdir/{e_A,newfile.rs}
163 # folder/subdir/tweaked/{g,h,Makefile_SUB_A}
164 # folder/unchanged/<LOTS OF FILES>
166 # (add newfile.pl, tweak Makefile_TOP)
167 # general/{leap1_O, leap2_O,newfile.pl}
168 # basename/{numbers_O, sequence_O, values_O}
169 # dir/{a,b,c,d,e_O,Makefile_TOP_B}
170 # dir/tweaked/{f,g,h,Makefile_SUB_O}
171 # dir/unchanged/<LOTS OF FILES>
173 # general/{jump1_A, jump2_A,newfile.pl}
174 # basename/subdir/{numbers_A, sequence_A, values_A}
175 # folder/subdir/{a,b,c,d,Makefile_TOP_Merged}
176 # folder/subdir/newsubdir/{e_A,newfile.rs}
177 # folder/subdir/tweaked/{g,h,Makefile_SUB_A}
178 # folder/unchanged/<LOTS OF FILES>
180 # Objects that need to be fetched:
183 # Basename-matches rename detection only needs to fetch these objects:
184 # Makefile_TOP_O, Makefile_TOP_A
185 # (Despite many renames, all others are content irrelevant. They
186 # are also location irrelevant because newfile.rs was added on
187 # the side doing the directory rename, and newfile.pl was added to
188 # a directory that was not renamed on either side.)
189 # General rename detection only needs to fetch these objects:
191 # (Even though newfile.rs, jump[12], basename/subdir/*, and e
192 # could all be used as destinations in rename detection, the
193 # basename detection for Makefile matches up all relevant
194 # sources, so these other files never end up needing to be
197 # Basename-matches rename detection only needs to fetch these objects:
199 # (there are no deleted files, so no possible sources)
200 # General rename detection only needs to fetch these objects:
202 # (there are no deleted files, so no possible sources)
204 # 3-way content merge needs to grab these objects:
206 # Nothing else needs to fetch objects
208 # Summary: 2 fetches (1 for 2 objects, 1 for 1 object)
210 test_expect_merge_algorithm failure success
'Objects downloaded for single relevant rename' '
212 git clone --sparse --filter=blob:none "file://$(pwd)/server" objects-single &&
216 git rev-list --objects --all --missing=print |
217 grep "^?" | sort >missing-objects-before &&
219 git checkout -q origin/A &&
221 GIT_TRACE2_PERF="$(pwd)/trace.output" git \
222 -c merge.directoryRenames=true merge --no-stat \
223 --no-progress origin/B-single &&
225 # Check the number of objects we reported we would fetch
226 cat >expect <<-EOF &&
230 grep fetch_count trace.output | cut -d "|" -f 9 | tr -d " ." >actual &&
231 test_cmp expect actual &&
233 # Check the number of fetch commands exec-ed
234 grep d0.*fetch.negotiationAlgorithm trace.output >fetches &&
235 test_line_count = 2 fetches &&
237 git rev-list --objects --all --missing=print |
238 grep "^?" | sort >missing-objects-after &&
239 comm -2 -3 missing-objects-before missing-objects-after >old &&
240 comm -1 -3 missing-objects-before missing-objects-after >new &&
241 # No new missing objects
242 test_must_be_empty new &&
243 # Fetched 2 + 1 = 3 objects
244 test_line_count = 3 old
248 # Testcase: Objects downloaded for directory rename
250 # general/{leap1_O, leap2_O}
251 # basename/{numbers_O, sequence_O, values_O}
252 # dir/subdir/{a,b,c,d,e_O,Makefile_TOP_O}
253 # dir/subdir/tweaked/{f,g,h,Makefile_SUB_O}
254 # dir/unchanged/<LOTS OF FILES>
256 # (Rename leap->jump, rename basename/ -> basename/subdir/, rename dir/ ->
257 # folder/, move e into newsubdir, add newfile.rs, remove f, modify
258 # both Makefiles and jumps)
259 # general/{jump1_A, jump2_A}
260 # basename/subdir/{numbers_A, sequence_A, values_A}
261 # folder/subdir/{a,b,c,d,Makefile_TOP_A}
262 # folder/subdir/newsubdir/{e_A,newfile.rs}
263 # folder/subdir/tweaked/{g,h,Makefile_SUB_A}
264 # folder/unchanged/<LOTS OF FILES>
266 # (add dir/subdir/newfile.{java,scala,groovy}
267 # general/{leap1_O, leap2_O}
268 # basename/{numbers_O, sequence_O, values_O}
269 # dir/subdir/{a,b,c,d,e_O,Makefile_TOP_O,
270 # newfile.java,newfile.scala,newfile.groovy}
271 # dir/subdir/tweaked/{f,g,h,Makefile_SUB_O}
272 # dir/unchanged/<LOTS OF FILES>
274 # general/{jump1_A, jump2_A}
275 # basename/subdir/{numbers_A, sequence_A, values_A}
276 # folder/subdir/{a,b,c,d,Makefile_TOP_A,
277 # newfile.java,newfile.scala,newfile.groovy}
278 # folder/subdir/newsubdir/{e_A,newfile.rs}
279 # folder/subdir/tweaked/{g,h,Makefile_SUB_A}
280 # folder/unchanged/<LOTS OF FILES>
282 # Objects that need to be fetched:
283 # Makefile_TOP_O, Makefile_TOP_A
284 # Makefile_SUB_O, Makefile_SUB_A
286 # * Despite A's rename of jump->leap, those renames are irrelevant.
287 # * Despite A's rename of basename/ -> basename/subdir/, those renames are
289 # * Because of A's rename of dir/ -> folder/ and B-dir's addition of
290 # newfile.* into dir/subdir/, we need to determine directory renames.
291 # (Technically, there are enough exact renames to determine directory
292 # rename detection, but the current implementation always does
293 # basename searching before directory rename detection. Running it
294 # also before basename searching would mean doing directory rename
295 # detection twice, but it's a bit expensive to do that and cases like
296 # this are not all that common.)
297 # Summary: 1 fetches for 6 objects
299 test_expect_merge_algorithm failure success
'Objects downloaded when a directory rename triggered' '
301 git clone --sparse --filter=blob:none "file://$(pwd)/server" objects-dir &&
305 git rev-list --objects --all --missing=print |
306 grep "^?" | sort >missing-objects-before &&
308 git checkout -q origin/A &&
310 GIT_TRACE2_PERF="$(pwd)/trace.output" git \
311 -c merge.directoryRenames=true merge --no-stat \
312 --no-progress origin/B-dir &&
314 # Check the number of objects we reported we would fetch
315 cat >expect <<-EOF &&
318 grep fetch_count trace.output | cut -d "|" -f 9 | tr -d " ." >actual &&
319 test_cmp expect actual &&
321 # Check the number of fetch commands exec-ed
322 grep d0.*fetch.negotiationAlgorithm trace.output >fetches &&
323 test_line_count = 1 fetches &&
325 git rev-list --objects --all --missing=print |
326 grep "^?" | sort >missing-objects-after &&
327 comm -2 -3 missing-objects-before missing-objects-after >old &&
328 comm -1 -3 missing-objects-before missing-objects-after >new &&
329 # No new missing objects
330 test_must_be_empty new &&
332 test_line_count = 6 old
336 # Testcase: Objects downloaded with lots of renames and modifications
338 # general/{leap1_O, leap2_O}
339 # basename/{numbers_O, sequence_O, values_O}
340 # dir/subdir/{a,b,c,d,e_O,Makefile_TOP_O}
341 # dir/subdir/tweaked/{f,g,h,Makefile_SUB_O}
342 # dir/unchanged/<LOTS OF FILES>
344 # (Rename leap->jump, rename basename/ -> basename/subdir/, rename dir/
345 # -> folder/, move e into newsubdir, add newfile.rs, remove f, modify
346 # both Makefiles and jumps)
347 # general/{jump1_A, jump2_A}
348 # basename/subdir/{numbers_A, sequence_A, values_A}
349 # folder/subdir/{a,b,c,d,Makefile_TOP_A}
350 # folder/subdir/newsubdir/{e_A,newfile.rs}
351 # folder/subdir/tweaked/{g,h,Makefile_SUB_A}
352 # folder/unchanged/<LOTS OF FILES>
353 # Commit B(-minimal):
354 # (modify both leaps, rename basename/ -> basename/subdir/, add
356 # general/{leap1_B, leap2_B}
357 # basename/subdir/{numbers_B, sequence_B, values_B}
358 # dir/{a,b,c,d,e_O,Makefile_TOP_O,newfile.c}
359 # dir/tweaked/{f,g,h,Makefile_SUB_O,newfile.py}
360 # dir/unchanged/<LOTS OF FILES>
362 # general/{jump1_Merged, jump2_Merged}
363 # basename/subdir/{numbers_Merged, sequence_Merged, values_Merged}
364 # folder/subdir/{a,b,c,d,Makefile_TOP_A,newfile.c}
365 # folder/subdir/newsubdir/e_A
366 # folder/subdir/tweaked/{g,h,Makefile_SUB_A,newfile.py}
367 # folder/unchanged/<LOTS OF FILES>
369 # Objects that need to be fetched:
372 # Basename-matches rename detection only needs to fetch these objects:
373 # numbers_O, numbers_A
374 # sequence_O, sequence_A
376 # Makefile_TOP_O, Makefile_TOP_A
377 # Makefile_SUB_O, Makefile_SUB_A
379 # General rename detection only needs to fetch these objects:
381 # jump1_A, jump2_A, newfile.rs
382 # (only need remaining relevant sources, but any relevant sources need
383 # to be matched against all possible unpaired destinations)
385 # Basename-matches rename detection only needs to fetch these objects:
389 # (because numbers_O, sequence_O, and values_O already fetched above)
390 # General rename detection only needs to fetch these objects:
393 # 3-way content merge needs to grab these objects:
396 # Nothing else needs to fetch objects
398 # Summary: 4 fetches (1 for 6 objects, 1 for 8, 1 for 3, 1 for 2)
400 test_expect_merge_algorithm failure success
'Objects downloaded with lots of renames and modifications' '
402 git clone --sparse --filter=blob:none "file://$(pwd)/server" objects-many &&
406 git rev-list --objects --all --missing=print |
407 grep "^?" | sort >missing-objects-before &&
409 git checkout -q origin/A &&
411 GIT_TRACE2_PERF="$(pwd)/trace.output" git \
412 -c merge.directoryRenames=true merge --no-stat \
413 --no-progress origin/B-many &&
415 # Check the number of objects we reported we would fetch
416 cat >expect <<-EOF &&
422 grep fetch_count trace.output | cut -d "|" -f 9 | tr -d " ." >actual &&
423 test_cmp expect actual &&
425 # Check the number of fetch commands exec-ed
426 grep d0.*fetch.negotiationAlgorithm trace.output >fetches &&
427 test_line_count = 4 fetches &&
429 git rev-list --objects --all --missing=print |
430 grep "^?" | sort >missing-objects-after &&
431 comm -2 -3 missing-objects-before missing-objects-after >old &&
432 comm -1 -3 missing-objects-before missing-objects-after >new &&
433 # No new missing objects
434 test_must_be_empty new &&
435 # Fetched 12 + 5 + 3 + 2 = 22 objects
436 test_line_count = 22 old