Merge branch 'cw/rebase-i-root'
authorJunio C Hamano <gitster@pobox.com>
Mon, 16 Jul 2012 04:38:41 +0000 (21:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 16 Jul 2012 04:38:42 +0000 (21:38 -0700)
"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

Documentation/git-rebase.txt
git-rebase--interactive.sh
git-rebase.sh
t/t3404-rebase-interactive.sh
t/t3412-rebase-root.sh
index 2d71e4b975db3355c6de9005e77a701e18723539..b30ed352e5a3f87d24119624966c33b4c194df5a 100644 (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::
index 3a3c3823571162853e4e88876e7646287c7c1af5..bef7bc0444bab7b7e511c6e29417ce6db44b77ef 100644 (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
                ;;
index 5bddfa9690e8dfbedd07e31dc7e768b60f156213..1cd0633b80a8daf00dbc50657ccba228bd578a73 100755 (executable)
@@ -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
index 68d61480fbeee024ca463ca0d5771bdfdf980866..8078db6856ba4428e9d6b13f5e02076da7963b09 100755 (executable)
@@ -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
index 1e9d1a737c5369d1b41bfa75f5939ad41004b944..0b521057283bf106da8ce55f25f61c7ec7e3ad35 100755 (executable)
@@ -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