Merge branch 'seq-builtin-dev' into mymaster
[git/sbeyer.git] / git-rebase--interactive.sh
blobf8353d456a3b9b25283d9eaf9739a56984a53397
1 #!/bin/sh
3 # Copyright (c) 2006 Johannes E. Schindelin
5 # SHORT DESCRIPTION
7 # This script makes it easy to fix up commits in the middle of a series,
8 # and rearrange commits.
10 # The original idea comes from Eric W. Biederman, in
11 # http://article.gmane.org/gmane.comp.version-control.git/22407
13 OPTIONS_KEEPDASHDASH=
14 OPTIONS_SPEC="\
15 git-rebase [-i] [options] [--] <upstream> [<branch>]
16 git-rebase [-i] (--continue | --abort | --skip)
18 Available options are
19 v,verbose display a diffstat of what changed upstream
20 onto= rebase onto given branch instead of upstream
21 p,preserve-merges try to recreate merges instead of ignoring them
22 s,strategy= use the given merge strategy
23 m,merge always used (no-op)
24 i,interactive always used (no-op)
25 Actions:
26 continue continue rebasing process
27 abort abort rebasing process and restore original branch
28 skip skip current patch and continue rebasing process
29 no-verify override pre-rebase hook from stopping the operation
30 root rebase all reachable commmits up to the root(s)
33 . git-sh-setup
34 require_work_tree
36 DOTEST="$GIT_DIR/rebase-merge"
37 TODO="$DOTEST"/git-rebase-todo
38 DONE="$DOTEST"/done
39 MSG="$DOTEST"/message
40 SQUASH_MSG="$DOTEST"/message-squash
41 REWRITTEN="$DOTEST"/rewritten
42 DROPPED="$DOTEST"/dropped
43 PRESERVE_MERGES=
44 STRATEGY=
45 ONTO=
46 VERBOSE=
47 OK_TO_SKIP_PRE_REBASE=
48 REBASE_ROOT=
50 warn () {
51 echo "$*" >&2
54 output () {
55 case "$VERBOSE" in
56 '')
57 output=$("$@" 2>&1 )
58 status=$?
59 test $status != 0 && printf "%s\n" "$output"
60 return $status
63 "$@"
65 esac
68 run_pre_rebase_hook () {
69 if test -z "$OK_TO_SKIP_PRE_REBASE" &&
70 test -x "$GIT_DIR/hooks/pre-rebase"
71 then
72 "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || {
73 echo >&2 "The pre-rebase hook refused to rebase."
74 exit 1
79 require_clean_work_tree () {
80 # test if working tree is dirty
81 git rev-parse --verify HEAD > /dev/null &&
82 git update-index --ignore-submodules --refresh &&
83 git diff-files --quiet --ignore-submodules &&
84 git diff-index --cached --quiet HEAD --ignore-submodules -- ||
85 die "Working tree is dirty"
88 die_abort () {
89 rm -rf "$DOTEST"
90 die "$1"
93 has_action () {
94 grep '^[^#]' "$1" > /dev/null
97 create_todo_line_preserving_merges () {
98 shortsha1=$sha1
99 sha1=$(git rev-parse $sha1)
101 preserve=t
102 test -z "$REBASE_ROOT" || preserve=f
103 new_parents=
104 first_parent=
105 pend=" $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)"
106 if test "$pend" = " "
107 then
108 pend=" root"
110 while [ "$pend" != "" ]
112 p=$(expr "$pend" : ' \([^ ]*\)')
113 pend="${pend# $p}"
115 # check if we've already seen this parent
116 if test -f "$REWRITTEN"/$p
117 then
118 preserve=f
119 new_p=$(cat "$REWRITTEN"/$p)
120 case "$new_parents" in
121 *$new_p*)
122 ;; # do nothing; that parent is already there
124 new_parents="$new_parents $new_p"
126 esac
127 else
128 if test -f "$DROPPED"/$p
129 then
130 replacement="$(cat "$DROPPED"/$p)"
131 test -z "$replacement" && replacement=root
132 pend=" $replacement$pend"
133 else
134 new_parents="$new_parents $p"
137 test -n "$first_parent" || first_parent=$p
138 done
139 # We do not have parent, so ignore this commit
140 test t = $preserve && return
142 # We always write a mark, because we do not know if there will
143 # be a "reset" or "merge".
144 # Filter the unneeded marks out afterwards.
145 echo "mark :$mark"
146 mark=$(($mark+1))
148 new_first_parent=$(expr "$new_parents" : ' \([^ ]*\)')
150 # Reset if needed
151 test -z "$first_parent" -o "$first_parent" = $lastsha1 || {
152 if expr $new_first_parent : ^: > /dev/null
153 then
154 echo "reset $new_first_parent"
155 else
156 git rev-list --pretty=format:"reset %h # %s" \
157 $new_first_parent'^!' | sed -e 1d
161 echo ":$mark" > "$REWRITTEN"/$sha1
163 # Merge or pick
164 case "$new_parents" in
165 ' '*' '*)
166 new_parents=${new_parents# $new_first_parent}
167 remotes=
168 # new_parents is a list of all new parents. But we only want
169 # remotes that have not already merged in.
170 for p in $new_parents
172 if expr $p : ^: > /dev/null ||
173 ! git rev-list $ONTO | grep $p > /dev/null
174 then
175 remotes=" $p"
177 done
178 test -n "$remotes" || return # No remotes? Ignore this commit!
179 printf 'merge%s -C %s%s\t%s\n' "$STRATEGY" \
180 "$shortsha1" "$remotes" "$rest"
183 printf 'pick %s\t%s\n' "$shortsha1" "$rest"
185 esac
187 lastsha1="$sha1"
188 return 0
191 update_refs_and_exit () {
192 HEADNAME=$(cat "$DOTEST"/head-name) &&
193 OLDHEAD=$(cat "$DOTEST"/head) &&
194 SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) &&
195 NEWHEAD=$(git rev-parse HEAD) &&
196 case $HEADNAME in
197 refs/*)
198 message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO" &&
199 git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD &&
200 git symbolic-ref HEAD $HEADNAME
202 esac && {
203 test ! -f "$DOTEST"/verbose ||
204 git diff-tree --stat $(cat "$DOTEST"/head)..HEAD
205 } &&
206 rm -rf "$DOTEST" &&
207 git gc --auto &&
208 warn "Successfully rebased and updated $HEADNAME."
210 exit
213 # check if no other options are set
214 is_standalone () {
215 test $# -eq 2 -a "$2" = '--' &&
216 test -z "$ONTO" &&
217 test -z "$PRESERVE_MERGES" &&
218 test -z "$STRATEGY" &&
219 test -z "$VERBOSE"
222 get_saved_options () {
223 test -d "$REWRITTEN" && PRESERVE_MERGES=t
224 test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
225 test -f "$DOTEST"/verbose && VERBOSE=t
226 test -f "$DOTEST"/rebase-root && REBASE_ROOT=t
229 run_sequencer () {
230 git sequencer --caller='git rebase -i|--abort|--continue|--skip' "$@"
231 case "$?" in
233 if test "$1" = --abort
234 then
235 HEADNAME=$(cat "$DOTEST"/head-name)
236 HEAD=$(cat "$DOTEST"/head)
237 case $HEADNAME in
238 refs/*)
239 git symbolic-ref HEAD $HEADNAME
241 esac &&
242 output git reset --hard $HEAD &&
243 rm -rf "$DOTEST"
244 exit
245 else
246 update_refs_and_exit
250 # pause
251 exit 0
254 # conflict
255 exit 1
258 die_abort 'git-sequencer died unexpected.'
260 esac
263 while test $# != 0
265 case "$1" in
266 --no-verify)
267 OK_TO_SKIP_PRE_REBASE=yes
269 --verify)
271 --abort|--continue|--skip)
272 is_standalone "$@" || usage
273 run_sequencer "$1"
276 case "$#,$1" in
277 *,*=*)
278 STRATEGY=" -s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;;
279 1,*)
280 usage ;;
282 STRATEGY=" -s $2"
283 shift ;;
284 esac
287 # we use merge anyway
290 VERBOSE=t
293 PRESERVE_MERGES=t
296 # yeah, we know
298 --root)
299 REBASE_ROOT=t
301 --onto)
302 shift
303 ONTO=$(git rev-parse --verify "$1") ||
304 die "Does not point to a valid commit: $1"
307 shift
308 test -z "$REBASE_ROOT" -a $# -ge 1 -a $# -le 2 ||
309 test ! -z "$REBASE_ROOT" -a $# -le 1 || usage
310 test -d "$DOTEST" &&
311 die "Interactive rebase already started"
312 git sequencer --status > /dev/null 2>&1 &&
313 die "Sequencer already started. Cannot run rebase."
315 git var GIT_COMMITTER_IDENT > /dev/null ||
316 die "You need to set your committer info first"
318 if test -z "$REBASE_ROOT"
319 then
320 UPSTREAM_ARG="$1"
321 UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
322 test -z "$ONTO" && ONTO=$UPSTREAM
323 shift
324 else
325 UPSTREAM=
326 UPSTREAM_ARG=--root
327 test -z "$ONTO" &&
328 die "You must specify --onto when using --root"
330 run_pre_rebase_hook "$UPSTREAM_ARG" "$@"
332 require_clean_work_tree
334 if test ! -z "$1"
335 then
336 output git show-ref --verify --quiet "refs/heads/$1" ||
337 die "Invalid branchname: $1"
338 output git checkout "$1" ||
339 die "Could not checkout $1"
342 HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?"
343 mkdir "$DOTEST" || die "Could not create temporary $DOTEST"
345 : > "$DOTEST"/interactive || die_abort "Could not mark as interactive"
346 git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null ||
347 echo "detached HEAD" > "$DOTEST"/head-name
349 echo $HEAD > "$DOTEST"/head
350 case "$REBASE_ROOT" in
352 rm -f "$DOTEST"/rebase-root ;;
354 : >"$DOTEST"/rebase-root ;;
355 esac
356 echo $ONTO > "$DOTEST"/onto
357 test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy
358 test t = "$VERBOSE" && : > "$DOTEST"/verbose
360 SHORTHEAD=$(git rev-parse --short $HEAD)
361 SHORTONTO=$(git rev-parse --short $ONTO)
362 if test -z "$REBASE_ROOT"
363 # this is now equivalent to ! -z "$UPSTREAM"
364 then
365 SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM)
366 REVISIONS=$UPSTREAM...$HEAD
367 SHORTREVISIONS=$SHORTUPSTREAM..$SHORTHEAD
368 else
369 REVISIONS=$ONTO...$HEAD
370 SHORTREVISIONS=$SHORTHEAD
373 if test t = "$PRESERVE_MERGES"
374 then
375 lastsha1=
376 # $REWRITTEN contains files for each commit that is
377 # reachable on the way between $UPSTREAM and $HEAD.
378 # The filename is the SHA1 of the old value and the
379 # content is the SHA1 or :mark of the new one.
380 if test -z "$REBASE_ROOT"
381 then
382 mkdir "$REWRITTEN" &&
383 for c in $(git merge-base --all $HEAD $UPSTREAM)
385 test -n "$lastsha1" || lastsha1=$c
386 echo $ONTO > "$REWRITTEN"/$c ||
387 die "Could not init rewritten commits"
388 done
389 else
390 mkdir "$REWRITTEN" &&
391 echo $ONTO > "$REWRITTEN"/root ||
392 die "Could not init rewritten commits"
395 git rev-list \
396 --pretty=format:"%m%h # %s" --topo-order \
397 --reverse --cherry-pick $REVISIONS | \
398 sed -n -e "s/^>//p" > "$DOTEST"/commit-list
400 mkdir "$DROPPED"
402 mark=0
403 # drop the --cherry-pick parameter this time
404 git rev-list \
405 --pretty=format:"%m%h # %s" --topo-order \
406 --reverse $REVISIONS | \
407 sed -n -e "s/^>//p" | \
408 while read -r sha1 rest
410 grep --quiet "$sha1" "$DOTEST"/commit-list
411 if [ $? -eq 0 ]
412 then
413 # The current commit is not dropped by
414 # --cherry-pick, so create a TODO line
415 create_todo_line_preserving_merges
416 else
417 # The current commit has been dropped
418 # so put its first parent into
419 # $DROPPED/$fullsha1
420 full=$(git rev-parse $sha1)
421 git rev-list --parents -1 $sha1 | \
422 cut -d' ' -s -f2 > "$DROPPED"/$full
423 # Use -f2 because if rev-list is
424 # telling this commit is not
425 # worthwhile, we don't want to track
426 # its multiple heads, just the history
427 # of its first-parent for others that
428 # will be rebasing on top of us
430 done > "$TODO"
432 # We now have more "mark :..." lines than needed.
433 # Remove the unused. This is just a step to keep
434 # the list clean.
435 keep_marks=$(sed -e "/^mark :/d" <"$TODO" |
436 sed -n -e 's/^[^#]* :\([0-9][0-9]*\).*$/:\1:/p')
437 while read -r line
439 case "$line" in
440 'mark :'*)
441 case "$keep_marks " in
442 *${line#mark }:*)
443 echo "$line"
445 esac
448 printf '%s\n' "$line"
450 esac
451 done < "$TODO" > "$TODO".new
452 mv "$TODO".new "$TODO"
453 else
454 git rev-list --no-merges \
455 --pretty=format:"%mpick %h # %s" \
456 --reverse --cherry-pick $REVISIONS | \
457 sed -n -e "s/^>//p" > "$TODO"
460 test -s "$TODO" || echo noop >> "$TODO"
461 cat >> "$TODO" << EOF
463 # Rebase $SHORTREVISIONS onto $SHORTONTO
465 # Commands:
466 # p, pick = use commit
467 # e, edit = use commit, but stop for amending
468 # s, squash = use commit, but meld into previous commit
470 # If you remove a line here THAT COMMIT WILL BE LOST.
471 # However, if you remove everything, the rebase will be aborted.
475 has_action "$TODO" ||
476 die_abort "Nothing to do"
478 cp "$TODO" "$TODO".backup
479 git_editor "$TODO" ||
480 die "Could not execute editor"
482 has_action "$TODO" ||
483 die_abort "Nothing to do"
485 git update-ref ORIG_HEAD $HEAD
486 output git checkout $ONTO && run_sequencer "$TODO"
488 esac
489 shift
490 done