{"thread":{"id":"8694","subject":"[PATCH 2/2] Teach rebase an interactive mode","startedAt":"2007-06-23T23:01:51Z","lastAt":"2007-06-25T00:11:14Z","messageCount":10,"participants":["Johannes Schindelin","Alex Riesen","Brian Gernhardt","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"45629","messageId":"Pine.LNX.4.64.0706240001150.4059@racer.site","threadId":"8694","inReplyTo":null,"subject":"[PATCH 2/2] Teach rebase an interactive mode","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-06-23T23:01:51Z","receivedAt":"2007-06-23T23:01:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nWith \"--interactive\", git-rebase now lets you edit the list of patches\nto be reapplied, so that you can reorder and/or delete patches.\n\nSuch a list will typically look like this:\n\n\tpick deadbee The oneline of this commit\n\tpick fa1afe1 The oneline of the next commit\n\t...\n\nBy replacing the command \"pick\" with the command \"edit\", you can amend\nthat patch and/or its commit message, and by replacing it with \"squash\"\nyou can tell rebase to fold that patch into the patch before that.\n\nIt is derived from the script sent to the list in\n<Pine.LNX.4.63.0702252156190.22628@wbgn013.biozentrum.uni-wuerzburg.de>\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/git-rebase.txt  |   51 ++++++-\n Makefile                      |    2 +-\n git-rebase--interactive.sh    |  336 +++++++++++++++++++++++++++++++++++++++++\n git-rebase.sh                 |   12 ++-\n t/t3404-rebase-interactive.sh |  163 ++++++++++++++++++++\n 5 files changed, 559 insertions(+), 5 deletions(-)\n create mode 100755 git-rebase--interactive.sh\n create mode 100755 t/t3404-rebase-interactive.sh\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 0c00090..2e474e8 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -8,7 +8,8 @@ git-rebase - Forward-port local commits to the updated upstream head\n SYNOPSIS\n --------\n [verse]\n-'git-rebase' [-v] [--merge] [-C<n>] [--onto <newbase>] <upstream> [<branch>]\n+'git-rebase' [-i | --interactive] [-v | --verbose] [--merge] [-C<n>]\n+\t[--onto <newbase>] <upstream> [<branch>]\n 'git-rebase' --continue | --skip | --abort\n \n DESCRIPTION\n@@ -208,6 +209,10 @@ OPTIONS\n \tcontext exist they all must match.  By default no context is\n \tever ignored.\n \n+-i, \\--interactive::\n+\tMake a list of the commits which are about to be rebased.  Let the\n+\tuser edit that list before rebasing.\n+\n include::merge-strategies.txt[]\n \n NOTES\n@@ -226,9 +231,49 @@ pre-rebase hook script for an example.\n You must be in the top directory of your project to start (or continue)\n a rebase.  Upon completion, <branch> will be the current branch.\n \n-Author\n+Interactive mode\n+----------------\n+\n+Rebasing interactively means that you have a chance to edit the commits\n+which are rebased.  You can not only reorder the commits, but also\n+remove them (weeding out bad or otherwise unwanted patches).\n+\n+The list will look like this:\n+\n+-------------------------------------------\n+pick deadbee The oneline of this commit\n+pick fa1afe1 The oneline of the next commit\n+...\n+-------------------------------------------\n+\n+The oneline descriptions are purely for your pleasure; `git-rebase` will\n+not look at them but at the commit names, so do not delete or edit the\n+names.\n+\n+By replacing the command \"pick\" with the command \"edit\", you can tell\n+`git-rebase` to stop after applying that commit, so that you can edit\n+the files and/or the commit message, amend the commit and continue\n+rebasing.\n+\n+If you want to fold two commits into one, just replace the command \"pick\"\n+with \"squash\" for the second commit.  After squashing the commits,\n+`git-rebase` will start an editor with both commit messages, so you\n+can compose the commit message for the squashed commit.\n+\n+A common use case for the interactive mode is when you want to reorder\n+the last 5 commits, such that what was HEAD~4 becomes the new HEAD. To\n+achieve that, you would call `git-rebase` like this:\n+\n+----------------------\n+$ git rebase -i HEAD~5\n+----------------------\n+\n+And move the first patch to the end of the list.\n+\n+Authors\n ------\n-Written by Junio C Hamano <junkio@cox.net>\n+Written by Junio C Hamano <junkio@cox.net> and\n+Johannes E. Schindelin <johannes.schindelin@gmx.de>\n \n Documentation\n --------------\ndiff --git a/Makefile b/Makefile\nindex 0d904a9..edb421b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -204,7 +204,7 @@ SCRIPT_SH = \\\n \tgit-fetch.sh \\\n \tgit-ls-remote.sh \\\n \tgit-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \\\n-\tgit-pull.sh git-rebase.sh \\\n+\tgit-pull.sh git-rebase.sh git-rebase--interactive.sh \\\n \tgit-repack.sh git-request-pull.sh git-reset.sh \\\n \tgit-sh-setup.sh \\\n \tgit-tag.sh git-verify-tag.sh \\\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nnew file mode 100755\nindex 0000000..6a960b4\n--- /dev/null\n+++ b/git-rebase--interactive.sh\n@@ -0,0 +1,336 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2006 Johannes E. Schindelin\n+\n+# SHORT DESCRIPTION\n+#\n+# This script makes it easy to fix up commits in the middle of a series,\n+# and rearrange commits.\n+#\n+# The original idea comes from Eric W. Biederman, in\n+# http://article.gmane.org/gmane.comp.version-control.git/22407\n+#\n+# MOTIVATION\n+#\n+# Consider this type of workflow:\n+#\n+#\t1. have a wonderful idea\n+#\t2. hack on the code\n+#\t3. prepare a series for submission\n+#\t4. submit\n+#\n+# where the point (2) consists of several instances of\n+#\n+#\tA.1. finish something worthy of a commit\n+#\tA.2. commit\n+#\n+# and/or\n+#\n+#\tB.1. realize that something does not work\n+#\tB.2. fix that\n+#\tB.3. commit it\n+#\n+# Sometimes the thing fixed in B.2. cannot be amended to the not-quite\n+# perfect commit it fixes, because that commit is buried deeply in a\n+# patch series.\n+#\n+# Use this script after plenty of \"A\"s and \"B\"s, by rearranging, and\n+# possibly editing and merging commits.\n+#\n+# USAGE:\n+#\n+# Start it with the last commit you want to retain as-is:\n+#\n+#\tgit rebase -i <after-this-commit>\n+#\n+# An editor will be fired up with all the commits in your current branch\n+# (ignoring merge commits), which come after the given commit.  You can\n+# reorder the commits in this list to your heart's content, and you can\n+# even remove them.  The list looks more or less like this:\n+#\n+#\tpick deadbee The oneline of this commit\n+#\tpick fa1afe1 The oneline of the next commit\n+#\t...\n+#\n+# By replacing the command \"pick\" with \"edit\" you can tell\n+# this script to stop the loop so you can fix up the commit by editing\n+# files and/or the commit message.\n+#\n+# When replacing the command \"pick\" with \"squash\", the script will squash\n+# this commit's changes into the commit which comes right before it, munge\n+# the commit messages of both, and open the commit message editor.  If the\n+# commits had different authors, it will attribute the squashed commit to\n+# the author of the last commit.\n+#\n+# In both cases, or when a \"pick\" does not succeed (because of merge\n+# errors), the loop will stop to let you fix things, and you can continue\n+# the loop with `git rebase --continue`.\n+\n+USAGE='(--continue | --abort | --skip | [--onto <branch>] <upstream> [<branch>])'\n+\n+. git-sh-setup\n+require_work_tree\n+\n+DOTEST=\"$GIT_DIR/.dotest-merge\"\n+TODO=\"$DOTEST\"/todo\n+DONE=\"$DOTEST\"/done\n+STRATEGY=\n+VERBOSE=\n+\n+warn () {\n+\techo \"$@\" >&2\n+}\n+\n+require_clean_work_tree () {\n+\t# test if working tree is dirty\n+\tgit rev-parse --verify HEAD > /dev/null &&\n+\tgit update-index --refresh &&\n+\ttest -z \"`git diff-files --name-only`\" &&\n+\ttest -z \"`git diff-index --cached --name-only HEAD`\" ||\n+\tdie \"Working tree is dirty\"\n+}\n+\n+ORIG_REFLOG_ACTION=\"$GIT_REFLOG_ACTION\"\n+\n+comment_for_reflog () {\n+\tif test -z \"$ORIG_REFLOG_ACTION\"; then\n+\t\tGIT_REFLOG_ACTION=\"rebase --interactive ($1)\"\n+\t\texport GIT_REFLOG_ACTION\n+\tfi\n+}\n+\n+mark_action_done () {\n+\tsed -n 1p < \"$TODO\" >> \"$DONE\"\n+\tsed -n '2,$p' < \"$TODO\" >> \"$TODO\".new\n+\tmv -f \"$TODO\".new \"$TODO\"\n+}\n+\n+make_patch () {\n+\tparent_sha1=$(git rev-parse --verify \"$1\"^ 2> /dev/null)\n+\tgit diff \"$parent_sha1\"..\"$1\" > \"$DOTEST\"/patch\n+}\n+\n+die_with_patch () {\n+\tmake_patch \"$1\"\n+\tdie \"$2\"\n+}\n+\n+pick_one () {\n+\tcase \"$1\" in -n) sha1=$2 ;; *) sha1=$1 ;; esac\n+\tparent_sha1=$(git rev-parse --verify $sha1^ 2>/dev/null)\n+\tcurrent_sha1=$(git rev-parse --verify HEAD)\n+\tif [ $current_sha1 = $parent_sha1 ]; then\n+\t\tgit reset --hard $sha1\n+\t\tsha1=$(git rev-parse --short $sha1)\n+\t\twarn Fast forward to $sha1\n+\telse\n+\t\tgit cherry-pick $STRATEGY \"$@\"\n+\tfi\n+}\n+\n+do_next () {\n+\tread command sha1 rest < \"$TODO\"\n+\tcase \"$command\" in\n+\t\\#)\n+\t\tmark_action_done\n+\t\tcontinue\n+\t;;\n+\tpick)\n+\t\tcomment_for_reflog pick\n+\n+\t\tmark_action_done\n+\t\tpick_one $sha1 ||\n+\t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n+\t;;\n+\tedit)\n+\t\tcomment_for_reflog edit\n+\n+\t\tmark_action_done\n+\t\tpick_one $sha1 ||\n+\t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n+\t\tmake_patch $sha1\n+\t\twarn\n+\t\twarn \"You can amend the commit now, with\"\n+\t\twarn\n+\t\twarn \"\tgit commit --amend\"\n+\t\twarn\n+\t\texit 0\n+\t;;\n+\tsquash)\n+\t\tcomment_for_reflog squash\n+\n+\t\ttest -s \"$DONE\" ||\n+\t\t\tdie \"Cannot 'squash' without a previous commit\"\n+\n+\t\tmark_action_done\n+\t\tfailed=f\n+\t\tpick_one -n $sha1 || failed=t\n+\t\tMSG=\"$DOTEST\"/message\n+\t\techo \"# This is a combination of two commits.\" > \"$MSG\"\n+\t\techo \"# The first commit's message is:\" >> \"$MSG\"\n+\t\tgit cat-file commit HEAD | sed -n '/^$/,$p' >> \"$MSG\"\n+\t\techo >> \"$MSG\"\n+\t\techo \"# And this is the 2nd commit message:\" >> \"$MSG\"\n+\t\techo >> \"$MSG\"\n+\t\tgit cat-file commit $sha1 | sed -n '/^$/,$p' >> \"$MSG\"\n+\t\tgit reset --soft HEAD^\n+\t\tauthor_script=$(get_author_ident_from_commit $sha1)\n+\t\tcase $failed in\n+\t\tf)\n+\t\t\t# This is like --amend, but with a different message\n+\t\t\teval $author_script\n+\t\t\texport GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE\n+\t\t\tgit commit -F \"$MSG\" -e\n+\t\t;;\n+\t\tt)\n+\t\t\tcp \"$MSG\" \"$GIT_DIR\"/MERGE_MSG\n+\t\t\twarn\n+\t\t\twarn \"Could not apply $sha1... $rest\"\n+\t\t\twarn \"After you fixed that, commit the result with\"\n+\t\t\twarn\n+\t\t\twarn \"  $(echo $author_script | tr '\\012' ' ') \\\\\"\n+\t\t\twarn \"\t  git commit -F \\\"$GIT_DIR\\\"/MERGE_MSG -e\"\n+\t\t\tdie_with_patch $sha1 \"\"\n+\t\tesac\n+\t;;\n+\t*)\n+\t\twarn \"Unknown command: $command $sha1 $rest\"\n+\t\tdie_with_patch $sha1 \"Please fix this in the file $TODO.\"\n+\tesac\n+\ttest -s \"$TODO\" && continue\n+\n+\tHEAD=$(git rev-parse HEAD)\n+\tHEADNAME=$(cat \"$DOTEST\"/head-name)\n+\tgit update-ref $HEADNAME $HEAD &&\n+\tgit symbolic-ref HEAD $HEADNAME || exit\n+\trm -rf \"$DOTEST\" &&\n+\twarn \"Successfully rebased and updated $HEADNAME.\"\n+\n+\texit $?\n+}\n+\n+do_rest () {\n+\twhile :\n+\tdo\n+\t\tdo_next\n+\tdone\n+\ttest $? = 0 -a -f \"$DOTEST\"/verbose &&\n+\t\tgit diff --stat $(cat \"$DOTEST\"/head)..HEAD\n+\texit\n+}\n+\n+while case \"$#\" in 0) break ;; esac\n+do\n+\tcase \"$1\" in\n+\t--continue)\n+\t\tcomment_for_reflog continue\n+\n+\t\ttest -d \"$DOTEST\" || die \"No interactive rebase running\"\n+\n+\t\trequire_clean_work_tree\n+\t\tdo_rest\n+\t;;\n+\t--abort)\n+\t\tcomment_for_reflog abort\n+\n+\t\ttest -d \"$DOTEST\" || die \"No interactive rebase running\"\n+\n+\t\tHEADNAME=$(cat \"$DOTEST\"/head-name)\n+\t\tHEAD=$(cat \"$DOTEST\"/head)\n+\t\tgit symbolic-ref HEAD $HEADNAME &&\n+\t\tgit reset --hard $HEAD &&\n+\t\trm -rf \"$DOTEST\"\n+\t\texit\n+\t;;\n+\t--skip)\n+\t\tcomment_for_reflog skip\n+\n+\t\ttest -d \"$DOTEST\" || die \"No interactive rebase running\"\n+\n+\t\tgit reset --hard && do_rest\n+\t;;\n+\t-s|--strategy)\n+\t\tcase \"$#,$1\" in\n+\t\t*,*=*)\n+\t\t\tSTRATEGY=\"-s `expr \"z$1\" : 'z-[^=]*=\\(.*\\)'`\" ;;\n+\t\t1,*)\n+\t\t\tusage ;;\n+\t\t*)\n+\t\t\tSTRATEGY=\"-s $2\"\n+\t\t\tshift ;;\n+\t\tesac\n+\t;;\n+\t--merge)\n+\t\t# we use merge anyway\n+\t;;\n+\t-C*)\n+\t\tdie \"Interactive rebase uses merge, so $1 does not make sense\"\n+\t;;\n+\t-v)\n+\t\tVERBOSE=t\n+\t;;\n+\t-i|--interactive)\n+\t\t# yeah, we know\n+\t;;\n+\t''|-h)\n+\t\tusage\n+\t;;\n+\t*)\n+\t\ttest -d \"$DOTEST\" &&\n+\t\t\tdie \"Interactive rebase already started\"\n+\n+\t\tgit var GIT_COMMITTER_IDENT >/dev/null ||\n+\t\t\tdie \"You need to set your committer info first\"\n+\n+\t\tcomment_for_reflog start\n+\n+\t\tONTO=\n+\t\tcase \"$1\" in\n+\t\t--onto)\n+\t\t\tONTO=$(git rev-parse --verify \"$2\") ||\n+\t\t\t\tdie \"Does not point to a valid commit: $2\"\n+\t\t\tshift; shift\n+\t\t\t;;\n+\t\tesac\n+\n+\t\trequire_clean_work_tree\n+\n+\t\ttest -z \"$2\" || git checkout \"$2\" ||\n+\t\t\tdie \"Could not checkout $2\"\n+\n+\t\tHEAD=$(git rev-parse --verify HEAD) || die \"No HEAD?\"\n+\t\tUPSTREAM=$(git rev-parse --verify \"$1\") || die \"Invalid base\"\n+\n+\t\ttest -z \"$ONTO\" && ONTO=$UPSTREAM\n+\n+\t\tmkdir \"$DOTEST\" || die \"Could not create temporary $DOTEST\"\n+\t\t: > \"$DOTEST\"/interactive || die \"Could not mark as interactive\"\n+\t\tgit symbolic-ref HEAD > \"$DOTEST\"/head-name ||\n+\t\t\tdie \"Could not get HEAD\"\n+\n+\t\techo $HEAD > \"$DOTEST\"/head\n+\t\techo $UPSTREAM > \"$DOTEST\"/upstream\n+\t\techo $ONTO > \"$DOTEST\"/onto\n+\t\ttest t = \"$VERBOSE\" && : > \"$DOTEST\"/verbose\n+\n+\t\tcat > \"$TODO\" << EOF\n+# Reorder by exchanging lines.  Skip by removing lines.  If you want to\n+# edit a commit, replace the \"pick\" command with \"edit\".  If you want to\n+# squash the changes of a commit A into a commit B, place A directly\n+# after B, and replace the \"pick\" command with \"squash\".\n+EOF\n+\t\tgit rev-list --no-merges --pretty=oneline --abbrev-commit \\\n+\t\t\t--abbrev=7 --reverse $UPSTREAM..$HEAD | \\\n+\t\t\tsed \"s/^/pick /\" >> \"$TODO\"\n+\n+\t\ttest -s \"$TODO\" || die \"Nothing to do\"\n+\n+\t\tcp \"$TODO\" \"$TODO\".backup\n+\t\t${VISUAL:-${EDITOR:-vi}} \"$TODO\" ||\n+\t\t\tdie \"Could not execute editor\"\n+\n+\t\tgit reset --hard $ONTO && do_rest\n+\t;;\n+\tesac\n+\tshift\n+done\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex 2aa3a01..9e25158 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -3,7 +3,7 @@\n # Copyright (c) 2005 Junio C Hamano.\n #\n \n-USAGE='[-v] [--onto <newbase>] <upstream> [<branch>]'\n+USAGE='[--interactive | -i] [-v] [--onto <newbase>] <upstream> [<branch>]'\n LONG_USAGE='git-rebase replaces <branch> with a new branch of the\n same name.  When the --onto option is provided the new branch starts\n out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>\n@@ -120,6 +120,16 @@ finish_rb_merge () {\n \techo \"All done.\"\n }\n \n+is_interactive () {\n+\ttest -f \"$dotest\"/interactive ||\n+\twhile case \"$1\" in ''|-i|--interactive) break ;; esac\n+\tdo\n+\t\tshift\n+\tdone && test -n \"$1\"\n+}\n+\n+is_interactive \"$@\" && exec git-rebase--interactive \"$@\"\n+\n while case \"$#\" in 0) break ;; esac\n do\n \tcase \"$1\" in\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nnew file mode 100755\nindex 0000000..48aa8ea\n--- /dev/null\n+++ b/t/t3404-rebase-interactive.sh\n@@ -0,0 +1,163 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2007 Johannes E. Schindelin\n+#\n+\n+test_description='git rebase interactive\n+\n+This test runs git rebase \"interactively\", by faking an edit, and verifies\n+that the result still makes sense.\n+'\n+. ./test-lib.sh\n+\n+# set up two branches like this:\n+#\n+# A - B - C - D - E\n+#   \\\n+#     F - G - H\n+#       \\\n+#         I\n+#\n+# where B, D and G touch the same file.\n+\n+test_expect_success 'setup' '\n+\t: > file1 &&\n+\tgit add file1 &&\n+\ttest_tick &&\n+\tgit commit -m A &&\n+\tgit tag A &&\n+\techo 1 > file1 &&\n+\ttest_tick &&\n+\tgit commit -m B file1 &&\n+\t: > file2 &&\n+\tgit add file2 &&\n+\ttest_tick &&\n+\tgit commit -m C &&\n+\techo 2 > file1 &&\n+\ttest_tick &&\n+\tgit commit -m D file1 &&\n+\t: > file3 &&\n+\tgit add file3 &&\n+\ttest_tick &&\n+\tgit commit -m E &&\n+\tgit checkout -b branch1 A &&\n+\t: > file4 &&\n+\tgit add file4 &&\n+\ttest_tick &&\n+\tgit commit -m F &&\n+\tgit tag F &&\n+\techo 3 > file1 &&\n+\ttest_tick &&\n+\tgit commit -m G file1 &&\n+\t: > file5 &&\n+\tgit add file5 &&\n+\ttest_tick &&\n+\tgit commit -m H &&\n+\tgit checkout -b branch2 F &&\n+\t: > file6 &&\n+\tgit add file6 &&\n+\ttest_tick &&\n+\tgit commit -m I &&\n+\tgit tag I\n+'\n+\n+cat > fake-editor.sh << EOF\n+#!/bin/sh\n+test \"\\$1\" = .git/COMMIT_EDITMSG && exit\n+test -z \"\\$FAKE_LINES\" && exit\n+grep -v \"^#\" < \"\\$1\" > \"\\$1\".tmp\n+rm \"\\$1\"\n+cat \"\\$1\".tmp\n+action=pick\n+for line in \\$FAKE_LINES; do\n+\tcase \\$line in\n+\tsquash)\n+\t\taction=\"\\$line\";;\n+\t*)\n+\t\techo sed -n \"\\${line}s/^pick/\\$action/p\"\n+\t\tsed -n \"\\${line}p\" < \"\\$1\".tmp\n+\t\tsed -n \"\\${line}s/^pick/\\$action/p\" < \"\\$1\".tmp >> \"\\$1\"\n+\t\taction=pick;;\n+\tesac\n+done\n+EOF\n+\n+chmod a+x fake-editor.sh\n+VISUAL=\"$(pwd)/fake-editor.sh\"\n+export VISUAL\n+\n+test_expect_success 'no changes are a nop' '\n+\tgit rebase -i F &&\n+\ttest $(git rev-parse I) = $(git rev-parse HEAD)\n+'\n+\n+test_expect_success 'rebase on top of a non-conflicting commit' '\n+\tgit checkout branch1 &&\n+\tgit tag original-branch1 &&\n+\tgit rebase -i branch2 &&\n+\ttest file6 = $(git diff --name-only original-branch1) &&\n+\ttest $(git rev-parse I) = $(git rev-parse HEAD~2)\n+'\n+\n+test_expect_success 'exchange two commits' '\n+\tFAKE_LINES=\"2 1\" git rebase -i HEAD~2 &&\n+\ttest H = $(git cat-file commit HEAD^ | tail -n 1) &&\n+\ttest G = $(git cat-file commit HEAD | tail -n 1)\n+'\n+\n+cat > expect << EOF\n+diff --git a/file1 b/file1\n+index e69de29..00750ed 100644\n+--- a/file1\n++++ b/file1\n+@@ -0,0 +1 @@\n++3\n+EOF\n+\n+cat > expect2 << EOF\n+<<<<<<< HEAD:file1\n+2\n+=======\n+3\n+>>>>>>> b7ca976... G:file1\n+EOF\n+\n+test_expect_success 'stop on conflicting pick' '\n+\tgit tag new-branch1 &&\n+\t! git rebase -i master &&\n+\tdiff -u expect .git/.dotest-merge/patch &&\n+\tdiff -u expect2 file1 &&\n+\ttest 4 = $(grep -v \"^#\" < .git/.dotest-merge/done | wc -l) &&\n+\ttest 0 = $(grep -v \"^#\" < .git/.dotest-merge/todo | wc -l)\n+'\n+\n+test_expect_success 'abort' '\n+\tgit rebase --abort &&\n+\ttest $(git rev-parse new-branch1) = $(git rev-parse HEAD) &&\n+\t! test -d .git/.dotest-merge\n+'\n+\n+test_expect_success 'retain authorship' '\n+\techo A > file7 &&\n+\tgit add file7 &&\n+\tGIT_AUTHOR_NAME=\"Twerp Snog\" git commit -m \"different author\" &&\n+\tgit tag twerp &&\n+\tgit rebase -i --onto master HEAD^ &&\n+\tgit show HEAD | grep \"^Author: Twerp Snog\"\n+'\n+\n+test_expect_success 'squash' '\n+\tgit reset --hard twerp &&\n+\techo B > file7 &&\n+\tGIT_AUTHOR_NAME=\"Nitfol\" git commit -m \"nitfol\" file7 &&\n+\techo \"******************************\" &&\n+\tFAKE_LINES=\"1 squash 2\" git rebase -i --onto master HEAD~2 &&\n+\ttest B = $(cat file7) &&\n+\ttest $(git rev-parse HEAD^) = $(git rev-parse master)\n+'\n+\n+test_expect_success 'retain authorship when squashing' '\n+\tgit show HEAD | grep \"^Author: Nitfol\"\n+'\n+\n+test_done\n-- \n1.5.2.2.279.g74fb3\n"},{"id":"45631","messageId":"81b0412b0706231633yd0f19e8xbb0b7004fd7f75ef@mail.gmail.com","threadId":"8694","inReplyTo":"Pine.LNX.4.64.0706240001150.4059@racer.site","subject":"Re: [PATCH 2/2] Teach rebase an interactive mode","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2007-06-23T23:33:42Z","receivedAt":"2007-06-23T23:33:42Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"On 6/24/07, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> +If you want to fold two commits into one, just replace the command \"pick\"\n> +with \"squash\" for the second commit.  After squashing the commits,\n\nCan I fold 3 commits into one? 4 and 5?\n"},{"id":"45632","messageId":"Pine.LNX.4.64.0706240035180.4059@racer.site","threadId":"8694","inReplyTo":"81b0412b0706231633yd0f19e8xbb0b7004fd7f75ef@mail.gmail.com","subject":"Re: [PATCH 2/2] Teach rebase an interactive mode","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-06-23T23:36:53Z","receivedAt":"2007-06-23T23:36:53Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 24 Jun 2007, Alex Riesen wrote:\n\n> On 6/24/07, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n>\n> > +If you want to fold two commits into one, just replace the command \"pick\"\n> > +with \"squash\" for the second commit.  After squashing the commits,\n> \n> Can I fold 3 commits into one? 4 and 5?\n\nYes, but the wording got too complicated. So I guessed that those who need \nto squash more than 2 commits into one would get the idea.\n\nCiao,\nDscho\n"},{"id":"45639","messageId":"31A266F6-775E-41AE-B757-BF0DDAC6CAA5@silverinsanity.com","threadId":"8694","inReplyTo":"Pine.LNX.4.64.0706240001150.4059@racer.site","subject":"Re: [PATCH 2/2] Teach rebase an interactive mode","fromName":"Brian Gernhardt","fromEmail":"benji@silverinsanity.com","sentAt":"2007-06-24T03:39:11Z","receivedAt":"2007-06-24T03:39:11Z","isPatch":true,"sender":{"key":"benji@silverinsanity.com","avatar":"https://gravatar.com/avatar/e06c101dbc25c68114d859b4a9ec7cf8a2c52fd2b0270ef0eac0e2e63ff22311?d=mp&s=160"},"body":"\nOn Jun 23, 2007, at 7:01 PM, Johannes Schindelin wrote:\n\n>\n> With \"--interactive\", git-rebase now lets you edit the list of patches\n> to be reapplied, so that you can reorder and/or delete patches.\n>\n> Such a list will typically look like this:\n>\n> \tpick deadbee The oneline of this commit\n> \tpick fa1afe1 The oneline of the next commit\n> \t...\n>\n\nIt's not at all obvious from your commit message or the documentation  \nwhere this list comes from.  At first look I thought I would have had  \nto type it in.  It took digging into the code to be sure how it  \nworked.  You probably want to mention in the documentation that the  \nlist is generated from the arguments and given in a text editor.\n\nIs there a way to say \"Oops, that's the wrong list.  Don't do  \nanything.\"?\n\nPerhaps starting the list with a header like the following would make  \nit more user-friendly (Obviously requires s/#.*$//):\n\n# Rebasing $from..$to onto $commit\n#\n# Commands:\n#  pick = use commit\n#  squash = meld commit into previous\n# <<More useful information here>>\n\nHowever, those are nits to take a good idea to a great one.  I like  \nit very much.\n\n~~ Brian\n"},{"id":"45648","messageId":"7v4pkxydim.fsf@assigned-by-dhcp.cox.net","threadId":"8694","inReplyTo":"Pine.LNX.4.64.0706240001150.4059@racer.site","subject":"Re: [PATCH 2/2] Teach rebase an interactive mode","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-06-24T08:15:13Z","receivedAt":"2007-06-24T08:15:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\n> index 0c00090..2e474e8 100644\n> --- a/Documentation/git-rebase.txt\n> +++ b/Documentation/git-rebase.txt\n> ...\n> +Interactive mode\n> +----------------\n> +\n> +Rebasing interactively means that you have a chance to edit the commits\n> +which are rebased.  You can not only reorder the commits, but also\n> +remove them (weeding out bad or otherwise unwanted patches).\n\nMaybe it's just my bad English comprehension skill, but I needed\nto read \"You can not only ... but also\" twice.  \"You can reorder\nand also remove\" would mean the same thing and would be much\neasier for non natives to understand.\n\n> +\n> +The list will look like this:\n> +\n> +-------------------------------------------\n> +pick deadbee The oneline of this commit\n> +pick fa1afe1 The oneline of the next commit\n> +...\n> +-------------------------------------------\n> +\n> +The oneline descriptions are purely for your pleasure; `git-rebase` will\n> +not look at them but at the commit names, so do not delete or edit the\n> +names.\n\nBy \"commit name\", do you mean deadbee?  After reading the full\npatch I know that is what you meant, but it was a bit unclear\nduring my initial pass.\n\n> +By replacing the command \"pick\" with the command \"edit\", you can tell\n> +`git-rebase` to stop after applying that commit, so that you can edit\n> +the files and/or the commit message, amend the commit and continue\n> +rebasing.\n> +\n> +If you want to fold two commits into one, just replace the command \"pick\"\n> +with \"squash\" for the second commit.  After squashing the commits,\n> +`git-rebase` will start an editor with both commit messages, so you\n> +can compose the commit message for the squashed commit.\n\nThere was a question about squashing more than two on the list,\nand you explained it as \"one would get the idea\", but I am not\nsure I got it right.  Would this be what you meant?\n\n\tIf you want to fold two or more commits into one,\n\treplace the command \"pick\" with \"squash\" for the second\n\tand subsequent commit.\n\n> diff --git a/Makefile b/Makefile\n> index 0d904a9..edb421b 100644\n> --- a/Makefile\n> +++ b/Makefile\n> ...\n> +# MOTIVATION\n> +#\n> +# Consider this type of workflow:\n> ...\n> +# Sometimes the thing fixed in B.2. cannot be amended to the not-quite\n> +# perfect commit it fixes, because that commit is buried deeply in a\n> +# patch series.\n> +#\n> +# Use this script after plenty of \"A\"s and \"B\"s, by rearranging, and\n> +# possibly editing and merging commits.\n\nThis part is missing from Documentation/git-rebase.txt and\nshould move there, and the USAGE: part of the documentation\nshould be removed from here; otherwise you need to maintain the\ntwo in sync.\n\n> +USAGE='(--continue | --abort | --skip | [--onto <branch>] <upstream> [<branch>])'\n> +\n> +. git-sh-setup\n> +require_work_tree\n> +\n> +DOTEST=\"$GIT_DIR/.dotest-merge\"\n> +TODO=\"$DOTEST\"/todo\n> +DONE=\"$DOTEST\"/done\n> +STRATEGY=\n> +VERBOSE=\n> +\n> +warn () {\n> +\techo \"$@\" >&2\n> +}\n\nI would have used \"$*\" instead.\n\n> +require_clean_work_tree () {\n> +\t# test if working tree is dirty\n> +\tgit rev-parse --verify HEAD > /dev/null &&\n> +\tgit update-index --refresh &&\n> +\ttest -z \"`git diff-files --name-only`\" &&\n> +\ttest -z \"`git diff-index --cached --name-only HEAD`\" ||\n> +\tdie \"Working tree is dirty\"\n> +}\n\nHeh, I think I showed\n\n\tgit diff-files --quiet && git diff-index --cached --quiet\n\nto somebody else today.  Let's be modern ;-).\n\nIs today my \"nitpick shell scripts day\"? ;-)\n\n> +ORIG_REFLOG_ACTION=\"$GIT_REFLOG_ACTION\"\n> +\n> +comment_for_reflog () {\n> +\tif test -z \"$ORIG_REFLOG_ACTION\"; then\n> +\t\tGIT_REFLOG_ACTION=\"rebase --interactive ($1)\"\n> +\t\texport GIT_REFLOG_ACTION\n\nPlease use shorter prefix, \"rebase -i\".  \"git reflog\" output is\neaser to read that way.\n\n> +\tfi\n> +}\n> +\n> +mark_action_done () {\n> +\tsed -n 1p < \"$TODO\" >> \"$DONE\"\n> +\tsed -n '2,$p' < \"$TODO\" >> \"$TODO\".new\n> +\tmv -f \"$TODO\".new \"$TODO\"\n> +}\n\nI would have written \"sed -e 1q\" and \"sed -e 1d\".\n\n> +make_patch () {\n> +\tparent_sha1=$(git rev-parse --verify \"$1\"^ 2> /dev/null)\n> +\tgit diff \"$parent_sha1\"..\"$1\" > \"$DOTEST\"/patch\n> +}\n> +\n> +die_with_patch () {\n> +\tmake_patch \"$1\"\n> +\tdie \"$2\"\n> +}\n> +\n> +pick_one () {\n> +\tcase \"$1\" in -n) sha1=$2 ;; *) sha1=$1 ;; esac\n> +\tparent_sha1=$(git rev-parse --verify $sha1^ 2>/dev/null)\n> +\tcurrent_sha1=$(git rev-parse --verify HEAD)\n> +\tif [ $current_sha1 = $parent_sha1 ]; then\n> +\t\tgit reset --hard $sha1\n> +\t\tsha1=$(git rev-parse --short $sha1)\n> +\t\twarn Fast forward to $sha1\n> +\telse\n> +\t\tgit cherry-pick $STRATEGY \"$@\"\n> +\tfi\n> +}\n\nI wonder what happens when the user mistakenly breaks the commit\nobject name on the 'pick' command line and the above --verify\nfails.  A bit better error checking is needed here.\n\n> +\n> +do_next () {\n> +\tread command sha1 rest < \"$TODO\"\n> +\tcase \"$command\" in\n> +\t\\#)\n\nHmph.  Don't you allow a blank line also as a comment (for readability)?\n\n> +\t\tmark_action_done\n> +\t\tcontinue\n> +\t;;\n\nStyle.  Indent ';;' one more level, that is:\n\n\tcase \"$to_test\" in\n        arm1)\n        \taction\n                ;;\n\t...\n        esac                \n\n> +\tsquash)\n> +\t\tcomment_for_reflog squash\n> +\n> +\t\ttest -s \"$DONE\" ||\n> +\t\t\tdie \"Cannot 'squash' without a previous commit\"\n\nAs '#' comment line is sent to DONE file with mark_action_done,\nnon empty DONE file does not necessarily mean you have a\nprevious commit --- am I mistaken?\n\n> +\n> +\t\tmark_action_done\n> +\t\tfailed=f\n> +\t\tpick_one -n $sha1 || failed=t\n> +\t\tMSG=\"$DOTEST\"/message\n> +\t\techo \"# This is a combination of two commits.\" > \"$MSG\"\n> +\t\techo \"# The first commit's message is:\" >> \"$MSG\"\n> +\t\tgit cat-file commit HEAD | sed -n '/^$/,$p' >> \"$MSG\"\n> +\t\techo >> \"$MSG\"\n> +\t\techo \"# And this is the 2nd commit message:\" >> \"$MSG\"\n> +\t\techo >> \"$MSG\"\n> +\t\tgit cat-file commit $sha1 | sed -n '/^$/,$p' >> \"$MSG\"\n\nSo you have one blank line after \"# The first commit's message is:\"\nbut have two blank lines after \"# And this is the 2nd\"?\n\nStyle.  Always prefix sed scripts with \"-e\", like so:\n\n\tgit cat-file commit HEAD | sed -e '1,/^$/d'\n\n> +\t\tgit reset --soft HEAD^\n> +\t\tauthor_script=$(get_author_ident_from_commit $sha1)\n> +\t\tcase $failed in\n> +\t\tf)\n> +\t\t\t# This is like --amend, but with a different message\n> +\t\t\teval $author_script\n\nMissing dq, like the other patch to git-commit?\n\n> +\t\t\texport GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE\n> +\t\t\tgit commit -F \"$MSG\" -e\n> +\t\t;;\n> +\t\tt)\n> +\t\t\tcp \"$MSG\" \"$GIT_DIR\"/MERGE_MSG\n> +\t\t\twarn\n> +\t\t\twarn \"Could not apply $sha1... $rest\"\n> +\t\t\twarn \"After you fixed that, commit the result with\"\n> +\t\t\twarn\n> +\t\t\twarn \"  $(echo $author_script | tr '\\012' ' ') \\\\\"\n> +\t\t\twarn \"\t  git commit -F \\\"$GIT_DIR\\\"/MERGE_MSG -e\"\n> +\t\t\tdie_with_patch $sha1 \"\"\n> +\t\tesac\n> +\t;;\n> +\t*)\n> +\t\twarn \"Unknown command: $command $sha1 $rest\"\n> +\t\tdie_with_patch $sha1 \"Please fix this in the file $TODO.\"\n> +\tesac\n> +\ttest -s \"$TODO\" && continue\n\nIt is not clear to me from reading the POSIX that this\n\"continue\" is allowed, although both bash and dash seem to work\nas you expect.  continue \"shall return to the top of the\nsmallest enclosing for, while or until loop\" and the enclosing\nwhile loop you are continuing is actually the one in do_rest\nfunction, which is the caller of this.  If POSIX meant dynamic\nscoping rules, that is fine, but lexically that 'while' does not\nenclose this 'continue'.\n\nI think this one can safely be changed to \"return\" and there\nwon't be any need for such an worry.\n\n> +\n> +\tHEAD=$(git rev-parse HEAD)\n> +\tHEADNAME=$(cat \"$DOTEST\"/head-name)\n> +\tgit update-ref $HEADNAME $HEAD &&\n> +\tgit symbolic-ref HEAD $HEADNAME || exit\n\nDon't we want reflog entries for these two operations?\n\n> +\trm -rf \"$DOTEST\" &&\n> +\twarn \"Successfully rebased and updated $HEADNAME.\"\n> +\n> +\texit $?\n> +}\n> +\n> +do_rest () {\n> +\twhile :\n> +\tdo\n> +\t\tdo_next\n> +\tdone\n>\n> +\ttest $? = 0 -a -f \"$DOTEST\"/verbose &&\n> +\t\tgit diff --stat $(cat \"$DOTEST\"/head)..HEAD\n\nI am not sure what command's exit status $? refers to at this\npoint.  do_next is not run in a subshell, so when it exits, you\nwould not reach here, would you?\n\n> +\texit\n> +}\n\nOne says \"exit $?\" the other says \"exit\" -- they mean the same\n;-).\n\n> +while case \"$#\" in 0) break ;; esac\n> +do\n\nNo need to quote $#.\n\n> +\tcase \"$1\" in\n> +\t--continue)\n> +\t\tcomment_for_reflog continue\n> +\n> +\t\ttest -d \"$DOTEST\" || die \"No interactive rebase running\"\n> +\n> +\t\trequire_clean_work_tree\n> +\t\tdo_rest\n> +\t;;\n\nIndent ;; one more level please.\n\n> +\t--abort)\n> +\t\tcomment_for_reflog abort\n> +\n> +\t\ttest -d \"$DOTEST\" || die \"No interactive rebase running\"\n> +\n> +\t\tHEADNAME=$(cat \"$DOTEST\"/head-name)\n> +\t\tHEAD=$(cat \"$DOTEST\"/head)\n> +\t\tgit symbolic-ref HEAD $HEADNAME &&\n> +\t\tgit reset --hard $HEAD &&\n> +\t\trm -rf \"$DOTEST\"\n> +\t\texit\n> +\t;;\n> +\t--skip)\n> +\t\tcomment_for_reflog skip\n> +\n> +\t\ttest -d \"$DOTEST\" || die \"No interactive rebase running\"\n> +\n> +\t\tgit reset --hard && do_rest\n> +\t;;\n> +\t-s|--strategy)\n> +\t\tcase \"$#,$1\" in\n> +\t\t*,*=*)\n> +\t\t\tSTRATEGY=\"-s `expr \"z$1\" : 'z-[^=]*=\\(.*\\)'`\" ;;\n> +\t\t1,*)\n> +\t\t\tusage ;;\n> +\t\t*)\n> +\t\t\tSTRATEGY=\"-s $2\"\n> +\t\t\tshift ;;\n> +\t\tesac\n\nAre we missing a shift before the above \"case\"?\n\n> +\t;;\n> +\t--merge)\n> +\t\t# we use merge anyway\n> +\t;;\n> +\t-C*)\n> +\t\tdie \"Interactive rebase uses merge, so $1 does not make sense\"\n> +\t;;\n> +\t-v)\n> +\t\tVERBOSE=t\n> +\t;;\n> +\t-i|--interactive)\n> +\t\t# yeah, we know\n> +\t;;\n> +\t''|-h)\n> +\t\tusage\n> +\t;;\n> +\t*)\n> +\t\ttest -d \"$DOTEST\" &&\n> +\t\t\tdie \"Interactive rebase already started\"\n> +\n> +\t\tgit var GIT_COMMITTER_IDENT >/dev/null ||\n> +\t\t\tdie \"You need to set your committer info first\"\n> +\n> +\t\tcomment_for_reflog start\n> +\n> +\t\tONTO=\n> +\t\tcase \"$1\" in\n> +\t\t--onto)\n> +\t\t\tONTO=$(git rev-parse --verify \"$2\") ||\n> +\t\t\t\tdie \"Does not point to a valid commit: $2\"\n> +\t\t\tshift; shift\n> +\t\t\t;;\n> +\t\tesac\n> +\n> +\t\trequire_clean_work_tree\n> +\n> +\t\ttest -z \"$2\" || git checkout \"$2\" ||\n> +\t\t\tdie \"Could not checkout $2\"\n\nCan you afford to detach HEAD here?  Later you check with\nsymbolic-ref so I think not, which means \"$2\" must be a valid\nbranch name, so it should be tested like:\n\n\tgit show-ref --verify --quiet \"refs/heads/$2\"\n\n> +\t\tHEAD=$(git rev-parse --verify HEAD) || die \"No HEAD?\"\n> +\t\tUPSTREAM=$(git rev-parse --verify \"$1\") || die \"Invalid base\"\n> +\n> +\t\ttest -z \"$ONTO\" && ONTO=$UPSTREAM\n> +\n> +\t\tmkdir \"$DOTEST\" || die \"Could not create temporary $DOTEST\"\n> +\t\t: > \"$DOTEST\"/interactive || die \"Could not mark as interactive\"\n> +\t\tgit symbolic-ref HEAD > \"$DOTEST\"/head-name ||\n> +\t\t\tdie \"Could not get HEAD\"\n> +\n> +\t\techo $HEAD > \"$DOTEST\"/head\n> +\t\techo $UPSTREAM > \"$DOTEST\"/upstream\n> +\t\techo $ONTO > \"$DOTEST\"/onto\n> +\t\ttest t = \"$VERBOSE\" && : > \"$DOTEST\"/verbose\n> +\n> +\t\tcat > \"$TODO\" << EOF\n> +# Reorder by exchanging lines.  Skip by removing lines.  If you want to\n> +# edit a commit, replace the \"pick\" command with \"edit\".  If you want to\n> +# squash the changes of a commit A into a commit B, place A directly\n> +# after B, and replace the \"pick\" command with \"squash\".\n> +EOF\n> +\t\tgit rev-list --no-merges --pretty=oneline --abbrev-commit \\\n> +\t\t\t--abbrev=7 --reverse $UPSTREAM..$HEAD | \\\n> +\t\t\tsed \"s/^/pick /\" >> \"$TODO\"\n> +\n> +\t\ttest -s \"$TODO\" || die \"Nothing to do\"\n> +\n> +\t\tcp \"$TODO\" \"$TODO\".backup\n> +\t\t${VISUAL:-${EDITOR:-vi}} \"$TODO\" ||\n> +\t\t\tdie \"Could not execute editor\"\n> +\n> +\t\tgit reset --hard $ONTO && do_rest\n> +\t;;\n> +\tesac\n> +\tshift\n> +done\n\n> diff --git a/git-rebase.sh b/git-rebase.sh\n> index 2aa3a01..9e25158 100755\n> --- a/git-rebase.sh\n> +++ b/git-rebase.sh\n> @@ -3,7 +3,7 @@\n>  # Copyright (c) 2005 Junio C Hamano.\n>  #\n>  \n> -USAGE='[-v] [--onto <newbase>] <upstream> [<branch>]'\n> +USAGE='[--interactive | -i] [-v] [--onto <newbase>] <upstream> [<branch>]'\n>  LONG_USAGE='git-rebase replaces <branch> with a new branch of the\n>  same name.  When the --onto option is provided the new branch starts\n>  out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>\n> @@ -120,6 +120,16 @@ finish_rb_merge () {\n>  \techo \"All done.\"\n>  }\n>  \n> +is_interactive () {\n> +\ttest -f \"$dotest\"/interactive ||\n> +\twhile case \"$1\" in ''|-i|--interactive) break ;; esac\n> +\tdo\n> +\t\tshift\n> +\tdone && test -n \"$1\"\n> +}\n\nI think by case \"$1\" in '') you meant \"we ran out\", but that is\nnot a good pattern.  Check $# at the same time, otherwise you\nwould stop at an empty argument that is followed by other\narguments.\n\nThis is just an idea, but I have been wondering if it would be\nuseful if we teach rebase (interactive or not) to handle a merge\nfrom an unrelated (wrt the rebase that is being performed)\nbranch.  That is, if you had this development on top of the\norigin 'O':\n\n             X\n              \\  \n           A---M---B     \n          /\n  ---o---O\n\nthat you committed A, merged X and then committed B, you should\nbe able to rebase on top of an updated upstream 'Q':\n\n             X\n              \\  \n           A---M---B     \n          /\n  ---o---O---P---Q\n\nby 'pick A/merge M/pick B', which would do:\n\n                     X\n                      \\\n                   A'--M'--B'\n                  /\n  ---o---O---P---Q\n\nNote that A', M' and B' are different commit objects (rebase\nrewrites the history) from the original picture, but X is the\nsame commit from the original picture.\n"},{"id":"45658","messageId":"Pine.LNX.4.64.0706241109410.4059@racer.site","threadId":"8694","inReplyTo":"31A266F6-775E-41AE-B757-BF0DDAC6CAA5@silverinsanity.com","subject":"Re: [PATCH 2/2] Teach rebase an interactive mode","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-06-24T10:12:33Z","receivedAt":"2007-06-24T10:12:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 23 Jun 2007, Brian Gernhardt wrote:\n\n> On Jun 23, 2007, at 7:01 PM, Johannes Schindelin wrote:\n> \n> > With \"--interactive\", git-rebase now lets you edit the list of patches\n> > to be reapplied, so that you can reorder and/or delete patches.\n> > \n> > Such a list will typically look like this:\n> > \n> > \tpick deadbee The oneline of this commit\n> > \tpick fa1afe1 The oneline of the next commit\n> > \t...\n> > \n> \n> It's not at all obvious from your commit message or the documentation \n> where this list comes from.\n\nOh, okay. I thought that this being just a mode for git-rebase, it would \nbe obvious where that list comes from... Any idea how to state that more \nclearly?\n\n> Is there a way to say \"Oops, that's the wrong list.  Don't do \n> anything.\"?\n\nIt is not at all documented, but yes. Just delete the complete list.\n\n> Perhaps starting the list with a header like the following would make it \n> more user-friendly (Obviously requires s/#.*$//):\n\nAll lines with \"#\" are ignored, yes.\n\n> # Rebasing $from..$to onto $commit\n> #\n> # Commands:\n> #  pick = use commit\n> #  squash = meld commit into previous\n> # <<More useful information here>>\n\nThat's a very good idea! (I completely suck at descriptive texts.) Will \nfix.\n\nCiao,\nDscho\n"},{"id":"45663","messageId":"Pine.LNX.4.64.0706241204450.4059@racer.site","threadId":"8694","inReplyTo":"7v4pkxydim.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 2/2] Teach rebase an interactive mode","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-06-24T11:09:03Z","receivedAt":"2007-06-24T11:09:03Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nthanks to both of you, for your comments. I am a little pressed on time, \nso I will only be able to fix all this up this evening, but I thought I \nlet you know what the state is.\n\nI am not answering point by point, because I agree with all of the nits.\n\nThe four missing points are:\n\n- fix up the commit message so that it is clear from where the list of \n  commits comes,\n- put a sensible reflog message at the last update,\n- move the usage section into the man page, and\n- fix the quoting in patch 1/2.\n\nWill resend (after using rebase -i myself, yeah!) tonight.\n\n---\n Documentation/git-rebase.txt |   12 ++---\n git-rebase--interactive.sh   |   90 ++++++++++++++++++++++++------------------\n 2 files changed, 56 insertions(+), 46 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 2e474e8..e96d72e 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -235,7 +235,7 @@ Interactive mode\n ----------------\n \n Rebasing interactively means that you have a chance to edit the commits\n-which are rebased.  You can not only reorder the commits, but also\n+which are rebased.  You can reorder the commits, and you can\n remove them (weeding out bad or otherwise unwanted patches).\n \n The list will look like this:\n@@ -247,18 +247,16 @@ pick fa1afe1 The oneline of the next commit\n -------------------------------------------\n \n The oneline descriptions are purely for your pleasure; `git-rebase` will\n-not look at them but at the commit names, so do not delete or edit the\n-names.\n+not look at them but at the commit names (\"deadbee\" and \"fa1afe1\" in this\n+example), so do not delete or edit the names.\n \n By replacing the command \"pick\" with the command \"edit\", you can tell\n `git-rebase` to stop after applying that commit, so that you can edit\n the files and/or the commit message, amend the commit and continue\n rebasing.\n \n-If you want to fold two commits into one, just replace the command \"pick\"\n-with \"squash\" for the second commit.  After squashing the commits,\n-`git-rebase` will start an editor with both commit messages, so you\n-can compose the commit message for the squashed commit.\n+If you want to fold two or more commits into one, replace the command\n+\"pick\" with \"squash\" for the second and subsequent commit.\n \n A common use case for the interactive mode is when you want to reorder\n the last 5 commits, such that what was HEAD~4 becomes the new HEAD. To\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex 6a960b4..1d1e927 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -78,30 +78,31 @@ STRATEGY=\n VERBOSE=\n \n warn () {\n-\techo \"$@\" >&2\n+\techo \"$*\" >&2\n }\n \n require_clean_work_tree () {\n \t# test if working tree is dirty\n \tgit rev-parse --verify HEAD > /dev/null &&\n \tgit update-index --refresh &&\n-\ttest -z \"`git diff-files --name-only`\" &&\n-\ttest -z \"`git diff-index --cached --name-only HEAD`\" ||\n+\tgit diff-files --quiet &&\n+\tgit diff-index --cached --quiet HEAD ||\n \tdie \"Working tree is dirty\"\n }\n \n ORIG_REFLOG_ACTION=\"$GIT_REFLOG_ACTION\"\n \n comment_for_reflog () {\n-\tif test -z \"$ORIG_REFLOG_ACTION\"; then\n-\t\tGIT_REFLOG_ACTION=\"rebase --interactive ($1)\"\n+\tcase \"$ORIG_REFLOG_ACTION\" in\n+\t''|rebase*)\n+\t\tGIT_REFLOG_ACTION=\"rebase -i ($1)\"\n \t\texport GIT_REFLOG_ACTION\n-\tfi\n+\tesac\n }\n \n mark_action_done () {\n-\tsed -n 1p < \"$TODO\" >> \"$DONE\"\n-\tsed -n '2,$p' < \"$TODO\" >> \"$TODO\".new\n+\tsed -e 1q < \"$TODO\" >> \"$DONE\"\n+\tsed -e 1d < \"$TODO\" >> \"$TODO\".new\n \tmv -f \"$TODO\".new \"$TODO\"\n }\n \n@@ -117,6 +118,7 @@ die_with_patch () {\n \n pick_one () {\n \tcase \"$1\" in -n) sha1=$2 ;; *) sha1=$1 ;; esac\n+\tgit rev-parse --verify $sha1 || die \"Invalid commit name: $sha1\"\n \tparent_sha1=$(git rev-parse --verify $sha1^ 2>/dev/null)\n \tcurrent_sha1=$(git rev-parse --verify HEAD)\n \tif [ $current_sha1 = $parent_sha1 ]; then\n@@ -131,17 +133,17 @@ pick_one () {\n do_next () {\n \tread command sha1 rest < \"$TODO\"\n \tcase \"$command\" in\n-\t\\#)\n+\t\\#|'')\n \t\tmark_action_done\n \t\tcontinue\n-\t;;\n+\t\t;;\n \tpick)\n \t\tcomment_for_reflog pick\n \n \t\tmark_action_done\n \t\tpick_one $sha1 ||\n \t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n-\t;;\n+\t\t;;\n \tedit)\n \t\tcomment_for_reflog edit\n \n@@ -155,11 +157,11 @@ do_next () {\n \t\twarn \"\tgit commit --amend\"\n \t\twarn\n \t\texit 0\n-\t;;\n+\t\t;;\n \tsquash)\n \t\tcomment_for_reflog squash\n \n-\t\ttest -s \"$DONE\" ||\n+\t\ttest -z \"$(grep -ve '^$' -e '^#' < $DONE)\" &&\n \t\t\tdie \"Cannot 'squash' without a previous commit\"\n \n \t\tmark_action_done\n@@ -168,20 +170,21 @@ do_next () {\n \t\tMSG=\"$DOTEST\"/message\n \t\techo \"# This is a combination of two commits.\" > \"$MSG\"\n \t\techo \"# The first commit's message is:\" >> \"$MSG\"\n-\t\tgit cat-file commit HEAD | sed -n '/^$/,$p' >> \"$MSG\"\n+\t\techo >> \"$MSG\"\n+\t\tgit cat-file commit HEAD | sed -e '1,/^$/d' >> \"$MSG\"\n \t\techo >> \"$MSG\"\n \t\techo \"# And this is the 2nd commit message:\" >> \"$MSG\"\n \t\techo >> \"$MSG\"\n-\t\tgit cat-file commit $sha1 | sed -n '/^$/,$p' >> \"$MSG\"\n+\t\tgit cat-file commit $sha1 | sed -e '1,/^$/d' >> \"$MSG\"\n \t\tgit reset --soft HEAD^\n \t\tauthor_script=$(get_author_ident_from_commit $sha1)\n \t\tcase $failed in\n \t\tf)\n \t\t\t# This is like --amend, but with a different message\n-\t\t\teval $author_script\n+\t\t\teval \"$author_script\"\n \t\t\texport GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE\n \t\t\tgit commit -F \"$MSG\" -e\n-\t\t;;\n+\t\t\t;;\n \t\tt)\n \t\t\tcp \"$MSG\" \"$GIT_DIR\"/MERGE_MSG\n \t\t\twarn\n@@ -192,21 +195,22 @@ do_next () {\n \t\t\twarn \"\t  git commit -F \\\"$GIT_DIR\\\"/MERGE_MSG -e\"\n \t\t\tdie_with_patch $sha1 \"\"\n \t\tesac\n-\t;;\n+\t\t;;\n \t*)\n \t\twarn \"Unknown command: $command $sha1 $rest\"\n \t\tdie_with_patch $sha1 \"Please fix this in the file $TODO.\"\n \tesac\n-\ttest -s \"$TODO\" && continue\n+\ttest -s \"$TODO\" && return\n \n \tHEAD=$(git rev-parse HEAD)\n \tHEADNAME=$(cat \"$DOTEST\"/head-name)\n+# TODO: reflog?\n \tgit update-ref $HEADNAME $HEAD &&\n \tgit symbolic-ref HEAD $HEADNAME || exit\n \trm -rf \"$DOTEST\" &&\n \twarn \"Successfully rebased and updated $HEADNAME.\"\n \n-\texit $?\n+\texit\n }\n \n do_rest () {\n@@ -214,12 +218,12 @@ do_rest () {\n \tdo\n \t\tdo_next\n \tdone\n-\ttest $? = 0 -a -f \"$DOTEST\"/verbose &&\n+\ttest -f \"$DOTEST\"/verbose &&\n \t\tgit diff --stat $(cat \"$DOTEST\"/head)..HEAD\n \texit\n }\n \n-while case \"$#\" in 0) break ;; esac\n+while case $# in 0) break ;; esac\n do\n \tcase \"$1\" in\n \t--continue)\n@@ -229,7 +233,7 @@ do\n \n \t\trequire_clean_work_tree\n \t\tdo_rest\n-\t;;\n+\t\t;;\n \t--abort)\n \t\tcomment_for_reflog abort\n \n@@ -241,15 +245,16 @@ do\n \t\tgit reset --hard $HEAD &&\n \t\trm -rf \"$DOTEST\"\n \t\texit\n-\t;;\n+\t\t;;\n \t--skip)\n \t\tcomment_for_reflog skip\n \n \t\ttest -d \"$DOTEST\" || die \"No interactive rebase running\"\n \n \t\tgit reset --hard && do_rest\n-\t;;\n+\t\t;;\n \t-s|--strategy)\n+\t\tshift\n \t\tcase \"$#,$1\" in\n \t\t*,*=*)\n \t\t\tSTRATEGY=\"-s `expr \"z$1\" : 'z-[^=]*=\\(.*\\)'`\" ;;\n@@ -259,22 +264,22 @@ do\n \t\t\tSTRATEGY=\"-s $2\"\n \t\t\tshift ;;\n \t\tesac\n-\t;;\n+\t\t;;\n \t--merge)\n \t\t# we use merge anyway\n-\t;;\n+\t\t;;\n \t-C*)\n \t\tdie \"Interactive rebase uses merge, so $1 does not make sense\"\n-\t;;\n+\t\t;;\n \t-v)\n \t\tVERBOSE=t\n-\t;;\n+\t\t;;\n \t-i|--interactive)\n \t\t# yeah, we know\n-\t;;\n+\t\t;;\n \t''|-h)\n \t\tusage\n-\t;;\n+\t\t;;\n \t*)\n \t\ttest -d \"$DOTEST\" &&\n \t\t\tdie \"Interactive rebase already started\"\n@@ -295,8 +300,13 @@ do\n \n \t\trequire_clean_work_tree\n \n-\t\ttest -z \"$2\" || git checkout \"$2\" ||\n-\t\t\tdie \"Could not checkout $2\"\n+\t\tif [ ! -z \"$2\"]\n+\t\tthen\n+\t\t\tgit show-ref --verify --quiet \"refs/heads/$2\" ||\n+\t\t\t\tdie \"Invalid branchname: $2\"\n+\t\t\tgit checkout \"$2\" ||\n+\t\t\t\tdie \"Could not checkout $2\"\n+\t\tfi\n \n \t\tHEAD=$(git rev-parse --verify HEAD) || die \"No HEAD?\"\n \t\tUPSTREAM=$(git rev-parse --verify \"$1\") || die \"Invalid base\"\n@@ -314,23 +324,25 @@ do\n \t\ttest t = \"$VERBOSE\" && : > \"$DOTEST\"/verbose\n \n \t\tcat > \"$TODO\" << EOF\n-# Reorder by exchanging lines.  Skip by removing lines.  If you want to\n-# edit a commit, replace the \"pick\" command with \"edit\".  If you want to\n-# squash the changes of a commit A into a commit B, place A directly\n-# after B, and replace the \"pick\" command with \"squash\".\n+# Rebasing $UPSTREAM..$HEAD onto $ONTO\n+#\n+# Commands:\n+#  pick = use commit\n+#  edit = use commit, but stop for amending\n+#  squash = use commit, but meld into previous commit\n EOF\n \t\tgit rev-list --no-merges --pretty=oneline --abbrev-commit \\\n \t\t\t--abbrev=7 --reverse $UPSTREAM..$HEAD | \\\n \t\t\tsed \"s/^/pick /\" >> \"$TODO\"\n \n-\t\ttest -s \"$TODO\" || die \"Nothing to do\"\n+\t\ttest -z \"$(grep -ve '^$' -e '^#' < $TODO)\" &&\n+\t\t\tdie \"Nothing to do\"\n \n \t\tcp \"$TODO\" \"$TODO\".backup\n \t\t${VISUAL:-${EDITOR:-vi}} \"$TODO\" ||\n \t\t\tdie \"Could not execute editor\"\n \n \t\tgit reset --hard $ONTO && do_rest\n-\t;;\n \tesac\n \tshift\n done\n-- \n1.5.2.2.279.g9b198-dirty\n"},{"id":"45670","messageId":"Pine.LNX.4.64.0706241533030.4059@racer.site","threadId":"8694","inReplyTo":"7v4pkxydim.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 2/2] Teach rebase an interactive mode","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-06-24T14:33:12Z","receivedAt":"2007-06-24T14:33:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 24 Jun 2007, Junio C Hamano wrote:\n\n> This is just an idea, but I have been wondering if it would be useful if \n> we teach rebase (interactive or not) to handle a merge from an unrelated \n> (wrt the rebase that is being performed) branch.  That is, if you had \n> this development on top of the origin 'O':\n> \n>              X\n>               \\  \n>            A---M---B     \n>           /\n>   ---o---O\n> \n> that you committed A, merged X and then committed B, you should\n> be able to rebase on top of an updated upstream 'Q':\n> \n>              X\n>               \\  \n>            A---M---B     \n>           /\n>   ---o---O---P---Q\n> \n> by 'pick A/merge M/pick B', which would do:\n> \n>                      X\n>                       \\\n>                    A'--M'--B'\n>                   /\n>   ---o---O---P---Q\n> \n> Note that A', M' and B' are different commit objects (rebase\n> rewrites the history) from the original picture, but X is the\n> same commit from the original picture.\n\nYes, I think that would be useful. But at the same time, I think it should \nnot be the default. Why? Because I will use rebase to beautify my history, \nand that looks mostly like this:\n\n--- A - B - C - D - E - junio/master\n      \\       \\   \\\n        M - N - O - P - my\n\nSo what I want is really clean up my side branch.\n\nTherefore I would like to have this --preserve-merges (short option -p?) \n_after_ rebase -i.\n\nIt will get a little tricky with different merge strategies, and saying \nwhich commits have to be rebased (A in your example), and which will be \npreserved (X in your example), and evil merges (merges which have fix ups \nin addition to the merge), but doable.\n\nCiao,\nDscho\n"},{"id":"45721","messageId":"Pine.LNX.4.64.0706250103510.4059@racer.site","threadId":"8694","inReplyTo":"Pine.LNX.4.64.0706241204450.4059@racer.site","subject":"[PATCH 1/2] Move the pick_author code to git-sh-setup","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-06-25T00:04:11Z","receivedAt":"2007-06-25T00:04:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nAt the moment, only git-commit uses that code, to pick the author name,\nemail and date from a given commit.\n\nThis code will be reused in git rebase --interactive.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tJust the eval was changed, by dq'ing its argument.\n\n git-commit.sh   |   30 ++----------------------------\n git-sh-setup.sh |   27 +++++++++++++++++++++++++++\n 2 files changed, 29 insertions(+), 28 deletions(-)\n\ndiff --git a/git-commit.sh b/git-commit.sh\nindex 5547a02..d43bdd8 100755\n--- a/git-commit.sh\n+++ b/git-commit.sh\n@@ -483,34 +483,8 @@ fi >>\"$GIT_DIR\"/COMMIT_EDITMSG\n # Author\n if test '' != \"$use_commit\"\n then\n-\tpick_author_script='\n-\t/^author /{\n-\t\ts/'\\''/'\\''\\\\'\\'\\''/g\n-\t\th\n-\t\ts/^author \\([^<]*\\) <[^>]*> .*$/\\1/\n-\t\ts/'\\''/'\\''\\'\\'\\''/g\n-\t\ts/.*/GIT_AUTHOR_NAME='\\''&'\\''/p\n-\n-\t\tg\n-\t\ts/^author [^<]* <\\([^>]*\\)> .*$/\\1/\n-\t\ts/'\\''/'\\''\\'\\'\\''/g\n-\t\ts/.*/GIT_AUTHOR_EMAIL='\\''&'\\''/p\n-\n-\t\tg\n-\t\ts/^author [^<]* <[^>]*> \\(.*\\)$/\\1/\n-\t\ts/'\\''/'\\''\\'\\'\\''/g\n-\t\ts/.*/GIT_AUTHOR_DATE='\\''&'\\''/p\n-\n-\t\tq\n-\t}\n-\t'\n-\tencoding=$(git config i18n.commitencoding || echo UTF-8)\n-\tset_author_env=`git show -s --pretty=raw --encoding=\"$encoding\" \"$use_commit\" |\n-\tLANG=C LC_ALL=C sed -ne \"$pick_author_script\"`\n-\teval \"$set_author_env\"\n-\texport GIT_AUTHOR_NAME\n-\texport GIT_AUTHOR_EMAIL\n-\texport GIT_AUTHOR_DATE\n+\teval \"$(get_author_ident_from_commit \"$use_commit\")\"\n+\texport GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE\n fi\n if test '' != \"$force_author\"\n then\ndiff --git a/git-sh-setup.sh b/git-sh-setup.sh\nindex f24c7f2..d861db3 100755\n--- a/git-sh-setup.sh\n+++ b/git-sh-setup.sh\n@@ -53,6 +53,33 @@ require_work_tree () {\n \tdie \"fatal: $0 cannot be used without a working tree.\"\n }\n \n+get_author_ident_from_commit () {\n+\tpick_author_script='\n+\t/^author /{\n+\t\ts/'\\''/'\\''\\\\'\\'\\''/g\n+\t\th\n+\t\ts/^author \\([^<]*\\) <[^>]*> .*$/\\1/\n+\t\ts/'\\''/'\\''\\'\\'\\''/g\n+\t\ts/.*/GIT_AUTHOR_NAME='\\''&'\\''/p\n+\n+\t\tg\n+\t\ts/^author [^<]* <\\([^>]*\\)> .*$/\\1/\n+\t\ts/'\\''/'\\''\\'\\'\\''/g\n+\t\ts/.*/GIT_AUTHOR_EMAIL='\\''&'\\''/p\n+\n+\t\tg\n+\t\ts/^author [^<]* <[^>]*> \\(.*\\)$/\\1/\n+\t\ts/'\\''/'\\''\\'\\'\\''/g\n+\t\ts/.*/GIT_AUTHOR_DATE='\\''&'\\''/p\n+\n+\t\tq\n+\t}\n+\t'\n+\tencoding=$(git config i18n.commitencoding || echo UTF-8)\n+\tgit show -s --pretty=raw --encoding=\"$encoding\" \"$1\" |\n+\tLANG=C LC_ALL=C sed -ne \"$pick_author_script\"\n+}\n+\n if [ -z \"$LONG_USAGE\" ]\n then\n \tLONG_USAGE=\"Usage: $0 $USAGE\"\n-- \n1.5.2.2.279.g9b198-dirty\n"},{"id":"45724","messageId":"Pine.LNX.4.64.0706250111020.4059@racer.site","threadId":"8694","inReplyTo":"Pine.LNX.4.64.0706241204450.4059@racer.site","subject":"[PATCH 2/2] Teach rebase an interactive mode","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-06-25T00:11:14Z","receivedAt":"2007-06-25T00:11:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nDon't you just hate the fact sometimes, that git-rebase just applies\nthe patches, without any possibility to edit them, or rearrange them?\nWith \"--interactive\", git-rebase now lets you edit the list of patches,\nso that you can reorder, edit and delete patches.\n\nSuch a list will typically look like this:\n\n\tpick deadbee The oneline of this commit\n\tpick fa1afe1 The oneline of the next commit\n\t...\n\nBy replacing the command \"pick\" with the command \"edit\", you can amend\nthat patch and/or its commit message, and by replacing it with \"squash\"\nyou can tell rebase to fold that patch into the patch before that.\n\nIt is derived from the script sent to the list in\n<Pine.LNX.4.63.0702252156190.22628@wbgn013.biozentrum.uni-wuerzburg.de>\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tJunio wrote:\n\n\t> Is today my \"nitpick shell scripts day\"? ;-)\n\n\tSeems so ;-)\n\n\tIn addition to your comments, I actually fixed the reflog\n\tentries, since they picked up the comment from rebase\n\t(without -i).\n\n\tAlso, the last updating step was not necessary at all, since\n\trebase -i works on the same branch all the time.\n\n\tFurthermore, your comment that a check for an empty $DONE file\n\tis not sufficient applies as well for the check that $TODO\n\tcontains some actions. Both are fixed.\n\n\tThe continue without a loop in that function was (as you probably\n\tsuspected) a refactoring leftover.\n\n\tAll other suggestions were included as-are.\n\n Documentation/git-rebase.txt  |   85 ++++++++++++-\n Makefile                      |    2 +-\n git-rebase--interactive.sh    |  289 +++++++++++++++++++++++++++++++++++++++++\n git-rebase.sh                 |   12 ++-\n t/t3404-rebase-interactive.sh |  163 +++++++++++++++++++++++\n 5 files changed, 546 insertions(+), 5 deletions(-)\n create mode 100755 git-rebase--interactive.sh\n create mode 100755 t/t3404-rebase-interactive.sh\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 0c00090..2e3363a 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -8,7 +8,8 @@ git-rebase - Forward-port local commits to the updated upstream head\n SYNOPSIS\n --------\n [verse]\n-'git-rebase' [-v] [--merge] [-C<n>] [--onto <newbase>] <upstream> [<branch>]\n+'git-rebase' [-i | --interactive] [-v | --verbose] [--merge] [-C<n>]\n+\t[--onto <newbase>] <upstream> [<branch>]\n 'git-rebase' --continue | --skip | --abort\n \n DESCRIPTION\n@@ -208,6 +209,10 @@ OPTIONS\n \tcontext exist they all must match.  By default no context is\n \tever ignored.\n \n+-i, \\--interactive::\n+\tMake a list of the commits which are about to be rebased.  Let the\n+\tuser edit that list before rebasing.\n+\n include::merge-strategies.txt[]\n \n NOTES\n@@ -226,9 +231,83 @@ pre-rebase hook script for an example.\n You must be in the top directory of your project to start (or continue)\n a rebase.  Upon completion, <branch> will be the current branch.\n \n-Author\n+INTERACTIVE MODE\n+----------------\n+\n+Rebasing interactively means that you have a chance to edit the commits\n+which are rebased.  You can reorder the commits, and you can\n+remove them (weeding out bad or otherwise unwanted patches).\n+\n+The interactive mode is meant for this type of workflow:\n+\n+1. have a wonderful idea\n+2. hack on the code\n+3. prepare a series for submission\n+4. submit\n+\n+where point 2. consists of several instances of\n+\n+a. regular use\n+ 1. finish something worthy of a commit\n+ 2. commit\n+b. independent fixup\n+ 1. realize that something does not work\n+ 2. fix that\n+ 3. commit it\n+\n+Sometimes the thing fixed in b.2. cannot be amended to the not-quite\n+perfect commit it fixes, because that commit is buried deeply in a\n+patch series.  That is exactly what interactive rebase is for: use it\n+after plenty of \"a\"s and \"b\"s, by rearranging and editing\n+commits, and squashing multiple commits into one.\n+\n+Start it with the last commit you want to retain as-is:\n+\n+\tgit rebase -i <after-this-commit>\n+\n+An editor will be fired up with all the commits in your current branch\n+(ignoring merge commits), which come after the given commit.  You can\n+reorder the commits in this list to your heart's content, and you can\n+remove them.  The list looks more or less like this:\n+\n+-------------------------------------------\n+pick deadbee The oneline of this commit\n+pick fa1afe1 The oneline of the next commit\n+...\n+-------------------------------------------\n+\n+The oneline descriptions are purely for your pleasure; `git-rebase` will\n+not look at them but at the commit names (\"deadbee\" and \"fa1afe1\" in this\n+example), so do not delete or edit the names.\n+\n+By replacing the command \"pick\" with the command \"edit\", you can tell\n+`git-rebase` to stop after applying that commit, so that you can edit\n+the files and/or the commit message, amend the commit, and continue\n+rebasing.\n+\n+If you want to fold two or more commits into one, replace the command\n+\"pick\" with \"squash\" for the second and subsequent commit.  If the\n+commits had different authors, it will attribute the squashed commit to\n+the author of the last commit.\n+\n+In both cases, or when a \"pick\" does not succeed (because of merge\n+errors), the loop will stop to let you fix things, and you can continue\n+the loop with `git rebase --continue`.\n+\n+For example, if you want to reorder the last 5 commits, such that what\n+was HEAD~4 becomes the new HEAD. To achieve that, you would call\n+`git-rebase` like this:\n+\n+----------------------\n+$ git rebase -i HEAD~5\n+----------------------\n+\n+And move the first patch to the end of the list.\n+\n+Authors\n ------\n-Written by Junio C Hamano <junkio@cox.net>\n+Written by Junio C Hamano <junkio@cox.net> and\n+Johannes E. Schindelin <johannes.schindelin@gmx.de>\n \n Documentation\n --------------\ndiff --git a/Makefile b/Makefile\nindex a98e27a..4ea5e45 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -204,7 +204,7 @@ SCRIPT_SH = \\\n \tgit-fetch.sh \\\n \tgit-ls-remote.sh \\\n \tgit-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \\\n-\tgit-pull.sh git-rebase.sh \\\n+\tgit-pull.sh git-rebase.sh git-rebase--interactive.sh \\\n \tgit-repack.sh git-request-pull.sh git-reset.sh \\\n \tgit-sh-setup.sh \\\n \tgit-tag.sh git-verify-tag.sh \\\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nnew file mode 100755\nindex 0000000..ab36572\n--- /dev/null\n+++ b/git-rebase--interactive.sh\n@@ -0,0 +1,289 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2006 Johannes E. Schindelin\n+\n+# SHORT DESCRIPTION\n+#\n+# This script makes it easy to fix up commits in the middle of a series,\n+# and rearrange commits.\n+#\n+# The original idea comes from Eric W. Biederman, in\n+# http://article.gmane.org/gmane.comp.version-control.git/22407\n+\n+USAGE='(--continue | --abort | --skip | [--onto <branch>] <upstream> [<branch>])'\n+\n+. git-sh-setup\n+require_work_tree\n+\n+DOTEST=\"$GIT_DIR/.dotest-merge\"\n+TODO=\"$DOTEST\"/todo\n+DONE=\"$DOTEST\"/done\n+STRATEGY=\n+VERBOSE=\n+\n+warn () {\n+\techo \"$*\" >&2\n+}\n+\n+require_clean_work_tree () {\n+\t# test if working tree is dirty\n+\tgit rev-parse --verify HEAD > /dev/null &&\n+\tgit update-index --refresh &&\n+\tgit diff-files --quiet &&\n+\tgit diff-index --cached --quiet HEAD ||\n+\tdie \"Working tree is dirty\"\n+}\n+\n+ORIG_REFLOG_ACTION=\"$GIT_REFLOG_ACTION\"\n+\n+comment_for_reflog () {\n+\tcase \"$ORIG_REFLOG_ACTION\" in\n+\t''|rebase*)\n+\t\tGIT_REFLOG_ACTION=\"rebase -i ($1)\"\n+\t\texport GIT_REFLOG_ACTION\n+\tesac\n+}\n+\n+mark_action_done () {\n+\tsed -e 1q < \"$TODO\" >> \"$DONE\"\n+\tsed -e 1d < \"$TODO\" >> \"$TODO\".new\n+\tmv -f \"$TODO\".new \"$TODO\"\n+}\n+\n+make_patch () {\n+\tparent_sha1=$(git rev-parse --verify \"$1\"^ 2> /dev/null)\n+\tgit diff \"$parent_sha1\"..\"$1\" > \"$DOTEST\"/patch\n+}\n+\n+die_with_patch () {\n+\tmake_patch \"$1\"\n+\tdie \"$2\"\n+}\n+\n+pick_one () {\n+\tcase \"$1\" in -n) sha1=$2 ;; *) sha1=$1 ;; esac\n+\tgit rev-parse --verify $sha1 || die \"Invalid commit name: $sha1\"\n+\tparent_sha1=$(git rev-parse --verify $sha1^ 2>/dev/null)\n+\tcurrent_sha1=$(git rev-parse --verify HEAD)\n+\tif [ $current_sha1 = $parent_sha1 ]; then\n+\t\tgit reset --hard $sha1\n+\t\tsha1=$(git rev-parse --short $sha1)\n+\t\twarn Fast forward to $sha1\n+\telse\n+\t\tgit cherry-pick $STRATEGY \"$@\"\n+\tfi\n+}\n+\n+do_next () {\n+\tread command sha1 rest < \"$TODO\"\n+\tcase \"$command\" in\n+\t\\#|'')\n+\t\tmark_action_done\n+\t\tcontinue\n+\t\t;;\n+\tpick)\n+\t\tcomment_for_reflog pick\n+\n+\t\tmark_action_done\n+\t\tpick_one $sha1 ||\n+\t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n+\t\t;;\n+\tedit)\n+\t\tcomment_for_reflog edit\n+\n+\t\tmark_action_done\n+\t\tpick_one $sha1 ||\n+\t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n+\t\tmake_patch $sha1\n+\t\twarn\n+\t\twarn \"You can amend the commit now, with\"\n+\t\twarn\n+\t\twarn \"\tgit commit --amend\"\n+\t\twarn\n+\t\texit 0\n+\t\t;;\n+\tsquash)\n+\t\tcomment_for_reflog squash\n+\n+\t\ttest -z \"$(grep -ve '^$' -e '^#' < $DONE)\" &&\n+\t\t\tdie \"Cannot 'squash' without a previous commit\"\n+\n+\t\tmark_action_done\n+\t\tfailed=f\n+\t\tpick_one -n $sha1 || failed=t\n+\t\tMSG=\"$DOTEST\"/message\n+\t\techo \"# This is a combination of two commits.\" > \"$MSG\"\n+\t\techo \"# The first commit's message is:\" >> \"$MSG\"\n+\t\techo >> \"$MSG\"\n+\t\tgit cat-file commit HEAD | sed -e '1,/^$/d' >> \"$MSG\"\n+\t\techo >> \"$MSG\"\n+\t\techo \"# And this is the 2nd commit message:\" >> \"$MSG\"\n+\t\techo >> \"$MSG\"\n+\t\tgit cat-file commit $sha1 | sed -e '1,/^$/d' >> \"$MSG\"\n+\t\tgit reset --soft HEAD^\n+\t\tauthor_script=$(get_author_ident_from_commit $sha1)\n+\t\tcase $failed in\n+\t\tf)\n+\t\t\t# This is like --amend, but with a different message\n+\t\t\teval \"$author_script\"\n+\t\t\texport GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE\n+\t\t\tgit commit -F \"$MSG\" -e\n+\t\t\t;;\n+\t\tt)\n+\t\t\tcp \"$MSG\" \"$GIT_DIR\"/MERGE_MSG\n+\t\t\twarn\n+\t\t\twarn \"Could not apply $sha1... $rest\"\n+\t\t\twarn \"After you fixed that, commit the result with\"\n+\t\t\twarn\n+\t\t\twarn \"  $(echo $author_script | tr '\\012' ' ') \\\\\"\n+\t\t\twarn \"\t  git commit -F \\\"$GIT_DIR\\\"/MERGE_MSG -e\"\n+\t\t\tdie_with_patch $sha1 \"\"\n+\t\tesac\n+\t\t;;\n+\t*)\n+\t\twarn \"Unknown command: $command $sha1 $rest\"\n+\t\tdie_with_patch $sha1 \"Please fix this in the file $TODO.\"\n+\tesac\n+\ttest -s \"$TODO\" && return\n+\n+\tHEAD=$(git rev-parse HEAD)\n+\tHEADNAME=$(cat \"$DOTEST\"/head-name)\n+\trm -rf \"$DOTEST\" &&\n+\twarn \"Successfully rebased and updated $HEADNAME.\"\n+\n+\texit\n+}\n+\n+do_rest () {\n+\twhile :\n+\tdo\n+\t\tdo_next\n+\tdone\n+\ttest -f \"$DOTEST\"/verbose &&\n+\t\tgit diff --stat $(cat \"$DOTEST\"/head)..HEAD\n+\texit\n+}\n+\n+while case $# in 0) break ;; esac\n+do\n+\tcase \"$1\" in\n+\t--continue)\n+\t\tcomment_for_reflog continue\n+\n+\t\ttest -d \"$DOTEST\" || die \"No interactive rebase running\"\n+\n+\t\trequire_clean_work_tree\n+\t\tdo_rest\n+\t\t;;\n+\t--abort)\n+\t\tcomment_for_reflog abort\n+\n+\t\ttest -d \"$DOTEST\" || die \"No interactive rebase running\"\n+\n+\t\tHEADNAME=$(cat \"$DOTEST\"/head-name)\n+\t\tHEAD=$(cat \"$DOTEST\"/head)\n+\t\tgit symbolic-ref HEAD $HEADNAME &&\n+\t\tgit reset --hard $HEAD &&\n+\t\trm -rf \"$DOTEST\"\n+\t\texit\n+\t\t;;\n+\t--skip)\n+\t\tcomment_for_reflog skip\n+\n+\t\ttest -d \"$DOTEST\" || die \"No interactive rebase running\"\n+\n+\t\tgit reset --hard && do_rest\n+\t\t;;\n+\t-s|--strategy)\n+\t\tshift\n+\t\tcase \"$#,$1\" in\n+\t\t*,*=*)\n+\t\t\tSTRATEGY=\"-s `expr \"z$1\" : 'z-[^=]*=\\(.*\\)'`\" ;;\n+\t\t1,*)\n+\t\t\tusage ;;\n+\t\t*)\n+\t\t\tSTRATEGY=\"-s $2\"\n+\t\t\tshift ;;\n+\t\tesac\n+\t\t;;\n+\t--merge)\n+\t\t# we use merge anyway\n+\t\t;;\n+\t-C*)\n+\t\tdie \"Interactive rebase uses merge, so $1 does not make sense\"\n+\t\t;;\n+\t-v)\n+\t\tVERBOSE=t\n+\t\t;;\n+\t-i|--interactive)\n+\t\t# yeah, we know\n+\t\t;;\n+\t''|-h)\n+\t\tusage\n+\t\t;;\n+\t*)\n+\t\ttest -d \"$DOTEST\" &&\n+\t\t\tdie \"Interactive rebase already started\"\n+\n+\t\tgit var GIT_COMMITTER_IDENT >/dev/null ||\n+\t\t\tdie \"You need to set your committer info first\"\n+\n+\t\tcomment_for_reflog start\n+\n+\t\tONTO=\n+\t\tcase \"$1\" in\n+\t\t--onto)\n+\t\t\tONTO=$(git rev-parse --verify \"$2\") ||\n+\t\t\t\tdie \"Does not point to a valid commit: $2\"\n+\t\t\tshift; shift\n+\t\t\t;;\n+\t\tesac\n+\n+\t\trequire_clean_work_tree\n+\n+\t\tif [ ! -z \"$2\"]\n+\t\tthen\n+\t\t\tgit show-ref --verify --quiet \"refs/heads/$2\" ||\n+\t\t\t\tdie \"Invalid branchname: $2\"\n+\t\t\tgit checkout \"$2\" ||\n+\t\t\t\tdie \"Could not checkout $2\"\n+\t\tfi\n+\n+\t\tHEAD=$(git rev-parse --verify HEAD) || die \"No HEAD?\"\n+\t\tUPSTREAM=$(git rev-parse --verify \"$1\") || die \"Invalid base\"\n+\n+\t\ttest -z \"$ONTO\" && ONTO=$UPSTREAM\n+\n+\t\tmkdir \"$DOTEST\" || die \"Could not create temporary $DOTEST\"\n+\t\t: > \"$DOTEST\"/interactive || die \"Could not mark as interactive\"\n+\t\tgit symbolic-ref HEAD > \"$DOTEST\"/head-name ||\n+\t\t\tdie \"Could not get HEAD\"\n+\n+\t\techo $HEAD > \"$DOTEST\"/head\n+\t\techo $UPSTREAM > \"$DOTEST\"/upstream\n+\t\techo $ONTO > \"$DOTEST\"/onto\n+\t\ttest t = \"$VERBOSE\" && : > \"$DOTEST\"/verbose\n+\n+\t\tcat > \"$TODO\" << EOF\n+# Rebasing $UPSTREAM..$HEAD onto $ONTO\n+#\n+# Commands:\n+#  pick = use commit\n+#  edit = use commit, but stop for amending\n+#  squash = use commit, but meld into previous commit\n+EOF\n+\t\tgit rev-list --no-merges --pretty=oneline --abbrev-commit \\\n+\t\t\t--abbrev=7 --reverse $UPSTREAM..$HEAD | \\\n+\t\t\tsed \"s/^/pick /\" >> \"$TODO\"\n+\n+\t\ttest -z \"$(grep -ve '^$' -e '^#' < $TODO)\" &&\n+\t\t\tdie \"Nothing to do\"\n+\n+\t\tcp \"$TODO\" \"$TODO\".backup\n+\t\t${VISUAL:-${EDITOR:-vi}} \"$TODO\" ||\n+\t\t\tdie \"Could not execute editor\"\n+\n+\t\tgit reset --hard $ONTO && do_rest\n+\tesac\n+\tshift\n+done\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex 2aa3a01..3887526 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -3,7 +3,7 @@\n # Copyright (c) 2005 Junio C Hamano.\n #\n \n-USAGE='[-v] [--onto <newbase>] <upstream> [<branch>]'\n+USAGE='[--interactive | -i] [-v] [--onto <newbase>] <upstream> [<branch>]'\n LONG_USAGE='git-rebase replaces <branch> with a new branch of the\n same name.  When the --onto option is provided the new branch starts\n out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>\n@@ -120,6 +120,16 @@ finish_rb_merge () {\n \techo \"All done.\"\n }\n \n+is_interactive () {\n+\ttest -f \"$dotest\"/interactive ||\n+\twhile case $#,\"$1\" in 0,|*,-i|*,--interactive) break ;; esac\n+\tdo\n+\t\tshift\n+\tdone && test -n \"$1\"\n+}\n+\n+is_interactive \"$@\" && exec git-rebase--interactive \"$@\"\n+\n while case \"$#\" in 0) break ;; esac\n do\n \tcase \"$1\" in\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nnew file mode 100755\nindex 0000000..48aa8ea\n--- /dev/null\n+++ b/t/t3404-rebase-interactive.sh\n@@ -0,0 +1,163 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2007 Johannes E. Schindelin\n+#\n+\n+test_description='git rebase interactive\n+\n+This test runs git rebase \"interactively\", by faking an edit, and verifies\n+that the result still makes sense.\n+'\n+. ./test-lib.sh\n+\n+# set up two branches like this:\n+#\n+# A - B - C - D - E\n+#   \\\n+#     F - G - H\n+#       \\\n+#         I\n+#\n+# where B, D and G touch the same file.\n+\n+test_expect_success 'setup' '\n+\t: > file1 &&\n+\tgit add file1 &&\n+\ttest_tick &&\n+\tgit commit -m A &&\n+\tgit tag A &&\n+\techo 1 > file1 &&\n+\ttest_tick &&\n+\tgit commit -m B file1 &&\n+\t: > file2 &&\n+\tgit add file2 &&\n+\ttest_tick &&\n+\tgit commit -m C &&\n+\techo 2 > file1 &&\n+\ttest_tick &&\n+\tgit commit -m D file1 &&\n+\t: > file3 &&\n+\tgit add file3 &&\n+\ttest_tick &&\n+\tgit commit -m E &&\n+\tgit checkout -b branch1 A &&\n+\t: > file4 &&\n+\tgit add file4 &&\n+\ttest_tick &&\n+\tgit commit -m F &&\n+\tgit tag F &&\n+\techo 3 > file1 &&\n+\ttest_tick &&\n+\tgit commit -m G file1 &&\n+\t: > file5 &&\n+\tgit add file5 &&\n+\ttest_tick &&\n+\tgit commit -m H &&\n+\tgit checkout -b branch2 F &&\n+\t: > file6 &&\n+\tgit add file6 &&\n+\ttest_tick &&\n+\tgit commit -m I &&\n+\tgit tag I\n+'\n+\n+cat > fake-editor.sh << EOF\n+#!/bin/sh\n+test \"\\$1\" = .git/COMMIT_EDITMSG && exit\n+test -z \"\\$FAKE_LINES\" && exit\n+grep -v \"^#\" < \"\\$1\" > \"\\$1\".tmp\n+rm \"\\$1\"\n+cat \"\\$1\".tmp\n+action=pick\n+for line in \\$FAKE_LINES; do\n+\tcase \\$line in\n+\tsquash)\n+\t\taction=\"\\$line\";;\n+\t*)\n+\t\techo sed -n \"\\${line}s/^pick/\\$action/p\"\n+\t\tsed -n \"\\${line}p\" < \"\\$1\".tmp\n+\t\tsed -n \"\\${line}s/^pick/\\$action/p\" < \"\\$1\".tmp >> \"\\$1\"\n+\t\taction=pick;;\n+\tesac\n+done\n+EOF\n+\n+chmod a+x fake-editor.sh\n+VISUAL=\"$(pwd)/fake-editor.sh\"\n+export VISUAL\n+\n+test_expect_success 'no changes are a nop' '\n+\tgit rebase -i F &&\n+\ttest $(git rev-parse I) = $(git rev-parse HEAD)\n+'\n+\n+test_expect_success 'rebase on top of a non-conflicting commit' '\n+\tgit checkout branch1 &&\n+\tgit tag original-branch1 &&\n+\tgit rebase -i branch2 &&\n+\ttest file6 = $(git diff --name-only original-branch1) &&\n+\ttest $(git rev-parse I) = $(git rev-parse HEAD~2)\n+'\n+\n+test_expect_success 'exchange two commits' '\n+\tFAKE_LINES=\"2 1\" git rebase -i HEAD~2 &&\n+\ttest H = $(git cat-file commit HEAD^ | tail -n 1) &&\n+\ttest G = $(git cat-file commit HEAD | tail -n 1)\n+'\n+\n+cat > expect << EOF\n+diff --git a/file1 b/file1\n+index e69de29..00750ed 100644\n+--- a/file1\n++++ b/file1\n+@@ -0,0 +1 @@\n++3\n+EOF\n+\n+cat > expect2 << EOF\n+<<<<<<< HEAD:file1\n+2\n+=======\n+3\n+>>>>>>> b7ca976... G:file1\n+EOF\n+\n+test_expect_success 'stop on conflicting pick' '\n+\tgit tag new-branch1 &&\n+\t! git rebase -i master &&\n+\tdiff -u expect .git/.dotest-merge/patch &&\n+\tdiff -u expect2 file1 &&\n+\ttest 4 = $(grep -v \"^#\" < .git/.dotest-merge/done | wc -l) &&\n+\ttest 0 = $(grep -v \"^#\" < .git/.dotest-merge/todo | wc -l)\n+'\n+\n+test_expect_success 'abort' '\n+\tgit rebase --abort &&\n+\ttest $(git rev-parse new-branch1) = $(git rev-parse HEAD) &&\n+\t! test -d .git/.dotest-merge\n+'\n+\n+test_expect_success 'retain authorship' '\n+\techo A > file7 &&\n+\tgit add file7 &&\n+\tGIT_AUTHOR_NAME=\"Twerp Snog\" git commit -m \"different author\" &&\n+\tgit tag twerp &&\n+\tgit rebase -i --onto master HEAD^ &&\n+\tgit show HEAD | grep \"^Author: Twerp Snog\"\n+'\n+\n+test_expect_success 'squash' '\n+\tgit reset --hard twerp &&\n+\techo B > file7 &&\n+\tGIT_AUTHOR_NAME=\"Nitfol\" git commit -m \"nitfol\" file7 &&\n+\techo \"******************************\" &&\n+\tFAKE_LINES=\"1 squash 2\" git rebase -i --onto master HEAD~2 &&\n+\ttest B = $(cat file7) &&\n+\ttest $(git rev-parse HEAD^) = $(git rev-parse master)\n+'\n+\n+test_expect_success 'retain authorship when squashing' '\n+\tgit show HEAD | grep \"^Author: Nitfol\"\n+'\n+\n+test_done\n-- \n1.5.2.2.279.g9b198-dirty\n"}]}