@@ -35,12 +35,67 @@ autosquash move commits that begin with squash!/fixup! under -i
3535require_work_tree
3636
3737DOTEST=" $GIT_DIR /rebase-merge"
38+
39+ # The file containing rebase commands, comments, and empty lines.
40+ # This file is created by "git rebase -i" then edited by the user. As
41+ # the lines are processed, they are removed from the front of this
42+ # file and written to the tail of $DONE.
3843TODO=" $DOTEST " /git-rebase-todo
44+
45+ # The rebase command lines that have already been processed. A line
46+ # is moved here when it is first handled, before any associated user
47+ # actions.
3948DONE=" $DOTEST " /done
49+
50+ # The commit message that is planned to be used for any changes that
51+ # need to be committed following a user interaction.
4052MSG=" $DOTEST " /message
53+
54+ # The file into which is accumulated the suggested commit message for
55+ # squash/fixup commands. When the first of a series of squash/fixups
56+ # is seen, the file is created and the commit message from the
57+ # previous commit and from the first squash/fixup commit are written
58+ # to it. The commit message for each subsequent squash/fixup commit
59+ # is appended to the file as it is processed.
60+ #
61+ # The first line of the file is of the form
62+ # # This is a combination of $COUNT commits.
63+ # where $COUNT is the number of commits whose messages have been
64+ # written to the file so far (including the initial "pick" commit).
65+ # Each time that a commit message is processed, this line is read and
66+ # updated. It is deleted just before the combined commit is made.
4167SQUASH_MSG=" $DOTEST " /message-squash
68+
69+ # If the current series of squash/fixups has not yet included a squash
70+ # command, then this file exists and holds the commit message of the
71+ # original "pick" commit. (If the series ends without a "squash"
72+ # command, then this can be used as the commit message of the combined
73+ # commit without opening the editor.)
74+ FIXUP_MSG=" $DOTEST " /message-fixup
75+
76+ # $REWRITTEN is the name of a directory containing files for each
77+ # commit that is reachable by at least one merge base of $HEAD and
78+ # $UPSTREAM. They are not necessarily rewritten, but their children
79+ # might be. This ensures that commits on merged, but otherwise
80+ # unrelated side branches are left alone. (Think "X" in the man page's
81+ # example.)
4282REWRITTEN=" $DOTEST " /rewritten
83+
4384DROPPED=" $DOTEST " /dropped
85+
86+ # A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
87+ # GIT_AUTHOR_DATE that will be used for the commit that is currently
88+ # being rebased.
89+ AUTHOR_SCRIPT=" $DOTEST " /author-script
90+
91+ # When an "edit" rebase command is being processed, the SHA1 of the
92+ # commit to be edited is recorded in this file. When "git rebase
93+ # --continue" is executed, if there are any staged changes then they
94+ # will be amended to the HEAD commit, but only provided the HEAD
95+ # commit is still the commit to be edited. When any other rebase
96+ # command is processed, this file is deleted.
97+ AMEND=" $DOTEST " /amend
98+
4499PRESERVE_MERGES=
45100STRATEGY=
46101ONTO=
@@ -72,6 +127,11 @@ output () {
72127 esac
73128}
74129
130+ # Output the commit message for the specified commit.
131+ commit_message () {
132+ git cat-file commit " $1 " | sed " 1,/^$/d"
133+ }
134+
75135run_pre_rebase_hook () {
76136 if test -z " $OK_TO_SKIP_PRE_REBASE " &&
77137 test -x " $GIT_DIR /hooks/pre-rebase"
@@ -131,10 +191,10 @@ make_patch () {
131191 echo " Root commit"
132192 ;;
133193 esac > " $DOTEST " /patch
134- test -f " $DOTEST " /message ||
135- git cat-file commit " $1 " | sed " 1,/^$/d " > " $DOTEST " /message
136- test -f " $DOTEST " /author-script ||
137- get_author_ident_from_commit " $1 " > " $DOTEST " /author-script
194+ test -f " $MSG " ||
195+ commit_message " $1 " > " $MSG "
196+ test -f " $AUTHOR_SCRIPT " ||
197+ get_author_ident_from_commit " $1 " > " $AUTHOR_SCRIPT "
138198}
139199
140200die_with_patch () {
@@ -152,25 +212,33 @@ has_action () {
152212 sane_grep ' ^[^#]' " $1 " > /dev/null
153213}
154214
215+ # Run command with GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
216+ # GIT_AUTHOR_DATE exported from the current environment.
217+ do_with_author () {
218+ GIT_AUTHOR_NAME=" $GIT_AUTHOR_NAME " \
219+ GIT_AUTHOR_EMAIL=" $GIT_AUTHOR_EMAIL " \
220+ GIT_AUTHOR_DATE=" $GIT_AUTHOR_DATE " \
221+ " $@ "
222+ }
223+
155224pick_one () {
156225 no_ff=
157226 case " $1 " in -n) sha1=$2 ; no_ff=t ;; * ) sha1=$1 ;; esac
158227 output git rev-parse --verify $sha1 || die " Invalid commit name: $sha1 "
159228 test -d " $REWRITTEN " &&
160229 pick_one_preserving_merges " $@ " && return
161- if test ! -z " $REBASE_ROOT "
230+ if test -n " $REBASE_ROOT "
162231 then
163232 output git cherry-pick " $@ "
164233 return
165234 fi
166235 parent_sha1=$( git rev-parse --verify $sha1 ^) ||
167236 die " Could not get the parent of $sha1 "
168237 current_sha1=$( git rev-parse --verify HEAD)
169- if test " $no_ff$current_sha1 " = " $parent_sha1 " ; then
238+ if test -z " $no_ff " -a " $current_sha1 " = " $parent_sha1 "
239+ then
170240 output git reset --hard $sha1
171- test " a$1 " = a-n && output git reset --soft $current_sha1
172- sha1=$( git rev-parse --short $sha1 )
173- output warn Fast-forward to $sha1
241+ output warn Fast-forward to $( git rev-parse --short $sha1 )
174242 else
175243 output git cherry-pick " $@ "
176244 fi
@@ -271,14 +339,11 @@ pick_one_preserving_merges () {
271339 # redo merge
272340 author_script=$( get_author_ident_from_commit $sha1 )
273341 eval " $author_script "
274- msg=" $( git cat-file commit $sha1 | sed -e ' 1,/^$/d ' ) "
342+ msg=" $( commit_message $sha1 ) "
275343 # No point in merging the first parent, that's HEAD
276344 new_parents=${new_parents# $first_parent }
277- if ! GIT_AUTHOR_NAME=" $GIT_AUTHOR_NAME " \
278- GIT_AUTHOR_EMAIL=" $GIT_AUTHOR_EMAIL " \
279- GIT_AUTHOR_DATE=" $GIT_AUTHOR_DATE " \
280- output git merge $STRATEGY -m " $msg " \
281- $new_parents
345+ if ! do_with_author output \
346+ git merge $STRATEGY -m " $msg " $new_parents
282347 then
283348 printf " %s\n" " $msg " > " $GIT_DIR " /MERGE_MSG
284349 die_with_patch $sha1 " Error redoing merge $sha1 "
@@ -302,50 +367,66 @@ nth_string () {
302367 esac
303368}
304369
305- make_squash_message () {
370+ update_squash_messages () {
306371 if test -f " $SQUASH_MSG " ; then
307- # We want to be careful about matching only the commit
308- # message comment lines generated by this function.
309- # "[snrt][tdh]" matches the nth_string endings.
310- COUNT=$(( $(sed - n "s/ ^# Th[^0 - 9 ]* \([1 - 9 ][0 - 9 ]* \)[snrt][tdh] commit message.*:/ \1 / p" \
311- < "$SQUASH_MSG " | sed -ne '$p ')+1 ))
312- echo " # This is a combination of $COUNT commits."
313- sed -e 1d -e ' 2,/^./{
314- /^$/d
315- }' < " $SQUASH_MSG "
372+ mv " $SQUASH_MSG " " $SQUASH_MSG " .bak || exit
373+ COUNT=$(( $(sed - n \
374+ -e "1 s/^# This is a combination of \(.*\) commits\./\1 /p" \
375+ -e "q" < "$SQUASH_MSG ".bak)+1 ))
376+ {
377+ echo " # This is a combination of $COUNT commits."
378+ sed -e 1d -e ' 2,/^./{
379+ /^$/d
380+ }' < " $SQUASH_MSG " .bak
381+ } > $SQUASH_MSG
316382 else
383+ commit_message HEAD > " $FIXUP_MSG " || die " Cannot write $FIXUP_MSG "
317384 COUNT=2
318- echo " # This is a combination of two commits."
319- echo " # The first commit's message is:"
320- echo
321- git cat-file commit HEAD | sed -e ' 1,/^$/d'
385+ {
386+ echo " # This is a combination of 2 commits."
387+ echo " # The first commit's message is:"
388+ echo
389+ cat " $FIXUP_MSG "
390+ } > $SQUASH_MSG
322391 fi
323392 case $1 in
324393 squash)
394+ rm -f " $FIXUP_MSG "
325395 echo
326396 echo " # This is the $( nth_string $COUNT ) commit message:"
327397 echo
328- git cat-file commit $2 | sed -e ' 1,/^$/d '
398+ commit_message $2
329399 ;;
330400 fixup)
331401 echo
332402 echo " # The $( nth_string $COUNT ) commit message will be skipped:"
333403 echo
334- # Comment the lines of the commit message out using
335- # "# " rather than "# " to make them less likely to
336- # confuse the sed regexp above.
337- git cat-file commit $2 | sed -e ' 1,/^$/d' -e ' s/^/# /'
404+ commit_message $2 | sed -e ' s/^/# /'
338405 ;;
339- esac
406+ esac >> $SQUASH_MSG
340407}
341408
342409peek_next_command () {
343410 sed -n -e " /^#/d" -e " /^$/d" -e " s/ .*//p" -e " q" < " $TODO "
344411}
345412
413+ # A squash/fixup has failed. Prepare the long version of the squash
414+ # commit message, then die_with_patch. This code path requires the
415+ # user to edit the combined commit message for all commits that have
416+ # been squashed/fixedup so far. So also erase the old squash
417+ # messages, effectively causing the combined commit to be used as the
418+ # new basis for any further squash/fixups. Args: sha1 rest
419+ die_failed_squash () {
420+ mv " $SQUASH_MSG " " $MSG " || exit
421+ rm -f " $FIXUP_MSG "
422+ cp " $MSG " " $GIT_DIR " /MERGE_MSG || exit
423+ warn
424+ warn " Could not apply $1 ... $2 "
425+ die_with_patch $1 " "
426+ }
427+
346428do_next () {
347- rm -f " $DOTEST " /message " $DOTEST " /author-script \
348- " $DOTEST " /amend || exit
429+ rm -f " $MSG " " $AUTHOR_SCRIPT " " $AMEND " || exit
349430 read command sha1 rest < " $TODO "
350431 case " $command " in
351432 ' #' * |' ' |noop)
@@ -373,7 +454,7 @@ do_next () {
373454 pick_one $sha1 ||
374455 die_with_patch $sha1 " Could not apply $sha1 ... $rest "
375456 make_patch $sha1
376- git rev-parse --verify HEAD > " $DOTEST " /amend
457+ git rev-parse --verify HEAD > " $AMEND "
377458 warn " Stopped at $sha1 ... $rest "
378459 warn " You can amend the commit now, with"
379460 warn
@@ -400,45 +481,34 @@ do_next () {
400481 die " Cannot '$squash_style ' without a previous commit"
401482
402483 mark_action_done
403- make_squash_message $squash_style $sha1 > " $MSG "
404- failed=f
484+ update_squash_messages $squash_style $sha1
405485 author_script=$( get_author_ident_from_commit HEAD)
486+ echo " $author_script " > " $AUTHOR_SCRIPT "
487+ eval " $author_script "
406488 output git reset --soft HEAD^
407- pick_one -n $sha1 || failed=t
489+ pick_one -n $sha1 || die_failed_squash $sha1 " $rest "
408490 case " $( peek_next_command) " in
409491 squash|s|fixup|f)
410- USE_OUTPUT=output
411- MSG_OPT=-F
412- EDIT_OR_FILE= " $MSG "
413- cp " $MSG " " $SQUASH_MSG "
492+ # This is an intermediate commit; its message will only be
493+ # used in case of trouble. So use the long version:
494+ do_with_author output git commit --no-verify -F " $SQUASH_MSG " ||
495+ die_failed_squash $sha1 " $rest "
414496 ;;
415497 * )
416- USE_OUTPUT=
417- MSG_OPT=
418- EDIT_OR_FILE=-e
419- rm -f " $SQUASH_MSG " || exit
420- cp " $MSG " " $GIT_DIR " /SQUASH_MSG
421- rm -f " $GIT_DIR " /MERGE_MSG || exit
498+ # This is the final command of this squash/fixup group
499+ if test -f " $FIXUP_MSG "
500+ then
501+ do_with_author git commit --no-verify -F " $FIXUP_MSG " ||
502+ die_failed_squash $sha1 " $rest "
503+ else
504+ cp " $SQUASH_MSG " " $GIT_DIR " /SQUASH_MSG || exit
505+ rm -f " $GIT_DIR " /MERGE_MSG
506+ do_with_author git commit --no-verify -e ||
507+ die_failed_squash $sha1 " $rest "
508+ fi
509+ rm -f " $SQUASH_MSG " " $FIXUP_MSG "
422510 ;;
423511 esac
424- echo " $author_script " > " $DOTEST " /author-script
425- if test $failed = f
426- then
427- # This is like --amend, but with a different message
428- eval " $author_script "
429- GIT_AUTHOR_NAME=" $GIT_AUTHOR_NAME " \
430- GIT_AUTHOR_EMAIL=" $GIT_AUTHOR_EMAIL " \
431- GIT_AUTHOR_DATE=" $GIT_AUTHOR_DATE " \
432- $USE_OUTPUT git commit --no-verify \
433- $MSG_OPT " $EDIT_OR_FILE " || failed=t
434- fi
435- if test $failed = t
436- then
437- cp " $MSG " " $GIT_DIR " /MERGE_MSG
438- warn
439- warn " Could not apply $sha1 ... $rest "
440- die_with_patch $sha1 " "
441- fi
442512 ;;
443513 * )
444514 warn " Unknown command: $command $sha1 $rest "
598668 then
599669 : Nothing to commit -- skip this
600670 else
601- . " $DOTEST " /author-script ||
671+ . " $AUTHOR_SCRIPT " ||
602672 die " Cannot find the author identity"
603673 amend=
604- if test -f " $DOTEST " /amend
674+ if test -f " $AMEND "
605675 then
606676 amend=$( git rev-parse --verify HEAD)
607- test " $amend " = $( cat " $DOTEST " /amend ) ||
677+ test " $amend " = $( cat " $AMEND " ) ||
608678 die " \
609679You have uncommitted changes in your working tree. Please, commit them
610680first and then run 'git rebase --continue' again."
611681 git reset --soft HEAD^ ||
612682 die " Cannot rewind the HEAD"
613683 fi
614- export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE &&
615- git commit --no-verify -F " $DOTEST " /message -e || {
684+ do_with_author git commit --no-verify -F " $MSG " -e || {
616685 test -n " $amend " && git reset --soft $amend
617686 die " Could not commit staged changes."
618687 }
@@ -739,13 +808,6 @@ first and then run 'git rebase --continue' again."
739808 test t = " $VERBOSE " && : > " $DOTEST " /verbose
740809 if test t = " $PRESERVE_MERGES "
741810 then
742- # $REWRITTEN contains files for each commit that is
743- # reachable by at least one merge base of $HEAD and
744- # $UPSTREAM. They are not necessarily rewritten, but
745- # their children might be.
746- # This ensures that commits on merged, but otherwise
747- # unrelated side branches are left alone. (Think "X"
748- # in the man page's example.)
749811 if test -z " $REBASE_ROOT "
750812 then
751813 mkdir " $REWRITTEN " &&
0 commit comments