Merge branch 'cw/rebase-i-root'

"git rebase [-i] --root $tip" can now be used to rewrite all the
history down to the root.

* cw/rebase-i-root:
  t3404: make test 57 work with dash and others
  Add tests for rebase -i --root without --onto
  rebase -i: support --root without --onto
This commit is contained in:
Junio C Hamano 2012-07-15 21:38:41 -07:00
commit 0cd993a778
5 changed files with 74 additions and 17 deletions

View file

@ -10,7 +10,7 @@ SYNOPSIS
[verse]
'git rebase' [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
[<upstream>] [<branch>]
'git rebase' [-i | --interactive] [options] [--exec <cmd>] --onto <newbase>
'git rebase' [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
--root [<branch>]
'git rebase' --continue | --skip | --abort
@ -369,10 +369,11 @@ squash/fixup series.
--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::

View file

@ -415,6 +415,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"
@ -426,16 +449,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"
@ -449,8 +470,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
;;

View file

@ -32,7 +32,7 @@ SUBDIRECTORY_OK=Yes
OPTIONS_KEEPDASHDASH=
OPTIONS_SPEC="\
git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
git rebase [-i] [options] [--exec <cmd>] --onto <newbase> --root [<branch>]
git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
git-rebase [-i] --continue | --abort | --skip
--
Available options are
@ -378,6 +378,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
@ -411,7 +416,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
test $# -gt 1 && usage

View file

@ -872,4 +872,35 @@ test_expect_success 'rebase -i --exec without <CMD>' '
git checkout master
'
test_expect_success 'rebase -i --root re-order and drop commits' '
git checkout E &&
FAKE_LINES="3 1 2 5" git rebase -i --root &&
test E = $(git cat-file commit HEAD | sed -ne \$p) &&
test B = $(git cat-file commit HEAD^ | sed -ne \$p) &&
test A = $(git cat-file commit HEAD^^ | sed -ne \$p) &&
test C = $(git cat-file commit HEAD^^^ | sed -ne \$p) &&
test 0 = $(git cat-file commit HEAD^^^ | grep -c ^parent\ )
'
test_expect_success 'rebase -i --root retain root commit author and message' '
git checkout A &&
echo B >file7 &&
git add file7 &&
GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" &&
FAKE_LINES="2" git rebase -i --root &&
git cat-file commit HEAD | grep -q "^author Twerp Snog" &&
git cat-file commit HEAD | grep -q "^different author$"
'
test_expect_success 'rebase -i --root temporary sentinel commit' '
git checkout B &&
(
FAKE_LINES="2" &&
export FAKE_LINES &&
test_must_fail git rebase -i --root
) &&
git cat-file commit HEAD | grep "^tree 4b825dc642cb" &&
git rebase --abort
'
test_done

View file

@ -22,11 +22,6 @@ test_expect_success 'prepare repository' '
test_commit 4 B
'
test_expect_success 'rebase --root expects --onto' '
git checkout -B fail other &&
test_must_fail git rebase --root
'
test_expect_success 'rebase --root fails with too many args' '
git checkout -B fail other &&
test_must_fail git rebase --onto master --root fail fail