Raw
1 #!/bin/sh
2
3 test_description='tests for git-history split subcommand'
4
5 . ./test-lib.sh
6 . "$TEST_DIRECTORY/lib-log-graph.sh"
7
8 # The fake editor takes multiple arguments, each of which represents a commit
9 # message. Subsequent invocations of the editor will then yield those messages
10 # in order.
11 #
12 set_fake_editor () {
13 printf "%s\n" "$@" >fake-input &&
14 write_script fake-editor.sh <<-\EOF &&
15 head -n1 fake-input >"$1"
16 sed 1d fake-input >fake-input.trimmed &&
17 mv fake-input.trimmed fake-input
18 EOF
19 test_set_editor "$(pwd)"/fake-editor.sh
20 }
21
22 expect_graph () {
23 cat >expect &&
24 lib_test_cmp_graph --graph --format=%s "$@"
25 }
26
27 expect_log () {
28 git log --format="%s" >actual &&
29 cat >expect &&
30 test_cmp expect actual
31 }
32
33 expect_tree_entries () {
34 git ls-tree --name-only "$1" >actual &&
35 cat >expect &&
36 test_cmp expect actual
37 }
38
39 test_expect_success 'refuses to work with merge commits' '
40 test_when_finished "rm -rf repo" &&
41 git init repo &&
42 (
43 cd repo &&
44 test_commit base &&
45 git branch branch &&
46 test_commit ours &&
47 git switch branch &&
48 test_commit theirs &&
49 git switch - &&
50 git merge theirs &&
51 test_must_fail git history split HEAD 2>err &&
52 test_grep "cannot split up merge commit" err &&
53 test_must_fail git history split HEAD~ 2>err &&
54 test_grep "replaying merge commits is not supported yet" err
55 )
56 '
57
58 test_expect_success 'errors on missing commit argument' '
59 test_when_finished "rm -rf repo" &&
60 git init repo &&
61 (
62 cd repo &&
63 test_commit initial &&
64 test_must_fail git history split 2>err &&
65 test_grep "command expects a committish" err
66 )
67 '
68
69 test_expect_success 'errors on unknown revision' '
70 test_when_finished "rm -rf repo" &&
71 git init repo &&
72 (
73 cd repo &&
74 test_commit initial &&
75 test_must_fail git history split does-not-exist 2>err &&
76 test_grep "commit cannot be found" err
77 )
78 '
79
80 test_expect_success '--dry-run does not modify any refs' '
81 test_when_finished "rm -rf repo" &&
82 git init repo &&
83 (
84 cd repo &&
85 test_commit base &&
86 touch bar foo &&
87 git add . &&
88 git commit -m split-me &&
89
90 git refs list --include-root-refs >before &&
91
92 set_fake_editor "first" "second" &&
93 git history split --dry-run HEAD <<-EOF &&
94 y
95 n
96 EOF
97
98 git refs list --include-root-refs >after &&
99 test_cmp before after
100 )
101 '
102
103 test_expect_success 'can split up tip commit' '
104 test_when_finished "rm -rf repo" &&
105 git init repo &&
106 (
107 cd repo &&
108 test_commit initial &&
109 touch bar foo &&
110 git add . &&
111 git commit -m split-me &&
112
113 git symbolic-ref HEAD >expect &&
114 set_fake_editor "first" "second" &&
115 git history split HEAD <<-EOF &&
116 y
117 n
118 EOF
119 git symbolic-ref HEAD >actual &&
120 test_cmp expect actual &&
121
122 expect_log <<-EOF &&
123 second
124 first
125 initial
126 EOF
127
128 expect_tree_entries HEAD~ <<-EOF &&
129 bar
130 initial.t
131 EOF
132
133 expect_tree_entries HEAD <<-EOF &&
134 bar
135 foo
136 initial.t
137 EOF
138
139 git reflog >reflog &&
140 test_grep "split: updating HEAD" reflog
141 )
142 '
143
144 test_expect_success 'can split up root commit' '
145 test_when_finished "rm -rf repo" &&
146 git init repo &&
147 (
148 cd repo &&
149 touch bar foo &&
150 git add . &&
151 git commit -m root &&
152 test_commit tip &&
153
154 set_fake_editor "first" "second" &&
155 git history split HEAD~ <<-EOF &&
156 y
157 n
158 EOF
159
160 expect_log <<-EOF &&
161 tip
162 second
163 first
164 EOF
165
166 expect_tree_entries HEAD~2 <<-EOF &&
167 bar
168 EOF
169
170 expect_tree_entries HEAD~ <<-EOF &&
171 bar
172 foo
173 EOF
174
175 expect_tree_entries HEAD <<-EOF
176 bar
177 foo
178 tip.t
179 EOF
180 )
181 '
182
183 test_expect_success 'can split up in-between commit' '
184 test_when_finished "rm -rf repo" &&
185 git init repo &&
186 (
187 cd repo &&
188 test_commit initial &&
189 touch bar foo &&
190 git add . &&
191 git commit -m split-me &&
192 test_commit tip &&
193
194 set_fake_editor "first" "second" &&
195 git history split HEAD~ <<-EOF &&
196 y
197 n
198 EOF
199
200 expect_log <<-EOF &&
201 tip
202 second
203 first
204 initial
205 EOF
206
207 expect_tree_entries HEAD~2 <<-EOF &&
208 bar
209 initial.t
210 EOF
211
212 expect_tree_entries HEAD~ <<-EOF &&
213 bar
214 foo
215 initial.t
216 EOF
217
218 expect_tree_entries HEAD <<-EOF
219 bar
220 foo
221 initial.t
222 tip.t
223 EOF
224 )
225 '
226
227 test_expect_success 'can split HEAD only' '
228 test_when_finished "rm -rf repo" &&
229 git init repo &&
230 (
231 cd repo &&
232 test_commit base &&
233 touch a b &&
234 git add . &&
235 git commit -m split-me &&
236 git branch unrelated &&
237
238 set_fake_editor "ours-a" "ours-b" &&
239 git history split --update-refs=head HEAD <<-EOF &&
240 y
241 n
242 EOF
243 expect_graph --branches <<-EOF
244 * ours-b
245 * ours-a
246 | * split-me
247 |/
248 * base
249 EOF
250 )
251 '
252
253 test_expect_success 'can split detached HEAD' '
254 test_when_finished "rm -rf repo" &&
255 git init repo &&
256 (
257 cd repo &&
258 test_commit initial &&
259 touch bar foo &&
260 git add . &&
261 git commit -m split-me &&
262 git checkout --detach HEAD &&
263
264 set_fake_editor "first" "second" &&
265 git history split --update-refs=head HEAD <<-EOF &&
266 y
267 n
268 EOF
269
270 # HEAD should be detached and updated.
271 test_must_fail git symbolic-ref HEAD &&
272
273 expect_log <<-EOF
274 second
275 first
276 initial
277 EOF
278 )
279 '
280
281 test_expect_success 'can split commit in unrelated branch' '
282 test_when_finished "rm -rf repo" &&
283 git init repo &&
284 (
285 cd repo &&
286 test_commit base &&
287 git branch ours &&
288 git switch --create theirs &&
289 touch theirs-a theirs-b &&
290 git add . &&
291 git commit -m theirs &&
292 git switch ours &&
293 test_commit ours &&
294
295 # With --update-refs=head it is not possible to split up a
296 # commit that is unrelated to HEAD.
297 test_must_fail git history split --update-refs=head theirs 2>err &&
298 test_grep "rewritten commit must be an ancestor of HEAD" err &&
299
300 set_fake_editor "theirs-rewritten-a" "theirs-rewritten-b" &&
301 git history split theirs <<-EOF &&
302 y
303 n
304 EOF
305 expect_graph --branches <<-EOF &&
306 * ours
307 | * theirs-rewritten-b
308 | * theirs-rewritten-a
309 |/
310 * base
311 EOF
312
313 expect_tree_entries theirs~ <<-EOF &&
314 base.t
315 theirs-a
316 EOF
317
318 expect_tree_entries theirs <<-EOF
319 base.t
320 theirs-a
321 theirs-b
322 EOF
323 )
324 '
325
326 test_expect_success 'updates multiple descendant branches' '
327 test_when_finished "rm -rf repo" &&
328 git init repo --initial-branch=main &&
329 (
330 cd repo &&
331 test_commit base &&
332 touch file-a file-b &&
333 git add . &&
334 git commit -m split-me &&
335 git branch branch &&
336 test_commit on-main &&
337 git switch branch &&
338 test_commit on-branch &&
339 git switch main &&
340
341 set_fake_editor "split-a" "split-b" &&
342 git history split HEAD~ <<-EOF &&
343 y
344 n
345 EOF
346
347 # Both branches should now descend from the split commits.
348 expect_graph --branches <<-EOF
349 * on-branch
350 | * on-main
351 |/
352 * split-b
353 * split-a
354 * base
355 EOF
356 )
357 '
358
359 test_expect_success 'can pick multiple hunks' '
360 test_when_finished "rm -rf repo" &&
361 git init repo &&
362 (
363 cd repo &&
364 touch bar baz foo qux &&
365 git add . &&
366 git commit -m split-me &&
367
368 set_fake_editor "first" "second" &&
369 git history split HEAD <<-EOF &&
370 y
371 n
372 y
373 n
374 EOF
375
376 expect_tree_entries HEAD~ <<-EOF &&
377 bar
378 foo
379 EOF
380
381 expect_tree_entries HEAD <<-EOF
382 bar
383 baz
384 foo
385 qux
386 EOF
387 )
388 '
389
390 test_expect_success 'can use only last hunk' '
391 test_when_finished "rm -rf repo" &&
392 git init repo &&
393 (
394 cd repo &&
395 touch bar foo &&
396 git add . &&
397 git commit -m split-me &&
398
399 set_fake_editor "first" "second" &&
400 git history split HEAD <<-EOF &&
401 n
402 y
403 EOF
404
405 expect_log <<-EOF &&
406 second
407 first
408 EOF
409
410 expect_tree_entries HEAD~ <<-EOF &&
411 foo
412 EOF
413
414 expect_tree_entries HEAD <<-EOF
415 bar
416 foo
417 EOF
418 )
419 '
420
421 test_expect_success 'can split commit with file deletions' '
422 test_when_finished "rm -rf repo" &&
423 git init repo &&
424 (
425 cd repo &&
426 echo a >a &&
427 echo b >b &&
428 echo c >c &&
429 git add . &&
430 git commit -m base &&
431 git rm a b &&
432 git commit -m delete-both &&
433
434 set_fake_editor "delete-a" "delete-b" &&
435 git history split HEAD <<-EOF &&
436 y
437 n
438 EOF
439
440 expect_log <<-EOF &&
441 delete-b
442 delete-a
443 base
444 EOF
445
446 expect_tree_entries HEAD~ <<-EOF &&
447 b
448 c
449 EOF
450
451 expect_tree_entries HEAD <<-EOF
452 c
453 EOF
454 )
455 '
456
457 test_expect_success 'preserves original authorship' '
458 test_when_finished "rm -rf repo" &&
459 git init repo &&
460 (
461 cd repo &&
462 test_commit initial &&
463 touch bar foo &&
464 git add . &&
465 GIT_AUTHOR_NAME="Other Author" \
466 GIT_AUTHOR_EMAIL="other@example.com" \
467 git commit -m split-me &&
468
469 set_fake_editor "first" "second" &&
470 git history split HEAD <<-EOF &&
471 y
472 n
473 EOF
474
475 git log -1 --format="%an <%ae>" HEAD~ >actual &&
476 echo "Other Author <other@example.com>" >expect &&
477 test_cmp expect actual &&
478
479 git log -1 --format="%an <%ae>" HEAD >actual &&
480 test_cmp expect actual
481 )
482 '
483
484 test_expect_success 'aborts with empty commit message' '
485 test_when_finished "rm -rf repo" &&
486 git init repo &&
487 (
488 cd repo &&
489 touch bar foo &&
490 git add . &&
491 git commit -m split-me &&
492
493 set_fake_editor "" &&
494 test_must_fail git history split HEAD <<-EOF 2>err &&
495 y
496 n
497 EOF
498 test_grep "Aborting commit due to empty commit message." err
499 )
500 '
501
502 test_expect_success 'commit message editor sees split-out changes' '
503 test_when_finished "rm -rf repo" &&
504 git init repo &&
505 (
506 cd repo &&
507 touch bar foo &&
508 git add . &&
509 git commit -m split-me &&
510
511 write_script fake-editor.sh <<-\EOF &&
512 cat "$1" >>MESSAGES &&
513 echo "some commit message" >"$1"
514 EOF
515 test_set_editor "$(pwd)"/fake-editor.sh &&
516
517 git history split HEAD <<-EOF &&
518 y
519 n
520 EOF
521
522 # Note that we expect to see the messages twice, once for each
523 # of the commits. The committed files are different though.
524 cat >expect <<-EOF &&
525 split-me
526
527 # Please enter the commit message for the split-out changes. Lines starting
528 # with ${SQ}#${SQ} will be ignored, and an empty message aborts the commit.
529 # Changes to be committed:
530 # new file: bar
531 #
532 split-me
533
534 # Please enter the commit message for the split-out changes. Lines starting
535 # with ${SQ}#${SQ} will be ignored, and an empty message aborts the commit.
536 # Changes to be committed:
537 # new file: foo
538 #
539 EOF
540 test_cmp expect MESSAGES &&
541
542 expect_log <<-EOF
543 some commit message
544 some commit message
545 EOF
546 )
547 '
548
549 test_expect_success 'can use pathspec to limit what gets split' '
550 test_when_finished "rm -rf repo" &&
551 git init repo &&
552 (
553 cd repo &&
554 touch bar foo &&
555 git add . &&
556 git commit -m split-me &&
557
558 set_fake_editor "first" "second" &&
559 git history split HEAD -- foo <<-EOF &&
560 y
561 EOF
562
563 expect_tree_entries HEAD~ <<-EOF &&
564 foo
565 EOF
566
567 expect_tree_entries HEAD <<-EOF
568 bar
569 foo
570 EOF
571 )
572 '
573
574 test_expect_success 'pathspec matching no files produces empty split error' '
575 test_when_finished "rm -rf repo" &&
576 git init repo &&
577 (
578 cd repo &&
579 test_commit initial &&
580 touch bar foo &&
581 git add . &&
582 git commit -m split-me &&
583
584 set_fake_editor "first" "second" &&
585 test_must_fail git history split HEAD -- nonexistent 2>err &&
586 test_grep "split commit is empty" err
587 )
588 '
589
590 test_expect_success 'split with multiple pathspecs' '
591 test_when_finished "rm -rf repo" &&
592 git init repo &&
593 (
594 cd repo &&
595 test_commit initial &&
596 touch a b c d &&
597 git add . &&
598 git commit -m split-me &&
599
600 # Only a and c should be offered for splitting.
601 set_fake_editor "split-ac" "remainder" &&
602 git history split HEAD -- a c <<-EOF &&
603 y
604 y
605 EOF
606
607 expect_tree_entries HEAD~ <<-EOF &&
608 a
609 c
610 initial.t
611 EOF
612
613 expect_tree_entries HEAD <<-EOF
614 a
615 b
616 c
617 d
618 initial.t
619 EOF
620 )
621 '
622
623 test_expect_success 'split with file mode change' '
624 test_when_finished "rm -rf repo" &&
625 git init repo &&
626 (
627 cd repo &&
628 echo content >script &&
629 git add . &&
630 git commit -m base &&
631 test_chmod +x script &&
632 echo change >script &&
633 git commit -a -m "mode and content change" &&
634
635 set_fake_editor "mode-change" "content-change" &&
636 git history split HEAD <<-EOF &&
637 y
638 n
639 EOF
640
641 expect_log <<-EOF
642 content-change
643 mode-change
644 base
645 EOF
646 )
647 '
648
649 test_expect_success 'refuses to create empty split-out commit' '
650 test_when_finished "rm -rf repo" &&
651 git init repo &&
652 (
653 cd repo &&
654 test_commit base &&
655 touch bar foo &&
656 git add . &&
657 git commit -m split-me &&
658
659 test_must_fail git history split HEAD 2>err <<-EOF &&
660 n
661 n
662 EOF
663 test_grep "split commit is empty" err
664 )
665 '
666
667 test_expect_success 'hooks are not executed for rewritten commits' '
668 test_when_finished "rm -rf repo" &&
669 git init repo &&
670 (
671 cd repo &&
672 touch bar foo &&
673 git add . &&
674 git commit -m split-me &&
675 old_head=$(git rev-parse HEAD) &&
676
677 ORIG_PATH="$(pwd)" &&
678 export ORIG_PATH &&
679 for hook in prepare-commit-msg pre-commit post-commit post-rewrite commit-msg
680 do
681 write_script .git/hooks/$hook <<-\EOF || exit 1
682 touch "$ORIG_PATH"/hooks.log
683 EOF
684 done &&
685
686 set_fake_editor "first" "second" &&
687 git history split HEAD <<-EOF &&
688 y
689 n
690 EOF
691
692 expect_log <<-EOF &&
693 second
694 first
695 EOF
696
697 test_path_is_missing hooks.log
698 )
699 '
700
701 test_expect_success 'refuses to create empty original commit' '
702 test_when_finished "rm -rf repo" &&
703 git init repo &&
704 (
705 cd repo &&
706 touch bar foo &&
707 git add . &&
708 git commit -m split-me &&
709
710 test_must_fail git history split HEAD 2>err <<-EOF &&
711 y
712 y
713 EOF
714 test_grep "split commit tree matches original commit" err
715 )
716 '
717
718 test_expect_success 'retains changes in the worktree and index' '
719 test_when_finished "rm -rf repo" &&
720 git init repo &&
721 (
722 cd repo &&
723 echo a >a &&
724 echo b >b &&
725 git add . &&
726 git commit -m "initial commit" &&
727 echo a-modified >a &&
728 echo b-modified >b &&
729 git add b &&
730 set_fake_editor "a-only" "remainder" &&
731 git history split HEAD <<-EOF &&
732 y
733 n
734 EOF
735
736 expect_tree_entries HEAD~ <<-EOF &&
737 a
738 EOF
739 expect_tree_entries HEAD <<-EOF &&
740 a
741 b
742 EOF
743
744 cat >expect <<-\EOF &&
745 M a
746 M b
747 ?? actual
748 ?? expect
749 ?? fake-editor.sh
750 ?? fake-input
751 EOF
752 git status --porcelain >actual &&
753 test_cmp expect actual
754 )
755 '
756
757 test_done