From 1b1dce4bae760248a1fc3e29548a72c446e77270 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 25 Jun 2007 01:11:14 +0100 Subject: Teach rebase an interactive mode Don't you just hate the fact sometimes, that git-rebase just applies the patches, without any possibility to edit them, or rearrange them? With "--interactive", git-rebase now lets you edit the list of patches, so that you can reorder, edit and delete patches. Such a list will typically look like this: pick deadbee The oneline of this commit pick fa1afe1 The oneline of the next commit ... By replacing the command "pick" with the command "edit", you can amend that patch and/or its commit message, and by replacing it with "squash" you can tell rebase to fold that patch into the patch before that. It is derived from the script sent to the list in Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/t3404-rebase-interactive.sh | 163 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100755 t/t3404-rebase-interactive.sh (limited to 't') diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh new file mode 100755 index 0000000000..48aa8ea814 --- /dev/null +++ b/t/t3404-rebase-interactive.sh @@ -0,0 +1,163 @@ +#!/bin/sh +# +# Copyright (c) 2007 Johannes E. Schindelin +# + +test_description='git rebase interactive + +This test runs git rebase "interactively", by faking an edit, and verifies +that the result still makes sense. +' +. ./test-lib.sh + +# set up two branches like this: +# +# A - B - C - D - E +# \ +# F - G - H +# \ +# I +# +# where B, D and G touch the same file. + +test_expect_success 'setup' ' + : > file1 && + git add file1 && + test_tick && + git commit -m A && + git tag A && + echo 1 > file1 && + test_tick && + git commit -m B file1 && + : > file2 && + git add file2 && + test_tick && + git commit -m C && + echo 2 > file1 && + test_tick && + git commit -m D file1 && + : > file3 && + git add file3 && + test_tick && + git commit -m E && + git checkout -b branch1 A && + : > file4 && + git add file4 && + test_tick && + git commit -m F && + git tag F && + echo 3 > file1 && + test_tick && + git commit -m G file1 && + : > file5 && + git add file5 && + test_tick && + git commit -m H && + git checkout -b branch2 F && + : > file6 && + git add file6 && + test_tick && + git commit -m I && + git tag I +' + +cat > fake-editor.sh << EOF +#!/bin/sh +test "\$1" = .git/COMMIT_EDITMSG && exit +test -z "\$FAKE_LINES" && exit +grep -v "^#" < "\$1" > "\$1".tmp +rm "\$1" +cat "\$1".tmp +action=pick +for line in \$FAKE_LINES; do + case \$line in + squash) + action="\$line";; + *) + echo sed -n "\${line}s/^pick/\$action/p" + sed -n "\${line}p" < "\$1".tmp + sed -n "\${line}s/^pick/\$action/p" < "\$1".tmp >> "\$1" + action=pick;; + esac +done +EOF + +chmod a+x fake-editor.sh +VISUAL="$(pwd)/fake-editor.sh" +export VISUAL + +test_expect_success 'no changes are a nop' ' + git rebase -i F && + test $(git rev-parse I) = $(git rev-parse HEAD) +' + +test_expect_success 'rebase on top of a non-conflicting commit' ' + git checkout branch1 && + git tag original-branch1 && + git rebase -i branch2 && + test file6 = $(git diff --name-only original-branch1) && + test $(git rev-parse I) = $(git rev-parse HEAD~2) +' + +test_expect_success 'exchange two commits' ' + FAKE_LINES="2 1" git rebase -i HEAD~2 && + test H = $(git cat-file commit HEAD^ | tail -n 1) && + test G = $(git cat-file commit HEAD | tail -n 1) +' + +cat > expect << EOF +diff --git a/file1 b/file1 +index e69de29..00750ed 100644 +--- a/file1 ++++ b/file1 +@@ -0,0 +1 @@ ++3 +EOF + +cat > expect2 << EOF +<<<<<<< HEAD:file1 +2 +======= +3 +>>>>>>> b7ca976... G:file1 +EOF + +test_expect_success 'stop on conflicting pick' ' + git tag new-branch1 && + ! git rebase -i master && + diff -u expect .git/.dotest-merge/patch && + diff -u expect2 file1 && + test 4 = $(grep -v "^#" < .git/.dotest-merge/done | wc -l) && + test 0 = $(grep -v "^#" < .git/.dotest-merge/todo | wc -l) +' + +test_expect_success 'abort' ' + git rebase --abort && + test $(git rev-parse new-branch1) = $(git rev-parse HEAD) && + ! test -d .git/.dotest-merge +' + +test_expect_success 'retain authorship' ' + echo A > file7 && + git add file7 && + GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" && + git tag twerp && + git rebase -i --onto master HEAD^ && + git show HEAD | grep "^Author: Twerp Snog" +' + +test_expect_success 'squash' ' + git reset --hard twerp && + echo B > file7 && + GIT_AUTHOR_NAME="Nitfol" git commit -m "nitfol" file7 && + echo "******************************" && + FAKE_LINES="1 squash 2" git rebase -i --onto master HEAD~2 && + test B = $(cat file7) && + test $(git rev-parse HEAD^) = $(git rev-parse master) +' + +test_expect_success 'retain authorship when squashing' ' + git show HEAD | grep "^Author: Nitfol" +' + +test_done -- cgit v1.2.1 From c54b7817f4f6bf422ea532d81217c28f63c259c5 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 25 Jun 2007 18:56:55 +0100 Subject: rebase -i: several cleanups Support "--verbose" in addition to "-v", show short names in the list comment, clean up if there is nothing to do, and add several "test_ticks" in the test script. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/t3404-rebase-interactive.sh | 2 ++ 1 file changed, 2 insertions(+) (limited to 't') diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 48aa8ea814..19a3a8e813 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -140,6 +140,7 @@ test_expect_success 'abort' ' test_expect_success 'retain authorship' ' echo A > file7 && git add file7 && + test_tick && GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" && git tag twerp && git rebase -i --onto master HEAD^ && @@ -149,6 +150,7 @@ test_expect_success 'retain authorship' ' test_expect_success 'squash' ' git reset --hard twerp && echo B > file7 && + test_tick && GIT_AUTHOR_NAME="Nitfol" git commit -m "nitfol" file7 && echo "******************************" && FAKE_LINES="1 squash 2" git rebase -i --onto master HEAD~2 && -- cgit v1.2.1 From 68a163c9b483ae352fcfee8c4505d113213daa73 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 25 Jun 2007 18:58:28 +0100 Subject: rebase -i: provide reasonable reflog for the rebased branch If your rebase succeeded, the HEAD's reflog will still show the whole mess, but "@{1}" now shows the state _before_ the rebase, so that you can reset (or compare) the original and the rebased revisions more easily. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/t3404-rebase-interactive.sh | 4 ++++ 1 file changed, 4 insertions(+) (limited to 't') diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 19a3a8e813..9f12bb9321 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -99,6 +99,10 @@ test_expect_success 'rebase on top of a non-conflicting commit' ' test $(git rev-parse I) = $(git rev-parse HEAD~2) ' +test_expect_success 'reflog for the branch shows state before rebase' ' + test $(git rev-parse branch1@{1}) = $(git rev-parse original-branch1) +' + test_expect_success 'exchange two commits' ' FAKE_LINES="2 1" git rebase -i HEAD~2 && test H = $(git cat-file commit HEAD^ | tail -n 1) && -- cgit v1.2.1 From f09c9b8c5ff9d8a15499b09ccd6c3e7b3c76af77 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 25 Jun 2007 18:59:43 +0100 Subject: Teach rebase -i about --preserve-merges The option "-p" (or long "--preserve-merges") makes it possible to rebase side branches including merges, without straightening the history. Example: X \ A---M---B / ---o---O---P---Q When the current HEAD is "B", "git rebase -i -p --onto Q O" will yield X \ ---o---O---P---Q---A'---M'---B' Note that this will - _not_ touch X [*1*], it does - _not_ work without the --interactive flag [*2*], it does - _not_ guess the type of the merge, but blindly uses recursive or whatever strategy you provided with "-s " for all merges it has to redo, and it does - _not_ make use of the original merge commit via git-rerere. *1*: only commits which reach a merge base between and HEAD are reapplied. The others are kept as-are. *2*: git-rebase without --interactive is inherently patch based (at least at the moment), and therefore merges cannot be preserved. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/t3404-rebase-interactive.sh | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 't') diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 9f12bb9321..883cf29595 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -166,4 +166,26 @@ test_expect_success 'retain authorship when squashing' ' git show HEAD | grep "^Author: Nitfol" ' +test_expect_success 'preserve merges with -p' ' + git checkout -b to-be-preserved master^ && + : > unrelated-file && + git add unrelated-file && + test_tick && + git commit -m "unrelated" && + git checkout -b to-be-rebased master && + echo B > file1 && + test_tick && + git commit -m J file1 && + test_tick && + git merge to-be-preserved && + echo C > file1 && + test_tick && + git commit -m K file1 && + git rebase -i -p --onto branch1 master && + test $(git rev-parse HEAD^^2) = $(git rev-parse to-be-preserved) && + test $(git rev-parse HEAD~3) = $(git rev-parse branch1) && + test $(git show HEAD:file1) = C && + test $(git show HEAD~2:file1) = B +' + test_done -- cgit v1.2.1