{"thread":{"id":"14494","subject":"[PATCH] Migrate rebase-i to sequencer","startedAt":"2008-07-16T20:45:17Z","lastAt":"2008-07-17T13:05:47Z","messageCount":5,"participants":["Stephan Beyer"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"83591","messageId":"14224c96008f30754acb021bc0af6b6641897a1e.1216233915.git.s-beyer@gmx.net","threadId":"14494","inReplyTo":"cfa3b96d13488d57caf8b758367cdf0679126462.1216233914.git.s-beyer@gmx.net","subject":"[PATCH 2/3] Add git-sequencer documentation","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2008-07-16T20:45:17Z","receivedAt":"2008-07-16T20:45:17Z","isPatch":true,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"Mentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Daniel Barkalow <barkalow@iabervon.org>\nSigned-off-by: Stephan Beyer <s-beyer@gmx.net>\n---\n Documentation/git-sequencer.txt |  673 +++++++++++++++++++++++++++++++++++++++\n 1 files changed, 673 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-sequencer.txt\n\ndiff --git a/Documentation/git-sequencer.txt b/Documentation/git-sequencer.txt\nnew file mode 100644\nindex 0000000..8a701c4\n--- /dev/null\n+++ b/Documentation/git-sequencer.txt\n@@ -0,0 +1,673 @@\n+git-sequencer(1)\n+================\n+\n+NAME\n+----\n+git-sequencer - Execute a sequence of git instructions\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git sequencer' [--batch] [--onto=<base>]\n+\t\t[--verbose | --no-advice | --quiet]\n+\t\t[--] [<file>]\n+'git sequencer' --continue | --skip | --abort | --edit | --status\n+\n+\n+DESCRIPTION\n+-----------\n+Executes a sequence of git instructions to HEAD or `<base>`.\n+The sequence is given by `<file>` or standard input.\n+Also see 'TODO FILE FORMAT' below.\n+\n+Before doing anything, the TODO file is checked for correct syntax\n+and sanity.\n+\n+In case of a conflict or request in the TODO file, 'git-sequencer' will\n+pause. On conflict you can use 'git-diff' to locate the markers (`<<<<<<<`)\n+and make edits to resolve the conflict.\n+\n+For each file you edit, you need to tell git the changes by doing\n+\n+    git add <file>\n+\n+After resolving the conflict manually and updating the index with the\n+desired resolution, you can continue the sequencing process with\n+\n+    git sequencer --continue\n+\n+Alternatively, you can undo the 'git-sequencer' progress with\n+\n+    git sequencer --abort\n+\n+or skip the current instruction with\n+\n+    git sequencer --skip\n+\n+or correct the TODO file with\n+\n+    git sequencer --edit\n+\n+During pauses or when finished with the sequencing task, the current\n+HEAD will always be the result of the last processed instruction.\n+\n+\n+OPTIONS\n+-------\n+<file>::\n+\tFilename of the TODO file.  If omitted, standard input is used.\n+\tSee 'TODO FILE FORMAT' below.\n+\n+-B::\n+--batch::\n+\tRun in batch mode. If unexpected user intervention is needed\n+\t(e.g. a conflict or the need to run an editor), 'git-sequencer' fails.\n++\n+Note that the sanity check fails, if you use this option\n+and an instruction like `edit` or `pause` is in the TODO file.\n+\n+--onto=<base>::\n+\tCheckout given commit or branch before sequencing.\n+\tIf you provide a branch, sequencer will make the provided\n+\tchanges on the branch, i.e. the branch will be changed.\n+\n+--continue::\n+\tRestart the sequencing process after having resolved a merge conflict.\n+\n+--abort::\n+\tRestore the original branch and abort the sequence operation.\n+\n+--skip::\n+\tRestart the sequencing process by skipping the current instruction.\n+\n+--status::\n+\tShow the current status of 'git-sequencer' and what\n+\toperations can be done to change that status.\n+\n+--edit::\n+\tInvoke editor to edit the unprocessed part of the TODO file.\n++\n+The file is syntax- and sanity-checked afterwards, so that you can\n+safely run `git sequencer --skip` or `--continue` after editing.\n+If you nonetheless noticed that you made a mistake, you can\n+overwrite `.git/sequencer/todo` with `.git/sequencer/todo.old` and\n+rerun `git sequencer --edit`.\n++\n+If the check fails you are prompted if you want to correct your\n+changes, edit again, cancel editing or really want to save.\n+\n+--no-advice::\n+\tSuppress advice on intentional and unintentional pauses.\n+\n+-q::\n+--quiet::\n+\tSuppress output. Implies `--no-advice`.\n+\t(Not yet implemented.)\n+\n+-v::\n+--verbose::\n+\tBe more verbose.\n+\n+\n+NOTES\n+-----\n+\n+When sequencing, it is possible, that you are changing the history of\n+a branch in a way that can cause problems for anyone who already has\n+a copy of the branch in their repository and tries to pull updates from\n+you.  You should understand the implications of using 'git-sequencer' on\n+a repository that you share.\n+\n+'git-sequencer' will usually be called by another git porcelain, like\n+linkgit:git-am[1] or linkgit:git-rebase[1].\n+\n+\n+TODO FILE FORMAT\n+----------------\n+\n+The TODO file contains basically one instruction per line.\n+\n+Blank lines will be ignored.\n+All characters after a `#` character will be ignored until the end of a line.\n+\n+The following instructions can be used:\n+\n+\n+edit <commit>::\n+\tPick a commit and pause the sequencer process to let you\n+\tmake changes.\n++\n+This is a short form for `pick <commit> and `pause` on separate lines.\n+\n+\n+mark <mark>::\n+\tSet a symbolic mark for the last commit.\n+\t`<mark>` is an unsigned integer starting at 1 and\n+\tprefixed with a colon, e.g. `:1`.\n++\n+The marks can help if you want to refer to commits that you\n+created during the sequencer process, e.g. if you want to\n+merge such a commit.\n++\n+The set marks are removed after the sequencer has completed.\n+\n+\n+merge [options] <commit-ish1> <commit-ish2> ... <commit-ishN>::\n+\tMerge commits into HEAD.\n++\n+You can refer to a commit by a mark.\n++\n+If you do not provide a commit message (using `-F`, `-m`, `-C`, `-M`,\n+or `--standard`), an editor will be invoked.\n++\n+See the following list and 'GENERAL OPTIONS' for values of `options`:\n+\n+\t--standard;;\n+\t\tGenerate a commit message like 'Merge ... into HEAD'.\n+\t\tSee also linkgit:git-fmt-merge-msg[1].\n+\n+\t-s <strategy>;;\n+\t--strategy=<strategy>;;\n+\t\tUse the given merge strategy.\n+\t\tSee also linkgit:git-merge[1].\n+\n+\n+pick [options] <commit>::\n+\tPick (see linkgit:git-cherry-pick[1]) a commit.\n+\tSequencer will pause on conflicts.\n++\n+See the following list and 'GENERAL OPTIONS' for values of `options`:\n+\n+\t-R;;\n+\t--reverse;;\n+\t\tRevert the changes introduced by pick <commit>.\n+\n+\t--mainline=<n>;;\n+\t\tAllow you to pick merge commits by specifying the\n+\t\tparent number (beginning from 1) to let sequencer\n+\t\treplay the changes relative to the specified parent.\n+\t\t+\n+This option does not work together with `-R`.\n+\n+\n+patch [options] <file>::\n+\tIf file `<file>` is a pure (diff) patch, then apply the patch.\n+\tIf no `--message` option is given, an editor will\n+\tbe invoked to enter a commit message.\n++\n+If `<file>` is a linkgit:git-format-patch[1]-formatted patch,\n+then the patch will be commited.\n++\n+See the following list and 'GENERAL OPTIONS' for values of `options`:\n+\n+\t-3;;\n+\t--3way;;\n+\t\tWhen the patch does not apply cleanly, fall back on\n+\t\t3-way merge, if the patch records the identity of blobs\n+\t\tit is supposed to apply to, and we have those blobs\n+\t\tavailable locally.\n+\n+\t-k;;\n+\t\tPass `-k` flag to 'git-mailinfo' (see linkgit:git-mailinfo[1]).\n+\n+\t-n;;\n+\t\tPass `-n` flag to 'git-mailinfo' (see linkgit:git-mailinfo[1]).\n+\n+\t--exclude=<path-pattern>;;\n+\t\tDo not apply changes to files matching the given path pattern.\n+\t\tThis can be useful when importing patchsets, where you want to\n+\t\texclude certain files or directories.\n+\n+\t-R;;\n+\t--reverse;;\n+\t\tApply the patch in reverse.\n+\n+\t--no-add;;\n+\t\tWhen applying a patch, ignore additions made by the\n+\t\tpatch.  This can be used to extract the common part between\n+\t\ttwo files by first running 'diff' on them and applying\n+\t\tthe result with this option, which would apply the\n+\t\tdeletion part but not addition part.\n+\n+\t--whitespace=<action>;;\n+\t\tSpecify behavior on whitespace errors.\n+\t\tSee linkgit:git-apply[1] for a detailed description.\n+\n+\t--context=<n>;;\n+\t\tEnsure at least <n> lines of surrounding context match before\n+\t\tand after each change.  When fewer lines of surrounding\n+\t\tcontext exist they all must match.  By default no context is\n+\t\tever ignored.\n+\n+\t--inaccurate-eof;;\n+\t\tUnder certain circumstances, some versions of 'diff' do not\n+\t\tcorrectly detect a missing new-line at the end of the file.\n+\t\tAs a result, patches created by such 'diff' programs do not\n+\t\trecord incomplete lines correctly.\n+\t\tThis option adds support for applying such patches by\n+\t\tworking around this bug.\n+\n+\t-p<n>;;\n+\t\tRemove <n> leading slashes from traditional diff paths.\n+\t\tThe default is 1.\n+\n+\t--unidiff-zero;;\n+\t\tBy default, 'git-apply' expects that the patch being\n+\t\tapplied is a unified diff with at least one line of context.\n+\t\tThis provides good safety measures, but breaks down when\n+\t\tapplying a diff generated with --unified=0. To bypass these\n+\t\tchecks use this option.\n+\n+\n+pause::\n+\tPause the sequencer process to let you manually make changes.\n+\tFor example, you can re-edit the done commit, split a commit,\n+\tfix bugs or typos, or make further commits on top of HEAD before\n+\tcontinuing.\n++\n+After you have finished your changes and added them to the index,\n+invoke `git sequencer --continue`.\n+If you only want to edit the last commit message with an editor,\n+run `git commit --amend` (see linkgit:git-commit[1]) before saying\n+`--continue`.\n+\n+\n+ref <ref>::\n+\tSet ref `<ref>` to the current HEAD, see also\n+\tlinkgit:git-update-ref[1].\n+\n+\n+reset <commit-ish>::\n+\tGo back (see linkgit:git-reset[1] `--hard`) to commit `<commit-ish>`.\n+\t`<commit-ish>` can also be given by a mark, if prefixed with a colon.\n+\n+\n+squash [options] <commit>::\n+\tAdd the changes introduced by `<commit>` to the last commit.\n++\n+See 'GENERAL OPTIONS' for values of `options`.\n+\n+squash [options] --from <mark>::\n+\tSquash all commits from the given mark into one commit.\n+\tThere must not be any `merge` instructions between the\n+\t`mark` instruction and this `squash --from` instruction.\n++\n+See the following list and 'GENERAL OPTIONS' for values of `options`:\n+\n+\t--collect-signoffs;;\n+\t\tCollect the Signed-off-by: lines of each commit and\n+\t\tadd them to the squashed commit message.\n+\t\t(Not yet implemented.)\n+\n+\t--include-merges;;\n+\t\tSanity check does not fail if you have merges\n+\t\tbetween HEAD and <mark>.\n+\n+\n+run [--dir=<path>] [--] <cmd> <args>...::\n+\tRun command `<cmd>` with arguments `<args>`.\n+\tPause (conflict-like) if exit status is non-zero.\n++\n+If `<path>` is set, sequencer will change directory to `<path>`\n+before running the command and change back after exit.\n+\n+\n+GENERAL OPTIONS\n+---------------\n+\n+Besides some special options, the instructions\n+`patch`, `merge`, `pick`, `squash` take the following general options:\n+\n+--author=<author>::\n+\tOverride the author name and e-mail address used in the commit.\n+\tUse `A U Thor <author@example.com>` format.\n+\n+-C <commit-ish>::\n+--reuse-commit=<commit-ish>::\n+\tReuse message and authorship data from specified commit.\n+\n+-M <commit-ish>\n+--reuse-message=<commit-ish>::\n+\tReuse message from specified commit.\n+\tNote, that only the commit message is reused\n+\tand not the authorship information.\n+\n+-F <file>::\n+--file=<file>::\n+\tTake the commit message from the given file.\n+\n+-m <msg>::\n+--message=<msg>::\n+\tUse the given `<msg>` as the commit message.\n+\n+--signoff::\n+\tAdd `Signed-off-by:` line to the commit message (if not yet there),\n+\tusing the committer identity of yourself.\n+\n+-e::\n+--edit::\n+\tRegardless what commit message options are given,\n+\tinvoke the editor to allow editing of the commit message.\n+\n+\n+RETURN VALUES\n+-------------\n+\n+'git-sequencer' returns:\n+\n+* `0`, if 'git-sequencer' successfully completed all the instructions\n+       in the TODO file or successfully aborted after\n+       `git sequencer --abort`,\n+* `2`, on user-requested pausing, e.g.\n+       when using the `edit` instruction.\n+* `3`, on pauses that are not requested, e.g.\n+       when there are conflicts to resolve\n+       or errors in the TODO file.\n+* any other value on error, e.g.\n+  running 'git-sequencer' on a bare repository.\n+\n+\n+EXAMPLES\n+--------\n+\n+Here are some examples that shall ease the start with the TODO\n+file format.\n+Make sure you have understood the `pick` and perhaps the `patch` command.\n+Those will not be explained further.\n+\n+Manually editing and adding commits\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+\n+Sequencer allows manual intervention in between.\n+This can be useful to\n+\n+* check if everything has gone right so far,\n+\n+* split commits,\n+\n+* edit changes and/or commit messages, or\n+\n+* add further manual commits on top of the current one.\n+\n+If you want to do one of this, either change `pick` to `edit`, or\n+add a `pause` line after the specific instruction.\n+\n+Note that if you only want to edit the commit message in an\n+editor, just use the `--edit` option of your `pick` or `patch`\n+instruction.\n+\n+`HEAD` refers to the last commit being done by sequencer.\n+So if you want to split a commit, repeat something like\n+\n+------------\n+$ git reset HEAD^   # Reset index to HEAD^, but keep working tree\n+\t\t    # HEAD is the last commit being done by sequencer\n+$ git add -p        # Add changes interactively to the index, and/or\n+$ git add file1     # Add changes from file1 to the index\n+$ git commit        # Commit staged changes\n+------------\n+\n+until you have no changes to commit, and then run\n+\n+------------\n+$ git sequencer --continue   # Continue sequencer process\n+------------\n+\n+Be aware that if there are still staged changes,\n+'git-sequencer' will add those changes to the last commit being done.\n+\n+\n+Squashing commits\n+~~~~~~~~~~~~~~~~~\n+\n+Squashing commits means putting the changes of many commits into one.\n+If you have two commits `abcdef1` and `fa1afe1` and you want to squash them,\n+feed 'git-sequencer' with a TODO file like:\n+\n+------------\n+pick abcdef1\n+squash fa1afe1\n+------------\n+\n+Squash will concatenate the commit messages of `abcdef1` and `fa1afe1` and\n+invoke an editor so that you can edit them.\n+Perhaps you just want to reuse the commit message of `abcdef1` and\n+add a signoff.  Then use:\n+\n+------------\n+pick abcdef1\n+squash -C abcdef1 --signoff fa1afe1\n+------------\n+\n+You can also squash more than two commits.\n+Basically you can do:\n+\n+------------\n+pick A\n+squash B\n+squash C\n+squash D\n+squash --message \"Make indentation consistent\" --signoff E\n+------------\n+\n+If somebody sent you a patch that you have not yet applied and you want\n+to apply it and squash it, or if you have a `pick <commit>` list generated\n+with something like\n+\n+------------\n+$ git rev-list --no-merges --reverse A^..E | sed -e 's/^/pick /'`\n+------------\n+\n+you can use the `mark` and `squash --from` instructions to\n+squash all commits between them into one:\n+\n+------------\n+mark :0\n+pick A\n+pick B\n+pick C\n+pick D\n+pick E\n+squash --message \"Make indentation consistent\" --signoff --from :0\n+------------\n+\n+\n+Branching and Merging\n+~~~~~~~~~~~~~~~~~~~~~\n+\n+Merging branches can easily be done using the `merge` instruction.\n+For an example, it is more interesting to branch, pick some commits\n+and merge.  Imagine you want to 'copy' this onto the current branch:\n+\n+------------\n+ ...--A1--A2--A3--A4--A5---MA-A6  refs/heads/old\n+       |       \\          /\n+       |        C1--MC--C2  refs/heads/topic\n+\t\\          /\n+\t B1--B2--B3\n+------------\n+\n+You want the copy to look exactly like this, except that you\n+are not on branch `old`, and you want to call the copy of `topic`\n+simply `topic2`.\n+Here is a way to achieve this:\n+\n+------------\n+pick A1\n+mark :0    # remember this to pick further commits on trunk A\n+\n+pick B1    # pick commits for trunk B\n+pick B2\n+pick B3\n+mark :1    # remember this for merge\n+# Why not just merge B3 later?\n+# Then you would merge the original B3 and not the copy.\n+# But in this example you want to merge the copy of B3.\n+\n+reset :0   # go back to the copy of A1\n+pick A2    # go on picking the commits of trunk A\n+pick A3\n+mark :2    # remember this to pick further commits on trunk A\n+\n+pick C1    # pick commits for trunk C\n+merge -C MC :1    # merge trunk B\n+pick C2\n+ref refs/heads/topic2    # create branch for the C trunk\n+\n+reset :2   # go back to last commit of trunk A (copy of A3)\n+pick A4    # go on picking the commits of trunk A\n+pick A5\n+merge --standard topic2   # merge trunk C\n+pick A6\n+------------\n+\n+\n+Proper handling of conflicts\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+\n+First of all, you are encouraged to use linkgit:git-rerere[1]:\n+\n+------------\n+$ git config rerere.enabled true    # enable rerere\n+------------\n+\n+Sequencer invokes 'git-rerere' automatically on conflict.\n+\n+If you experience conflicts, try\n+\n+------------\n+$ git diff     # Show conflicting code\n+$ git status   # Show conflicting files\n+------------\n+\n+Then fix these conflicts using your editor and run\n+\n+------------\n+$ git add file1 file2 file3   # Add modified files to the index\n+$ git status                  # Make sure working tree is clean\n+$ git sequencer --continue    # Continue sequencer process\n+------------\n+\n+Now assume a conflict happens because you have unproperly edited\n+the TODO file.\n+\n+Imagine your initial TODO file was:\n+\n+------------\n+pick A\n+pick C\n+pick D\n+------------\n+\n+But you wanted to pick B before C, and now you have this conflict on\n+picking C.  You may first have a look at:\n+\n+------------\n+$ git sequencer --status\n+------------\n+\n+This will show you, what has been done, in what step the conflict\n+happened and what is still to do, like this:\n+\n+------------\n+Already done (or tried):\n+  pick A\n+  pick C\n+\n+Interrupted by conflict at\n+  pick C\n+\n+Still to do:\n+  pick D\n+\n+To abort & restore, invoke:\n+    git sequencer --abort\n+To continue, invoke:\n+    git sequencer --continue\n+To skip the current instruction, invoke:\n+    git sequencer --skip\n+------------\n+\n+A good way to solve that situation is running\n+\n+------------\n+$ git sequencer --edit\n+------------\n+\n+and change the file to:\n+\n+------------\n+pick B\n+pick C\n+pick D\n+------------\n+\n+Save the file, and invoke:\n+\n+------------\n+$ git sequencer --skip\n+------------\n+\n+Then the conflict-ridden `pick C` will be skipped and B is picked,\n+before C will again be picked.\n+\n+\n+Running tests\n+~~~~~~~~~~~~~\n+\n+Imagine you have test programs within a `tests/` directory in your working\n+tree.  But before running your test programs, you have to invoke `make` in\n+the root directory of the working tree to compile your project.\n+\n+If the commit policy of your project says that after every commit the\n+software must be able to compile and the test suite must pass, you\n+are required to check this after every pick.\n+\n+This example shows how 'git-sequencer' can assist you:\n+\n+------------\n+pick A      # Fix foo\n+run make\n+run --dir=tests ./test-foo\n+pick B      # Extend bar\n+run make\n+run --dir tests -- ./test-bar --expensive-tests\n+pick C\n+run make\n+run --dir tests make tests\n+------------\n+\n+Sequencer will be paused, when a run fails (i.e. on non-zero exit status).\n+Then it is your turn to fix the problem and make the tests pass.\n+\n+Note, that on `git sequencer --continue`, 'git-sequencer' will not\n+repeat the failed `run` instruction.\n+\n+\n+SEE ALSO\n+--------\n+\n+linkgit:git-add[1],\n+linkgit:git-am[1],\n+linkgit:git-cherry-pick[1],\n+linkgit:git-commit[1],\n+linkgit:git-fmt-merge-msg[1],\n+linkgit:git-format-patch[1],\n+linkgit:git-rebase[1],\n+linkgit:git-rerere[1],\n+linkgit:git-reset[1],\n+linkgit:git-update-ref[1]\n+\n+\n+Authors\n+-------\n+Written by Stephan Beyer <s-beyer@gmx.net>.\n+\n+\n+Documentation\n+-------------\n+Documentation by Stephan Beyer and the git-list <git@vger.kernel.org>.\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\n-- \n1.5.6.3.391.ge45b\n"},{"id":"83589","messageId":"fd9b4f2b04c9b997c6bdba90352eb1ef973114ae.1216233915.git.s-beyer@gmx.net","threadId":"14494","inReplyTo":"14224c96008f30754acb021bc0af6b6641897a1e.1216233915.git.s-beyer@gmx.net","subject":"[PATCH 3/3] Add git-sequencer test suite (t3350)","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2008-07-16T20:45:18Z","receivedAt":"2008-07-16T20:45:18Z","isPatch":true,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"Mentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Daniel Barkalow <barkalow@iabervon.org>\nSigned-off-by: Stephan Beyer <s-beyer@gmx.net>\n---\n t/t3350-sequencer.sh |  838 ++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 838 insertions(+), 0 deletions(-)\n create mode 100755 t/t3350-sequencer.sh\n\ndiff --git a/t/t3350-sequencer.sh b/t/t3350-sequencer.sh\nnew file mode 100755\nindex 0000000..3cc7da8\n--- /dev/null\n+++ b/t/t3350-sequencer.sh\n@@ -0,0 +1,838 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2008 Stephan Beyer\n+#\n+# `setup' is based on t3404* by Johannes Schindelin.\n+\n+test_description='git sequencer\n+\n+These are basic usage tests for git sequencer.\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 increment value in file1.\n+# The others generate empty file[23456].\n+\n+SEQDIR=\".git/sequencer\"\n+SEQMARK=\"refs/sequencer-marks\"\n+MARKDIR=\".git/$SEQMARK\"\n+\n+test_expect_success 'setup' '\n+\t: >file1 &&\n+\tgit add file1 &&\n+\ttest_tick &&\n+\tgit commit -m \"generate empty file1\" &&\n+\tgit tag A &&\n+\techo 1 >file1 &&\n+\ttest_tick &&\n+\tgit commit -m \"write 1 into file1\" file1 &&\n+\tgit tag B &&\n+\t: >file2 &&\n+\tgit add file2 &&\n+\ttest_tick &&\n+\tgit commit -m \"generate empty file2\" &&\n+\tgit tag C &&\n+\techo 2 >file1 &&\n+\ttest_tick &&\n+\tgit commit -m \"write 2 into file1\" file1 &&\n+\tgit tag D &&\n+\t: >file3 &&\n+\tgit add file3 &&\n+\ttest_tick &&\n+\tgit commit -m \"generate empty file3\" &&\n+\tgit tag E &&\n+\tgit checkout -b branch1 A &&\n+\t: >file4 &&\n+\tgit add file4 &&\n+\ttest_tick &&\n+\tgit commit -m \"generate empty file4\" &&\n+\tgit tag F &&\n+\techo 3 >file1 &&\n+\ttest_tick &&\n+\tgit commit -m \"write 3 into file1\" file1 &&\n+\tgit tag G &&\n+\t: >file5 &&\n+\tgit add file5 &&\n+\ttest_tick &&\n+\tgit commit -m \"generate empty file5\" &&\n+\tgit tag H &&\n+\tgit checkout -b branch2 F &&\n+\t: >file6 &&\n+\tgit add file6 &&\n+\ttest_tick &&\n+\tgit commit -m \"generate empty file6\" &&\n+\tgit tag I &&\n+\tgit diff -p --raw C..D >patchD.raw &&\n+\tgit diff -p --raw A..F >patchF.raw &&\n+\tgit format-patch --stdout A..B >patchB &&\n+\tgit format-patch --stdout B..C >patchC &&\n+\tgit format-patch --stdout C..D >patchD &&\n+\tgit format-patch --stdout A..F >patchF &&\n+\tgit format-patch --stdout F..G >patchG\n+'\n+\n+orig_author=\"$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>\"\n+\n+# Functions to verify exit status of sequencer.\n+# Do not just use \"test_must_fail git sequencer ...\"!\n+expect_fail () {\n+\t\"$@\"\n+\ttest $? -eq 1\n+}\n+expect_continue () {\n+\t\"$@\"\n+\ttest $? -eq 2\n+}\n+expect_conflict () {\n+\t\"$@\"\n+\ttest $? -eq 3\n+}\n+\n+\n+# Other test helpers:\n+\n+# Test if commit $1 has author $2\n+expect_author () {\n+\ttest \"$2\" = \"$(git cat-file commit \"$1\" |\n+\t\tsed -n -e \"s/^author \\(.*\\)> .*$/\\1>/p\")\"\n+}\n+\n+# Test if commit $1 has commit message in file $2\n+# Side effect: overwrites actual\n+expect_msg () {\n+\tgit cat-file commit \"$1\" | sed -e \"1,/^$/d\" >actual &&\n+\ttest_cmp \"$2\" actual\n+}\n+\n+# Test that no marks are set.\n+no_marks_set () {\n+\tif test -e \"$MARKDIR\"\n+\tthen\n+\t\trmdir \"$MARKDIR\"\n+\tfi\n+}\n+\n+test_expect_success 'fail on empty TODO from stdin' '\n+\texpect_fail git sequencer <file6 &&\n+\t! test -d \"$SEQDIR\"\n+'\n+\n+# Generate fake editor\n+#\n+# Simple and practical concept:\n+#  We use only a small string identifier for \"editor sessions\".\n+#  Each sessions knows what to do and perhaps defines\n+#  which session to choose next.\n+echo \"#!$SHELL_PATH\" >fake-editor.sh\n+cat >>fake-editor.sh <<\\EOF\n+test -f fake-editor-session || exit 1\n+#test -t 1 || exit 1\n+# This test could be useful, but as the test-lib is not always\n+# verbose, this will fail.\n+next=ok\n+read this <fake-editor-session\n+case \"$this\" in\n+commitmsg)\n+\techo 'echo 2 >file1'\n+\t;;\n+squashCE)\n+\techo 'generate file2 and file3'\n+\t;;\n+squashCI)\n+\techo 'generate file2 and file6'\n+\tnext=squashDCE\n+\t;;\n+squashDCE)\n+\techo 'generate file2 and file3 and write 2 into file1'\n+\tnext=merge1\n+\t;;\n+merge1)\n+\techo 'A typed merge message.'\n+\t;;\n+merge2)\n+\ttest \"$(sed -n -e 1p \"$1\")\" = 'test merge' &&\n+\t\techo 'cleanup merge' ||\n+\t\techo error\n+\tsed -e 1d \"$1\"\n+\t;;\n+editXXXXXXXXX)\n+\tprintf 'last edited'\n+\t;;\n+edit*)\n+\tprintf 'edited: '\n+\tcat \"$1\"\n+\tnext=\"${this}X\"\n+\t;;\n+nochange)\n+\tcat \"$1\"\n+\t;;\n+ok|fail)\n+\techo '-- THIS IS UNEXPECTED --'\n+\tnext=fail\n+\t;;\n+*)\n+\techo 'I do not know.'\n+\t;;\n+esac >\"$1\".tmp\n+mv \"$1\".tmp \"$1\"\n+echo $next >fake-editor-session\n+exit 0\n+EOF\n+chmod a+x fake-editor.sh\n+test_set_editor \"$(pwd)/fake-editor.sh\"\n+\n+next_session () {\n+\techo \"$1\" >fake-editor-session\n+}\n+\n+# check if fake-editor-session is ok.\n+# If \"$1\" is set to anything, it will set the\n+# next session to \"ok\", which is nice for\n+# test_expect_failure.\n+session_ok () {\n+\ttest \"ok\" = $(cat fake-editor-session)\n+\tret=$?\n+\ttest -n \"$1\" && next_session ok\n+\treturn $ret\n+}\n+\n+\n+cat >todotest1 <<EOF\n+pick C\n+squash E\n+ref refs/tags/CE\n+EOF\n+\n+test_expect_success '\"pick\", \"squash\", \"ref\" from stdin' '\n+\tnext_session squashCE &&\n+\tgit sequencer <todotest1 &&\n+\t! test -d \"$SEQDIR\" &&\n+\tsession_ok &&\n+\ttest -f file2 &&\n+\ttest -f file3 &&\n+\ttest $(git rev-parse CE) = $(git rev-parse HEAD) &&\n+\ttest $(git rev-parse I) = $(git rev-parse HEAD^)\n+'\n+\n+cat >todotest2 <<EOF\n+# This is a test\n+\n+reset I  # go back to I\n+\n+EOF\n+\n+test_expect_success '\"reset\" from file with comments and blank lines' '\n+\tgit sequencer todotest2 &&\n+\tsession_ok &&\n+\ttest $(git rev-parse I) = $(git rev-parse HEAD)\n+'\n+\n+cat >todotest1 <<EOF\n+pick C\n+EOF\n+\n+test_expect_success '--onto <branch> keeps branch' '\n+\tgit checkout -b test-branch A &&\n+\tgit checkout master &&\n+\tgit sequencer --onto test-branch <todotest1 &&\n+\tsession_ok &&\n+\ttest \"$(git symbolic-ref -q HEAD)\" = \"refs/heads/test-branch\" &&\n+\ttest \"$(git rev-parse test-branch^)\" = \"$(git rev-parse A)\"\n+'\n+\n+test_expect_success '--onto commit (detached HEAD) works' '\n+\tgit sequencer --onto A <todotest1 &&\n+\tsession_ok &&\n+\ttest_must_fail git symbolic-ref -q HEAD &&\n+\ttest \"$(git rev-parse HEAD)\" = \"$(git rev-parse test-branch)\"\n+'\n+\n+echo 'pick -R C' >>todotest1\n+\n+test_expect_success 'pick -R works' '\n+\tgit checkout A &&\n+\tgit sequencer todotest1 &&\n+\tsession_ok &&\n+\t! test -f file2\n+'\n+\n+mkdir testdir\n+cat >testdir/script <<EOF\n+#!/bin/sh\n+test -s ../file1\n+EOF\n+chmod 755 testdir/script\n+cat >todotest1 <<EOF\n+run -- test -s file1  # this will fail\n+pick B\n+run --dir testdir -- test -s ../file1\n+pick D\n+run --dir=testdir ./script\n+EOF\n+\n+test_expect_success '\"run\" insn works' '\n+\tgit checkout A &&\n+\texpect_conflict git sequencer todotest1 &&\n+\t: >newfile &&\n+\tgit add newfile &&\n+\tnext_session nochange &&\n+\tgit sequencer --continue &&\n+\tsession_ok &&\n+\ttest -f newfile\n+'\n+\n+echo thisdoesnotexist >>todotest1\n+\n+test_expect_success 'junk is conflict' '\n+\tgit checkout A &&\n+\texpect_conflict git sequencer todotest1 &&\n+\ttest -d \"$SEQDIR\" &&\n+\tgit sequencer --abort &&\n+\t! test -d \"$SEQDIR\" &&\n+\tsession_ok &&\n+\ttest $(git rev-parse A) = $(git rev-parse HEAD)\n+'\n+\n+GIT_AUTHOR_NAME=\"Another 'ant' Thor\"\n+GIT_AUTHOR_EMAIL=\"a.thor@example.com\"\n+GIT_COMMITTER_NAME=\"Co M Miter\"\n+GIT_COMMITTER_EMAIL=\"c.miter@example.com\"\n+export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL\n+yet_another=\"Max Min <mm@example.com>\"\n+\n+cat >todotest1 <<EOF\n+patch patchB      # write 1 into file1\n+patch -k patchC   # generate file2\n+patch patchD.raw  # write 2 into file1\n+EOF\n+\n+echo 'write 1 into file1' >expected1\n+echo '[PATCH] generate empty file2' >expected2\n+echo 'echo 2 >file1' >expected3\n+\n+test_expect_success '\"patch\" insn works' '\n+\tgit checkout A &&\n+\tnext_session commitmsg &&\n+\tgit sequencer todotest1 &&\n+\t! test -d \"$SEQDIR\" &&\n+\tsession_ok &&\n+\ttest \"$(git rev-parse HEAD~3)\" = \"$(git rev-parse A)\" &&\n+\ttest \"$(git show HEAD~2:file1)\" = \"1\" &&\n+\ttest -z \"$(git show HEAD^:file2)\" &&\n+\ttest \"$(git show HEAD:file1)\" = \"2\" &&\n+\texpect_author HEAD~2 \"$orig_author\" &&\n+\texpect_author HEAD~1 \"$orig_author\" &&\n+\texpect_author HEAD~0 \"$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>\" &&\n+\texpect_msg HEAD~2 expected1 &&\n+\texpect_msg HEAD~1 expected2 &&\n+\texpect_msg HEAD~0 expected3\n+'\n+\n+cat >todotest1 <<EOF\n+pick B\t# write 1 into file1\n+pause\n+pick C\t# generate file2\n+EOF\n+\n+echo 'generate empty file2' >expected1\n+echo 'write 1 into file1' >expected2\n+\n+test_expect_success \"pick ; pause insns and --continue works\" '\n+\tgit checkout A &&\n+\texpect_continue git sequencer todotest1 &&\n+\tsession_ok &&\n+\techo 5 >file1 &&\n+\tgit add file1 &&\n+\tnext_session nochange &&\n+\tgit sequencer --continue &&\n+\ttest \"$(git show HEAD:file1)\" = 5 &&\n+\ttest -z \"$(git show HEAD:file2)\" &&\n+\texpect_msg HEAD expected1 &&\n+\texpect_msg HEAD^ expected2 &&\n+\tsession_ok\n+'\n+\n+cat >todotest1 <<EOF\n+edit B\t# write 1 into file1\n+pick C\t# generate file2\n+EOF\n+\n+test_expect_success \"edit insn and --continue works\" '\n+\tgit checkout A &&\n+\texpect_continue git sequencer todotest1 &&\n+\tsession_ok &&\n+\techo 5 >file1 &&\n+\tgit add file1 &&\n+\tnext_session nochange &&\n+\tgit sequencer --continue &&\n+\ttest \"$(git show HEAD:file1)\" = 5 &&\n+\ttest -z \"$(git show HEAD:file2)\" &&\n+\texpect_msg HEAD expected1 &&\n+\texpect_msg HEAD^ expected2 &&\n+\tsession_ok\n+'\n+\n+cat >todotest1 <<EOF\n+patch patchB\t# write 1 into file1\n+pick H\t\t# generate file5\n+mark :1\n+patch patchC\t# generate file2\n+squash I\t# generate file6\n+patch patchD\t# write 2 into file1\n+ref refs/tags/CID\n+mark :2\n+reset :1\t# reset to new H\n+patch patchD\t# write 2 into file1\n+squash CE\t# generate file2 and file3\n+ref refs/tags/DCE\n+merge :2\t# merge :2 into HEAD\n+patch patchF\t# generate file4\n+EOF\n+\n+test_expect_success 'all insns work without options' '\n+\tgit checkout A &&\n+\tnext_session squashCI &&\n+\tno_marks_set &&\n+\tgit sequencer todotest1 &&\n+\tno_marks_set &&\n+\ttest \"$(git show HEAD:file1)\" = \"2\" &&\n+\ttest -z \"$(git show HEAD:file2)\" &&\n+\ttest -z \"$(git show HEAD:file3)\" &&\n+\ttest -z \"$(git show HEAD:file4)\" &&\n+\ttest -z \"$(git show HEAD:file5)\" &&\n+\ttest -z \"$(git show HEAD:file6)\" &&\n+\techo \"$(git rev-parse DCE)\" >expected &&\n+\techo \"$(git rev-parse CID)\" >>expected &&\n+\tgit cat-file commit HEAD^ | sed -n -e \"s/^parent //p\" >actual &&\n+\ttest_cmp expected actual &&\n+\tsession_ok\n+'\n+\n+cat >todotest1 <<EOF\n+merge --standard DCE\n+EOF\n+\n+echo \"Merge DCE into HEAD\" >expected1\n+\n+test_expect_success 'merge --standard works' '\n+\tgit checkout CID &&\n+\tgit sequencer todotest1 &&\n+\texpect_msg HEAD expected1 &&\n+\tsession_ok\n+'\n+\n+cat >todotest1 <<EOF\n+merge --standard --message=\"foo\" DCE\n+EOF\n+\n+\n+test_expect_success 'merge --standard --message=\"foo\" is conflict' '\n+\tgit checkout CID &&\n+\texpect_conflict git sequencer todotest1 &&\n+\tgit sequencer --abort &&\n+\tsession_ok\n+'\n+\n+for command in 'pick ' 'patch patch' 'squash ' 'merge --standard '\n+do\n+\tcat >todotest1 <<EOF\n+patch patchB\t# 1 into file1\n+${command}G\t# 3 into file1\n+patch -3 patchF\t# empty file4\n+EOF\n+\n+\ttest_expect_success \"conflict test: ${command%% *} and --abort\" '\n+\t\tgit checkout A &&\n+\t\texpect_conflict git sequencer todotest1 &&\n+\t\tsession_ok &&\n+\t\ttest -d \"$SEQDIR\" &&\n+\t\tgit sequencer --abort &&\n+\t\tsession_ok &&\n+\t\ttest $(git rev-parse HEAD) = $(git rev-parse A)\n+\t'\n+\n+\ttest_expect_success \"conflict test: ${command%% *} and --continue\" '\n+\t\tgit checkout A &&\n+\t\texpect_conflict git sequencer todotest1 &&\n+\t\tsession_ok &&\n+\t\ttest -d \"$SEQDIR\" &&\n+\t\t## XXX: It would be perfect if we could remove the if\n+\t\t{ if test \"${command%% *}\" != \"patch\"\n+\t\tthen grep \"^<<<<<<<\" file1 ; fi } &&\n+\t\techo 3 >file1 &&\n+\t\tgit add file1 &&\n+\t\tnext_session nochange &&\n+\t\tgit sequencer --continue &&\n+\t\tsession_ok &&\n+\t\t! test -d \"$SEQDIR\" &&\n+\t\ttest \"$(git show HEAD:file1)\" = \"3\" &&\n+\t\ttest -f file4\n+\t'\n+\n+\ttest_expect_success \"conflict test: ${command%% *} and --skip\" '\n+\t\tgit checkout A &&\n+\t\texpect_conflict git sequencer todotest1 &&\n+\t\tsession_ok &&\n+\t\ttest -d \"$SEQDIR\" &&\n+\t\tgit sequencer --skip &&\n+\t\tsession_ok &&\n+\t\t! test -d \"$SEQDIR\" &&\n+\t\ttest \"$(git show HEAD:file1)\" = \"1\" &&\n+\t\ttest -f file4\n+\t'\n+done\n+\n+echo 'file5-gen' >commitmsg\n+\n+cat >todotest1 <<EOF\n+patch --signoff patchB\n+pause\n+pick --author=\"$yet_another\" --file=\"commitmsg\" --signoff H\n+mark :1\n+patch --message=\"file2-gen\" patchC\n+squash --signoff --author=\"$yet_another\" I\n+pause\n+patch --message=\"echo 2 >file1\" patchD\n+mark :2\n+reset :1\n+patch --author=\"$yet_another\" patchD\n+squash --signoff --message=\"generate file[23]\" CE\n+merge --signoff --message=\"test merge\" --author=\"$yet_another\" :2\n+pause\n+ref refs/tags/a_merge\n+patch --message=\"Generate file4 and write 23 into it\" patchF.raw\n+pause\n+pick I\n+EOF\n+\n+cat >expected1 <<EOF\n+write 1 into file\n+\n+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>\n+EOF\n+\n+test_expect_success 'insns work with options and another author 1' '\n+\tgit checkout A &&\n+\tno_marks_set &&\n+\n+\t# patch --signoff patchB  # write 1 into file1\n+\t# pause\n+\texpect_continue git sequencer todotest1 &&\n+\ttest \"Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>\" = \\\n+\t\t\"$(git cat-file commit HEAD | grep \"^Signed-off-by\")\" &&\n+\texpect_author HEAD \"$orig_author\" &&\n+\ttest -d \"$SEQDIR\" &&\n+\tsession_ok\n+'\n+\n+cat >expected1 <<EOF\n+file5-gen\n+\n+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>\n+EOF\n+\n+cat >expected2 <<EOF\n+file2-gen\n+\n+generate empty file6\n+\n+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>\n+EOF\n+\n+test_expect_success 'insns work with options and another author 2' '\n+\t: >file7 &&\n+\tgit add file7 &&\n+\tnext_session nochange &&\n+\tgit commit --amend &&\n+\tsession_ok &&\n+\n+\tnext_session nochange &&\n+\texpect_continue git sequencer --continue &&\n+\tsession_ok &&\n+\n+\t# amended commit\n+\ttest \"Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>\" = \\\n+\t\t\"$(git cat-file commit HEAD^^ | grep \"^Signed-off-by\")\" &&\n+\texpect_author HEAD^^ \"$orig_author\" &&\n+\ttest -z \"$(git show HEAD:file7)\" &&\n+\n+\t# pick --author=\"$yet_another\" --file=\"commitmsg\" --signoff H\n+\texpect_author HEAD^ \"$yet_another\" &&\n+\texpect_msg HEAD^ expected1 &&\n+\ttest -z \"$(git show HEAD:file5)\" &&\n+\n+\t# mark :1\n+\ttest \"$(git rev-parse \"$SEQMARK/1\")\" = \"$(git rev-parse HEAD^)\" &&\n+\n+\t# patch --message=\"file2-gen\" patchC\n+\t# squash --signoff --author=\"$yet_another\" I # generate file6\n+\t# pause\n+\ttest -z \"$(git show HEAD:file2)\" &&\n+\ttest -z \"$(git show HEAD:file6)\" &&\n+\tgit ls-files | grep \"^file2\" &&\n+\tgit ls-files | grep \"^file6\" &&\n+\texpect_msg HEAD expected2 &&\n+\texpect_author HEAD \"$yet_another\"\n+'\n+\n+echo 'echo 2 >file1' >expected1\n+\n+cat >expected2 <<EOF\n+generate file[23]\n+\n+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>\n+EOF\n+\n+cat >expected3 <<EOF\n+test merge\n+\n+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>\n+EOF\n+\n+cat >expected4 <<EOF\n+file1\n+file2\n+file3\n+file5\n+file6\n+file7\n+EOF\n+\n+test_expect_success 'insns work with options and another author 3' '\n+\t# do not change anything\n+\texpect_continue git sequencer --continue &&\n+\tsession_ok &&\n+\n+\t# patch --message=\"echo 2 >file1\" patchD\n+\t# mark :2\n+\tcommit=\"$(git rev-parse --verify \"$SEQMARK/2\")\" &&\n+\texpect_author \"$commit\" \"$orig_author\" &&\n+\texpect_msg \"$commit\" expected1 &&\n+\n+\t# reset :1\n+\t# patch --author=\"$yet_another\" patchD # write 2 into file1\n+\t# squash --signoff --message=\"generate file[23]\" CE\n+\texpect_author HEAD^ \"$yet_another\" &&\n+\texpect_msg HEAD^ expected2 &&\n+\n+\t# merge --signoff --message=\"test merge\" --author=\"$yet_another\" :2\n+\t# pause\n+\texpect_author HEAD \"$yet_another\" &&\n+\texpect_msg HEAD expected3 &&\n+\tgit ls-files >actual &&\n+\ttest_cmp expected4 actual\n+'\n+\n+cat >expected_merge <<EOF\n+cleanup merge\n+\n+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>\n+EOF\n+\n+echo 'Generate file4 and write 23 into it' >expected2\n+\n+test_expect_success 'insns work with options and another author 4' '\n+\tgit rm file5 file6 file7 &&\n+\tnext_session merge2 &&\n+\texpect_continue git sequencer --continue &&\n+\tsession_ok &&\n+\n+\t# ref refs/tags/a_merge\n+\texpect_author a_merge \"$yet_another\" &&\n+\texpect_msg a_merge expected_merge &&\n+\n+\t# patch --message=\"Generate file4 and write 23 into it\" patchF.raw\n+\t# pause\n+\tgit ls-files | grep \"^file4\" &&\n+\techo 23 >file4 &&\n+\tgit add file4 &&\n+\tnext_session nochange &&\n+\tgit sequencer --continue &&\n+\tsession_ok &&\n+\tno_marks_set &&\n+\ttest \"$(git show HEAD:file1)\" = \"2\" &&\n+\ttest \"$(git show HEAD:file4)\" = \"23\" &&\n+\texpect_msg HEAD^ expected2 &&\n+\texpect_author HEAD^ \"$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>\" &&\n+\n+\t# pick I\n+\ttest -z \"$(git show HEAD:file6)\" &&\n+\tgit ls-files | grep \"^file6\" &&\n+\tsession_ok\n+'\n+\n+# almost the same to test --quiet\n+cat >todotest1 <<EOF\n+patch patchB\n+pick H\n+mark :1\n+patch patchC\n+squash --message=\"a squash\" I\n+patch patchD\n+mark :2\n+reset :1\n+patch patchD\n+squash --message=\"another squash\" CE\n+merge --message=\"test merge\" :2\n+pause\n+patch patchF\n+EOF\n+\n+test_expect_failure '--quiet works' '\n+\tgit checkout A &&\n+\texpect_continue git sequencer --quiet todotest1 >actual &&\n+\tsession_ok &&\n+\t! test -s actual\n+'\n+\n+test_expect_failure '--quiet works on continue' '\n+\tgit sequencer --continue >>actual &&\n+\tsession_ok &&\n+\t! test -s actual\n+'\n+\n+echo 'merge --strategy=ours --reuse-commit=a_merge branch1 branch2 CE CID' >todotest1\n+\n+test_expect_success 'merge multiple branches and --reuse-commit works' '\n+\tgit checkout -b merge-multiple master &&\n+\tgit sequencer todotest1 &&\n+\tsession_ok &&\n+\texpect_msg HEAD expected_merge &&\n+\tgit rev-parse HEAD^ >expected &&\n+\tgit rev-parse branch1 >>expected &&\n+\tgit rev-parse branch2 >>expected &&\n+\tgit rev-parse CE >>expected &&\n+\tgit rev-parse CID >>expected &&\n+\tgit cat-file commit HEAD | sed -n -e \"s/^parent //p\" >actual &&\n+\ttest_cmp expected actual &&\n+\t! test -f file6\n+'\n+\n+echo 'pick --mainline=5 merge-multiple' >todotest1\n+\n+test_expect_success 'pick --mainline works' '\n+\tgit checkout -b mainline CID &&\n+\tgit sequencer todotest1 &&\n+\tsession_ok &&\n+\texpect_msg HEAD expected_merge &&\n+\t! test -f file6 &&\n+\ttest -f file3 &&\n+\ttest -f file2 &&\n+\ttest \"$(git show HEAD:file1)\" = 2\n+'\n+\n+cat >todotest1 <<EOF\n+pick C\t\t# file2\n+mark :1\n+patch patchB\t# write 1 into file1\n+patch patchD\t# write 2 into file1\n+pick I\t\t# file6\n+squash --message=\"2 in file1 and file6 exists\" --signoff --from :1\n+EOF\n+\n+cat >expected1 <<EOF\n+2 in file1 and file6 exists\n+\n+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>\n+EOF\n+\n+test_expect_success 'squash --from works' '\n+\tgit checkout A &&\n+\tgit sequencer <todotest1 &&\n+\tsession_ok &&\n+\ttest \"$(git rev-parse A)\" = \"$(git rev-parse HEAD~2)\" &&\n+\ttest \"$(git show HEAD:file1)\" = \"2\" &&\n+\ttest -z \"$(git show HEAD:file6)\" &&\n+\texpect_msg HEAD expected1\n+'\n+\n+cat >todotest1 <<EOF\n+patch patchB\t# write 1 into file1\n+pick H\t\t# generate file5\n+mark :1\n+patch patchC\t# generate file2\n+squash --message=\"file5\" I\t# generate file6\n+patch patchD\t# write 2 into file1\n+mark :2\n+reset :1\t# reset to new H\n+patch patchD\t# write 2 into file1\n+squash --message=\"CE\" CE\t# generate file2 and file3\n+merge --standard :2\t# merge :2 into HEAD\n+patch patchF\t# generate file4\n+EOF\n+cp todotest1 todotest2\n+cat todotest1 | sed -e 's/^\\(patch\\|pick\\|squash\\|merge\\) /&--edit /' >todotest3\n+echo 'squash --message=\"doesnt work either\" --from :1' >>todotest1\n+echo 'squash --include-merges --message=\"stupid\" --from :1' >>todotest2\n+\n+test_expect_success 'squash --from conflicts with merge in between' '\n+\tgit checkout A &&\n+\texpect_conflict git sequencer todotest1 &&\n+\tgit sequencer --abort &&\n+\tsession_ok &&\n+\t! test -d \"$SEQDIR\"\n+'\n+\n+test_expect_success 'squash --include-merges --from succeeds with merge in between' '\n+\tgit checkout A &&\n+\tgit sequencer todotest2 &&\n+\tsession_ok &&\n+\ttest \"$(git rev-parse HEAD~3)\" = \"$(git rev-parse A)\"\n+'\n+\n+test_expect_success 'patch|pick|squash|merge --edit works' '\n+\tgit checkout A &&\n+\tnext_session editX &&\n+\tgit sequencer todotest3 &&\n+\tsession_ok\n+'\n+\n+cat >todotest1 <<EOF\n+patch patchB\n+pause\n+EOF\n+\n+test_expect_success 'batch mode fails on pause insn' '\n+\tgit checkout A &&\n+\texpect_fail git sequencer --batch todotest1 &&\n+\tsession_ok &&\n+\t! test -d \"$SEQDIR\"\n+'\n+\n+cat >todotest1 <<EOF\n+patch patchB\n+pick G\n+EOF\n+\n+test_expect_success 'batch mode fails on conflict' '\n+\tgit checkout A &&\n+\texpect_fail git sequencer --batch <todotest1 &&\n+\tsession_ok &&\n+\t! test -d \"$SEQDIR\" &&\n+\ttest -z \"$(git show HEAD:file1)\"\n+'\n+\n+cat >todotest1 <<EOF\n+patch patchB\n+pause\n+EOF\n+\n+test_expect_success '--caller works' '\n+\tgit checkout A &&\n+\texpect_continue git sequencer \\\n+\t\t--caller=\"this works|abrt||skip\" todotest1 &&\n+\texpect_fail git sequencer --abort &&\n+\texpect_fail git sequencer --skip &&\n+\tgit sequencer --continue &&\n+\tsession_ok\n+'\n+\n+test_done\n-- \n1.5.6.3.391.ge45b\n"},{"id":"83586","messageId":"cover.1216233918.git.s-beyer@gmx.net","threadId":"14494","inReplyTo":"fd9b4f2b04c9b997c6bdba90352eb1ef973114ae.1216233915.git.s-beyer@gmx.net","subject":"Sequencer migration patches","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2008-07-16T20:45:19Z","receivedAt":"2008-07-16T20:45:19Z","isPatch":false,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"Hi,\n\nthe following patches migrate git-am and git-rebase--interactive to\ngit-sequencer.\n\nBut there are some issues I want to mention.\n\nI've compared performance of original rebase and am with the\nsequencer-based ones and the sequencer-based ones perform fairly bad.\nFor example, applying 45 patches with git-am took 3 seconds using the\noriginal and 8 seconds using the sequencer-based one.\nRebasing 100 commits takes 10.1 seconds instead of only 4.8 seconds\non my test machine.\n\nI expect that the builtin-sequencer performs muuuch better.\nBut as long as there is no builtin-sequencer these patches should\nperhaps not be applied.\nWell, I could offer to provide patches to put sequencer-based git-am and\ngit-rebase-i scripts to contrib/examples/ (or contrib/sequencer-examples/\nor something else). ;-)\n\nRegards,\n  Stephan\n\n\nStephan Beyer (2):\n  Migrate git-am to use git-sequencer\n  Introduce git am --abort\n\n Documentation/git-am.txt               |    5 +-\n Documentation/git-rerere.txt           |    2 +-\n contrib/completion/git-completion.bash |    2 +-\n git-am.sh                              |  617 +++++++++++---------------------\n git-rebase.sh                          |    7 +-\n t/t4150-am.sh                          |   27 +-\n 6 files changed, 244 insertions(+), 416 deletions(-)\n\n\nStephan Beyer (1):\n  Migrate rebase-i to sequencer\n\n git-rebase--interactive.sh    |  438 ++++++++++-------------------------------\n t/t3404-rebase-interactive.sh |    9 +-\n 2 files changed, 113 insertions(+), 334 deletions(-)\n"},{"id":"83585","messageId":"7ab7e9bf8e40a5317045ee44497422a29c43c61c.1216233922.git.s-beyer@gmx.net","threadId":"14494","inReplyTo":"cover.1216233918.git.s-beyer@gmx.net","subject":"[PATCH] Migrate rebase-i to sequencer","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2008-07-16T20:45:22Z","receivedAt":"2008-07-16T20:45:22Z","isPatch":true,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"The migration of pure rebase-i to sequencer is simply done by\ngenerating the todo list, but with a comment marker (`#')\nbefore the description, and then feed it to git sequencer.\n\nFor git-rebase-i -p (preserving merges) merges should be\nrewritten. For this, the sequencer instructions \"mark\", \"merge\"\nand \"reset\" are used.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Daniel Barkalow <barkalow@iabervon.org>\nSigned-off-by: Stephan Beyer <s-beyer@gmx.net>\n---\n git-rebase--interactive.sh    |  438 ++++++++++-------------------------------\n t/t3404-rebase-interactive.sh |    9 +-\n 2 files changed, 113 insertions(+), 334 deletions(-)\n\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex da79a24..2136e02 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -42,11 +42,6 @@ STRATEGY=\n ONTO=\n VERBOSE=\n \n-GIT_CHERRY_PICK_HELP=\"  After resolving the conflicts,\n-mark the corrected paths with 'git add <paths>', and\n-run 'git rebase --continue'\"\n-export GIT_CHERRY_PICK_HELP\n-\n warn () {\n \techo \"$*\" >&2\n }\n@@ -74,48 +69,6 @@ require_clean_work_tree () {\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-\t\t;;\n-\tesac\n-}\n-\n-last_count=\n-mark_action_done () {\n-\tsed -e 1q < \"$TODO\" >> \"$DONE\"\n-\tsed -e 1d < \"$TODO\" >> \"$TODO\".new\n-\tmv -f \"$TODO\".new \"$TODO\"\n-\tcount=$(grep -c '^[^#]' < \"$DONE\")\n-\ttotal=$(($count+$(grep -c '^[^#]' < \"$TODO\")))\n-\tif test \"$last_count\" != \"$count\"\n-\tthen\n-\t\tlast_count=$count\n-\t\tprintf \"Rebasing (%d/%d)\\r\" $count $total\n-\t\ttest -z \"$VERBOSE\" || echo\n-\tfi\n-}\n-\n-make_patch () {\n-\tparent_sha1=$(git rev-parse --verify \"$1\"^) ||\n-\t\tdie \"Cannot get patch for $1^\"\n-\tgit diff-tree -p \"$parent_sha1\"..\"$1\" > \"$DOTEST\"/patch\n-\ttest -f \"$DOTEST\"/message ||\n-\t\tgit cat-file commit \"$1\" | sed \"1,/^$/d\" > \"$DOTEST\"/message\n-\ttest -f \"$DOTEST\"/author-script ||\n-\t\tget_author_ident_from_commit \"$1\" > \"$DOTEST\"/author-script\n-}\n-\n-die_with_patch () {\n-\tmake_patch \"$1\"\n-\tgit rerere\n-\tdie \"$2\"\n-}\n-\n die_abort () {\n \trm -rf \"$DOTEST\"\n \tdie \"$1\"\n@@ -125,48 +78,20 @@ has_action () {\n \tgrep '^[^#]' \"$1\" >/dev/null\n }\n \n-pick_one () {\n-\tno_ff=\n-\tcase \"$1\" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac\n-\toutput git rev-parse --verify $sha1 || die \"Invalid commit name: $sha1\"\n-\ttest -d \"$REWRITTEN\" &&\n-\t\tpick_one_preserving_merges \"$@\" && return\n-\tparent_sha1=$(git rev-parse --verify $sha1^) ||\n-\t\tdie \"Could not get the parent of $sha1\"\n-\tcurrent_sha1=$(git rev-parse --verify HEAD)\n-\tif test \"$no_ff$current_sha1\" = \"$parent_sha1\"; then\n-\t\toutput git reset --hard $sha1\n-\t\ttest \"a$1\" = a-n && output git reset --soft $current_sha1\n-\t\tsha1=$(git rev-parse --short $sha1)\n-\t\toutput warn Fast forward to $sha1\n-\telse\n-\t\toutput git cherry-pick \"$@\"\n-\tfi\n-}\n-\n-pick_one_preserving_merges () {\n-\tcase \"$1\" in -n) sha1=$2 ;; *) sha1=$1 ;; esac\n+create_todo_preserving_merges () {\n+\tshortsha1=$sha1\n \tsha1=$(git rev-parse $sha1)\n \n-\tif test -f \"$DOTEST\"/current-commit\n-\tthen\n-\t\tcurrent_commit=$(cat \"$DOTEST\"/current-commit) &&\n-\t\tgit rev-parse HEAD > \"$REWRITTEN\"/$current_commit &&\n-\t\trm \"$DOTEST\"/current-commit ||\n-\t\tdie \"Cannot write current commit's replacement sha1\"\n-\tfi\n-\n-\t# rewrite parents; if none were rewritten, we can fast-forward.\n-\tfast_forward=t\n \tpreserve=t\n \tnew_parents=\n+\tfirst_parent=\n \tfor p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)\n \tdo\n+\t\t# check if we've already seen this parent\n \t\tif test -f \"$REWRITTEN\"/$p\n \t\tthen\n \t\t\tpreserve=f\n \t\t\tnew_p=$(cat \"$REWRITTEN\"/$p)\n-\t\t\ttest $p != $new_p && fast_forward=f\n \t\t\tcase \"$new_parents\" in\n \t\t\t*$new_p*)\n \t\t\t\t;; # do nothing; that parent is already there\n@@ -174,186 +99,49 @@ pick_one_preserving_merges () {\n \t\t\t\tnew_parents=\"$new_parents $new_p\"\n \t\t\t\t;;\n \t\t\tesac\n+\t\telse\n+\t\t\tnew_parents=\"$new_parents $p\"\n \t\tfi\n+\t\ttest -n \"$first_parent\" || first_parent=$p\n \tdone\n-\tcase $fast_forward in\n-\tt)\n-\t\toutput warn \"Fast forward to $sha1\"\n-\t\ttest $preserve = f || echo $sha1 > \"$REWRITTEN\"/$sha1\n-\t\t;;\n-\tf)\n-\t\ttest \"a$1\" = a-n && die \"Refusing to squash a merge: $sha1\"\n-\n-\t\tfirst_parent=$(expr \"$new_parents\" : ' \\([^ ]*\\)')\n-\t\t# detach HEAD to current parent\n-\t\toutput git checkout $first_parent 2> /dev/null ||\n-\t\t\tdie \"Cannot move HEAD to $first_parent\"\n-\n-\t\techo $sha1 > \"$DOTEST\"/current-commit\n-\t\tcase \"$new_parents\" in\n-\t\t' '*' '*)\n-\t\t\t# redo merge\n-\t\t\tauthor_script=$(get_author_ident_from_commit $sha1)\n-\t\t\teval \"$author_script\"\n-\t\t\tmsg=\"$(git cat-file commit $sha1 | sed -e '1,/^$/d')\"\n-\t\t\t# No point in merging the first parent, that's HEAD\n-\t\t\tnew_parents=${new_parents# $first_parent}\n-\t\t\tif ! GIT_AUTHOR_NAME=\"$GIT_AUTHOR_NAME\" \\\n-\t\t\t\tGIT_AUTHOR_EMAIL=\"$GIT_AUTHOR_EMAIL\" \\\n-\t\t\t\tGIT_AUTHOR_DATE=\"$GIT_AUTHOR_DATE\" \\\n-\t\t\t\toutput git merge $STRATEGY -m \"$msg\" \\\n-\t\t\t\t\t$new_parents\n-\t\t\tthen\n-\t\t\t\tgit rerere\n-\t\t\t\tprintf \"%s\\n\" \"$msg\" > \"$GIT_DIR\"/MERGE_MSG\n-\t\t\t\tdie Error redoing merge $sha1\n-\t\t\tfi\n-\t\t\t;;\n-\t\t*)\n-\t\t\toutput git cherry-pick \"$@\" ||\n-\t\t\t\tdie_with_patch $sha1 \"Could not pick $sha1\"\n-\t\t\t;;\n-\t\tesac\n-\t\t;;\n-\tesac\n-}\n+\t# We do not have parent, so ignore this commit\n+\ttest t = $preserve && return\n \n-nth_string () {\n-\tcase \"$1\" in\n-\t*1[0-9]|*[04-9]) echo \"$1\"th;;\n-\t*1) echo \"$1\"st;;\n-\t*2) echo \"$1\"nd;;\n-\t*3) echo \"$1\"rd;;\n-\tesac\n-}\n+\t# We always write a mark, because we do not know if there will\n+\t# be a \"reset\" or \"merge\"\n+\t# Filter the unneeded marks out afterwards.\n+\techo \"mark :$mark\"\n+\tmark=$(($mark+1))\n \n-make_squash_message () {\n-\tif test -f \"$SQUASH_MSG\"; then\n-\t\tCOUNT=$(($(sed -n \"s/^# This is [^0-9]*\\([1-9][0-9]*\\).*/\\1/p\" \\\n-\t\t\t< \"$SQUASH_MSG\" | sed -ne '$p')+1))\n-\t\techo \"# This is a combination of $COUNT commits.\"\n-\t\tsed -e 1d -e '2,/^./{\n-\t\t\t/^$/d\n-\t\t}' <\"$SQUASH_MSG\"\n-\telse\n-\t\tCOUNT=2\n-\t\techo \"# This is a combination of two commits.\"\n-\t\techo \"# The first commit's message is:\"\n-\t\techo\n-\t\tgit cat-file commit HEAD | sed -e '1,/^$/d'\n-\tfi\n-\techo\n-\techo \"# This is the $(nth_string $COUNT) commit message:\"\n-\techo\n-\tgit cat-file commit $1 | sed -e '1,/^$/d'\n-}\n+\tnew_first_parent=$(expr \"$new_parents\" : ' \\([^ ]*\\)')\n \n-peek_next_command () {\n-\tsed -n \"1s/ .*$//p\" < \"$TODO\"\n-}\n+\t# Reset if needed\n+\ttest -z \"$first_parent\" -o \"$first_parent\" = $lastsha1 ||\n+\t\techo \"reset $new_first_parent\"\n \n-do_next () {\n-\trm -f \"$DOTEST\"/message \"$DOTEST\"/author-script \\\n-\t\t\"$DOTEST\"/amend || exit\n-\tread command sha1 rest < \"$TODO\"\n-\tcase \"$command\" in\n-\t'#'*|'')\n-\t\tmark_action_done\n-\t\t;;\n-\tpick|p)\n-\t\tcomment_for_reflog pick\n+\techo \":$mark\" > \"$REWRITTEN\"/$sha1\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|e)\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\t: > \"$DOTEST\"/amend\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\twarn \"Once you are satisfied with your changes, run\"\n-\t\twarn\n-\t\twarn \"\tgit rebase --continue\"\n-\t\twarn\n-\t\texit 0\n-\t\t;;\n-\tsquash|s)\n-\t\tcomment_for_reflog squash\n-\n-\t\thas_action \"$DONE\" ||\n-\t\t\tdie \"Cannot 'squash' without a previous commit\"\n-\n-\t\tmark_action_done\n-\t\tmake_squash_message $sha1 > \"$MSG\"\n-\t\tcase \"$(peek_next_command)\" in\n-\t\tsquash|s)\n-\t\t\tEDIT_COMMIT=\n-\t\t\tUSE_OUTPUT=output\n-\t\t\tcp \"$MSG\" \"$SQUASH_MSG\"\n-\t\t\t;;\n-\t\t*)\n-\t\t\tEDIT_COMMIT=-e\n-\t\t\tUSE_OUTPUT=\n-\t\t\trm -f \"$SQUASH_MSG\" || exit\n-\t\t\t;;\n-\t\tesac\n-\n-\t\tfailed=f\n-\t\tauthor_script=$(get_author_ident_from_commit HEAD)\n-\t\toutput git reset --soft HEAD^\n-\t\tpick_one -n $sha1 || failed=t\n-\t\techo \"$author_script\" > \"$DOTEST\"/author-script\n-\t\tif test $failed = f\n-\t\tthen\n-\t\t\t# This is like --amend, but with a different message\n-\t\t\teval \"$author_script\"\n-\t\t\tGIT_AUTHOR_NAME=\"$GIT_AUTHOR_NAME\" \\\n-\t\t\tGIT_AUTHOR_EMAIL=\"$GIT_AUTHOR_EMAIL\" \\\n-\t\t\tGIT_AUTHOR_DATE=\"$GIT_AUTHOR_DATE\" \\\n-\t\t\t$USE_OUTPUT git commit --no-verify -F \"$MSG\" $EDIT_COMMIT || failed=t\n-\t\tfi\n-\t\tif test $failed = t\n-\t\tthen\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\tdie_with_patch $sha1 \"\"\n-\t\tfi\n+\t# Merge or pick\n+\tcase \"$new_parents\" in\n+\t' '*' '*)\n+\t\tnew_parents=${new_parents# $new_first_parent}\n+\t\tprintf 'merge%s -C %s%s\\t%s\\n' \"$STRATEGY\" \\\n+\t\t\t\"$shortsha1\" \"$new_parents\" \"$rest\"\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+\t\tprintf 'pick %s\\t%s\\n' \"$shortsha1\" \"$rest\"\n \t\t;;\n \tesac\n-\ttest -s \"$TODO\" && return\n \n-\tcomment_for_reflog finish &&\n+\tlastsha1=\"$sha1\"\n+\treturn 0\n+}\n+\n+update_refs_and_exit () {\n \tHEADNAME=$(cat \"$DOTEST\"/head-name) &&\n \tOLDHEAD=$(cat \"$DOTEST\"/head) &&\n \tSHORTONTO=$(git rev-parse --short $(cat \"$DOTEST\"/onto)) &&\n-\tif test -d \"$REWRITTEN\"\n-\tthen\n-\t\ttest -f \"$DOTEST\"/current-commit &&\n-\t\t\tcurrent_commit=$(cat \"$DOTEST\"/current-commit) &&\n-\t\t\tgit rev-parse HEAD > \"$REWRITTEN\"/$current_commit\n-\t\tif test -f \"$REWRITTEN\"/$OLDHEAD\n-\t\tthen\n-\t\t\tNEWHEAD=$(cat \"$REWRITTEN\"/$OLDHEAD)\n-\t\telse\n-\t\t\tNEWHEAD=$OLDHEAD\n-\t\tfi\n-\telse\n-\t\tNEWHEAD=$(git rev-parse HEAD)\n-\tfi &&\n+\tNEWHEAD=$(git rev-parse HEAD) &&\n \tcase $HEADNAME in\n \trefs/*)\n \t\tmessage=\"$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)\" &&\n@@ -371,13 +159,6 @@ do_next () {\n \texit\n }\n \n-do_rest () {\n-\twhile :\n-\tdo\n-\t\tdo_next\n-\tdone\n-}\n-\n # check if no other options are set\n is_standalone () {\n \ttest $# -eq 2 -a \"$2\" = '--' &&\n@@ -393,80 +174,47 @@ get_saved_options () {\n \ttest -f \"$DOTEST\"/verbose && VERBOSE=t\n }\n \n-while test $# != 0\n-do\n-\tcase \"$1\" in\n-\t--continue)\n-\t\tis_standalone \"$@\" || usage\n-\t\tget_saved_options\n-\t\tcomment_for_reflog continue\n-\n-\t\ttest -d \"$DOTEST\" || die \"No interactive rebase running\"\n-\n-\t\t# Sanity check\n-\t\tgit rev-parse --verify HEAD >/dev/null ||\n-\t\t\tdie \"Cannot read HEAD\"\n-\t\tgit update-index --ignore-submodules --refresh &&\n-\t\t\tgit diff-files --quiet --ignore-submodules ||\n-\t\t\tdie \"Working tree is dirty\"\n-\n-\t\t# do we have anything to commit?\n-\t\tif git diff-index --cached --quiet --ignore-submodules HEAD --\n+run_sequencer () {\n+\tgit sequencer --caller='git rebase -i|--abort|--continue|--skip' \"$@\"\n+\tcase \"$?\" in\n+\t0)\n+\t\tif test \"$1\" = --abort\n \t\tthen\n-\t\t\t: Nothing to commit -- skip this\n+\t\t\trm -rf \"$DOTEST\"\n+\t\t\texit\n \t\telse\n-\t\t\t. \"$DOTEST\"/author-script ||\n-\t\t\t\tdie \"Cannot find the author identity\"\n-\t\t\tif test -f \"$DOTEST\"/amend\n-\t\t\tthen\n-\t\t\t\tgit reset --soft HEAD^ ||\n-\t\t\t\tdie \"Cannot rewind the HEAD\"\n-\t\t\tfi\n-\t\t\texport GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE &&\n-\t\t\tgit commit --no-verify -F \"$DOTEST\"/message -e ||\n-\t\t\tdie \"Could not commit staged changes.\"\n+\t\t\tupdate_refs_and_exit\n \t\tfi\n-\n-\t\trequire_clean_work_tree\n-\t\tdo_rest\n \t\t;;\n-\t--abort)\n-\t\tis_standalone \"$@\" || usage\n-\t\tget_saved_options\n-\t\tcomment_for_reflog abort\n-\n-\t\tgit rerere clear\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\tcase $HEADNAME in\n-\t\trefs/*)\n-\t\t\tgit symbolic-ref HEAD $HEADNAME\n-\t\t\t;;\n-\t\tesac &&\n-\t\toutput git reset --hard $HEAD &&\n-\t\trm -rf \"$DOTEST\"\n-\t\texit\n+\t2)\n+\t\t# pause\n+\t\texit 0\n \t\t;;\n-\t--skip)\n-\t\tis_standalone \"$@\" || usage\n-\t\tget_saved_options\n-\t\tcomment_for_reflog skip\n-\n-\t\tgit rerere clear\n-\t\ttest -d \"$DOTEST\" || die \"No interactive rebase running\"\n+\t3)\n+\t\t# conflict\n+\t\texit 1\n+\t\t;;\n+\t*)\n+\t\tdie_abort 'git-sequencer died unexpected.'\n+\t\t;;\n+\tesac\n+}\n \n-\t\toutput git reset --hard && do_rest\n+while test $# != 0\n+do\n+\tcase \"$1\" in\n+\t--abort|--continue|--skip)\n+\t\tis_standalone \"$@\" || usage\n+\t\trun_sequencer \"$1\"\n \t\t;;\n \t-s)\n \t\tcase \"$#,$1\" in\n \t\t*,*=*)\n-\t\t\tSTRATEGY=\"-s \"$(expr \"z$1\" : 'z-[^=]*=\\(.*\\)') ;;\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\tSTRATEGY=\" -s $2\"\n \t\t\tshift ;;\n \t\tesac\n \t\t;;\n@@ -492,12 +240,12 @@ do\n \t\ttest $# -eq 1 -o $# -eq 2 || usage\n \t\ttest -d \"$DOTEST\" &&\n \t\t\tdie \"Interactive rebase already started\"\n+\t\tgit sequencer --status >/dev/null 2>&1 &&\n+\t\t\tdie \"Sequencer already started. Cannot run rebase.\"\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\trequire_clean_work_tree\n \n \t\tUPSTREAM=$(git rev-parse --verify \"$1\") || die \"Invalid base\"\n@@ -514,42 +262,72 @@ do\n \t\tHEAD=$(git rev-parse --verify HEAD) || die \"No HEAD?\"\n \t\tmkdir \"$DOTEST\" || die \"Could not create temporary $DOTEST\"\n \n-\t\t: > \"$DOTEST\"/interactive || die \"Could not mark as interactive\"\n+\t\t: > \"$DOTEST\"/interactive || die_abort \"Could not mark as interactive\"\n \t\tgit symbolic-ref HEAD > \"$DOTEST\"/head-name 2> /dev/null ||\n \t\t\techo \"detached HEAD\" > \"$DOTEST\"/head-name\n \n \t\techo $HEAD > \"$DOTEST\"/head\n-\t\techo $UPSTREAM > \"$DOTEST\"/upstream\n \t\techo $ONTO > \"$DOTEST\"/onto\n \t\ttest -z \"$STRATEGY\" || echo \"$STRATEGY\" > \"$DOTEST\"/strategy\n \t\ttest t = \"$VERBOSE\" && : > \"$DOTEST\"/verbose\n \t\tif test t = \"$PRESERVE_MERGES\"\n \t\tthen\n+\t\t\tlastsha1=\n \t\t\t# $REWRITTEN contains files for each commit that is\n-\t\t\t# reachable by at least one merge base of $HEAD and\n-\t\t\t# $UPSTREAM. They are not necessarily rewritten, but\n-\t\t\t# their children might be.\n-\t\t\t# This ensures that commits on merged, but otherwise\n-\t\t\t# unrelated side branches are left alone. (Think \"X\"\n-\t\t\t# in the man page's example.)\n+\t\t\t# reachable on the way between $UPSTREAM and $HEAD.\n+\t\t\t# The filename is the SHA1 of the old value and the\n+\t\t\t# content is the SHA1 or :mark of the new one.\n \t\t\tmkdir \"$REWRITTEN\" &&\n \t\t\tfor c in $(git merge-base --all $HEAD $UPSTREAM)\n \t\t\tdo\n+\t\t\t\ttest -n \"$lastsha1\" || lastsha1=$c\n \t\t\t\techo $ONTO > \"$REWRITTEN\"/$c ||\n \t\t\t\t\tdie \"Could not init rewritten commits\"\n \t\t\tdone\n-\t\t\tMERGES_OPTION=\n-\t\telse\n-\t\t\tMERGES_OPTION=--no-merges\n+\t\t\tgit rev-list --abbrev-commit --abbrev=7 \\\n+\t\t\t\t--pretty=format:\"%m%h\t# %s\" --topo-order \\\n+\t\t\t\t--reverse --cherry-pick $UPSTREAM...$HEAD | \\\n+\t\t\t\tsed -n -e \"s/^>//p\" > \"$DOTEST\"/commit-list\n+\n+\t\t\tmark=0\n+\t\t\twhile read -r sha1 rest\n+\t\t\tdo\n+\t\t\t\tcreate_todo_preserving_merges\n+\t\t\tdone < \"$DOTEST\"/commit-list > \"$TODO\"\n+\n+\t\t\t# We now have more \"mark :...\" lines than needed.\n+\t\t\t# Remove the unused.  This is just a step to keep\n+\t\t\t# the list clean.\n+\t\t\tkeep_marks=$(sed -e \"/^mark :/d\" <\"$TODO\" |\n+\t\t\t\tsed -n -e 's/^[^#]* :\\([0-9][0-9]*\\).*$/:\\1:/p')\n+\t\t\twhile read -r line\n+\t\t\tdo\n+\t\t\t\tcase \"$line\" in\n+\t\t\t\t'mark :'*)\n+\t\t\t\t\tcase \"$keep_marks \" in\n+\t\t\t\t\t*${line#mark }:*)\n+\t\t\t\t\t\techo \"$line\"\n+\t\t\t\t\t\t;;\n+\t\t\t\t\tesac\n+\t\t\t\t\t;;\n+\t\t\t\t*)\n+\t\t\t\t\tprintf '%s\\n' \"$line\"\n+\t\t\t\t\t;;\n+\t\t\t\tesac\n+\t\t\tdone < \"$TODO\" > \"$TODO\".new\n+\t\t\tmv \"$TODO\".new \"$TODO\"\n+\t\tfi\n+\t\tif test -z \"$PRESERVE_MERGES\"\n+\t\tthen\n+\t\t\tgit rev-list --no-merges --abbrev-commit --abbrev=7 \\\n+\t\t\t\t--pretty=format:\"%mpick %h\t# %s\" \\\n+\t\t\t\t--reverse --cherry-pick $UPSTREAM...$HEAD | \\\n+\t\t\t\tsed -n -e \"s/^>//p\" > \"$TODO\"\n \t\tfi\n \n \t\tSHORTUPSTREAM=$(git rev-parse --short $UPSTREAM)\n \t\tSHORTHEAD=$(git rev-parse --short $HEAD)\n \t\tSHORTONTO=$(git rev-parse --short $ONTO)\n-\t\tgit rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \\\n-\t\t\t--abbrev=7 --reverse --left-right --cherry-pick \\\n-\t\t\t$UPSTREAM...$HEAD | \\\n-\t\t\tsed -n \"s/^>/pick /p\" > \"$TODO\"\n \t\tcat >> \"$TODO\" << EOF\n \n # Rebase $SHORTUPSTREAM..$SHORTHEAD onto $SHORTONTO\n@@ -575,7 +353,7 @@ EOF\n \t\t\tdie_abort \"Nothing to do\"\n \n \t\tgit update-ref ORIG_HEAD $HEAD\n-\t\toutput git checkout $ONTO && do_rest\n+\t\trun_sequencer --onto \"$ONTO\" \"$TODO\"\n \t\t;;\n \tesac\n \tshift\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex ffe3dd9..64a28ef 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -159,19 +159,20 @@ test_expect_success 'stop on conflicting pick' '\n \tgit tag new-branch1 &&\n \ttest_must_fail git rebase -i master &&\n \ttest \"$(git rev-parse HEAD~3)\" = \"$(git rev-parse master)\" &&\n-\ttest_cmp expect .git/rebase-merge/patch &&\n+\ttest_cmp expect .git/sequencer/patch &&\n \ttest_cmp expect2 file1 &&\n \ttest \"$(git-diff --name-status |\n \t\tsed -n -e \"/^U/s/^U[^a-z]*//p\")\" = file1 &&\n-\ttest 4 = $(grep -v \"^#\" < .git/rebase-merge/done | wc -l) &&\n-\ttest 0 = $(grep -c \"^[^#]\" < .git/rebase-merge/git-rebase-todo)\n+\ttest 4 = $(grep -v \"^#\" < .git/sequencer/done | wc -l) &&\n+\ttest 0 = $(grep -c \"^[^#]\" < .git/sequencer/todo) &&\n+\ttest -d .git/rebase-merge\n '\n \n test_expect_success 'abort' '\n \tgit rebase --abort &&\n \ttest $(git rev-parse new-branch1) = $(git rev-parse HEAD) &&\n \ttest \"$(git symbolic-ref -q HEAD)\" = \"refs/heads/branch1\" &&\n-\t! test -d .git/rebase-merge\n+\t! test -d .git/sequencer\n '\n \n test_expect_success 'retain authorship' '\n-- \n1.5.6.3.391.ge45b\n"},{"id":"83683","messageId":"20080717130547.GA8421@leksak.fem-net","threadId":"14494","inReplyTo":"cover.1216233918.git.s-beyer@gmx.net","subject":"Re: Sequencer migration patches","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2008-07-17T13:05:47Z","receivedAt":"2008-07-17T13:05:47Z","isPatch":false,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"Hi,\n\nStephan Beyer wrote:\n> Rebasing 100 commits takes 10.1 seconds instead of only 4.8 seconds\n> on my test machine.\n\nBtw, the builtin-sequencer needs 1.7 seconds to pick that 100 commits\non my test machine.\n\nRegards,\n  Stephan\n\n-- \nStephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F\n"}]}