3 # Copyright (c) 2006 Johannes E. Schindelin
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
15 git-rebase [-i] [options] [--] <upstream> [<branch>]
16 git-rebase [-i] (--continue | --abort | --skip)
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)
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)
36 DOTEST
="$GIT_DIR/rebase-merge"
37 TODO
="$DOTEST"/git-rebase-todo
40 SQUASH_MSG
="$DOTEST"/message-squash
41 REWRITTEN
="$DOTEST"/rewritten
42 DROPPED
="$DOTEST"/dropped
47 OK_TO_SKIP_PRE_REBASE
=
59 test $status != 0 && printf "%s\n" "$output"
68 run_pre_rebase_hook
() {
69 if test -z "$OK_TO_SKIP_PRE_REBASE" &&
70 test -x "$GIT_DIR/hooks/pre-rebase"
72 "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} ||
{
73 echo >&2 "The pre-rebase hook refused to rebase."
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"
94 grep '^[^#]' "$1" > /dev
/null
97 create_todo_line_preserving_merges
() {
99 sha1
=$
(git rev-parse
$sha1)
102 test -z "$REBASE_ROOT" || preserve
=f
105 pend
=" $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)"
106 if test "$pend" = " "
110 while [ "$pend" != "" ]
112 p
=$
(expr "$pend" : ' \([^ ]*\)')
115 # check if we've already seen this parent
116 if test -f "$REWRITTEN"/$p
119 new_p
=$
(cat "$REWRITTEN"/$p)
120 case "$new_parents" in
122 ;; # do nothing; that parent is already there
124 new_parents
="$new_parents $new_p"
128 if test -f "$DROPPED"/$p
130 replacement
="$(cat "$DROPPED"/$p)"
131 test -z "$replacement" && replacement
=root
132 pend
=" $replacement$pend"
134 new_parents
="$new_parents $p"
137 test -n "$first_parent" || first_parent
=$p
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.
148 new_first_parent
=$
(expr "$new_parents" : ' \([^ ]*\)')
151 test -z "$first_parent" -o "$first_parent" = $lastsha1 ||
{
152 if expr $new_first_parent : ^
: > /dev
/null
154 echo "reset $new_first_parent"
156 git rev-list
--pretty=format
:"reset %h # %s" \
157 $new_first_parent'^!' |
sed -e 1d
161 echo ":$mark" > "$REWRITTEN"/$sha1
164 case "$new_parents" in
166 new_parents
=${new_parents# $new_first_parent}
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
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"
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
) &&
198 message
="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO" &&
199 git update-ref
-m "$message" $HEADNAME $NEWHEAD $OLDHEAD &&
200 git symbolic-ref HEAD
$HEADNAME
203 test ! -f "$DOTEST"/verbose ||
204 git diff-tree
--stat $
(cat "$DOTEST"/head)..HEAD
208 warn
"Successfully rebased and updated $HEADNAME."
213 # check if no other options are set
215 test $# -eq 2 -a "$2" = '--' &&
217 test -z "$PRESERVE_MERGES" &&
218 test -z "$STRATEGY" &&
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
230 git sequencer
--caller='git rebase -i|--abort|--continue|--skip' "$@"
233 if test "$1" = --abort
235 HEADNAME
=$
(cat "$DOTEST"/head-name
)
236 HEAD
=$
(cat "$DOTEST"/head)
239 git symbolic-ref HEAD
$HEADNAME
242 output git
reset --hard $HEAD &&
258 die_abort
'git-sequencer died unexpected.'
267 OK_TO_SKIP_PRE_REBASE
=yes
271 --abort|
--continue|
--skip)
272 is_standalone
"$@" || usage
278 STRATEGY
=" -s "$
(expr "z$1" : 'z-[^=]*=\(.*\)') ;;
287 # we use merge anyway
303 ONTO
=$
(git rev-parse
--verify "$1") ||
304 die
"Does not point to a valid commit: $1"
308 test -z "$REBASE_ROOT" -a $# -ge 1 -a $# -le 2 ||
309 test ! -z "$REBASE_ROOT" -a $# -le 1 || usage
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"
321 UPSTREAM
=$
(git rev-parse
--verify "$1") || die
"Invalid base"
322 test -z "$ONTO" && ONTO
=$UPSTREAM
328 die
"You must specify --onto when using --root"
330 run_pre_rebase_hook
"$UPSTREAM_ARG" "$@"
332 require_clean_work_tree
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
;;
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"
365 SHORTUPSTREAM
=$
(git rev-parse
--short $UPSTREAM)
366 REVISIONS
=$UPSTREAM...
$HEAD
367 SHORTREVISIONS
=$SHORTUPSTREAM..
$SHORTHEAD
369 REVISIONS
=$ONTO...
$HEAD
370 SHORTREVISIONS
=$SHORTHEAD
373 if test t
= "$PRESERVE_MERGES"
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"
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"
390 mkdir
"$REWRITTEN" &&
391 echo $ONTO > "$REWRITTEN"/root ||
392 die
"Could not init rewritten commits"
396 --pretty=format
:"%m%h # %s" --topo-order \
397 --reverse --cherry-pick $REVISIONS | \
398 sed -n -e "s/^>//p" > "$DOTEST"/commit-list
403 # drop the --cherry-pick parameter this time
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
413 # The current commit is not dropped by
414 # --cherry-pick, so create a TODO line
415 create_todo_line_preserving_merges
417 # The current commit has been dropped
418 # so put its first parent into
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
432 # We now have more "mark :..." lines than needed.
433 # Remove the unused. This is just a step to keep
435 keep_marks
=$
(sed -e "/^mark :/d" <"$TODO" |
436 sed -n -e 's/^[^#]* :\([0-9][0-9]*\).*$/:\1:/p')
441 case "$keep_marks " in
448 printf '%s\n' "$line"
451 done < "$TODO" > "$TODO".new
452 mv "$TODO".new
"$TODO"
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
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"