]> git.ipfire.org Git - thirdparty/git.git/blame - git-rebase--interactive.sh
fast-import: introduce "feature notes" command
[thirdparty/git.git] / git-rebase--interactive.sh
CommitLineData
1b1dce4b
JS
1#!/bin/sh
2#
3# Copyright (c) 2006 Johannes E. Schindelin
4
5# SHORT DESCRIPTION
6#
7# This script makes it easy to fix up commits in the middle of a series,
8# and rearrange commits.
9#
10# The original idea comes from Eric W. Biederman, in
11# http://article.gmane.org/gmane.comp.version-control.git/22407
12
7970aaf0
SB
13OPTIONS_KEEPDASHDASH=
14OPTIONS_SPEC="\
15git-rebase [-i] [options] [--] <upstream> [<branch>]
16git-rebase [-i] (--continue | --abort | --skip)
17--
18 Available options are
19v,verbose display a diffstat of what changed upstream
20onto= rebase onto given branch instead of upstream
21p,preserve-merges try to recreate merges instead of ignoring them
22s,strategy= use the given merge strategy
23m,merge always used (no-op)
24i,interactive always used (no-op)
25 Actions:
26continue continue rebasing process
27abort abort rebasing process and restore original branch
28skip skip current patch and continue rebasing process
c4427656 29no-verify override pre-rebase hook from stopping the operation
d911d146 30root rebase all reachable commmits up to the root(s)
f59baa50 31autosquash move commits that begin with squash!/fixup! under -i
7970aaf0 32"
1b1dce4b
JS
33
34. git-sh-setup
35require_work_tree
36
28ed6e7b 37DOTEST="$GIT_DIR/rebase-merge"
80883bb3
MH
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.
c22486c9 43TODO="$DOTEST"/git-rebase-todo
80883bb3
MH
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.
1b1dce4b 48DONE="$DOTEST"/done
80883bb3
MH
49
50# The commit message that is planned to be used for any changes that
51# need to be committed following a user interaction.
6368f3f8 52MSG="$DOTEST"/message
80883bb3
MH
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.
6368f3f8 67SQUASH_MSG="$DOTEST"/message-squash
80883bb3 68
a25eb139
MH
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.)
74FIXUP_MSG="$DOTEST"/message-fixup
75
80883bb3
MH
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.)
f09c9b8c 82REWRITTEN="$DOTEST"/rewritten
80883bb3 83
faae853c 84DROPPED="$DOTEST"/dropped
0aac0de4
MH
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.
89AUTHOR_SCRIPT="$DOTEST"/author-script
90
a4049ae7
MH
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.
97AMEND="$DOTEST"/amend
98
f09c9b8c 99PRESERVE_MERGES=
1b1dce4b 100STRATEGY=
7970aaf0 101ONTO=
1b1dce4b 102VERBOSE=
c4427656 103OK_TO_SKIP_PRE_REBASE=
d911d146 104REBASE_ROOT=
f59baa50 105AUTOSQUASH=
1b1dce4b 106
804c7174
WC
107GIT_CHERRY_PICK_HELP=" After resolving the conflicts,
108mark the corrected paths with 'git add <paths>', and
109run 'git rebase --continue'"
110export GIT_CHERRY_PICK_HELP
111
1b1dce4b
JS
112warn () {
113 echo "$*" >&2
114}
115
dfa49f33
JS
116output () {
117 case "$VERBOSE" in
118 '')
5166810b 119 output=$("$@" 2>&1 )
dfa49f33 120 status=$?
5166810b 121 test $status != 0 && printf "%s\n" "$output"
dfa49f33 122 return $status
376ccb8c 123 ;;
dfa49f33
JS
124 *)
125 "$@"
376ccb8c 126 ;;
dfa49f33
JS
127 esac
128}
129
ee0a4afb
MH
130# Output the commit message for the specified commit.
131commit_message () {
132 git cat-file commit "$1" | sed "1,/^$/d"
133}
134
d70b4a8f 135run_pre_rebase_hook () {
c4427656
NS
136 if test -z "$OK_TO_SKIP_PRE_REBASE" &&
137 test -x "$GIT_DIR/hooks/pre-rebase"
d70b4a8f
NS
138 then
139 "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || {
140 echo >&2 "The pre-rebase hook refused to rebase."
141 exit 1
142 }
143 fi
144}
145
1b1dce4b
JS
146require_clean_work_tree () {
147 # test if working tree is dirty
148 git rev-parse --verify HEAD > /dev/null &&
6848d58c
JS
149 git update-index --ignore-submodules --refresh &&
150 git diff-files --quiet --ignore-submodules &&
151 git diff-index --cached --quiet HEAD --ignore-submodules -- ||
1b1dce4b
JS
152 die "Working tree is dirty"
153}
154
155ORIG_REFLOG_ACTION="$GIT_REFLOG_ACTION"
156
157comment_for_reflog () {
158 case "$ORIG_REFLOG_ACTION" in
159 ''|rebase*)
160 GIT_REFLOG_ACTION="rebase -i ($1)"
161 export GIT_REFLOG_ACTION
376ccb8c 162 ;;
1b1dce4b
JS
163 esac
164}
165
4e673877 166last_count=
1b1dce4b
JS
167mark_action_done () {
168 sed -e 1q < "$TODO" >> "$DONE"
169 sed -e 1d < "$TODO" >> "$TODO".new
170 mv -f "$TODO".new "$TODO"
e1622bfc
JH
171 count=$(sane_grep -c '^[^#]' < "$DONE")
172 total=$(($count+$(sane_grep -c '^[^#]' < "$TODO")))
4e673877
JH
173 if test "$last_count" != "$count"
174 then
175 last_count=$count
176 printf "Rebasing (%d/%d)\r" $count $total
177 test -z "$VERBOSE" || echo
178 fi
1b1dce4b
JS
179}
180
181make_patch () {
4fb1a19d
JS
182 sha1_and_parents="$(git rev-list --parents -1 "$1")"
183 case "$sha1_and_parents" in
184 ?*' '?*' '?*)
185 git diff --cc $sha1_and_parents
186 ;;
187 ?*' '?*)
188 git diff-tree -p "$1^!"
189 ;;
190 *)
191 echo "Root commit"
192 ;;
193 esac > "$DOTEST"/patch
bdb011ad 194 test -f "$MSG" ||
ee0a4afb 195 commit_message "$1" > "$MSG"
0aac0de4
MH
196 test -f "$AUTHOR_SCRIPT" ||
197 get_author_ident_from_commit "$1" > "$AUTHOR_SCRIPT"
1b1dce4b
JS
198}
199
200die_with_patch () {
201 make_patch "$1"
ecfe72ff 202 git rerere
1b1dce4b
JS
203 die "$2"
204}
205
c54b7817
JS
206die_abort () {
207 rm -rf "$DOTEST"
208 die "$1"
209}
210
376ccb8c 211has_action () {
e1622bfc 212 sane_grep '^[^#]' "$1" >/dev/null
376ccb8c
JS
213}
214
7756ecff
MH
215# Run command with GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
216# GIT_AUTHOR_DATE exported from the current environment.
217do_with_author () {
76c9c0db
JH
218 (
219 export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
220 "$@"
221 )
7756ecff
MH
222}
223
1b1dce4b 224pick_one () {
1d25c8cf
JS
225 no_ff=
226 case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac
dfa49f33 227 output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
f09c9b8c
JS
228 test -d "$REWRITTEN" &&
229 pick_one_preserving_merges "$@" && return
699f13ca 230 if test -n "$REBASE_ROOT"
d911d146
TR
231 then
232 output git cherry-pick "$@"
233 return
234 fi
376ccb8c
JS
235 parent_sha1=$(git rev-parse --verify $sha1^) ||
236 die "Could not get the parent of $sha1"
1b1dce4b 237 current_sha1=$(git rev-parse --verify HEAD)
8cddaeec 238 if test -z "$no_ff" && test "$current_sha1" = "$parent_sha1"
1d621fea 239 then
dfa49f33 240 output git reset --hard $sha1
aa7eaff8 241 output warn Fast-forward to $(git rev-parse --short $sha1)
1b1dce4b 242 else
2a9c53e0 243 output git cherry-pick "$@"
1b1dce4b
JS
244 fi
245}
246
f09c9b8c 247pick_one_preserving_merges () {
71d9451e
TR
248 fast_forward=t
249 case "$1" in
250 -n)
251 fast_forward=f
252 sha1=$2
253 ;;
254 *)
255 sha1=$1
256 ;;
257 esac
f09c9b8c
JS
258 sha1=$(git rev-parse $sha1)
259
3b38ec16 260 if test -f "$DOTEST"/current-commit
f09c9b8c 261 then
4c1360f4 262 if test "$fast_forward" = t
bb645071
SH
263 then
264 cat "$DOTEST"/current-commit | while read current_commit
265 do
266 git rev-parse HEAD > "$REWRITTEN"/$current_commit
267 done
268 rm "$DOTEST"/current-commit ||
269 die "Cannot write current commit's replacement sha1"
270 fi
f09c9b8c
JS
271 fi
272
bb645071 273 echo $sha1 >> "$DOTEST"/current-commit
a96dc01e 274
f09c9b8c 275 # rewrite parents; if none were rewritten, we can fast-forward.
f09c9b8c 276 new_parents=
d911d146
TR
277 pend=" $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)"
278 if test "$pend" = " "
279 then
280 pend=" root"
281 fi
faae853c 282 while [ "$pend" != "" ]
f09c9b8c 283 do
faae853c
SH
284 p=$(expr "$pend" : ' \([^ ]*\)')
285 pend="${pend# $p}"
286
3b38ec16 287 if test -f "$REWRITTEN"/$p
f09c9b8c 288 then
f09c9b8c 289 new_p=$(cat "$REWRITTEN"/$p)
80fe82e4
SH
290
291 # If the todo reordered commits, and our parent is marked for
292 # rewriting, but hasn't been gotten to yet, assume the user meant to
293 # drop it on top of the current HEAD
294 if test -z "$new_p"
295 then
296 new_p=$(git rev-parse HEAD)
297 fi
298
f09c9b8c
JS
299 test $p != $new_p && fast_forward=f
300 case "$new_parents" in
301 *$new_p*)
302 ;; # do nothing; that parent is already there
303 *)
304 new_parents="$new_parents $new_p"
376ccb8c 305 ;;
f09c9b8c 306 esac
1c5fa0a1 307 else
faae853c
SH
308 if test -f "$DROPPED"/$p
309 then
310 fast_forward=f
d911d146
TR
311 replacement="$(cat "$DROPPED"/$p)"
312 test -z "$replacement" && replacement=root
313 pend=" $replacement$pend"
faae853c
SH
314 else
315 new_parents="$new_parents $p"
316 fi
f09c9b8c
JS
317 fi
318 done
319 case $fast_forward in
320 t)
a75d7b54 321 output warn "Fast-forward to $sha1"
71d9451e 322 output git reset --hard $sha1 ||
a75d7b54 323 die "Cannot fast-forward to $sha1"
f09c9b8c
JS
324 ;;
325 f)
376ccb8c 326 first_parent=$(expr "$new_parents" : ' \([^ ]*\)')
a4f25e36
SH
327
328 if [ "$1" != "-n" ]
329 then
330 # detach HEAD to current parent
331 output git checkout $first_parent 2> /dev/null ||
332 die "Cannot move HEAD to $first_parent"
333 fi
f09c9b8c 334
f09c9b8c 335 case "$new_parents" in
376ccb8c 336 ' '*' '*)
a4f25e36
SH
337 test "a$1" = a-n && die "Refusing to squash a merge: $sha1"
338
f09c9b8c
JS
339 # redo merge
340 author_script=$(get_author_ident_from_commit $sha1)
341 eval "$author_script"
ee0a4afb 342 msg="$(commit_message $sha1)"
f91333d6
BS
343 # No point in merging the first parent, that's HEAD
344 new_parents=${new_parents# $first_parent}
7756ecff
MH
345 if ! do_with_author output \
346 git merge $STRATEGY -m "$msg" $new_parents
f09c9b8c 347 then
376ccb8c 348 printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
f5b49ea6 349 die_with_patch $sha1 "Error redoing merge $sha1"
f09c9b8c
JS
350 fi
351 ;;
352 *)
2a9c53e0 353 output git cherry-pick "$@" ||
f09c9b8c 354 die_with_patch $sha1 "Could not pick $sha1"
376ccb8c 355 ;;
f09c9b8c 356 esac
376ccb8c 357 ;;
f09c9b8c
JS
358 esac
359}
360
6368f3f8
JS
361nth_string () {
362 case "$1" in
363 *1[0-9]|*[04-9]) echo "$1"th;;
364 *1) echo "$1"st;;
365 *2) echo "$1"nd;;
366 *3) echo "$1"rd;;
367 esac
368}
369
a25eb139 370update_squash_messages () {
3b38ec16 371 if test -f "$SQUASH_MSG"; then
bde1a686 372 mv "$SQUASH_MSG" "$SQUASH_MSG".bak || exit
f99e269c
MH
373 COUNT=$(($(sed -n \
374 -e "1s/^# This is a combination of \(.*\) commits\./\1/p" \
bde1a686
MH
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
3fa7c3da 381 } >"$SQUASH_MSG"
6368f3f8 382 else
a25eb139 383 commit_message HEAD > "$FIXUP_MSG" || die "Cannot write $FIXUP_MSG"
6368f3f8 384 COUNT=2
bde1a686
MH
385 {
386 echo "# This is a combination of 2 commits."
387 echo "# The first commit's message is:"
388 echo
a25eb139 389 cat "$FIXUP_MSG"
3fa7c3da 390 } >"$SQUASH_MSG"
6368f3f8 391 fi
0205e72f
MH
392 case $1 in
393 squash)
a25eb139 394 rm -f "$FIXUP_MSG"
0205e72f
MH
395 echo
396 echo "# This is the $(nth_string $COUNT) commit message:"
397 echo
ee0a4afb 398 commit_message $2
0205e72f
MH
399 ;;
400 fixup)
401 echo
402 echo "# The $(nth_string $COUNT) commit message will be skipped:"
403 echo
ee0a4afb 404 commit_message $2 | sed -e 's/^/# /'
0205e72f 405 ;;
3fa7c3da 406 esac >>"$SQUASH_MSG"
6368f3f8
JS
407}
408
409peek_next_command () {
9524cf29 410 sed -n -e "/^#/d" -e '/^$/d' -e "s/ .*//p" -e "q" < "$TODO"
6368f3f8
JS
411}
412
6bdcd0d2
MH
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
419die_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
1b1dce4b 428do_next () {
a4049ae7 429 rm -f "$MSG" "$AUTHOR_SCRIPT" "$AMEND" || exit
1b1dce4b
JS
430 read command sha1 rest < "$TODO"
431 case "$command" in
ff74126c 432 '#'*|''|noop)
1b1dce4b 433 mark_action_done
1b1dce4b 434 ;;
f8babc4d 435 pick|p)
1b1dce4b
JS
436 comment_for_reflog pick
437
438 mark_action_done
439 pick_one $sha1 ||
440 die_with_patch $sha1 "Could not apply $sha1... $rest"
441 ;;
6741aa6c
BG
442 reword|r)
443 comment_for_reflog reword
444
445 mark_action_done
446 pick_one $sha1 ||
447 die_with_patch $sha1 "Could not apply $sha1... $rest"
7725cb5e 448 git commit --amend
6741aa6c 449 ;;
f8babc4d 450 edit|e)
1b1dce4b
JS
451 comment_for_reflog edit
452
453 mark_action_done
454 pick_one $sha1 ||
455 die_with_patch $sha1 "Could not apply $sha1... $rest"
456 make_patch $sha1
a4049ae7 457 git rev-parse --verify HEAD > "$AMEND"
a8ccc204 458 warn "Stopped at $sha1... $rest"
1b1dce4b
JS
459 warn "You can amend the commit now, with"
460 warn
461 warn " git commit --amend"
462 warn
0460fb44
JS
463 warn "Once you are satisfied with your changes, run"
464 warn
465 warn " git rebase --continue"
466 warn
1b1dce4b
JS
467 exit 0
468 ;;
0205e72f
MH
469 squash|s|fixup|f)
470 case "$command" in
471 squash|s)
472 squash_style=squash
473 ;;
474 fixup|f)
475 squash_style=fixup
476 ;;
477 esac
478 comment_for_reflog $squash_style
1b1dce4b 479
12dd1112 480 test -f "$DONE" && has_action "$DONE" ||
0205e72f 481 die "Cannot '$squash_style' without a previous commit"
1b1dce4b
JS
482
483 mark_action_done
a25eb139 484 update_squash_messages $squash_style $sha1
7c418836 485 author_script=$(get_author_ident_from_commit HEAD)
5c5d059a
MH
486 echo "$author_script" > "$AUTHOR_SCRIPT"
487 eval "$author_script"
7c418836 488 output git reset --soft HEAD^
6bdcd0d2 489 pick_one -n $sha1 || die_failed_squash $sha1 "$rest"
6368f3f8 490 case "$(peek_next_command)" in
0205e72f 491 squash|s|fixup|f)
a25eb139
MH
492 # This is an intermediate commit; its message will only be
493 # used in case of trouble. So use the long version:
6bdcd0d2
MH
494 do_with_author output git commit --no-verify -F "$SQUASH_MSG" ||
495 die_failed_squash $sha1 "$rest"
376ccb8c 496 ;;
6368f3f8 497 *)
a25eb139 498 # This is the final command of this squash/fixup group
6bdcd0d2 499 if test -f "$FIXUP_MSG"
a25eb139 500 then
6bdcd0d2
MH
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"
a25eb139 508 fi
6bdcd0d2 509 rm -f "$SQUASH_MSG" "$FIXUP_MSG"
376ccb8c 510 ;;
6368f3f8 511 esac
1b1dce4b
JS
512 ;;
513 *)
514 warn "Unknown command: $command $sha1 $rest"
f1be316a
JK
515 if git rev-parse --verify -q "$sha1" >/dev/null
516 then
517 die_with_patch $sha1 "Please fix this in the file $TODO."
518 else
519 die "Please fix this in the file $TODO."
520 fi
376ccb8c 521 ;;
1b1dce4b
JS
522 esac
523 test -s "$TODO" && return
524
68a163c9
JS
525 comment_for_reflog finish &&
526 HEADNAME=$(cat "$DOTEST"/head-name) &&
527 OLDHEAD=$(cat "$DOTEST"/head) &&
528 SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) &&
72583e6c 529 NEWHEAD=$(git rev-parse HEAD) &&
73697a0b
JS
530 case $HEADNAME in
531 refs/*)
a0c0447b 532 message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO" &&
73697a0b
JS
533 git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD &&
534 git symbolic-ref HEAD $HEADNAME
535 ;;
536 esac && {
3df0a859 537 test ! -f "$DOTEST"/verbose ||
f3d5e463 538 git diff-tree --stat $(cat "$DOTEST"/head)..HEAD
3df0a859 539 } &&
1b1dce4b 540 rm -rf "$DOTEST" &&
73697a0b 541 git gc --auto &&
1b1dce4b
JS
542 warn "Successfully rebased and updated $HEADNAME."
543
544 exit
545}
546
547do_rest () {
548 while :
549 do
550 do_next
551 done
1b1dce4b
JS
552}
553
0e757e30
JS
554# skip picking commits whose parents are unchanged
555skip_unnecessary_picks () {
556 fd=3
557 while read command sha1 rest
558 do
559 # fd=3 means we skip the command
560 case "$fd,$command,$(git rev-parse --verify --quiet $sha1^)" in
561 3,pick,"$ONTO"*|3,p,"$ONTO"*)
562 # pick a commit whose parent is current $ONTO -> skip
563 ONTO=$sha1
564 ;;
565 3,#*|3,,*)
566 # copy comments
567 ;;
568 *)
569 fd=1
570 ;;
571 esac
572 echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd
573 done <"$TODO" >"$TODO.new" 3>>"$DONE" &&
574 mv -f "$TODO".new "$TODO" ||
575 die "Could not skip unnecessary pick commands"
576}
577
7970aaf0
SB
578# check if no other options are set
579is_standalone () {
580 test $# -eq 2 -a "$2" = '--' &&
581 test -z "$ONTO" &&
582 test -z "$PRESERVE_MERGES" &&
583 test -z "$STRATEGY" &&
584 test -z "$VERBOSE"
585}
586
587get_saved_options () {
588 test -d "$REWRITTEN" && PRESERVE_MERGES=t
589 test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
590 test -f "$DOTEST"/verbose && VERBOSE=t
a6c7a276 591 test -f "$DOTEST"/rebase-root && REBASE_ROOT=t
7970aaf0
SB
592}
593
f59baa50
NS
594# Rearrange the todo list that has both "pick sha1 msg" and
595# "pick sha1 fixup!/squash! msg" appears in it so that the latter
596# comes immediately after the former, and change "pick" to
597# "fixup"/"squash".
598rearrange_squash () {
599 sed -n -e 's/^pick \([0-9a-f]*\) \(squash\)! /\1 \2 /p' \
600 -e 's/^pick \([0-9a-f]*\) \(fixup\)! /\1 \2 /p' \
601 "$1" >"$1.sq"
602 test -s "$1.sq" || return
603
604 used=
605 while read pick sha1 message
606 do
607 case " $used" in
608 *" $sha1 "*) continue ;;
609 esac
610 echo "$pick $sha1 $message"
611 while read squash action msg
612 do
613 case "$message" in
614 "$msg"*)
615 echo "$action $squash $action! $msg"
616 used="$used$squash "
617 ;;
618 esac
619 done <"$1.sq"
620 done >"$1.rearranged" <"$1"
621 cat "$1.rearranged" >"$1"
622 rm -f "$1.sq" "$1.rearranged"
623}
624
230a4566
NS
625LF='
626'
627parse_onto () {
628 case "$1" in
629 *...*)
630 if left=${1%...*} right=${1#*...} &&
631 onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD})
632 then
633 case "$onto" in
634 ?*"$LF"?* | '')
635 exit 1 ;;
636 esac
637 echo "$onto"
638 exit 0
639 fi
640 esac
641 git rev-parse --verify "$1^0"
642}
643
822f7c73 644while test $# != 0
1b1dce4b
JS
645do
646 case "$1" in
c4427656
NS
647 --no-verify)
648 OK_TO_SKIP_PRE_REBASE=yes
649 ;;
650 --verify)
651 ;;
1b1dce4b 652 --continue)
7970aaf0
SB
653 is_standalone "$@" || usage
654 get_saved_options
1b1dce4b
JS
655 comment_for_reflog continue
656
657 test -d "$DOTEST" || die "No interactive rebase running"
658
ab119032
JH
659 # Sanity check
660 git rev-parse --verify HEAD >/dev/null ||
661 die "Cannot read HEAD"
6848d58c
JS
662 git update-index --ignore-submodules --refresh &&
663 git diff-files --quiet --ignore-submodules ||
ab119032
JH
664 die "Working tree is dirty"
665
666 # do we have anything to commit?
6848d58c 667 if git diff-index --cached --quiet --ignore-submodules HEAD --
03270628 668 then
ab119032
JH
669 : Nothing to commit -- skip this
670 else
0aac0de4 671 . "$AUTHOR_SCRIPT" ||
ab119032 672 die "Cannot find the author identity"
8beb1f33 673 amend=
a4049ae7 674 if test -f "$AMEND"
ab119032 675 then
8beb1f33 676 amend=$(git rev-parse --verify HEAD)
a4049ae7 677 test "$amend" = $(cat "$AMEND") ||
c14c3c82
DP
678 die "\
679You have uncommitted changes in your working tree. Please, commit them
680first and then run 'git rebase --continue' again."
ab119032
JH
681 git reset --soft HEAD^ ||
682 die "Cannot rewind the HEAD"
683 fi
7756ecff 684 do_with_author git commit --no-verify -F "$MSG" -e || {
8beb1f33
DP
685 test -n "$amend" && git reset --soft $amend
686 die "Could not commit staged changes."
687 }
03270628 688 fi
18640d99 689
1b1dce4b
JS
690 require_clean_work_tree
691 do_rest
692 ;;
693 --abort)
7970aaf0
SB
694 is_standalone "$@" || usage
695 get_saved_options
1b1dce4b
JS
696 comment_for_reflog abort
697
ecfe72ff 698 git rerere clear
1b1dce4b
JS
699 test -d "$DOTEST" || die "No interactive rebase running"
700
701 HEADNAME=$(cat "$DOTEST"/head-name)
702 HEAD=$(cat "$DOTEST"/head)
73697a0b
JS
703 case $HEADNAME in
704 refs/*)
705 git symbolic-ref HEAD $HEADNAME
706 ;;
707 esac &&
dfa49f33 708 output git reset --hard $HEAD &&
1b1dce4b
JS
709 rm -rf "$DOTEST"
710 exit
711 ;;
712 --skip)
7970aaf0
SB
713 is_standalone "$@" || usage
714 get_saved_options
1b1dce4b
JS
715 comment_for_reflog skip
716
ecfe72ff 717 git rerere clear
1b1dce4b
JS
718 test -d "$DOTEST" || die "No interactive rebase running"
719
dfa49f33 720 output git reset --hard && do_rest
1b1dce4b 721 ;;
7970aaf0 722 -s)
1b1dce4b
JS
723 case "$#,$1" in
724 *,*=*)
b5e960b1 725 STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;;
1b1dce4b
JS
726 1,*)
727 usage ;;
728 *)
729 STRATEGY="-s $2"
730 shift ;;
731 esac
732 ;;
7970aaf0 733 -m)
1b1dce4b
JS
734 # we use merge anyway
735 ;;
7970aaf0 736 -v)
1b1dce4b
JS
737 VERBOSE=t
738 ;;
7970aaf0 739 -p)
f09c9b8c
JS
740 PRESERVE_MERGES=t
741 ;;
7970aaf0 742 -i)
1b1dce4b
JS
743 # yeah, we know
744 ;;
d911d146
TR
745 --root)
746 REBASE_ROOT=t
747 ;;
f59baa50
NS
748 --autosquash)
749 AUTOSQUASH=t
750 ;;
7970aaf0
SB
751 --onto)
752 shift
230a4566 753 ONTO=$(parse_onto "$1") ||
7970aaf0 754 die "Does not point to a valid commit: $1"
1b1dce4b 755 ;;
7970aaf0
SB
756 --)
757 shift
277d7e91
JS
758 test -z "$REBASE_ROOT" -a $# -ge 1 -a $# -le 2 ||
759 test ! -z "$REBASE_ROOT" -a $# -le 1 || usage
1b1dce4b
JS
760 test -d "$DOTEST" &&
761 die "Interactive rebase already started"
762
763 git var GIT_COMMITTER_IDENT >/dev/null ||
764 die "You need to set your committer info first"
765
d911d146
TR
766 if test -z "$REBASE_ROOT"
767 then
768 UPSTREAM_ARG="$1"
769 UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
770 test -z "$ONTO" && ONTO=$UPSTREAM
771 shift
772 else
a6c7a276 773 UPSTREAM=
d911d146
TR
774 UPSTREAM_ARG=--root
775 test -z "$ONTO" &&
776 die "You must specify --onto when using --root"
777 fi
778 run_pre_rebase_hook "$UPSTREAM_ARG" "$@"
d8fab023 779
1b1dce4b
JS
780 comment_for_reflog start
781
1b1dce4b
JS
782 require_clean_work_tree
783
d911d146 784 if test ! -z "$1"
1b1dce4b 785 then
d911d146
TR
786 output git show-ref --verify --quiet "refs/heads/$1" ||
787 die "Invalid branchname: $1"
788 output git checkout "$1" ||
789 die "Could not checkout $1"
1b1dce4b
JS
790 fi
791
792 HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?"
5166810b
MK
793 mkdir "$DOTEST" || die "Could not create temporary $DOTEST"
794
1b1dce4b 795 : > "$DOTEST"/interactive || die "Could not mark as interactive"
73697a0b
JS
796 git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null ||
797 echo "detached HEAD" > "$DOTEST"/head-name
1b1dce4b
JS
798
799 echo $HEAD > "$DOTEST"/head
a6c7a276
JH
800 case "$REBASE_ROOT" in
801 '')
802 rm -f "$DOTEST"/rebase-root ;;
803 *)
804 : >"$DOTEST"/rebase-root ;;
805 esac
1b1dce4b 806 echo $ONTO > "$DOTEST"/onto
8e4a91bd 807 test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy
1b1dce4b 808 test t = "$VERBOSE" && : > "$DOTEST"/verbose
3b38ec16 809 if test t = "$PRESERVE_MERGES"
f09c9b8c 810 then
d911d146
TR
811 if test -z "$REBASE_ROOT"
812 then
813 mkdir "$REWRITTEN" &&
814 for c in $(git merge-base --all $HEAD $UPSTREAM)
815 do
816 echo $ONTO > "$REWRITTEN"/$c ||
817 die "Could not init rewritten commits"
818 done
819 else
820 mkdir "$REWRITTEN" &&
821 echo $ONTO > "$REWRITTEN"/root ||
f09c9b8c 822 die "Could not init rewritten commits"
d911d146 823 fi
acc8559a
SH
824 # No cherry-pick because our first pass is to determine
825 # parents to rewrite and skipping dropped commits would
826 # prematurely end our probe
f09c9b8c 827 MERGES_OPTION=
d80d6bc1 828 first_after_upstream="$(git rev-list --reverse --first-parent $UPSTREAM..$HEAD | head -n 1)"
f09c9b8c 829 else
acc8559a 830 MERGES_OPTION="--no-merges --cherry-pick"
f09c9b8c 831 fi
1b1dce4b 832
c54b7817
JS
833 SHORTHEAD=$(git rev-parse --short $HEAD)
834 SHORTONTO=$(git rev-parse --short $ONTO)
d911d146
TR
835 if test -z "$REBASE_ROOT"
836 # this is now equivalent to ! -z "$UPSTREAM"
837 then
838 SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM)
839 REVISIONS=$UPSTREAM...$HEAD
840 SHORTREVISIONS=$SHORTUPSTREAM..$SHORTHEAD
841 else
842 REVISIONS=$ONTO...$HEAD
843 SHORTREVISIONS=$SHORTHEAD
844 fi
6047a234 845 git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
acc8559a 846 --abbrev=7 --reverse --left-right --topo-order \
d911d146 847 $REVISIONS | \
acc8559a
SH
848 sed -n "s/^>//p" | while read shortsha1 rest
849 do
850 if test t != "$PRESERVE_MERGES"
851 then
852 echo "pick $shortsha1 $rest" >> "$TODO"
853 else
854 sha1=$(git rev-parse $shortsha1)
d911d146
TR
855 if test -z "$REBASE_ROOT"
856 then
857 preserve=t
858 for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)
859 do
1830d9cb 860 if test -f "$REWRITTEN"/$p -a \( $p != $ONTO -o $sha1 = $first_after_upstream \)
d911d146
TR
861 then
862 preserve=f
863 fi
864 done
865 else
866 preserve=f
867 fi
acc8559a
SH
868 if test f = "$preserve"
869 then
870 touch "$REWRITTEN"/$sha1
871 echo "pick $shortsha1 $rest" >> "$TODO"
872 fi
873 fi
874 done
875
876 # Watch for commits that been dropped by --cherry-pick
877 if test t = "$PRESERVE_MERGES"
878 then
879 mkdir "$DROPPED"
880 # Save all non-cherry-picked changes
d911d146 881 git rev-list $REVISIONS --left-right --cherry-pick | \
acc8559a
SH
882 sed -n "s/^>//p" > "$DOTEST"/not-cherry-picks
883 # Now all commits and note which ones are missing in
884 # not-cherry-picks and hence being dropped
d911d146 885 git rev-list $REVISIONS |
e249044c 886 while read rev
acc8559a 887 do
e1622bfc 888 if test -f "$REWRITTEN"/$rev -a "$(sane_grep "$rev" "$DOTEST"/not-cherry-picks)" = ""
acc8559a
SH
889 then
890 # Use -f2 because if rev-list is telling us this commit is
891 # not worthwhile, we don't want to track its multiple heads,
892 # just the history of its first-parent for others that will
893 # be rebasing on top of it
d911d146 894 git rev-list --parents -1 $rev | cut -d' ' -s -f2 > "$DROPPED"/$rev
e249044c 895 short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev)
e1622bfc 896 sane_grep -v "^[a-z][a-z]* $short" <"$TODO" > "${TODO}2" ; mv "${TODO}2" "$TODO"
acc8559a
SH
897 rm "$REWRITTEN"/$rev
898 fi
899 done
900 fi
d911d146 901
ff74126c 902 test -s "$TODO" || echo noop >> "$TODO"
f59baa50 903 test -n "$AUTOSQUASH" && rearrange_squash "$TODO"
6047a234
JS
904 cat >> "$TODO" << EOF
905
d911d146 906# Rebase $SHORTREVISIONS onto $SHORTONTO
1b1dce4b
JS
907#
908# Commands:
88b1f0b8 909# p, pick = use commit
6741aa6c 910# r, reword = use commit, but edit the commit message
88b1f0b8
MV
911# e, edit = use commit, but stop for amending
912# s, squash = use commit, but meld into previous commit
0205e72f 913# f, fixup = like "squash", but discard this commit's log message
82576ddb
JS
914#
915# If you remove a line here THAT COMMIT WILL BE LOST.
6047a234 916# However, if you remove everything, the rebase will be aborted.
82576ddb 917#
1b1dce4b 918EOF
1b1dce4b 919
376ccb8c 920 has_action "$TODO" ||
c54b7817 921 die_abort "Nothing to do"
1b1dce4b
JS
922
923 cp "$TODO" "$TODO".backup
ef0c2abf 924 git_editor "$TODO" ||
e49ca974 925 die_abort "Could not execute editor"
1b1dce4b 926
376ccb8c 927 has_action "$TODO" ||
c54b7817
JS
928 die_abort "Nothing to do"
929
0e757e30
JS
930 test -d "$REWRITTEN" || skip_unnecessary_picks
931
22e40795 932 git update-ref ORIG_HEAD $HEAD
dfa49f33 933 output git checkout $ONTO && do_rest
376ccb8c 934 ;;
1b1dce4b
JS
935 esac
936 shift
937done