3 test_description
='git commit porcelain-ish'
5 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
=main
6 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
11 expect
=commit_msg_is.expect
12 actual
=commit_msg_is.actual
14 printf "%s" "$(git log --pretty=format:%s%b -1)" >$actual &&
15 printf "%s" "$1" >$expect &&
16 test_cmp
$expect $actual
19 # Arguments: [<prefix] [<commit message>] [<commit options>]
20 check_summary_oneline
() {
22 git commit
${3+"$3"} -m "$2" >raw
&&
26 SUMMARY_PREFIX
="$(git name-rev --name-only HEAD)" &&
28 # append the "special" prefix, like "root-commit", "detached HEAD"
31 SUMMARY_PREFIX
="$SUMMARY_PREFIX ($1)"
35 SUMMARY_POSTFIX
="$(git log -1 --pretty='format:%h')"
36 echo "[$SUMMARY_PREFIX $SUMMARY_POSTFIX] $2" >exp
&&
41 trailer_commit_base
() {
44 git commit
-s --trailer "Signed-off-by=C1 E1 " \
45 --trailer "Helped-by:C2 E2 " \
46 --trailer "Reported-by=C3 E3" \
47 --trailer "Mentored-by:C4 E4" \
51 test_expect_success
'output summary format' '
55 check_summary_oneline "root-commit" "initial" &&
57 echo change >>file1 &&
61 test_expect_success
'output summary format: root-commit' '
62 check_summary_oneline "" "a change"
65 test_expect_success
'output summary format for commit with an empty diff' '
67 check_summary_oneline "" "empty" "--allow-empty"
70 test_expect_success
'output summary format for merges' '
72 git checkout -b recursive-base &&
73 test_commit base file1 &&
75 git checkout -b recursive-a recursive-base &&
76 test_commit commit-a file1 &&
78 git checkout -b recursive-b recursive-base &&
79 test_commit commit-b file1 &&
82 git checkout recursive-a &&
83 test_must_fail git merge recursive-b &&
84 # resolve the conflict
85 echo commit-a >file1 &&
87 check_summary_oneline "" "Merge"
90 output_tests_cleanup
() {
91 # this is needed for "do not fire editor in the presence of conflicts"
94 # this is needed for the "partial removal" test to pass
96 git commit
-m "cleanup"
99 test_expect_success
'the basics' '
101 output_tests_cleanup &&
103 echo doing partial >"commit is" &&
105 echo very much encouraged but we should >not/forbid &&
106 git add "commit is" not &&
107 echo update added "commit is" file >"commit is" &&
108 echo also update another >not/forbid &&
110 git commit -a -m "initial with -a" &&
112 git cat-file blob HEAD:"commit is" >current.1 &&
113 git cat-file blob HEAD:not/forbid >current.2 &&
115 cmp current.1 "commit is" &&
116 cmp current.2 not/forbid
120 test_expect_success
'partial' '
122 echo another >"commit is" &&
123 echo another >not/forbid &&
125 git commit -m "partial commit to handle a file" "commit is" &&
127 changed=$(git diff-tree --name-only HEAD^ HEAD) &&
128 test "$changed" = "commit is"
132 test_expect_success
'partial modification in a subdirectory' '
135 git commit -m "partial commit to subdirectory" not &&
137 changed=$(git diff-tree -r --name-only HEAD^ HEAD) &&
138 test "$changed" = "not/forbid"
142 test_expect_success
'partial removal' '
145 git commit -m "partial commit to remove not/forbid" not &&
147 changed=$(git diff-tree -r --name-only HEAD^ HEAD) &&
148 test "$changed" = "not/forbid" &&
149 remain=$(git ls-tree -r --name-only HEAD) &&
150 test "$remain" = "commit is"
154 test_expect_success
'sign off' '
158 git commit -s -m "thank you" &&
159 git cat-file commit HEAD >commit.msg &&
160 sed -ne "s/Signed-off-by: //p" commit.msg >actual &&
161 git var GIT_COMMITTER_IDENT >ident &&
162 sed -e "s/>.*/>/" ident >expected &&
163 test_cmp expected actual
167 test_expect_success
'commit --trailer with "="' '
168 trailer_commit_base &&
169 cat >expected <<-\EOF &&
172 Signed-off-by: C O Mitter <committer@example.com>
178 git cat-file commit HEAD >commit.msg &&
179 sed -e "1,/^\$/d" commit.msg >actual &&
180 test_cmp expected actual
183 test_expect_success
'commit --trailer with -c and "replace" as ifexists' '
184 trailer_commit_base &&
185 cat >expected <<-\EOF &&
188 Signed-off-by: C O Mitter <committer@example.com>
194 git -c trailer.ifexists="replace" \
195 commit --trailer "Mentored-by: C4 E4" \
196 --trailer "Helped-by: C3 E3" \
198 git cat-file commit HEAD >commit.msg &&
199 sed -e "1,/^\$/d" commit.msg >actual &&
200 test_cmp expected actual
203 test_expect_success
'commit --trailer with -c and "add" as ifexists' '
204 trailer_commit_base &&
205 cat >expected <<-\EOF &&
208 Signed-off-by: C O Mitter <committer@example.com>
216 git -c trailer.ifexists="add" \
217 commit --trailer "Reported-by: C3 E3" \
218 --trailer "Mentored-by: C4 E4" \
220 git cat-file commit HEAD >commit.msg &&
221 sed -e "1,/^\$/d" commit.msg >actual &&
222 test_cmp expected actual
225 test_expect_success
'commit --trailer with -c and "donothing" as ifexists' '
226 trailer_commit_base &&
227 cat >expected <<-\EOF &&
230 Signed-off-by: C O Mitter <committer@example.com>
237 git -c trailer.ifexists="donothing" \
238 commit --trailer "Mentored-by: C5 E5" \
239 --trailer "Reviewed-by: C6 E6" \
241 git cat-file commit HEAD >commit.msg &&
242 sed -e "1,/^\$/d" commit.msg >actual &&
243 test_cmp expected actual
246 test_expect_success
'commit --trailer with -c and "addIfDifferent" as ifexists' '
247 trailer_commit_base &&
248 cat >expected <<-\EOF &&
251 Signed-off-by: C O Mitter <committer@example.com>
258 git -c trailer.ifexists="addIfDifferent" \
259 commit --trailer "Reported-by: C3 E3" \
260 --trailer "Mentored-by: C5 E5" \
262 git cat-file commit HEAD >commit.msg &&
263 sed -e "1,/^\$/d" commit.msg >actual &&
264 test_cmp expected actual
267 test_expect_success
'commit --trailer with -c and "addIfDifferentNeighbor" as ifexists' '
268 trailer_commit_base &&
269 cat >expected <<-\EOF &&
272 Signed-off-by: C O Mitter <committer@example.com>
279 git -c trailer.ifexists="addIfDifferentNeighbor" \
280 commit --trailer "Mentored-by: C4 E4" \
281 --trailer "Reported-by: C3 E3" \
283 git cat-file commit HEAD >commit.msg &&
284 sed -e "1,/^\$/d" commit.msg >actual &&
285 test_cmp expected actual
288 test_expect_success
'commit --trailer with -c and "end" as where' '
289 trailer_commit_base &&
290 cat >expected <<-\EOF &&
293 Signed-off-by: C O Mitter <committer@example.com>
301 git -c trailer.where="end" \
302 commit --trailer "Reported-by: C3 E3" \
303 --trailer "Mentored-by: C4 E4" \
305 git cat-file commit HEAD >commit.msg &&
306 sed -e "1,/^\$/d" commit.msg >actual &&
307 test_cmp expected actual
310 test_expect_success
'commit --trailer with -c and "start" as where' '
311 trailer_commit_base &&
312 cat >expected <<-\EOF &&
316 Signed-off-by: C O Mitter <committer@example.com>
322 git -c trailer.where="start" \
323 commit --trailer "Signed-off-by: C O Mitter <committer@example.com>" \
324 --trailer "Signed-off-by: C1 E1" \
326 git cat-file commit HEAD >commit.msg &&
327 sed -e "1,/^\$/d" commit.msg >actual &&
328 test_cmp expected actual
331 test_expect_success
'commit --trailer with -c and "after" as where' '
332 trailer_commit_base &&
333 cat >expected <<-\EOF &&
336 Signed-off-by: C O Mitter <committer@example.com>
343 git -c trailer.where="after" \
344 commit --trailer "Mentored-by: C4 E4" \
345 --trailer "Mentored-by: C5 E5" \
347 git cat-file commit HEAD >commit.msg &&
348 sed -e "1,/^\$/d" commit.msg >actual &&
349 test_cmp expected actual
352 test_expect_success
'commit --trailer with -c and "before" as where' '
353 trailer_commit_base &&
354 cat >expected <<-\EOF &&
357 Signed-off-by: C O Mitter <committer@example.com>
365 git -c trailer.where="before" \
366 commit --trailer "Mentored-by: C3 E3" \
367 --trailer "Mentored-by: C2 E2" \
369 git cat-file commit HEAD >commit.msg &&
370 sed -e "1,/^\$/d" commit.msg >actual &&
371 test_cmp expected actual
374 test_expect_success
'commit --trailer with -c and "donothing" as ifmissing' '
375 trailer_commit_base &&
376 cat >expected <<-\EOF &&
379 Signed-off-by: C O Mitter <committer@example.com>
386 git -c trailer.ifmissing="donothing" \
387 commit --trailer "Helped-by: C5 E5" \
388 --trailer "Based-by: C6 E6" \
390 git cat-file commit HEAD >commit.msg &&
391 sed -e "1,/^\$/d" commit.msg >actual &&
392 test_cmp expected actual
395 test_expect_success
'commit --trailer with -c and "add" as ifmissing' '
396 trailer_commit_base &&
397 cat >expected <<-\EOF &&
400 Signed-off-by: C O Mitter <committer@example.com>
408 git -c trailer.ifmissing="add" \
409 commit --trailer "Helped-by: C5 E5" \
410 --trailer "Based-by: C6 E6" \
412 git cat-file commit HEAD >commit.msg &&
413 sed -e "1,/^\$/d" commit.msg >actual &&
414 test_cmp expected actual
417 test_expect_success
'commit --trailer with -c ack.key ' '
418 echo "fun" >>file1 &&
420 cat >expected <<-\EOF &&
425 git -c trailer.ack.key="Acked-by" \
426 commit --trailer "ack = Peff" -m "hello" &&
427 git cat-file commit HEAD >commit.msg &&
428 sed -e "1,/^\$/d" commit.msg >actual &&
429 test_cmp expected actual
432 test_expect_success
'commit --trailer with -c and ":=#" as separators' '
433 echo "fun" >>file1 &&
435 cat >expected <<-\EOF &&
440 git -c trailer.separators=":=#" \
441 -c trailer.bug.key="Bug #" \
442 commit --trailer "bug = 42" -m "I hate bug" &&
443 git cat-file commit HEAD >commit.msg &&
444 sed -e "1,/^\$/d" commit.msg >actual &&
445 test_cmp expected actual
448 test_expect_success
'commit --trailer with -c and command' '
449 trailer_commit_base &&
450 cat >expected <<-\EOF &&
453 Signed-off-by: C O Mitter <committer@example.com>
457 Reported-by: A U Thor <author@example.com>
459 git -c trailer.report.key="Reported-by: " \
460 -c trailer.report.ifexists="replace" \
461 -c trailer.report.command="NAME=\"\$ARG\"; test -n \"\$NAME\" && \
462 git log --author=\"\$NAME\" -1 --format=\"format:%aN <%aE>\" || true" \
463 commit --trailer "report = author" --amend &&
464 git cat-file commit HEAD >commit.msg &&
465 sed -e "1,/^\$/d" commit.msg >actual &&
466 test_cmp expected actual
469 test_expect_success
'multiple -m' '
473 git commit -m "one" -m "two" -m "three" &&
474 actual=$(git cat-file commit HEAD >tmp && sed -e "1,/^\$/d" tmp && rm tmp) &&
475 expected=$(test_write_lines "one" "" "two" "" "three") &&
476 test "z$actual" = "z$expected"
480 test_expect_success
'verbose' '
482 echo minus >negative &&
484 git status -v >raw &&
485 sed -ne "/^diff --git /p" raw >actual &&
486 echo "diff --git a/negative b/negative" >expect &&
487 test_cmp expect actual
491 test_expect_success
'verbose respects diff config' '
493 test_config diff.noprefix true &&
494 git status -v >actual &&
495 grep "diff --git negative negative" actual
498 mesg_with_comment_and_newlines
='
503 test_expect_success
'prepare file with comment line and trailing newlines' '
504 printf "%s" "$mesg_with_comment_and_newlines" >expect
507 test_expect_success
'cleanup commit messages (verbatim option,-t)' '
510 git commit --cleanup=verbatim --no-status -t expect -a &&
511 git cat-file -p HEAD >raw &&
512 sed -e "1,/^\$/d" raw >actual &&
513 test_cmp expect actual
517 test_expect_success
'cleanup commit messages (verbatim option,-F)' '
520 git commit --cleanup=verbatim -F expect -a &&
521 git cat-file -p HEAD >raw &&
522 sed -e "1,/^\$/d" raw >actual &&
523 test_cmp expect actual
527 test_expect_success
'cleanup commit messages (verbatim option,-m)' '
530 git commit --cleanup=verbatim -m "$mesg_with_comment_and_newlines" -a &&
531 git cat-file -p HEAD >raw &&
532 sed -e "1,/^\$/d" raw >actual &&
533 test_cmp expect actual
537 test_expect_success
'cleanup commit messages (whitespace option,-F)' '
540 test_write_lines "" "# text" "" >text &&
541 echo "# text" >expect &&
542 git commit --cleanup=whitespace -F text -a &&
543 git cat-file -p HEAD >raw &&
544 sed -e "1,/^\$/d" raw >actual &&
545 test_cmp expect actual
549 test_expect_success
'cleanup commit messages (scissors option,-F,-e)' '
556 # ------------------------ >8 ------------------------
558 # ------------------------ >8 ------------------------
560 # ------------------------ >8 ------------------------
564 cat >expect <<-\EOF &&
567 # ------------------------ >8 ------------------------
570 git commit --cleanup=scissors -e -F text -a &&
571 git cat-file -p HEAD >raw &&
572 sed -e "1,/^\$/d" raw >actual &&
573 test_cmp expect actual
576 test_expect_success
'cleanup commit messages (scissors option,-F,-e, scissors on first line)' '
580 # ------------------------ >8 ------------------------
583 git commit --cleanup=scissors -e -F text -a --allow-empty-message &&
584 git cat-file -p HEAD >raw &&
585 sed -e "1,/^\$/d" raw >actual &&
586 test_must_be_empty actual
589 test_expect_success
'cleanup commit messages (strip option,-F)' '
592 test_write_lines "" "# text" "sample" "" >text &&
593 echo sample >expect &&
594 git commit --cleanup=strip -F text -a &&
595 git cat-file -p HEAD >raw &&
596 sed -e "1,/^\$/d" raw >actual &&
597 test_cmp expect actual
601 test_expect_success
'cleanup commit messages (strip option,-F,-e)' '
604 test_write_lines "" "sample" "" >text &&
605 git commit -e -F text -a &&
606 head -n 4 .git/COMMIT_EDITMSG >actual
611 # Please enter the commit message for your changes. Lines starting
612 # with '#' will be ignored, and an empty message aborts the commit." >expect
614 test_expect_success
'cleanup commit messages (strip option,-F,-e): output' '
615 test_cmp expect actual
618 test_expect_success
'cleanup commit message (fail on invalid cleanup mode option)' '
619 test_must_fail git commit --cleanup=non-existent
622 test_expect_success
'cleanup commit message (fail on invalid cleanup mode configuration)' '
623 test_must_fail git -c commit.cleanup=non-existent commit
626 test_expect_success
'cleanup commit message (no config and no option uses default)' '
627 echo content >>file &&
630 test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment &&
631 git commit --no-status
633 commit_msg_is "commit message"
636 test_expect_success
'cleanup commit message (option overrides default)' '
637 echo content >>file &&
640 test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment &&
641 git commit --cleanup=whitespace --no-status
643 commit_msg_is "commit message # comment"
646 test_expect_success
'cleanup commit message (config overrides default)' '
647 echo content >>file &&
650 test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment &&
651 git -c commit.cleanup=whitespace commit --no-status
653 commit_msg_is "commit message # comment"
656 test_expect_success
'cleanup commit message (option overrides config)' '
657 echo content >>file &&
660 test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment &&
661 git -c commit.cleanup=whitespace commit --cleanup=default
663 commit_msg_is "commit message"
666 test_expect_success
'cleanup commit message (default, -m)' '
667 echo content >>file &&
669 git commit -m "message #comment " &&
670 commit_msg_is "message #comment"
673 test_expect_success
'cleanup commit message (whitespace option, -m)' '
674 echo content >>file &&
676 git commit --cleanup=whitespace --no-status -m "message #comment " &&
677 commit_msg_is "message #comment"
680 test_expect_success
'cleanup commit message (whitespace config, -m)' '
681 echo content >>file &&
683 git -c commit.cleanup=whitespace commit --no-status -m "message #comment " &&
684 commit_msg_is "message #comment"
687 test_expect_success
'message shows author when it is not equal to committer' '
689 git commit -e -m "sample" -a &&
691 "^# Author: *A U Thor <author@example.com>\$" \
695 test_expect_success
'message shows date when it is explicitly set' '
696 git commit --allow-empty -e -m foo --date="2010-01-02T03:04:05" &&
698 "^# Date: *Sat Jan 2 03:04:05 2010 +0000" \
702 test_expect_success AUTOIDENT
'message shows committer when it is automatic' '
706 sane_unset GIT_COMMITTER_EMAIL &&
707 sane_unset GIT_COMMITTER_NAME &&
708 git commit -e -m "sample" -a
710 # the ident is calculated from the system, so we cannot
711 # check the actual value, only that it is there
712 test_i18ngrep "^# Committer: " .git/COMMIT_EDITMSG
715 write_script .git
/FAKE_EDITOR
<<EOF
716 echo editor started >"$(pwd)/.git/result"
720 test_expect_success
!FAIL_PREREQS
,!AUTOIDENT
'do not fire editor when committer is bogus' '
725 sane_unset GIT_COMMITTER_EMAIL &&
726 sane_unset GIT_COMMITTER_NAME &&
727 GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" &&
729 test_must_fail git commit -e -m sample -a
731 test_must_be_empty .git/result
734 test_expect_success
'do not fire editor if -m <msg> was given' '
737 echo "editor not started" >.git/result &&
738 (GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" git commit -m tick) &&
739 test "$(cat .git/result)" = "editor not started"
742 test_expect_success
'do not fire editor if -m "" was given' '
745 echo "editor not started" >.git/result &&
746 (GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" \
747 git commit -m "" --allow-empty-message) &&
748 test "$(cat .git/result)" = "editor not started"
751 test_expect_success
'do not fire editor in the presence of conflicts' '
756 git commit -m "add g" &&
761 git commit -m "modify g and add h" &&
762 git checkout second &&
765 git commit -m second &&
766 # Must fail due to conflict
767 test_must_fail git cherry-pick -n main &&
768 echo "editor not started" >.git/result &&
770 GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" &&
772 test_must_fail git commit
774 test "$(cat .git/result)" = "editor not started"
777 write_script .git
/FAKE_EDITOR
<<EOF
778 # kill -TERM command added below.
781 test_expect_success EXECKEEPSPID
'a SIGTERM should break locks' '
783 ! "$SHELL_PATH" -c '\''
784 echo kill -TERM $$ >>.git/FAKE_EDITOR
785 GIT_EDITOR=.git/FAKE_EDITOR
787 exec git commit -a'\'' &&
788 test ! -f .git/index.lock
791 rm -f .git
/MERGE_MSG .git
/COMMIT_EDITMSG
794 test_expect_success
'Hand committing of a redundant merge removes dups' '
796 git rev-parse second main >expect &&
797 test_must_fail git merge second main &&
798 git checkout main g &&
799 EDITOR=: git commit -a &&
800 git cat-file commit HEAD >raw &&
801 sed -n -e "s/^parent //p" -e "/^$/q" raw >actual &&
802 test_cmp expect actual
806 test_expect_success
'A single-liner subject with a token plus colon is not a footer' '
809 git commit -s -m "hello: kitty" --allow-empty &&
810 git cat-file commit HEAD >raw &&
811 sed -e "1,/^$/d" raw >actual &&
812 test_line_count = 3 actual
816 test_expect_success
'commit -s places sob on third line after two empty lines' '
817 git commit -s --allow-empty --allow-empty-message &&
818 cat <<-EOF >expect &&
821 Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
824 sed -e "/^#/d" -e "s/^:.*//" .git/COMMIT_EDITMSG >actual &&
825 test_cmp expect actual
828 write_script .git
/FAKE_EDITOR
<<\EOF
836 echo '## Custom template' >template
841 GIT_EDITOR
=.git
/FAKE_EDITOR git commit
-a $
* $use_template &&
842 case "$use_template" in
844 test_i18ngrep
! "^## Custom template" .git
/COMMIT_EDITMSG
;;
846 test_i18ngrep
"^## Custom template" .git
/COMMIT_EDITMSG
;;
850 try_commit_status_combo
() {
852 test_expect_success
'commit' '
854 test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
857 test_expect_success
'commit --status' '
858 try_commit --status &&
859 test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
862 test_expect_success
'commit --no-status' '
863 try_commit --no-status &&
864 test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
867 test_expect_success
'commit with commit.status = yes' '
868 test_config commit.status yes &&
870 test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
873 test_expect_success
'commit with commit.status = no' '
874 test_config commit.status no &&
876 test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
879 test_expect_success
'commit --status with commit.status = yes' '
880 test_config commit.status yes &&
881 try_commit --status &&
882 test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
885 test_expect_success
'commit --no-status with commit.status = yes' '
886 test_config commit.status yes &&
887 try_commit --no-status &&
888 test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
891 test_expect_success
'commit --status with commit.status = no' '
892 test_config commit.status no &&
893 try_commit --status &&
894 test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
897 test_expect_success
'commit --no-status with commit.status = no' '
898 test_config commit.status no &&
899 try_commit --no-status &&
900 test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
905 try_commit_status_combo
907 use_template
="-t template"
909 try_commit_status_combo
911 test_expect_success
'commit --status with custom comment character' '
912 test_config core.commentchar ";" &&
913 try_commit --status &&
914 test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG
917 test_expect_success
'switch core.commentchar' '
918 test_commit "#foo" foo &&
919 GIT_EDITOR=.git/FAKE_EDITOR git -c core.commentChar=auto commit --amend &&
920 test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG
923 test_expect_success
'switch core.commentchar but out of options' '
936 git commit --amend -F text &&
938 test_set_editor .git/FAKE_EDITOR &&
939 test_must_fail git -c core.commentChar=auto commit --amend