2
3
test_description='git merge-tree --write-tree'
4
5
-TEST_PASSES_SANITIZE_LEAK=true
5
. ./test-lib.sh
6
7
# This test is ort-specific
137
test_cmp expect actual
138
'
139
140
+# directory rename + content conflict
141
+# Commit O: foo, olddir/{a,b,c}
142
+# Commit A: modify foo, newdir/{a,b,c}
143
+# Commit B: modify foo differently & rename foo -> olddir/bar
144
+# Expected: CONFLICT(content) for for newdir/bar (not olddir/bar or foo)
145
+
146
+test_expect_success 'directory rename + content conflict' '
147
+ # Setup
148
+ git init dir-rename-and-content &&
149
+ (
150
+ cd dir-rename-and-content &&
151
+ test_write_lines 1 2 3 4 5 >foo &&
152
+ mkdir olddir &&
153
+ for i in a b c; do echo $i >olddir/$i; done
154
+ git add foo olddir &&
155
+ git commit -m "original" &&
156
+
157
+ git branch O &&
158
+ git branch A &&
159
+ git branch B &&
160
+
161
+ git checkout A &&
162
+ test_write_lines 1 2 3 4 5 6 >foo &&
163
+ git add foo &&
164
+ git mv olddir newdir &&
165
+ git commit -m "Modify foo, rename olddir to newdir" &&
166
+
167
+ git checkout B &&
168
+ test_write_lines 1 2 3 4 5 six >foo &&
169
+ git add foo &&
170
+ git mv foo olddir/bar &&
171
+ git commit -m "Modify foo & rename foo -> olddir/bar"
172
+ ) &&
173
+ # Testing
174
+ (
175
+ cd dir-rename-and-content &&
176
+
177
+ test_expect_code 1 \
178
+ git merge-tree -z A^0 B^0 >out &&
179
+ printf "\\n" >>out &&
180
+ anonymize_hash out >actual &&
181
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
182
+ HASH
183
+ 100644 HASH 1Qnewdir/bar
184
+ 100644 HASH 2Qnewdir/bar
185
+ 100644 HASH 3Qnewdir/bar
186
+ EOF
187
+
188
+ q_to_nul <<-EOF >>expect &&
189
+ Q2Qnewdir/barQolddir/barQCONFLICT (directory rename suggested)QCONFLICT (file location): foo renamed to olddir/bar in B^0, inside a directory that was renamed in A^0, suggesting it should perhaps be moved to newdir/bar.
190
+ Q1Qnewdir/barQAuto-mergingQAuto-merging newdir/bar
191
+ Q1Qnewdir/barQCONFLICT (contents)QCONFLICT (content): Merge conflict in newdir/bar
192
+ Q
193
+ EOF
194
+ test_cmp expect actual
195
+ )
196
+'
197
+
198
+# rename/delete + modify/delete handling
199
+# Commit O: foo
200
+# Commit A: modify foo + rename to bar
201
+# Commit B: delete foo
202
+# Expected: CONFLICT(rename/delete) + CONFLICT(modify/delete)
203
+
204
+test_expect_success 'rename/delete handling' '
205
+ # Setup
206
+ git init rename-delete &&
207
+ (
208
+ cd rename-delete &&
209
+ test_write_lines 1 2 3 4 5 >foo &&
210
+ git add foo &&
211
+ git commit -m "original" &&
212
+
213
+ git branch O &&
214
+ git branch A &&
215
+ git branch B &&
216
+
217
+ git checkout A &&
218
+ test_write_lines 1 2 3 4 5 6 >foo &&
219
+ git add foo &&
220
+ git mv foo bar &&
221
+ git commit -m "Modify foo, rename to bar" &&
222
+
223
+ git checkout B &&
224
+ git rm foo &&
225
+ git commit -m "remove foo"
226
+ ) &&
227
+ # Testing
228
+ (
229
+ cd rename-delete &&
230
+
231
+ test_expect_code 1 \
232
+ git merge-tree -z A^0 B^0 >out &&
233
+ printf "\\n" >>out &&
234
+ anonymize_hash out >actual &&
235
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
236
+ HASH
237
+ 100644 HASH 1Qbar
238
+ 100644 HASH 2Qbar
239
+ EOF
240
+
241
+ q_to_nul <<-EOF >>expect &&
242
+ Q2QbarQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to bar in A^0, but deleted in B^0.
243
+ Q1QbarQCONFLICT (modify/delete)QCONFLICT (modify/delete): bar deleted in B^0 and modified in A^0. Version A^0 of bar left in tree.
244
+ Q
245
+ EOF
246
+ test_cmp expect actual
247
+ )
248
+'
249
+
250
+# rename/add handling
251
+# Commit O: foo
252
+# Commit A: modify foo, add different bar
253
+# Commit B: modify & rename foo->bar
254
+# Expected: CONFLICT(add/add) [via rename collide] for bar
255
+
256
+test_expect_success 'rename/add handling' '
257
+ # Setup
258
+ git init rename-add &&
259
+ (
260
+ cd rename-add &&
261
+ test_write_lines original 1 2 3 4 5 >foo &&
262
+ git add foo &&
263
+ git commit -m "original" &&
264
+
265
+ git branch O &&
266
+ git branch A &&
267
+ git branch B &&
268
+
269
+ git checkout A &&
270
+ test_write_lines 1 2 3 4 5 >foo &&
271
+ echo "different file" >bar &&
272
+ git add foo bar &&
273
+ git commit -m "Modify foo, add bar" &&
274
+
275
+ git checkout B &&
276
+ test_write_lines original 1 2 3 4 5 6 >foo &&
277
+ git add foo &&
278
+ git mv foo bar &&
279
+ git commit -m "rename foo to bar"
280
+ ) &&
281
+ # Testing
282
+ (
283
+ cd rename-add &&
284
+
285
+ test_expect_code 1 \
286
+ git merge-tree -z A^0 B^0 >out &&
287
+ printf "\\n" >>out &&
288
+
289
+ #
290
+ # First, check that the bar that appears at stage 3 does not
291
+ # correspond to an individual blob anywhere in history
292
+ #
293
+ hash=$(cat out | tr "\0" "\n" | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
294
+ git rev-list --objects --all >all_blobs &&
295
+ ! grep $hash all_blobs &&
296
+
297
+ #
298
+ # Second, check anonymized hash output against expectation
299
+ #
300
+ anonymize_hash out >actual &&
301
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
302
+ HASH
303
+ 100644 HASH 2Qbar
304
+ 100644 HASH 3Qbar
305
+ EOF
306
+
307
+ q_to_nul <<-EOF >>expect &&
308
+ Q1QbarQAuto-mergingQAuto-merging bar
309
+ Q1QbarQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in bar
310
+ Q1QfooQAuto-mergingQAuto-merging foo
311
+ Q
312
+ EOF
313
+ test_cmp expect actual
314
+ )
315
+'
316
+
317
+# rename/add, where add is a mode conflict
318
+# Commit O: foo
319
+# Commit A: modify foo, add symlink bar
320
+# Commit B: modify & rename foo->bar
321
+# Expected: CONFLICT(distinct modes) for bar
322
+
323
+test_expect_success SYMLINKS 'rename/add, where add is a mode conflict' '
324
+ # Setup
325
+ git init rename-add-symlink &&
326
+ (
327
+ cd rename-add-symlink &&
328
+ test_write_lines original 1 2 3 4 5 >foo &&
329
+ git add foo &&
330
+ git commit -m "original" &&
331
+
332
+ git branch O &&
333
+ git branch A &&
334
+ git branch B &&
335
+
336
+ git checkout A &&
337
+ test_write_lines 1 2 3 4 5 >foo &&
338
+ ln -s foo bar &&
339
+ git add foo bar &&
340
+ git commit -m "Modify foo, add symlink bar" &&
341
+
342
+ git checkout B &&
343
+ test_write_lines original 1 2 3 4 5 6 >foo &&
344
+ git add foo &&
345
+ git mv foo bar &&
346
+ git commit -m "rename foo to bar"
347
+ ) &&
348
+ # Testing
349
+ (
350
+ cd rename-add-symlink &&
351
+
352
+ test_expect_code 1 \
353
+ git merge-tree -z A^0 B^0 >out &&
354
+ printf "\\n" >>out &&
355
+
356
+ #
357
+ # First, check that the bar that appears at stage 3 does not
358
+ # correspond to an individual blob anywhere in history
359
+ #
360
+ hash=$(cat out | tr "\0" "\n" | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
361
+ git rev-list --objects --all >all_blobs &&
362
+ ! grep $hash all_blobs &&
363
+
364
+ #
365
+ # Second, check anonymized hash output against expectation
366
+ #
367
+ anonymize_hash out >actual &&
368
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
369
+ HASH
370
+ 120000 HASH 2Qbar
371
+ 100644 HASH 3Qbar~B^0
372
+ EOF
373
+
374
+ q_to_nul <<-EOF >>expect &&
375
+ Q2QbarQbar~B^0QCONFLICT (distinct modes)QCONFLICT (distinct types): bar had different types on each side; renamed one of them so each can be recorded somewhere.
376
+ Q1QfooQAuto-mergingQAuto-merging foo
377
+ Q
378
+ EOF
379
+ test_cmp expect actual
380
+ )
381
+'
382
+
383
+# rename/rename(1to2) + content conflict handling
384
+# Commit O: foo
385
+# Commit A: modify foo & rename to bar
386
+# Commit B: modify foo & rename to baz
387
+# Expected: CONFLICT(rename/rename)
388
+
389
+test_expect_success 'rename/rename + content conflict' '
390
+ # Setup
391
+ git init rr-plus-content &&
392
+ (
393
+ cd rr-plus-content &&
394
+ test_write_lines 1 2 3 4 5 >foo &&
395
+ git add foo &&
396
+ git commit -m "original" &&
397
+
398
+ git branch O &&
399
+ git branch A &&
400
+ git branch B &&
401
+
402
+ git checkout A &&
403
+ test_write_lines 1 2 3 4 5 six >foo &&
404
+ git add foo &&
405
+ git mv foo bar &&
406
+ git commit -m "Modify foo + rename to bar" &&
407
+
408
+ git checkout B &&
409
+ test_write_lines 1 2 3 4 5 6 >foo &&
410
+ git add foo &&
411
+ git mv foo baz &&
412
+ git commit -m "Modify foo + rename to baz"
413
+ ) &&
414
+ # Testing
415
+ (
416
+ cd rr-plus-content &&
417
+
418
+ test_expect_code 1 \
419
+ git merge-tree -z A^0 B^0 >out &&
420
+ printf "\\n" >>out &&
421
+ anonymize_hash out >actual &&
422
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
423
+ HASH
424
+ 100644 HASH 2Qbar
425
+ 100644 HASH 3Qbaz
426
+ 100644 HASH 1Qfoo
427
+ EOF
428
+
429
+ q_to_nul <<-EOF >>expect &&
430
+ Q1QfooQAuto-mergingQAuto-merging foo
431
+ Q3QfooQbarQbazQCONFLICT (rename/rename)QCONFLICT (rename/rename): foo renamed to bar in A^0 and to baz in B^0.
432
+ Q
433
+ EOF
434
+ test_cmp expect actual
435
+ )
436
+'
437
+
438
+# rename/add/delete
439
+# Commit O: foo
440
+# Commit A: rm foo, add different bar
441
+# Commit B: rename foo->bar
442
+# Expected: CONFLICT (rename/delete), CONFLICT(add/add) [via rename collide]
443
+# for bar
444
+
445
+test_expect_success 'rename/add/delete conflict' '
446
+ # Setup
447
+ git init rad &&
448
+ (
449
+ cd rad &&
450
+ echo "original file" >foo &&
451
+ git add foo &&
452
+ git commit -m "original" &&
453
+
454
+ git branch O &&
455
+ git branch A &&
456
+ git branch B &&
457
+
458
+ git checkout A &&
459
+ git rm foo &&
460
+ echo "different file" >bar &&
461
+ git add bar &&
462
+ git commit -m "Remove foo, add bar" &&
463
+
464
+ git checkout B &&
465
+ git mv foo bar &&
466
+ git commit -m "rename foo to bar"
467
+ ) &&
468
+ # Testing
469
+ (
470
+ cd rad &&
471
+
472
+ test_expect_code 1 \
473
+ git merge-tree -z B^0 A^0 >out &&
474
+ printf "\\n" >>out &&
475
+ anonymize_hash out >actual &&
476
+
477
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
478
+ HASH
479
+ 100644 HASH 2Qbar
480
+ 100644 HASH 3Qbar
481
+
482
+ EOF
483
+
484
+ q_to_nul <<-EOF >>expect &&
485
+ 2QbarQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to bar in B^0, but deleted in A^0.
486
+ Q1QbarQAuto-mergingQAuto-merging bar
487
+ Q1QbarQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in bar
488
+ Q
489
+ EOF
490
+ test_cmp expect actual
491
+ )
492
+'
493
+
494
+# rename/rename(2to1)/delete/delete
495
+# Commit O: foo, bar
496
+# Commit A: rename foo->baz, rm bar
497
+# Commit B: rename bar->baz, rm foo
498
+# Expected: 2x CONFLICT (rename/delete), CONFLICT (add/add) via colliding
499
+# renames for baz
500
+
501
+test_expect_success 'rename/rename(2to1)/delete/delete conflict' '
502
+ # Setup
503
+ git init rrdd &&
504
+ (
505
+ cd rrdd &&
506
+ echo foo >foo &&
507
+ echo bar >bar &&
508
+ git add foo bar &&
509
+ git commit -m O &&
510
+
511
+ git branch O &&
512
+ git branch A &&
513
+ git branch B &&
514
+
515
+ git checkout A &&
516
+ git mv foo baz &&
517
+ git rm bar &&
518
+ git commit -m "Rename foo, remove bar" &&
519
+
520
+ git checkout B &&
521
+ git mv bar baz &&
522
+ git rm foo &&
523
+ git commit -m "Rename bar, remove foo"
524
+ ) &&
525
+ # Testing
526
+ (
527
+ cd rrdd &&
528
+
529
+ test_expect_code 1 \
530
+ git merge-tree -z A^0 B^0 >out &&
531
+ printf "\\n" >>out &&
532
+ anonymize_hash out >actual &&
533
+
534
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
535
+ HASH
536
+ 100644 HASH 2Qbaz
537
+ 100644 HASH 3Qbaz
538
+
539
+ EOF
540
+
541
+ q_to_nul <<-EOF >>expect &&
542
+ 2QbazQbarQCONFLICT (rename/delete)QCONFLICT (rename/delete): bar renamed to baz in B^0, but deleted in A^0.
543
+ Q2QbazQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to baz in A^0, but deleted in B^0.
544
+ Q1QbazQAuto-mergingQAuto-merging baz
545
+ Q1QbazQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in baz
546
+ Q
547
+ EOF
548
+ test_cmp expect actual
549
+ )
550
+'
551
+
552
+# mod6: chains of rename/rename(1to2) + add/add via colliding renames
553
+# Commit O: one, three, five
554
+# Commit A: one->two, three->four, five->six
555
+# Commit B: one->six, three->two, five->four
556
+# Expected: three CONFLICT(rename/rename) messages + three CONFLICT(add/add)
557
+# messages; each path in two of the multi-way merged contents
558
+# found in two, four, six
559
+
560
+test_expect_success 'mod6: chains of rename/rename(1to2) and add/add via colliding renames' '
561
+ # Setup
562
+ git init mod6 &&
563
+ (
564
+ cd mod6 &&
565
+ test_seq 11 19 >one &&
566
+ test_seq 31 39 >three &&
567
+ test_seq 51 59 >five &&
568
+ git add . &&
569
+ test_tick &&
570
+ git commit -m "O" &&
571
+
572
+ git branch O &&
573
+ git branch A &&
574
+ git branch B &&
575
+
576
+ git checkout A &&
577
+ test_seq 10 19 >one &&
578
+ echo 40 >>three &&
579
+ git add one three &&
580
+ git mv one two &&
581
+ git mv three four &&
582
+ git mv five six &&
583
+ test_tick &&
584
+ git commit -m "A" &&
585
+
586
+ git checkout B &&
587
+ echo 20 >>one &&
588
+ echo forty >>three &&
589
+ echo 60 >>five &&
590
+ git add one three five &&
591
+ git mv one six &&
592
+ git mv three two &&
593
+ git mv five four &&
594
+ test_tick &&
595
+ git commit -m "B"
596
+ ) &&
597
+ # Testing
598
+ (
599
+ cd mod6 &&
600
+
601
+ test_expect_code 1 \
602
+ git merge-tree -z A^0 B^0 >out &&
603
+ printf "\\n" >>out &&
604
+
605
+ #
606
+ # First, check that some of the hashes that appear as stage
607
+ # conflict entries do not appear as individual blobs anywhere
608
+ # in history.
609
+ #
610
+ hash1=$(cat out | tr "\0" "\n" | head | grep 2.four | cut -f 2 -d " ") &&
611
+ hash2=$(cat out | tr "\0" "\n" | head | grep 3.two | cut -f 2 -d " ") &&
612
+ git rev-list --objects --all >all_blobs &&
613
+ ! grep $hash1 all_blobs &&
614
+ ! grep $hash2 all_blobs &&
615
+
616
+ #
617
+ # Now compare anonymized hash output with expectation
618
+ #
619
+ anonymize_hash out >actual &&
620
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
621
+ HASH
622
+ 100644 HASH 1Qfive
623
+ 100644 HASH 2Qfour
624
+ 100644 HASH 3Qfour
625
+ 100644 HASH 1Qone
626
+ 100644 HASH 2Qsix
627
+ 100644 HASH 3Qsix
628
+ 100644 HASH 1Qthree
629
+ 100644 HASH 2Qtwo
630
+ 100644 HASH 3Qtwo
631
+
632
+ EOF
633
+
634
+ q_to_nul <<-EOF >>expect &&
635
+ 3QfiveQsixQfourQCONFLICT (rename/rename)QCONFLICT (rename/rename): five renamed to six in A^0 and to four in B^0.
636
+ Q1QfourQAuto-mergingQAuto-merging four
637
+ Q1QfourQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in four
638
+ Q1QoneQAuto-mergingQAuto-merging one
639
+ Q3QoneQtwoQsixQCONFLICT (rename/rename)QCONFLICT (rename/rename): one renamed to two in A^0 and to six in B^0.
640
+ Q1QsixQAuto-mergingQAuto-merging six
641
+ Q1QsixQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in six
642
+ Q1QthreeQAuto-mergingQAuto-merging three
643
+ Q3QthreeQfourQtwoQCONFLICT (rename/rename)QCONFLICT (rename/rename): three renamed to four in A^0 and to two in B^0.
644
+ Q1QtwoQAuto-mergingQAuto-merging two
645
+ Q1QtwoQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in two
646
+ Q
647
+ EOF
648
+ test_cmp expect actual
649
+ )
650
+'
651
+
652
+# directory rename + rename/delete + modify/delete + directory/file conflict
653
+# Commit O: foo, olddir/{a,b,c}
654
+# Commit A: delete foo, rename olddir/ -> newdir/, add newdir/bar/file
655
+# Commit B: modify foo & rename foo -> olddir/bar
656
+# Expected: CONFLICT(content) for for newdir/bar (not olddir/bar or foo)
657
+
658
+test_expect_success 'directory rename + rename/delete + modify/delete + directory/file conflict' '
659
+ # Setup
660
+ git init 4-stacked-conflict &&
661
+ (
662
+ cd 4-stacked-conflict &&
663
+ test_write_lines 1 2 3 4 5 >foo &&
664
+ mkdir olddir &&
665
+ for i in a b c; do echo $i >olddir/$i; done
666
+ git add foo olddir &&
667
+ git commit -m "original" &&
668
+
669
+ git branch O &&
670
+ git branch A &&
671
+ git branch B &&
672
+
673
+ git checkout A &&
674
+ git rm foo &&
675
+ git mv olddir newdir &&
676
+ mkdir newdir/bar &&
677
+ >newdir/bar/file &&
678
+ git add newdir/bar/file &&
679
+ git commit -m "rm foo, olddir/ -> newdir/, + newdir/bar/file" &&
680
+
681
+ git checkout B &&
682
+ test_write_lines 1 2 3 4 5 6 >foo &&
683
+ git add foo &&
684
+ git mv foo olddir/bar &&
685
+ git commit -m "Modify foo & rename foo -> olddir/bar"
686
+ ) &&
687
+ # Testing
688
+ (
689
+ cd 4-stacked-conflict &&
690
+
691
+ test_expect_code 1 \
692
+ git merge-tree -z A^0 B^0 >out &&
693
+ printf "\\n" >>out &&
694
+ anonymize_hash out >actual &&
695
+
696
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
697
+ HASH
698
+ 100644 HASH 1Qnewdir/bar~B^0
699
+ 100644 HASH 3Qnewdir/bar~B^0
700
+ EOF
701
+
702
+ q_to_nul <<-EOF >>expect &&
703
+ Q2Qnewdir/barQolddir/barQCONFLICT (directory rename suggested)QCONFLICT (file location): foo renamed to olddir/bar in B^0, inside a directory that was renamed in A^0, suggesting it should perhaps be moved to newdir/bar.
704
+ Q2Qnewdir/barQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to newdir/bar in B^0, but deleted in A^0.
705
+ Q2Qnewdir/bar~B^0Qnewdir/barQCONFLICT (file/directory)QCONFLICT (file/directory): directory in the way of newdir/bar from B^0; moving it to newdir/bar~B^0 instead.
706
+ Q1Qnewdir/bar~B^0QCONFLICT (modify/delete)QCONFLICT (modify/delete): newdir/bar~B^0 deleted in A^0 and modified in B^0. Version B^0 of newdir/bar~B^0 left in tree.
707
+ Q
708
+ EOF
709
+ test_cmp expect actual
710
+ )
711
+'
712
+
713
for opt in $(git merge-tree --git-completion-helper-all)
714
do
715
if test $opt = "--trivial-merge" || test $opt = "--write-tree"