Skip to content

Commit

Permalink
rebase -i: support --root without --onto
Browse files Browse the repository at this point in the history
Allow --root to be specified to rebase -i without --onto, making it
possible to edit and re-order all commits right back to the root(s).

If there is a conflict to be resolved when applying the first change,
the user will expect a sane index and working tree to get sensible
behaviour from git-diff and friends, so create a sentinel commit with an
empty tree to rebase onto. Automatically squash the sentinel with any
commits rebased directly onto it, so they end up as root commits in
their own right and retain their authorship and commit message.

Implicitly use rebase -i for non-interactive rebase of --root without
an --onto argument now that rebase -i can correctly do this.

Signed-off-by: Chris Webb <chris@arachsys.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Chris Webb authored and Junio C Hamano committed Jun 26, 2012
1 parent bc9e7dd commit df5df20
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 12 deletions.
9 changes: 5 additions & 4 deletions Documentation/git-rebase.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ SYNOPSIS
[verse]
'git rebase' [-i | --interactive] [options] [--onto <newbase>]
[<upstream>] [<branch>]
'git rebase' [-i | --interactive] [options] --onto <newbase>
'git rebase' [-i | --interactive] [options] [--onto <newbase>]
--root [<branch>]
'git rebase' --continue | --skip | --abort

Expand Down Expand Up @@ -348,10 +348,11 @@ idea unless you know what you are doing (see BUGS below).
--root::
Rebase all commits reachable from <branch>, instead of
limiting them with an <upstream>. This allows you to rebase
the root commit(s) on a branch. Must be used with --onto, and
the root commit(s) on a branch. When used with --onto, it
will skip changes already contained in <newbase> (instead of
<upstream>). When used together with --preserve-merges, 'all'
root commits will be rewritten to have <newbase> as parent
<upstream>) whereas without --onto it will operate on every change.
When used together with both --onto and --preserve-merges,
'all' root commits will be rewritten to have <newbase> as parent
instead.

--autosquash::
Expand Down
32 changes: 26 additions & 6 deletions git-rebase--interactive.sh
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,29 @@ record_in_rewritten() {
esac
}

do_pick () {
if test "$(git rev-parse HEAD)" = "$squash_onto"
then
# Set the correct commit message and author info on the
# sentinel root before cherry-picking the original changes
# without committing (-n). Finally, update the sentinel again
# to include these changes. If the cherry-pick results in a
# conflict, this means our behaviour is similar to a standard
# failed cherry-pick during rebase, with a dirty index to
# resolve before manually running git commit --amend then git
# rebase --continue.
git commit --allow-empty --allow-empty-message --amend \
--no-post-rewrite -n -q -C $1 &&
pick_one -n $1 &&
git commit --allow-empty --allow-empty-message \
--amend --no-post-rewrite -n -q -C $1 ||
die_with_patch $1 "Could not apply $1... $2"
else
pick_one $1 ||
die_with_patch $1 "Could not apply $1... $2"
fi
}

do_next () {
rm -f "$msg" "$author_script" "$amend" || exit
read -r command sha1 rest < "$todo"
Expand All @@ -428,16 +451,14 @@ do_next () {
comment_for_reflog pick

mark_action_done
pick_one $sha1 ||
die_with_patch $sha1 "Could not apply $sha1... $rest"
do_pick $sha1 "$rest"
record_in_rewritten $sha1
;;
reword|r)
comment_for_reflog reword

mark_action_done
pick_one $sha1 ||
die_with_patch $sha1 "Could not apply $sha1... $rest"
do_pick $sha1 "$rest"
git commit --amend --no-post-rewrite || {
warn "Could not amend commit after successfully picking $sha1... $rest"
warn "This is most likely due to an empty commit message, or the pre-commit hook"
Expand All @@ -451,8 +472,7 @@ do_next () {
comment_for_reflog edit

mark_action_done
pick_one $sha1 ||
die_with_patch $sha1 "Could not apply $sha1... $rest"
do_pick $sha1 "$rest"
warn "Stopped at $sha1... $rest"
exit_with_patch $sha1 0
;;
Expand Down
14 changes: 12 additions & 2 deletions git-rebase.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ SUBDIRECTORY_OK=Yes
OPTIONS_KEEPDASHDASH=
OPTIONS_SPEC="\
git rebase [-i] [options] [--onto <newbase>] [<upstream>] [<branch>]
git rebase [-i] [options] --onto <newbase> --root [<branch>]
git rebase [-i] [options] [--onto <newbase>] --root [<branch>]
git-rebase [-i] --continue | --abort | --skip
--
Available options are
Expand Down Expand Up @@ -364,6 +364,11 @@ and run me again. I am stopping in case you still have something
valuable there.'
fi

if test -n "$rebase_root" && test -z "$onto"
then
test -z "$interactive_rebase" && interactive_rebase=implied
fi

if test -n "$interactive_rebase"
then
type=interactive
Expand Down Expand Up @@ -397,7 +402,12 @@ then
die "invalid upstream $upstream_name"
upstream_arg="$upstream_name"
else
test -z "$onto" && die "You must specify --onto when using --root"
if test -z "$onto"
then
empty_tree=`git hash-object -t tree /dev/null`
onto=`git commit-tree $empty_tree </dev/null`
squash_onto="$onto"
fi
unset upstream_name
unset upstream
upstream_arg=--root
Expand Down

0 comments on commit df5df20

Please sign in to comment.