summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/git-rebase.txt9
-rw-r--r--git-rebase--interactive.sh32
-rwxr-xr-xgit-rebase.sh14
-rwxr-xr-xt/t3404-rebase-interactive.sh31
-rwxr-xr-xt/t3412-rebase-root.sh5
5 files changed, 74 insertions, 17 deletions
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 2d71e4b975..b30ed352e5 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -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::
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 3a3c382357..bef7bc0444 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -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
;;
diff --git a/git-rebase.sh b/git-rebase.sh
index 5bddfa9690..1cd0633b80 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -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
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 68d61480fb..8078db6856 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -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
diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh
index 1e9d1a737c..0b52105728 100755
--- a/t/t3412-rebase-root.sh
+++ b/t/t3412-rebase-root.sh
@@ -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