{"thread":{"id":"34108","subject":"rebase --autosquash does not handle fixup! of fixup!","startedAt":"2013-06-11T18:05:30Z","lastAt":"2013-06-28T14:20:51Z","messageCount":23,"participants":["Andrew Pimlott","Thomas Rast","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"220487","messageId":"20130611180530.GA18488@oinkpad.pimlott.net","threadId":"34108","inReplyTo":null,"subject":"rebase --autosquash does not handle fixup! of fixup!","fromName":"Andrew Pimlott","fromEmail":"andrew@pimlott.net","sentAt":"2013-06-11T18:05:30Z","receivedAt":"2013-06-11T18:05:30Z","isPatch":false,"sender":{"key":"andrew@pimlott.net","avatar":null},"body":"git rebase -i --autosquash does not handle a fixup! of a fixup!, such as\nthe history:\n\n    aaaaaaa fix nasty bug\n    ...\n    bbbbbbb fixup! fix nasty bug\n    ...\n    ccccccc fixup! fixup! fix nasty bug\n\n--autosquash produces:\n\n    pick aaaaaaa fix nasty bug\n    fixup bbbbbbb fixup! fix nasty bug\n    ...\n    pick ccccccc fixup! fixup! fix nasty bug\n\nThis defeats the workflow I was hoping to use:\n\n    git commit -m 'fix nasty bug'\n    ...\n    git commit --fixup :/nasty\n    ...\n    git commit --fixup :/nasty\n\nThe second :/nasty resolves to the previous fixup, not the initial\ncommit.  I could have made the regular expression more precise, but this\nwould be a hassle.\n\nWould a change to support fixup! fixup! be considered?\n\nAndrew\n\n(Please Cc: me on replies.)\n"},{"id":"220491","messageId":"87obbc8otc.fsf@hexa.v.cablecom.net","threadId":"34108","inReplyTo":"20130611180530.GA18488@oinkpad.pimlott.net","subject":"Re: rebase --autosquash does not handle fixup! of fixup!","fromName":"Thomas Rast","fromEmail":"trast@inf.ethz.ch","sentAt":"2013-06-11T18:50:07Z","receivedAt":"2013-06-11T18:50:07Z","isPatch":false,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Andrew Pimlott <andrew@pimlott.net> writes:\n\n> git rebase -i --autosquash does not handle a fixup! of a fixup!, such as\n> the history:\n>\n>     aaaaaaa fix nasty bug\n>     ...\n>     bbbbbbb fixup! fix nasty bug\n>     ...\n>     ccccccc fixup! fixup! fix nasty bug\n>\n> --autosquash produces:\n>\n>     pick aaaaaaa fix nasty bug\n>     fixup bbbbbbb fixup! fix nasty bug\n>     ...\n>     pick ccccccc fixup! fixup! fix nasty bug\n>\n> This defeats the workflow I was hoping to use:\n>\n>     git commit -m 'fix nasty bug'\n>     ...\n>     git commit --fixup :/nasty\n>     ...\n>     git commit --fixup :/nasty\n>\n> The second :/nasty resolves to the previous fixup, not the initial\n> commit.  I could have made the regular expression more precise, but this\n> would be a hassle.\n>\n> Would a change to support fixup! fixup! be considered?\n\nSure, why not.  You could start with something like the patch below\n(untested).  If that happens to work, just add a test and a good commit\nmessage.\n\n\ndiff --git i/git-rebase--interactive.sh w/git-rebase--interactive.sh\nindex f953d8d..798ae81 100644\n--- i/git-rebase--interactive.sh\n+++ w/git-rebase--interactive.sh\n@@ -689,7 +689,17 @@ rearrange_squash () {\n \t\tcase \"$message\" in\n \t\t\"squash! \"*|\"fixup! \"*)\n \t\t\taction=\"${message%%!*}\"\n-\t\t\trest=\"${message#*! }\"\n+\t\t\trest=$message\n+\t\t\twhile : ; do\n+\t\t\t\tcase \"$rest\" in\n+\t\t\t\t\"squash! \"*|\"fixup! \"*)\n+\t\t\t\t\trest=\"${rest#*! }\"\n+\t\t\t\t\t;;\n+\t\t\t\t*)\n+\t\t\t\t\tbreak\n+\t\t\t\t\t;;\n+\t\t\t\tesac\n+\t\t\tdone\n \t\t\techo \"$sha1 $action $rest\"\n \t\t\t# if it's a single word, try to resolve to a full sha1 and\n \t\t\t# emit a second copy. This allows us to match on both message\n\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"220907","messageId":"1371237209-sup-639@pimlott.net","threadId":"34108","inReplyTo":"87obbc8otc.fsf@hexa.v.cablecom.net","subject":"[PATCH] rebase -i: fixup fixup! fixup!","fromName":"Andrew Pimlott","fromEmail":"andrew@pimlott.net","sentAt":"2013-06-14T19:31:57Z","receivedAt":"2013-06-14T19:31:57Z","isPatch":true,"sender":{"key":"andrew@pimlott.net","avatar":null},"body":"Excerpts from Thomas Rast's message of Tue Jun 11 11:50:07 -0700 2013:\n> Andrew Pimlott <andrew@pimlott.net> writes:\n> >     git commit -m 'fix nasty bug'\n> >     ...\n> >     git commit --fixup :/nasty\n> >     ...\n> >     git commit --fixup :/nasty\n> >\n> > The second :/nasty resolves to the previous fixup, not the initial\n> > commit.  I could have made the regular expression more precise, but this\n> > would be a hassle.\n> >\n> > Would a change to support fixup! fixup! be considered?\n> \n> Sure, why not.  You could start with something like the patch below\n> (untested).  If that happens to work, just add a test and a good commit\n> message.\n\nIt happened to work and I added a test.  But then it occurred to me that\nit might have been better to fix commit --fixup/--squash to strip the\nfixup! or squash! from the referenced commit in the first place.\nAnyhow, below is my patch for --autosquash, but unles someone has an\nobjection to doing it in commit, I'll work on that.\n\nAndrew\n\nIgnore subsequent \"fixup! \" or \"squash! \" after the first.  Handy in case a\ngit commit --fixup/--squash referred to a previous fixup/squash instead of\nthe original commit, for example with :/msg.\n\nSigned-off-by: Andrew Pimlott <andrew@pimlott.net>\n---\n Documentation/git-rebase.txt |    4 +++-\n git-rebase--interactive.sh   |   13 ++++++++++-\n t/t3415-rebase-autosquash.sh |   49 ++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 64 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex c84854a..725cf27 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -389,7 +389,9 @@ squash/fixup series.\n \tthe same ..., automatically modify the todo list of rebase -i\n \tso that the commit marked for squashing comes right after the\n \tcommit to be modified, and change the action of the moved\n-\tcommit from `pick` to `squash` (or `fixup`).\n+\tcommit from `pick` to `squash` (or `fixup`).  Ignores subsequent\n+\t\"fixup! \" or \"squash! \" after the first, in case you referred to a\n+\tprevious fixup/squash with `git commit --fixup/--squash`.\n +\n This option is only valid when the '--interactive' option is used.\n +\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex f953d8d..54ed4c3 100644\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -689,7 +689,18 @@ rearrange_squash () {\n \t\tcase \"$message\" in\n \t\t\"squash! \"*|\"fixup! \"*)\n \t\t\taction=\"${message%%!*}\"\n-\t\t\trest=\"${message#*! }\"\n+\t\t\trest=$message\n+\t\t\t# ignore any squash! or fixup! after the first\n+\t\t\twhile : ; do\n+\t\t\t\tcase \"$rest\" in\n+\t\t\t\t\"squash! \"*|\"fixup! \"*)\n+\t\t\t\t\trest=\"${rest#*! }\"\n+\t\t\t\t\t;;\n+\t\t\t\t*)\n+\t\t\t\t\tbreak\n+\t\t\t\t\t;;\n+\t\t\t\tesac\n+\t\t\tdone\n \t\t\techo \"$sha1 $action $rest\"\n \t\t\t# if it's a single word, try to resolve to a full sha1 and\n \t\t\t# emit a second copy. This allows us to match on both message\ndiff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh\nindex a1e86c4..1a3f40a 100755\n--- a/t/t3415-rebase-autosquash.sh\n+++ b/t/t3415-rebase-autosquash.sh\n@@ -193,4 +193,53 @@ test_expect_success 'use commit --squash' '\n \ttest_auto_commit_flags squash 2\n '\n \n+test_auto_fixup_fixup () {\n+\tgit reset --hard base &&\n+\techo 1 >file1 &&\n+\tgit add -u &&\n+\ttest_tick &&\n+\tgit commit -m \"$1! first\" &&\n+\techo 2 >file1 &&\n+\tgit add -u &&\n+\ttest_tick &&\n+\tgit commit -m \"$1! $2! first\" &&\n+\tgit tag \"final-$1-$2\" &&\n+\ttest_tick &&\n+\tgit rebase --autosquash -i HEAD^^^^ &&\n+\tgit log --oneline >actual &&\n+\ttest_pause &&\n+\tif [ \"$1\" = \"fixup\" ]; then\n+\t\ttest_line_count = 3 actual\n+\telif [ \"$1\" = \"squash\" ]; then\n+\t\ttest_line_count = 4 actual\n+\telse\n+\t\tfalse\n+\tfi &&\n+\tgit diff --exit-code \"final-$1-$2\" &&\n+\ttest 2 = \"$(git cat-file blob HEAD^:file1)\" &&\n+\tif [ \"$1\" = \"fixup\" ]; then\n+\t\ttest 1 = $(git cat-file commit HEAD^ | grep first | wc -l)\n+\telif [ \"$1\" = \"squash\" ]; then\n+\t\ttest 3 = $(git cat-file commit HEAD^ | grep first | wc -l)\n+\telse\n+\t\tfalse\n+\tfi\n+}\n+\n+test_expect_success 'fixup! fixup!' '\n+\ttest_auto_fixup_fixup fixup fixup\n+'\n+\n+test_expect_success 'fixup! squash!' '\n+\ttest_auto_fixup_fixup fixup squash\n+'\n+\n+test_expect_success 'squash! squash!' '\n+\ttest_auto_fixup_fixup squash squash\n+'\n+\n+test_expect_success 'squash! fixup!' '\n+\ttest_auto_fixup_fixup squash fixup\n+'\n+\n test_done\n-- \n1.7.10.4\n"},{"id":"220927","messageId":"1371278908-sup-1930@pimlott.net","threadId":"34108","inReplyTo":"1371237209-sup-639@pimlott.net","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Andrew Pimlott","fromEmail":"andrew@pimlott.net","sentAt":"2013-06-15T06:50:21Z","receivedAt":"2013-06-15T06:50:21Z","isPatch":true,"sender":{"key":"andrew@pimlott.net","avatar":null},"body":"Excerpts from Andrew Pimlott's message of Fri Jun 14 12:31:57 -0700 2013:\n> It happened to work and I added a test.  But then it occurred to me that\n> it might have been better to fix commit --fixup/--squash to strip the\n> fixup! or squash! from the referenced commit in the first place.\n> Anyhow, below is my patch for --autosquash, but unles someone has an\n> objection to doing it in commit, I'll work on that.\n\nHere is a patch for commit.c that does this.\n\nAndrew\n\nWhen building the commit message for --fixup/--squash, ignore a leading\nfixup! or squash! on the referenced commit.  Handy in case the user referred\nto an earlier squash/fixup commit instead of the original commit, for\nexample with :/msg.\n\nSigned-off-by: Andrew Pimlott <andrew@pimlott.net>\n---\n builtin/commit.c  |   18 ++++++++++++++----\n t/t7500-commit.sh |   36 ++++++++++++++++++++++++++++++++++++\n 2 files changed, 50 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 1621dfc..370df88 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -601,8 +601,13 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\tif (!c)\n \t\t\t\tdie(_(\"could not lookup commit %s\"), squash_message);\n \t\t\tctx.output_encoding = get_commit_output_encoding();\n-\t\t\tformat_commit_message(c, \"squash! %s\\n\\n\", &sb,\n-\t\t\t\t\t      &ctx);\n+\t\t\tformat_commit_message(c, \"%s\\n\\n\", &sb, &ctx);\n+\t\t\tif (!prefixcmp(sb.buf, \"fixup! \")) {\n+\t\t\t\tstrbuf_remove(&sb, 0, strlen(\"fixup! \"));\n+\t\t\t} else if (!prefixcmp(sb.buf, \"squash! \")) {\n+\t\t\t\tstrbuf_remove(&sb, 0, strlen(\"squash! \"));\n+\t\t\t}\n+\t\t\tstrbuf_insert(&sb, 0, \"squash! \", strlen(\"squash! \"));\n \t\t}\n \t}\n \n@@ -634,8 +639,13 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\tif (!commit)\n \t\t\tdie(_(\"could not lookup commit %s\"), fixup_message);\n \t\tctx.output_encoding = get_commit_output_encoding();\n-\t\tformat_commit_message(commit, \"fixup! %s\\n\\n\",\n-\t\t\t\t      &sb, &ctx);\n+\t\tformat_commit_message(commit, \"%s\\n\\n\", &sb, &ctx);\n+\t\tif (!prefixcmp(sb.buf, \"fixup! \")) {\n+\t\t\tstrbuf_remove(&sb, 0, strlen(\"fixup! \"));\n+\t\t} else if (!prefixcmp(sb.buf, \"squash! \")) {\n+\t\t\tstrbuf_remove(&sb, 0, strlen(\"squash! \"));\n+\t\t}\n+\t\tstrbuf_insert(&sb, 0, \"fixup! \", strlen(\"fixup! \"));\n \t\thook_arg1 = \"message\";\n \t} else if (!stat(git_path(\"MERGE_MSG\"), &statbuf)) {\n \t\tif (strbuf_read_file(&sb, git_path(\"MERGE_MSG\"), 0) < 0)\ndiff --git a/t/t7500-commit.sh b/t/t7500-commit.sh\nindex 436b7b6..ecdf74a 100755\n--- a/t/t7500-commit.sh\n+++ b/t/t7500-commit.sh\n@@ -320,4 +320,40 @@ test_expect_success 'invalid message options when using --fixup' '\n \ttest_must_fail git commit --fixup HEAD~1 -F log\n '\n \n+test_expect_success 'commit --fixup of existing fixup' '\n+\tcommit_for_rebase_autosquash_setup &&\n+\tgit commit --fixup HEAD~1 &&\n+\techo \"fourth content line\" >>foo &&\n+\tgit add foo\n+\tgit commit --fixup HEAD &&\n+\tcommit_msg_is \"fixup! target message subject line\"\n+'\n+\n+test_expect_success 'commit --fixup of existing squash' '\n+\tcommit_for_rebase_autosquash_setup &&\n+\tgit commit --squash HEAD~1 &&\n+\techo \"fourth content line\" >>foo &&\n+\tgit add foo\n+\tgit commit --fixup HEAD &&\n+\tcommit_msg_is \"fixup! target message subject line\"\n+'\n+\n+test_expect_success 'commit --squash of existing squash' '\n+\tcommit_for_rebase_autosquash_setup &&\n+\tgit commit --squash HEAD~1 &&\n+\techo \"fourth content line\" >>foo &&\n+\tgit add foo\n+\tgit commit --squash HEAD &&\n+\tcommit_msg_is \"squash! target message subject linecommit message\"\n+'\n+\n+test_expect_success 'commit --squash of existing fixup' '\n+\tcommit_for_rebase_autosquash_setup &&\n+\tgit commit --fixup HEAD~1 &&\n+\techo \"fourth content line\" >>foo &&\n+\tgit add foo\n+\tgit commit --squash HEAD &&\n+\tcommit_msg_is \"squash! target message subject linecommit message\"\n+'\n+\n test_done\n-- \n1.7.10.4\n"},{"id":"220932","messageId":"7vk3lvlmat.fsf@alter.siamese.dyndns.org","threadId":"34108","inReplyTo":"1371278908-sup-1930@pimlott.net","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-15T10:07:22Z","receivedAt":"2013-06-15T10:07:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andrew Pimlott <andrew@pimlott.net> writes:\n\n> Excerpts from Andrew Pimlott's message of Fri Jun 14 12:31:57 -0700 2013:\n>> It happened to work and I added a test.  But then it occurred to me that\n>> it might have been better to fix commit --fixup/--squash to strip the\n>> fixup! or squash! from the referenced commit in the first place.\n>> Anyhow, below is my patch for --autosquash, but unles someone has an\n>> objection to doing it in commit, I'll work on that.\n\nIs it always true that you would squash and fixup in the same order\nas these follow-up commits happened?\n\nThat is, if you did this (time flows from top to bottom):\n\n\t1 A\n        2 B\n        3 fixup A\n        4 squash B\n        5 fixup fixup A\n        6 fixup A\n\nI am wondering if applying 6 on top of 5 is always what you want, or\nyou would want to apply it to 3 instead.  Otherwise you would have\nwritten\n\n\t6 fixup fixup fixup A\n\ninstead.\n\nThe two reordering possibilities are:\n\n        1 A                        1 A             \n        3 fixup A                  3 fixup A       \n        5 fixup fixup A            6 fixup A       \n        6 fixup A                  5 fixup fixup A\n        2 B                        2 B             \n        4 squash B                 4 squash B      \n\nIf you strip out the prefix when you make commits, you may lose the\ninformation if you want to use in order to express these different\norders.  I am not sure if it matters in practice, but I am not yet\nconvinced it is a good idea.\n\nBy the way, the message I am responding to is not something we can\napply. I am assuming these paches are for discussion-only; before\nsending the final one, please check Documentation/SubmittingPatches.\n\nThanks.\n"},{"id":"220970","messageId":"7vy5aakg1y.fsf@alter.siamese.dyndns.org","threadId":"34108","inReplyTo":"7vk3lvlmat.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-16T01:19:53Z","receivedAt":"2013-06-16T01:19:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Andrew Pimlott <andrew@pimlott.net> writes:\n>\n>> Excerpts from Andrew Pimlott's message of Fri Jun 14 12:31:57 -0700 2013:\n>>> It happened to work and I added a test.  But then it occurred to me that\n>>> it might have been better to fix commit --fixup/--squash to strip the\n>>> fixup! or squash! from the referenced commit in the first place.\n>>> Anyhow, below is my patch for --autosquash, but unles someone has an\n>>> objection to doing it in commit, I'll work on that.\n> ...\n> If you strip out the prefix when you make commits, you may lose the\n> information if you want to use in order to express these different\n> orders.\n\nOne design principle I would use as a yardstick is to see any code\nthat deliberately lose information to achieve something as highly\nsuspicious.  You can discard extra information when you read and\nuse, if you do not need it, but if you do not record it in the first\nplace, you cannot later enhance the reader to take advantage of it.\n\nIn general, whenever you see yourself _discarding_ information to\nsolve an issue, you should carefully ask yourself if that is the\nright solution.\n\nI wish we can make sure contributors can learn various design\nprinciples we have benefited from over the course of this project\nmuch better.\n\nBut it is a bit difficult to _teach_ others.\n\nWriting them down is difficult, not because the rules are vague, but\nbecause they are like air.  I am sure regular contributors with good\ndesign taste share this sentiment.\n\nYou will know a violation of them when you see one, you naturally\nstick to them yourself without even having to think about them, but\nenumerating them without seeing concrete issues takes effort.\n\nAnd this \"lets squash multiple --fixup/--squash\" happened to realize\nthat \"we try not to deliberately lose information\" is one of them.\n\nThanks.\n"},{"id":"220996","messageId":"87ip1e2tzx.fsf@hexa.v.cablecom.net","threadId":"34108","inReplyTo":"7vk3lvlmat.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Thomas Rast","fromEmail":"trast@inf.ethz.ch","sentAt":"2013-06-16T11:08:18Z","receivedAt":"2013-06-16T11:08:18Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Andrew Pimlott <andrew@pimlott.net> writes:\n>\n>> Excerpts from Andrew Pimlott's message of Fri Jun 14 12:31:57 -0700 2013:\n>>> It happened to work and I added a test.  But then it occurred to me that\n>>> it might have been better to fix commit --fixup/--squash to strip the\n>>> fixup! or squash! from the referenced commit in the first place.\n>>> Anyhow, below is my patch for --autosquash, but unles someone has an\n>>> objection to doing it in commit, I'll work on that.\n>\n> Is it always true that you would squash and fixup in the same order\n> as these follow-up commits happened?\n>\n> That is, if you did this (time flows from top to bottom):\n>\n> \t1 A\n>         2 B\n>         3 fixup A\n>         4 squash B\n>         5 fixup fixup A\n>         6 fixup A\n>\n> I am wondering if applying 6 on top of 5 is always what you want, or\n> you would want to apply it to 3 instead.  Otherwise you would have\n> written\n>\n> \t6 fixup fixup fixup A\n>\n> instead.\n>\n> The two reordering possibilities are:\n>\n>         1 A                        1 A             \n>         3 fixup A                  3 fixup A       \n>         5 fixup fixup A            6 fixup A       \n>         6 fixup A                  5 fixup fixup A\n>         2 B                        2 B             \n>         4 squash B                 4 squash B      \n>\n> If you strip out the prefix when you make commits, you may lose the\n> information if you want to use in order to express these different\n> orders.  I am not sure if it matters in practice, but I am not yet\n> convinced it is a good idea.\n\nIsn't it a bit of an academic question?\n\nAll 'fixup* A' are clearly intended to be squashed into A eventually.\nYou could reorder them, but unless you arranged your fixups as nonlinear\nhistory (does anyone do that?) they have been built sequentially.  So at\nbest the extra reordering does not buy you anything, because you're\ngoing to fix up A anyways.  You may even get extra conflicts during the\nreordering, which make the process less automatic and more error-prone.\n\n  [If you did actually arrange things nonlinearly, so that you have\n\n    * A\n    |\\\n    | * fixup A\n    | |\n    * | fixup A\n    |/\n    * M  (you need M so that you can rebase both fixups simultaneously)\n\n  then you might actually use the number of 'fixup' prefixes to determine\n  their order.  However, if you actually generate such history, you have\n  to go out of your way to look at the other branches too, and make sure\n  that the number of prefixes is sufficiently unique to disambiguate the\n  order as far as you want it to do that, etc.  It sounds like too much of\n  a headache to be worth using like that.]\n\nAnd once you have that, it seems a nicer and cleaner idea to generate\n'fixup! A' each time, instead of a successive sequence of\n\n  fixup! A\n  fixup! fixup! A\n  fixup! fixup! fixup! A\n  ...\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"221023","messageId":"7v7ghtjwbb.fsf@alter.siamese.dyndns.org","threadId":"34108","inReplyTo":"87ip1e2tzx.fsf@hexa.v.cablecom.net","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-17T02:38:32Z","receivedAt":"2013-06-17T02:38:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@inf.ethz.ch> writes:\n\n> Isn't it a bit of an academic question?\n> ...\n> And once you have that, it seems a nicer and cleaner idea to generate\n> 'fixup! A' each time, instead of a successive sequence of\n>\n>   fixup! A\n>   fixup! fixup! A\n>   fixup! fixup! fixup! A\n>   ...\n\nAs to reordering, you are absolutely correct.\n\nIf you are going to apply all three anyway, then the end result\neither does not change at all (when none of them overlap textually),\nor you will end up with unnecessary conflicts (when they do).\n\nBut if you were to pick (and drop some), all three labeled with\n\"fixup A\" vs later ones having more \"fixup\" in front will make a\ndifference in identification and usability.  When you want to drop\nthe second fixup, \"fixup fixup A\" can be chosen unambiguously in\nyour editor among \"fixup A\", \"fixup fixup A\" and \"fixup fixup fixup\nA\".\n\nIt also somewhat feels wrong when the user sees this:\n\n    $ git log --oneline -2\n    xxxx A\n    yyyy fixup! A\n\nand asks to do this:\n\n    $ git commit --fixup yyyy\n\nand if you end up with \"fixup! A\", not \"fixup! fixup! A\".  The user\nis asking to follow-up on the \"fixup! A\", not on the original \"A\".\n\nDoes dropping these leading \"fixup!\" (or \"squash!\") at commit time\nmake the application in \"rebase -i --autosquash\" significantly\neasier to do?\n"},{"id":"221039","messageId":"8738shi2ht.fsf@linux-k42r.v.cablecom.net","threadId":"34108","inReplyTo":"7v7ghtjwbb.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Thomas Rast","fromEmail":"trast@inf.ethz.ch","sentAt":"2013-06-17T08:07:58Z","receivedAt":"2013-06-17T08:07:58Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Thomas Rast <trast@inf.ethz.ch> writes:\n>\n>> Isn't it a bit of an academic question?\n>> ...\n>> And once you have that, it seems a nicer and cleaner idea to generate\n>> 'fixup! A' each time, instead of a successive sequence of\n>>\n>>   fixup! A\n>>   fixup! fixup! A\n>>   fixup! fixup! fixup! A\n>>   ...\n>\n> As to reordering, you are absolutely correct.\n[...]\n> Does dropping these leading \"fixup!\" (or \"squash!\") at commit time\n> make the application in \"rebase -i --autosquash\" significantly\n> easier to do?\n\nConveniently enough we have seen both already ;-)  Andrew's version for\ncommit.c could use a bit of refactorization, since it inserts the same\ncode in two places, but then it's about the same complexity as the\nchange for rebase.\n\nI'm not sure it's worth arguing about whether the \"fixup! fixup!\"  is a\nsymptom of some underlying problem, and changing rebase is only tapering\nover the symptom; or whether it's actually a useful distinction.  Either\none works fine as a fix for an annoyance that Andrew had, and that bit\nme in the past too.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"221062","messageId":"7vwqpshkxj.fsf@alter.siamese.dyndns.org","threadId":"34108","inReplyTo":"8738shi2ht.fsf@linux-k42r.v.cablecom.net","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-17T14:27:20Z","receivedAt":"2013-06-17T14:27:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@inf.ethz.ch> writes:\n\n> Conveniently enough we have seen both already ;-)  Andrew's version for\n> commit.c could use a bit of refactorization, since it inserts the same\n> code in two places, but then it's about the same complexity as the\n> change for rebase.\n>\n> I'm not sure it's worth arguing about whether the \"fixup! fixup!\"  is a\n> symptom of some underlying problem, and changing rebase is only tapering\n> over the symptom; or whether it's actually a useful distinction.\n\nIf they are about the same complexity, then my instict tells me that\nit is a better design not to strip on the writing side.\n\nThanks.\n"},{"id":"221968","messageId":"1372190294-sup-1398@pimlott.net","threadId":"34108","inReplyTo":"7vwqpshkxj.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Andrew Pimlott","fromEmail":"andrew@pimlott.net","sentAt":"2013-06-25T20:41:48Z","receivedAt":"2013-06-25T20:41:48Z","isPatch":true,"sender":{"key":"andrew@pimlott.net","avatar":null},"body":"Excerpts from Junio C Hamano's message of Mon Jun 17 07:27:20 -0700 2013:\n> Thomas Rast <trast@inf.ethz.ch> writes:\n> > I'm not sure it's worth arguing about whether the \"fixup! fixup!\"  is a\n> > symptom of some underlying problem, and changing rebase is only tapering\n> > over the symptom; or whether it's actually a useful distinction.\n> \n> If they are about the same complexity, then my instict tells me that\n> it is a better design not to strip on the writing side.\n\nThank you for the discussion.  Sorry I have joined recently.\n\nI agree that it is better to preserve information as long as feasible.\nIf we are going to strip it, it may as well be later.  That is Thomas's\nrearrange_squash patch, which I will send again.\n\nThe next question is, do we go all the way and respect the nested\nfixup!s in rearrange_squash?  I understand the case for it, though it's\nhardly compelling to me in practice. :-)  That would be more complicated\nthan Thomas's patch.  But I'm happy to try it if someone gives me a\nnudge.  If not, at least the information is preserved in case someone\nwants to do this later.\n\nRegarding patches, I tried to follow the SubmittingPatches guidelines,\nbut I was confused about how to include a commit in an existing thread.\nI think I was mislead by git-format-patch(1), \"When a patch is part of\nan ongoing discussion...\", which says to remove most header fields.\n\nSo if I don't want to break the discussion, should I append the unedited\nformat-patch output to my message after \"scissors\", or should I send it\nas a whole new message with --in-reply-to?  Or something else?  I'll try\nthe first.\n\nAndrew\n\n---8<---\nFrom 99023bff23f18a341441d6b7c447d9630a11b489 Mon Sep 17 00:00:00 2001\nFrom: Andrew Pimlott <andrew@pimlott.net>\nDate: Fri, 14 Jun 2013 10:33:16 -0700\nSubject: [PATCH 1/4] rebase -i: handle fixup! fixup! in --autosquash\n\nIn rebase -i --autosquash, ignore all \"fixup! \" or \"squash! \" after the\nfirst.  Handy in case a git commit --fixup/--squash referred to an earlier\nfixup/squash instead of the original commit, for example with :/msg.\n\nSigned-off-by: Andrew Pimlott <andrew@pimlott.net>\n---\n Documentation/git-rebase.txt |    4 +++-\n git-rebase--interactive.sh   |   13 ++++++++++-\n t/t3415-rebase-autosquash.sh |   49 ++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 64 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex c84854a..6b2e1c8 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -389,7 +389,9 @@ squash/fixup series.\n \tthe same ..., automatically modify the todo list of rebase -i\n \tso that the commit marked for squashing comes right after the\n \tcommit to be modified, and change the action of the moved\n-\tcommit from `pick` to `squash` (or `fixup`).\n+\tcommit from `pick` to `squash` (or `fixup`).  Ignores subsequent\n+\t\"fixup! \" or \"squash! \" after the first, in case you referred to an\n+\tearlier fixup/squash with `git commit --fixup/--squash`.\n +\n This option is only valid when the '--interactive' option is used.\n +\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex f953d8d..54ed4c3 100644\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -689,7 +689,18 @@ rearrange_squash () {\n \t\tcase \"$message\" in\n \t\t\"squash! \"*|\"fixup! \"*)\n \t\t\taction=\"${message%%!*}\"\n-\t\t\trest=\"${message#*! }\"\n+\t\t\trest=$message\n+\t\t\t# ignore any squash! or fixup! after the first\n+\t\t\twhile : ; do\n+\t\t\t\tcase \"$rest\" in\n+\t\t\t\t\"squash! \"*|\"fixup! \"*)\n+\t\t\t\t\trest=\"${rest#*! }\"\n+\t\t\t\t\t;;\n+\t\t\t\t*)\n+\t\t\t\t\tbreak\n+\t\t\t\t\t;;\n+\t\t\t\tesac\n+\t\t\tdone\n \t\t\techo \"$sha1 $action $rest\"\n \t\t\t# if it's a single word, try to resolve to a full sha1 and\n \t\t\t# emit a second copy. This allows us to match on both message\ndiff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh\nindex a1e86c4..1a3f40a 100755\n--- a/t/t3415-rebase-autosquash.sh\n+++ b/t/t3415-rebase-autosquash.sh\n@@ -193,4 +193,53 @@ test_expect_success 'use commit --squash' '\n \ttest_auto_commit_flags squash 2\n '\n \n+test_auto_fixup_fixup () {\n+\tgit reset --hard base &&\n+\techo 1 >file1 &&\n+\tgit add -u &&\n+\ttest_tick &&\n+\tgit commit -m \"$1! first\" &&\n+\techo 2 >file1 &&\n+\tgit add -u &&\n+\ttest_tick &&\n+\tgit commit -m \"$1! $2! first\" &&\n+\tgit tag \"final-$1-$2\" &&\n+\ttest_tick &&\n+\tgit rebase --autosquash -i HEAD^^^^ &&\n+\tgit log --oneline >actual &&\n+\ttest_pause &&\n+\tif [ \"$1\" = \"fixup\" ]; then\n+\t\ttest_line_count = 3 actual\n+\telif [ \"$1\" = \"squash\" ]; then\n+\t\ttest_line_count = 4 actual\n+\telse\n+\t\tfalse\n+\tfi &&\n+\tgit diff --exit-code \"final-$1-$2\" &&\n+\ttest 2 = \"$(git cat-file blob HEAD^:file1)\" &&\n+\tif [ \"$1\" = \"fixup\" ]; then\n+\t\ttest 1 = $(git cat-file commit HEAD^ | grep first | wc -l)\n+\telif [ \"$1\" = \"squash\" ]; then\n+\t\ttest 3 = $(git cat-file commit HEAD^ | grep first | wc -l)\n+\telse\n+\t\tfalse\n+\tfi\n+}\n+\n+test_expect_success 'fixup! fixup!' '\n+\ttest_auto_fixup_fixup fixup fixup\n+'\n+\n+test_expect_success 'fixup! squash!' '\n+\ttest_auto_fixup_fixup fixup squash\n+'\n+\n+test_expect_success 'squash! squash!' '\n+\ttest_auto_fixup_fixup squash squash\n+'\n+\n+test_expect_success 'squash! fixup!' '\n+\ttest_auto_fixup_fixup squash fixup\n+'\n+\n test_done\n-- \n1.7.10.4\n"},{"id":"221972","messageId":"7vehbp27vl.fsf@alter.siamese.dyndns.org","threadId":"34108","inReplyTo":"1372190294-sup-1398@pimlott.net","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-25T21:33:18Z","receivedAt":"2013-06-25T21:33:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andrew Pimlott <andrew@pimlott.net> writes:\n\nJust reponding for the \"procedual\" part for now.\n\n> So if I don't want to break the discussion, should I append the unedited\n> format-patch output to my message after \"scissors\", or should I send it\n> as a whole new message with --in-reply-to?  Or something else?  I'll try\n> the first.\n\nWhich is fine, and you are almost there, but you do not want\n\n (1) \"From 99023b...\" that is not part of the message (it is a\n     delimiter between multiple patches when/in case a file contains\n     more than one);\n\n (2) \"From: Andrew...\" that is the same as the e-mail header in the\n     message I am responding to;\n\n (3) \"Date: ...\" which is older than the e-mail header in the\n     message I am responding to---the latter is the date people\n     actually saw this patch on the mailing list, so it is\n     preferrable to use it than the timestamp in your repository.\n\nSo in this case, I'd expect to see, after the \"-- >8 --\" line, only\n\"Subject: \" line, a blank and the log message.\n\n>\n> ---8<---\n> From 99023bff23f18a341441d6b7c447d9630a11b489 Mon Sep 17 00:00:00 2001\n> From: Andrew Pimlott <andrew@pimlott.net>\n> Date: Fri, 14 Jun 2013 10:33:16 -0700\n> Subject: [PATCH 1/4] rebase -i: handle fixup! fixup! in --autosquash\n>\n> In rebase -i --autosquash, ignore all \"fixup! \" or \"squash! \" after the\n"},{"id":"221980","messageId":"7va9md27qw.fsf@alter.siamese.dyndns.org","threadId":"34108","inReplyTo":"1372190294-sup-1398@pimlott.net","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-25T21:36:07Z","receivedAt":"2013-06-25T21:36:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andrew Pimlott <andrew@pimlott.net> writes:\n\n> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\n> index c84854a..6b2e1c8 100644\n> --- a/Documentation/git-rebase.txt\n> +++ b/Documentation/git-rebase.txt\n> @@ -389,7 +389,9 @@ squash/fixup series.\n>  \tthe same ..., automatically modify the todo list of rebase -i\n>  \tso that the commit marked for squashing comes right after the\n>  \tcommit to be modified, and change the action of the moved\n> -\tcommit from `pick` to `squash` (or `fixup`).\n> +\tcommit from `pick` to `squash` (or `fixup`).  Ignores subsequent\n> +\t\"fixup! \" or \"squash! \" after the first, in case you referred to an\n> +\tearlier fixup/squash with `git commit --fixup/--squash`.\n>  +\n>  This option is only valid when the '--interactive' option is used.\n>  +\n> diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\n> index f953d8d..54ed4c3 100644\n> --- a/git-rebase--interactive.sh\n> +++ b/git-rebase--interactive.sh\n> @@ -689,7 +689,18 @@ rearrange_squash () {\n>  \t\tcase \"$message\" in\n>  \t\t\"squash! \"*|\"fixup! \"*)\n>  \t\t\taction=\"${message%%!*}\"\n> -\t\t\trest=\"${message#*! }\"\n> +\t\t\trest=$message\n> +\t\t\t# ignore any squash! or fixup! after the first\n> +\t\t\twhile : ; do\n\nStyle:\n\n\twhile :\n        do\n\n> +\t\t\t\tcase \"$rest\" in\n> +\t\t\t\t\"squash! \"*|\"fixup! \"*)\n> +\t\t\t\t\trest=\"${rest#*! }\"\n> +\t\t\t\t\t;;\n> +\t\t\t\t*)\n> +\t\t\t\t\tbreak\n> +\t\t\t\t\t;;\n> +\t\t\t\tesac\n> +\t\t\tdone\n>  \t\t\techo \"$sha1 $action $rest\"\n>  \t\t\t# if it's a single word, try to resolve to a full sha1 and\n>  \t\t\t# emit a second copy. This allows us to match on both message\n> diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh\n> index a1e86c4..1a3f40a 100755\n> --- a/t/t3415-rebase-autosquash.sh\n> +++ b/t/t3415-rebase-autosquash.sh\n> @@ -193,4 +193,53 @@ test_expect_success 'use commit --squash' '\n>  \ttest_auto_commit_flags squash 2\n>  '\n>  \n> +test_auto_fixup_fixup () {\n> +\tgit reset --hard base &&\n> +\techo 1 >file1 &&\n> +\tgit add -u &&\n> +\ttest_tick &&\n> +\tgit commit -m \"$1! first\" &&\n> +\techo 2 >file1 &&\n> +\tgit add -u &&\n> +\ttest_tick &&\n> +\tgit commit -m \"$1! $2! first\" &&\n> +\tgit tag \"final-$1-$2\" &&\n> +\ttest_tick &&\n> +\tgit rebase --autosquash -i HEAD^^^^ &&\n> +\tgit log --oneline >actual &&\n> +\ttest_pause &&\n\nThis patch obviously hasn't been tested.  It breaks without -v.\n\n> +\tif [ \"$1\" = \"fixup\" ]; then\n> +\t\ttest_line_count = 3 actual\n> +\telif [ \"$1\" = \"squash\" ]; then\n> +\t\ttest_line_count = 4 actual\n> +\telse\n> +\t\tfalse\n> +\tfi &&\n\nStyle\n\n\tif test \"$1\" = \"fixup\"\n\tthen\n        \t...\n\telif test \"$1\" = \"squash\"\n\tthen\n\t\t...\n\n(you got the idea).\n\n> +\tgit diff --exit-code \"final-$1-$2\" &&\n> +\ttest 2 = \"$(git cat-file blob HEAD^:file1)\" &&\n> +\tif [ \"$1\" = \"fixup\" ]; then\n> +\t\ttest 1 = $(git cat-file commit HEAD^ | grep first | wc -l)\n> +\telif [ \"$1\" = \"squash\" ]; then\n> +\t\ttest 3 = $(git cat-file commit HEAD^ | grep first | wc -l)\n> +\telse\n> +\t\tfalse\n> +\tfi\n> +}\n> +\n> +test_expect_success 'fixup! fixup!' '\n> +\ttest_auto_fixup_fixup fixup fixup\n> +'\n> +\n> +test_expect_success 'fixup! squash!' '\n> +\ttest_auto_fixup_fixup fixup squash\n> +'\n> +\n> +test_expect_success 'squash! squash!' '\n> +\ttest_auto_fixup_fixup squash squash\n> +'\n\nThis does not seem to pass for me.\n\n> +test_expect_success 'squash! fixup!' '\n> +\ttest_auto_fixup_fixup squash fixup\n> +'\n> +\n>  test_done\n"},{"id":"221981","messageId":"7v61x127bw.fsf@alter.siamese.dyndns.org","threadId":"34108","inReplyTo":"1372190294-sup-1398@pimlott.net","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-25T21:45:07Z","receivedAt":"2013-06-25T21:45:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andrew Pimlott <andrew@pimlott.net> writes:\n\n> I agree that it is better to preserve information as long as feasible.\n> If we are going to strip it, it may as well be later.  That is Thomas's\n> rearrange_squash patch, which I will send again.\n\nThanks.\n\n> The next question is, do we go all the way and respect the nested\n> fixup!s in rearrange_squash?  I understand the case for it, though it's\n> hardly compelling to me in practice. :-)  That would be more complicated\n> than Thomas's patch.  But I'm happy to try it if someone gives me a\n> nudge.  If not, at least the information is preserved in case someone\n> wants to do this later.\n\nI think it is fine not to be too smart, as long as we do not lose\ninformation that would help the user to compensate.\n\nAfter all, autosquash will give the user an opportunity to eyeball\nthe result of automatic rearrangement.  If the user did this:\n\n\tgit commit -m original\n        git commit --fixup original ;# obviously fixing the first one\n        git commit --fixup '!fixup original' ;# explicitly fixing the second\n\tgit commit --fixup original ;# may want to fix the first one\n\nand then \"git rebase --autosquash\" gave him this:\n\n\tpick d78c915 original\n        fixup 0c6388e original\n        fixup d15b556 !fixup original\n        fixup 1e39bcd original\n\nit may not be what the user originally intended, but I think it is\nOK.\n\nAs long as \"!fixup original\" message is kept in the buffer, the user\ncan notice and rearrange, e.g.\n\n\tpick d78c915 original\n        fixup 0c6388e original\n        fixup 1e39bcd original\n        fixup d15b556 !fixup original\n\nif the user really wants to.\n"},{"id":"221982","messageId":"7vy59xzw6s.fsf@alter.siamese.dyndns.org","threadId":"34108","inReplyTo":"7v61x127bw.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-25T22:01:47Z","receivedAt":"2013-06-25T22:01:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\nI guess I made typoes in the examples that made then unusable...\n\n> I think it is fine not to be too smart, as long as we do not lose\n> information that would help the user to compensate.\n>\n> After all, autosquash will give the user an opportunity to eyeball\n> the result of automatic rearrangement.  If the user did this:\n>\n> \tgit commit -m original\n>       git commit --fixup original ;# obviously fixing the first one\n>       git commit --fixup '!fixup original' ;# explicitly fixing the second\n> \tgit commit --fixup original ;# may want to fix the first one\n>\n> and then \"git rebase --autosquash\" gave him this:\n>\n(the result of automatic rearrangement should read like this)\n\n        pick d78c915 original\n        fixup 0c6388e !fixup original\n        fixup d15b556 !fixup !fixup original\n        fixup 1e39bcd !fixup original\n\n> it may not be what the user originally intended, but I think it is\n> OK.\n>\n> As long as \"!fixup original\" message is kept in the buffer, the user\n> can notice and rearrange, e.g.\n\n(and the manual rearrangement should read like this)\n\n        pick d78c915 original\n        fixup 0c6388e !fixup original\n        fixup 1e39bcd !fixup original\n        fixup d15b556 !fixup !fixup original\n\n> if the user really wants to.\n"},{"id":"221991","messageId":"1372198415-sup-2114@pimlott.net","threadId":"34108","inReplyTo":"7v61x127bw.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Andrew Pimlott","fromEmail":"andrew@pimlott.net","sentAt":"2013-06-25T23:03:52Z","receivedAt":"2013-06-25T23:03:52Z","isPatch":true,"sender":{"key":"andrew@pimlott.net","avatar":null},"body":"Excerpts from Junio C Hamano's message of Tue Jun 25 14:45:07 -0700 2013:\n> After all, autosquash will give the user an opportunity to eyeball\n> the result of automatic rearrangement.  If the user did this:\n> \n>     git commit -m original\n>         git commit --fixup original ;# obviously fixing the first one\n>         git commit --fixup '!fixup original' ;# explicitly fixing the second\n>     git commit --fixup original ;# may want to fix the first one\n> \n> and then \"git rebase --autosquash\" gave him this:\n> \n>     pick d78c915 original\n>         fixup 0c6388e original\n>         fixup d15b556 !fixup original\n>         fixup 1e39bcd original\n\nI assume you mean:\n\n    pick d78c915 original\n    fixup 0c6388e fixup! original\n    fixup d15b556 fixup! fixup! original\n    fixup 1e39bcd !fixup! original\n\nThe current master code tries to keep the original commit message\nintact.  I assume you would preserve that behavior, so you would want to\nsee \"fixup! fixup!\"\n\n> it may not be what the user originally intended, but I think it is\n> OK.\n> \n> As long as \"!fixup original\" message is kept in the buffer, the user\n> can notice and rearrange, e.g.\n\nThomas's patch didn't do this: fixup! or squash! after the first is\nsimply discarded, so you see:\n\n    pick d78c915 original\n    fixup 0c6388e fixup! original\n    fixup d15b556 fixup! original\n    fixup 1e39bcd !fixup! original\n\nBut it will be a simple change to keep all the fixup!s and squash!s.  I\nwill do this (and try to make up for the carelessness of my previous\npatch).\n\nAndrew\n"},{"id":"221996","messageId":"1372201438-sup-833@pimlott.net","threadId":"34108","inReplyTo":"7vehbp27vl.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Andrew Pimlott","fromEmail":"andrew@pimlott.net","sentAt":"2013-06-25T23:17:46Z","receivedAt":"2013-06-25T23:17:46Z","isPatch":true,"sender":{"key":"andrew@pimlott.net","avatar":null},"body":"Excerpts from Junio C Hamano's message of Tue Jun 25 14:33:18 -0700 2013:\n> Andrew Pimlott <andrew@pimlott.net> writes:\n> \n> Just reponding for the \"procedual\" part for now.\n> \n> > So if I don't want to break the discussion, should I append the unedited\n> > format-patch output to my message after \"scissors\", or should I send it\n> > as a whole new message with --in-reply-to?  Or something else?  I'll try\n> > the first.\n> \n> Which is fine, and you are almost there, but you do not want\n> \n>  (1) \"From 99023b...\" that is not part of the message (it is a\n>      delimiter between multiple patches when/in case a file contains\n>      more than one);\n> \n>  (2) \"From: Andrew...\" that is the same as the e-mail header in the\n>      message I am responding to;\n> \n>  (3) \"Date: ...\" which is older than the e-mail header in the\n>      message I am responding to---the latter is the date people\n>      actually saw this patch on the mailing list, so it is\n>      preferrable to use it than the timestamp in your repository.\n> \n> So in this case, I'd expect to see, after the \"-- >8 --\" line, only\n> \"Subject: \" line, a blank and the log message.\n\nThank you.  It was not clear to me even after several doc readings what\ngit-mailinfo would look for where.  I think I assumed that the idea was\nto transmit the original commit perfectly, and I stubbornly failed to\ngive up that assumption even when it clearly didn't fit.  Everything\nmakes more sense with the understanding that the receiver will pull\ntogether non-patch metadata in the way that makes sense from his point\nof view (and that a different commit will come back via fetch).  I will\ntake a whack at clarifying the docs if I have time.\n\nAndrew\n"},{"id":"222040","messageId":"1372283778-sup-5704@pimlott.net","threadId":"34108","inReplyTo":"1372198415-sup-2114@pimlott.net","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Andrew Pimlott","fromEmail":"andrew@pimlott.net","sentAt":"2013-06-26T22:00:26Z","receivedAt":"2013-06-26T22:00:26Z","isPatch":true,"sender":{"key":"andrew@pimlott.net","avatar":null},"body":"Excerpts from Andrew Pimlott's message of Tue Jun 25 16:03:52 -0700 2013:\n> Thomas's patch didn't do this: fixup! or squash! after the first is\n> simply discarded, so you see:\n> \n>     pick d78c915 original\n>     fixup 0c6388e fixup! original\n>     fixup d15b556 fixup! original\n>     fixup 1e39bcd fixup! original\n> \n> But it will be a simple change to keep all the fixup!s and squash!s.  I\n> will do this (and try to make up for the carelessness of my previous\n> patch).\n\nIn order to test this, I wrote a helper function to dump the rebase -i\ntodo list.  Would you like this introduced in its own patch, or\ncombined?  See below.\n\nAndrew\n\n---8<---\nSubject: [PATCH] lib-rebase: set_cat_todo_editor\n\nAdd a helper for testing rebase -i todo lists.  This can be used to verify\nthe expected user experience, even for todo list changes that do not affect\nthe outcome of rebase.\n\nSigned-off-by: Andrew Pimlott <andrew@pimlott.net>\n---\n t/lib-rebase.sh |   13 +++++++++++++\n 1 file changed, 13 insertions(+)\n\ndiff --git a/t/lib-rebase.sh b/t/lib-rebase.sh\nindex 4b74ae4..d118dd6 100644\n--- a/t/lib-rebase.sh\n+++ b/t/lib-rebase.sh\n@@ -66,6 +66,19 @@ EOF\n \tchmod a+x fake-editor.sh\n }\n \n+# After set_cat_todo_editor, rebase -i will write the todo list (ignoring\n+# blank lines and comments) to stdout, and exit failure.\n+\n+set_cat_todo_editor () {\n+\techo \"#!$SHELL_PATH\" >fake-editor.sh\n+\tcat >> fake-editor.sh <<\\EOF\n+grep \"^[^#]\" \"$1\"\n+exit 1\n+EOF\n+\tchmod a+x fake-editor.sh\n+\ttest_set_editor \"$(pwd)/fake-editor.sh\"\n+}\n+\n # checks that the revisions in \"$2\" represent a linear range with the\n # subjects in \"$1\"\n test_linear_range () {\n-- \n1.7.10.4\n"},{"id":"222051","messageId":"7vd2r8v3fa.fsf@alter.siamese.dyndns.org","threadId":"34108","inReplyTo":"1372283778-sup-5704@pimlott.net","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-26T23:48:57Z","receivedAt":"2013-06-26T23:48:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andrew Pimlott <andrew@pimlott.net> writes:\n\n> In order to test this, I wrote a helper function to dump the rebase -i\n> todo list.  Would you like this introduced in its own patch, or\n> combined?  See below.\n\nDepends on how involved the addition of the tests that actually use\nthe helper, but in general it would be a good idea to add it in the\nfirst patch that actually uses it.  Unused code added in a separate\npatch will not point at that patch when bisecting, if that unused\ncode was broken from the beginning (not that I see anything\nimmediately broken in the code the following adds).\n\n\n> ---8<---\n> Subject: [PATCH] lib-rebase: set_cat_todo_editor\n>\n> Add a helper for testing rebase -i todo lists.  This can be used to verify\n> the expected user experience, even for todo list changes that do not affect\n> the outcome of rebase.\n>\n> Signed-off-by: Andrew Pimlott <andrew@pimlott.net>\n> ---\n>  t/lib-rebase.sh |   13 +++++++++++++\n>  1 file changed, 13 insertions(+)\n>\n> diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh\n> index 4b74ae4..d118dd6 100644\n> --- a/t/lib-rebase.sh\n> +++ b/t/lib-rebase.sh\n> @@ -66,6 +66,19 @@ EOF\n>  \tchmod a+x fake-editor.sh\n>  }\n>  \n> +# After set_cat_todo_editor, rebase -i will write the todo list (ignoring\n> +# blank lines and comments) to stdout, and exit failure.\n> +\n> +set_cat_todo_editor () {\n> +\techo \"#!$SHELL_PATH\" >fake-editor.sh\n> +\tcat >> fake-editor.sh <<\\EOF\n> +grep \"^[^#]\" \"$1\"\n> +exit 1\n> +EOF\n> +\tchmod a+x fake-editor.sh\n\nThese days we should use write_script to do this kind of thing, I\nthink.\n\n> +\ttest_set_editor \"$(pwd)/fake-editor.sh\"\n> +}\n> +\n>  # checks that the revisions in \"$2\" represent a linear range with the\n>  # subjects in \"$1\"\n>  test_linear_range () {\n"},{"id":"222052","messageId":"1372291877-sup-8201@pimlott.net","threadId":"34108","inReplyTo":"7vd2r8v3fa.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Andrew Pimlott","fromEmail":"andrew@pimlott.net","sentAt":"2013-06-27T00:20:32Z","receivedAt":"2013-06-27T00:20:32Z","isPatch":true,"sender":{"key":"andrew@pimlott.net","avatar":null},"body":"Excerpts from Junio C Hamano's message of Wed Jun 26 16:48:57 -0700 2013:\n> Andrew Pimlott <andrew@pimlott.net> writes:\n> > In order to test this, I wrote a helper function to dump the rebase -i\n> > todo list.  Would you like this introduced in its own patch, or\n> > combined?  See below.\n> \n> Depends on how involved the addition of the tests that actually use\n> the helper, but in general it would be a good idea to add it in the\n> first patch that actually uses it.  Unused code added in a separate\n> patch will not point at that patch when bisecting, if that unused\n> code was broken from the beginning (not that I see anything\n> immediately broken in the code the following adds).\n\nOk, here is the complete commit, incorporating all feedback.\n\nAndrew\n\n---8<---\nSubject: [PATCH 1/3] rebase -i: handle fixup! fixup! in --autosquash\n\nIn rebase -i --autosquash, ignore all \"fixup! \" or \"squash! \" after the\nfirst.  This supports the case when a git commit --fixup/--squash referred\nto an earlier fixup/squash instead of the original commit (whether\nintentionally, as when the user expressly meant to note that the commit\nfixes an earlier fixup; or inadvertently, as when the user meant to refer to\nthe original commit with :/msg; or out of laziness, as when the user could\nremember how to refer to the fixup but not the original).\n\nIn the todo list, the full commit message is preserved, in case it provides\nuseful cues to the user.  A test helper set_cat_todo_editor is introduced to\ncheck this.\n\nHelped-by: Thomas Rast <trast@inf.ethz.ch>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Andrew Pimlott <andrew@pimlott.net>\n---\n Documentation/git-rebase.txt |    4 ++-\n git-rebase--interactive.sh   |   25 ++++++++++++++----\n t/lib-rebase.sh              |   14 +++++++++++\n t/t3415-rebase-autosquash.sh |   57 ++++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 94 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex c84854a..6b2e1c8 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -389,7 +389,9 @@ squash/fixup series.\n \tthe same ..., automatically modify the todo list of rebase -i\n \tso that the commit marked for squashing comes right after the\n \tcommit to be modified, and change the action of the moved\n-\tcommit from `pick` to `squash` (or `fixup`).\n+\tcommit from `pick` to `squash` (or `fixup`).  Ignores subsequent\n+\t\"fixup! \" or \"squash! \" after the first, in case you referred to an\n+\tearlier fixup/squash with `git commit --fixup/--squash`.\n +\n This option is only valid when the '--interactive' option is used.\n +\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex f953d8d..169e876 100644\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -689,8 +689,22 @@ rearrange_squash () {\n \t\tcase \"$message\" in\n \t\t\"squash! \"*|\"fixup! \"*)\n \t\t\taction=\"${message%%!*}\"\n-\t\t\trest=\"${message#*! }\"\n-\t\t\techo \"$sha1 $action $rest\"\n+\t\t\trest=$message\n+\t\t\tprefix=\n+\t\t\t# skip all squash! or fixup! (but save for later)\n+\t\t\twhile :\n+\t\t\tdo\n+\t\t\t\tcase \"$rest\" in\n+\t\t\t\t\"squash! \"*|\"fixup! \"*)\n+\t\t\t\t\tprefix=\"$prefix${rest%%!*},\"\n+\t\t\t\t\trest=\"${rest#*! }\"\n+\t\t\t\t\t;;\n+\t\t\t\t*)\n+\t\t\t\t\tbreak\n+\t\t\t\t\t;;\n+\t\t\t\tesac\n+\t\t\tdone\n+\t\t\techo \"$sha1 $action $prefix $rest\"\n \t\t\t# if it's a single word, try to resolve to a full sha1 and\n \t\t\t# emit a second copy. This allows us to match on both message\n \t\t\t# and on sha1 prefix\n@@ -699,7 +713,7 @@ rearrange_squash () {\n \t\t\t\tif test -n \"$fullsha\"; then\n \t\t\t\t\t# prefix the action to uniquely identify this line as\n \t\t\t\t\t# intended for full sha1 match\n-\t\t\t\t\techo \"$sha1 +$action $fullsha\"\n+\t\t\t\t\techo \"$sha1 +$action $prefix $fullsha\"\n \t\t\t\tfi\n \t\t\tfi\n \t\tesac\n@@ -714,7 +728,7 @@ rearrange_squash () {\n \t\tesac\n \t\tprintf '%s\\n' \"$pick $sha1 $message\"\n \t\tused=\"$used$sha1 \"\n-\t\twhile read -r squash action msg_content\n+\t\twhile read -r squash action msg_prefix msg_content\n \t\tdo\n \t\t\tcase \" $used\" in\n \t\t\t*\" $squash \"*) continue ;;\n@@ -730,7 +744,8 @@ rearrange_squash () {\n \t\t\t\tcase \"$message\" in \"$msg_content\"*) emit=1;; esac ;;\n \t\t\tesac\n \t\t\tif test $emit = 1; then\n-\t\t\t\tprintf '%s\\n' \"$action $squash $action! $msg_content\"\n+\t\t\t\treal_prefix=$(echo \"$msg_prefix\" | sed \"s/,/! /g\")\n+\t\t\t\tprintf '%s\\n' \"$action $squash ${real_prefix}$msg_content\"\n \t\t\t\tused=\"$used$squash \"\n \t\t\tfi\n \t\tdone <\"$1.sq\"\ndiff --git a/t/lib-rebase.sh b/t/lib-rebase.sh\nindex 4b74ae4..be5a4c7 100644\n--- a/t/lib-rebase.sh\n+++ b/t/lib-rebase.sh\n@@ -66,6 +66,20 @@ EOF\n \tchmod a+x fake-editor.sh\n }\n \n+# After set_cat_todo_editor, rebase -i will write the todo list (ignoring\n+# blank lines and comments) to stdout, and exit failure (so you should run\n+# it with test_must_fail).  This can be used to verify the expected user\n+# experience, for todo list changes that do not affect the outcome of\n+# rebase; or as an extra check in addition to checking the outcome.\n+\n+set_cat_todo_editor () {\n+\twrite_script fake-editor.sh <<\\EOF\n+grep \"^[^#]\" \"$1\"\n+exit 1\n+EOF\n+\ttest_set_editor \"$(pwd)/fake-editor.sh\"\n+}\n+\n # checks that the revisions in \"$2\" represent a linear range with the\n # subjects in \"$1\"\n test_linear_range () {\ndiff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh\nindex a1e86c4..7c989f9 100755\n--- a/t/t3415-rebase-autosquash.sh\n+++ b/t/t3415-rebase-autosquash.sh\n@@ -4,6 +4,8 @@ test_description='auto squash'\n \n . ./test-lib.sh\n \n+. \"$TEST_DIRECTORY\"/lib-rebase.sh\n+\n test_expect_success setup '\n \techo 0 >file0 &&\n \tgit add . &&\n@@ -193,4 +195,59 @@ test_expect_success 'use commit --squash' '\n \ttest_auto_commit_flags squash 2\n '\n \n+test_auto_fixup_fixup () {\n+\tgit reset --hard base &&\n+\techo 1 >file1 &&\n+\tgit add -u &&\n+\ttest_tick &&\n+\tgit commit -m \"$1! first\" &&\n+\techo 2 >file1 &&\n+\tgit add -u &&\n+\ttest_tick &&\n+\tgit commit -m \"$1! $2! first\" &&\n+\tgit tag \"final-$1-$2\" &&\n+\ttest_tick &&\n+\t(\n+\t\tset_cat_todo_editor &&\n+\t\ttest_must_fail git rebase --autosquash -i HEAD^^^^ >actual &&\n+\t\tcat >expected <<EOF\n+pick $(git rev-parse --short HEAD^^^) first commit\n+$1 $(git rev-parse --short HEAD^) $1! first\n+$1 $(git rev-parse --short HEAD) $1! $2! first\n+pick $(git rev-parse --short HEAD^^) second commit\n+EOF\n+\t\ttest_cmp expected actual\n+\t) &&\n+\tgit rebase --autosquash -i HEAD^^^^ &&\n+\tgit log --oneline >actual &&\n+\ttest_line_count = 3 actual\n+\tgit diff --exit-code \"final-$1-$2\" &&\n+\ttest 2 = \"$(git cat-file blob HEAD^:file1)\" &&\n+\tif test \"$1\" = \"fixup\"\n+\tthen\n+\t\ttest 1 = $(git cat-file commit HEAD^ | grep first | wc -l)\n+\telif test \"$1\" = \"squash\"\n+\tthen\n+\t\ttest 3 = $(git cat-file commit HEAD^ | grep first | wc -l)\n+\telse\n+\t\tfalse\n+\tfi\n+}\n+\n+test_expect_success 'fixup! fixup!' '\n+\ttest_auto_fixup_fixup fixup fixup\n+'\n+\n+test_expect_success 'fixup! squash!' '\n+\ttest_auto_fixup_fixup fixup squash\n+'\n+\n+test_expect_success 'squash! squash!' '\n+\ttest_auto_fixup_fixup squash squash\n+'\n+\n+test_expect_success 'squash! fixup!' '\n+\ttest_auto_fixup_fixup squash fixup\n+'\n+\n test_done\n-- \n1.7.10.4\n"},{"id":"222125","messageId":"1372359783-sup-4507@pimlott.net","threadId":"34108","inReplyTo":"1372291877-sup-8201@pimlott.net","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Andrew Pimlott","fromEmail":"andrew@pimlott.net","sentAt":"2013-06-27T19:26:31Z","receivedAt":"2013-06-27T19:26:31Z","isPatch":true,"sender":{"key":"andrew@pimlott.net","avatar":null},"body":"Excerpts from Andrew Pimlott's message of Wed Jun 26 17:20:32 -0700 2013:\n> Excerpts from Junio C Hamano's message of Wed Jun 26 16:48:57 -0700 2013:\n> > Andrew Pimlott <andrew@pimlott.net> writes:\n> > > In order to test this, I wrote a helper function to dump the rebase -i\n> > > todo list.  Would you like this introduced in its own patch, or\n> > > combined?  See below.\n> > \n> > Depends on how involved the addition of the tests that actually use\n> > the helper, but in general it would be a good idea to add it in the\n> > first patch that actually uses it.  Unused code added in a separate\n> > patch will not point at that patch when bisecting, if that unused\n> > code was broken from the beginning (not that I see anything\n> > immediately broken in the code the following adds).\n> \n> Ok, here is the complete commit, incorporating all feedback.\n\nUpdated for recommended here-doc style.\n\nAndrew\n\n---8<---\nSubject: [PATCH] rebase -i: handle fixup! fixup! in --autosquash\n\nIn rebase -i --autosquash, ignore all \"fixup! \" or \"squash! \" after the\nfirst.  This supports the case when a git commit --fixup/--squash referred\nto an earlier fixup/squash instead of the original commit (whether\nintentionally, as when the user expressly meant to note that the commit\nfixes an earlier fixup; or inadvertently, as when the user meant to refer to\nthe original commit with :/msg; or out of laziness, as when the user could\nremember how to refer to the fixup but not the original).\n\nIn the todo list, the full commit message is preserved, in case it provides\nuseful cues to the user.  A test helper set_cat_todo_editor is introduced to\ncheck this.\n\nHelped-by: Thomas Rast <trast@inf.ethz.ch>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Andrew Pimlott <andrew@pimlott.net>\n---\n Documentation/git-rebase.txt |    4 ++-\n git-rebase--interactive.sh   |   25 ++++++++++++++----\n t/lib-rebase.sh              |   14 +++++++++++\n t/t3415-rebase-autosquash.sh |   57 ++++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 94 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex c84854a..6b2e1c8 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -389,7 +389,9 @@ squash/fixup series.\n \tthe same ..., automatically modify the todo list of rebase -i\n \tso that the commit marked for squashing comes right after the\n \tcommit to be modified, and change the action of the moved\n-\tcommit from `pick` to `squash` (or `fixup`).\n+\tcommit from `pick` to `squash` (or `fixup`).  Ignores subsequent\n+\t\"fixup! \" or \"squash! \" after the first, in case you referred to an\n+\tearlier fixup/squash with `git commit --fixup/--squash`.\n +\n This option is only valid when the '--interactive' option is used.\n +\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex f953d8d..169e876 100644\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -689,8 +689,22 @@ rearrange_squash () {\n \t\tcase \"$message\" in\n \t\t\"squash! \"*|\"fixup! \"*)\n \t\t\taction=\"${message%%!*}\"\n-\t\t\trest=\"${message#*! }\"\n-\t\t\techo \"$sha1 $action $rest\"\n+\t\t\trest=$message\n+\t\t\tprefix=\n+\t\t\t# skip all squash! or fixup! (but save for later)\n+\t\t\twhile :\n+\t\t\tdo\n+\t\t\t\tcase \"$rest\" in\n+\t\t\t\t\"squash! \"*|\"fixup! \"*)\n+\t\t\t\t\tprefix=\"$prefix${rest%%!*},\"\n+\t\t\t\t\trest=\"${rest#*! }\"\n+\t\t\t\t\t;;\n+\t\t\t\t*)\n+\t\t\t\t\tbreak\n+\t\t\t\t\t;;\n+\t\t\t\tesac\n+\t\t\tdone\n+\t\t\techo \"$sha1 $action $prefix $rest\"\n \t\t\t# if it's a single word, try to resolve to a full sha1 and\n \t\t\t# emit a second copy. This allows us to match on both message\n \t\t\t# and on sha1 prefix\n@@ -699,7 +713,7 @@ rearrange_squash () {\n \t\t\t\tif test -n \"$fullsha\"; then\n \t\t\t\t\t# prefix the action to uniquely identify this line as\n \t\t\t\t\t# intended for full sha1 match\n-\t\t\t\t\techo \"$sha1 +$action $fullsha\"\n+\t\t\t\t\techo \"$sha1 +$action $prefix $fullsha\"\n \t\t\t\tfi\n \t\t\tfi\n \t\tesac\n@@ -714,7 +728,7 @@ rearrange_squash () {\n \t\tesac\n \t\tprintf '%s\\n' \"$pick $sha1 $message\"\n \t\tused=\"$used$sha1 \"\n-\t\twhile read -r squash action msg_content\n+\t\twhile read -r squash action msg_prefix msg_content\n \t\tdo\n \t\t\tcase \" $used\" in\n \t\t\t*\" $squash \"*) continue ;;\n@@ -730,7 +744,8 @@ rearrange_squash () {\n \t\t\t\tcase \"$message\" in \"$msg_content\"*) emit=1;; esac ;;\n \t\t\tesac\n \t\t\tif test $emit = 1; then\n-\t\t\t\tprintf '%s\\n' \"$action $squash $action! $msg_content\"\n+\t\t\t\treal_prefix=$(echo \"$msg_prefix\" | sed \"s/,/! /g\")\n+\t\t\t\tprintf '%s\\n' \"$action $squash ${real_prefix}$msg_content\"\n \t\t\t\tused=\"$used$squash \"\n \t\t\tfi\n \t\tdone <\"$1.sq\"\ndiff --git a/t/lib-rebase.sh b/t/lib-rebase.sh\nindex 4b74ae4..cfd3409 100644\n--- a/t/lib-rebase.sh\n+++ b/t/lib-rebase.sh\n@@ -66,6 +66,20 @@ EOF\n \tchmod a+x fake-editor.sh\n }\n \n+# After set_cat_todo_editor, rebase -i will write the todo list (ignoring\n+# blank lines and comments) to stdout, and exit failure (so you should run\n+# it with test_must_fail).  This can be used to verify the expected user\n+# experience, for todo list changes that do not affect the outcome of\n+# rebase; or as an extra check in addition to checking the outcome.\n+\n+set_cat_todo_editor () {\n+\twrite_script fake-editor.sh <<-\\EOF\n+\tgrep \"^[^#]\" \"$1\"\n+\texit 1\n+\tEOF\n+\ttest_set_editor \"$(pwd)/fake-editor.sh\"\n+}\n+\n # checks that the revisions in \"$2\" represent a linear range with the\n # subjects in \"$1\"\n test_linear_range () {\ndiff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh\nindex a1e86c4..7c989f9 100755\n--- a/t/t3415-rebase-autosquash.sh\n+++ b/t/t3415-rebase-autosquash.sh\n@@ -4,6 +4,8 @@ test_description='auto squash'\n \n . ./test-lib.sh\n \n+. \"$TEST_DIRECTORY\"/lib-rebase.sh\n+\n test_expect_success setup '\n \techo 0 >file0 &&\n \tgit add . &&\n@@ -193,4 +195,59 @@ test_expect_success 'use commit --squash' '\n \ttest_auto_commit_flags squash 2\n '\n \n+test_auto_fixup_fixup () {\n+\tgit reset --hard base &&\n+\techo 1 >file1 &&\n+\tgit add -u &&\n+\ttest_tick &&\n+\tgit commit -m \"$1! first\" &&\n+\techo 2 >file1 &&\n+\tgit add -u &&\n+\ttest_tick &&\n+\tgit commit -m \"$1! $2! first\" &&\n+\tgit tag \"final-$1-$2\" &&\n+\ttest_tick &&\n+\t(\n+\t\tset_cat_todo_editor &&\n+\t\ttest_must_fail git rebase --autosquash -i HEAD^^^^ >actual &&\n+\t\tcat >expected <<EOF\n+pick $(git rev-parse --short HEAD^^^) first commit\n+$1 $(git rev-parse --short HEAD^) $1! first\n+$1 $(git rev-parse --short HEAD) $1! $2! first\n+pick $(git rev-parse --short HEAD^^) second commit\n+EOF\n+\t\ttest_cmp expected actual\n+\t) &&\n+\tgit rebase --autosquash -i HEAD^^^^ &&\n+\tgit log --oneline >actual &&\n+\ttest_line_count = 3 actual\n+\tgit diff --exit-code \"final-$1-$2\" &&\n+\ttest 2 = \"$(git cat-file blob HEAD^:file1)\" &&\n+\tif test \"$1\" = \"fixup\"\n+\tthen\n+\t\ttest 1 = $(git cat-file commit HEAD^ | grep first | wc -l)\n+\telif test \"$1\" = \"squash\"\n+\tthen\n+\t\ttest 3 = $(git cat-file commit HEAD^ | grep first | wc -l)\n+\telse\n+\t\tfalse\n+\tfi\n+}\n+\n+test_expect_success 'fixup! fixup!' '\n+\ttest_auto_fixup_fixup fixup fixup\n+'\n+\n+test_expect_success 'fixup! squash!' '\n+\ttest_auto_fixup_fixup fixup squash\n+'\n+\n+test_expect_success 'squash! squash!' '\n+\ttest_auto_fixup_fixup squash squash\n+'\n+\n+test_expect_success 'squash! fixup!' '\n+\ttest_auto_fixup_fixup squash fixup\n+'\n+\n test_done\n-- \n1.7.10.4\n"},{"id":"222133","messageId":"7vr4fnqnse.fsf@alter.siamese.dyndns.org","threadId":"34108","inReplyTo":"1372359783-sup-4507@pimlott.net","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-27T20:52:33Z","receivedAt":"2013-06-27T20:52:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andrew Pimlott <andrew@pimlott.net> writes:\n\n> Updated for recommended here-doc style.\n\nThanks.\n\n> +test_auto_fixup_fixup () {\n> +\tgit reset --hard base &&\n> +\techo 1 >file1 &&\n> +\tgit add -u &&\n> +\ttest_tick &&\n> +\tgit commit -m \"$1! first\" &&\n> +\techo 2 >file1 &&\n> +\tgit add -u &&\n> +\ttest_tick &&\n> +\tgit commit -m \"$1! $2! first\" &&\n> +\tgit tag \"final-$1-$2\" &&\n> +\ttest_tick &&\n> +\t(\n> +\t\tset_cat_todo_editor &&\n> +\t\ttest_must_fail git rebase --autosquash -i HEAD^^^^ >actual &&\n> +\t\tcat >expected <<EOF\n> +pick $(git rev-parse --short HEAD^^^) first commit\n> +$1 $(git rev-parse --short HEAD^) $1! first\n> +$1 $(git rev-parse --short HEAD) $1! $2! first\n> +pick $(git rev-parse --short HEAD^^) second commit\n> +EOF\n> +\t\ttest_cmp expected actual\n\nTwo issues here, which I'll locally amend (no need to resend):\n\n\t\tcat >expected <<-EOF &&\n\t\tpick ...\n\t\t...\n                EOF\n\t\ttest_cmp expected actual\n"},{"id":"222177","messageId":"1372428798-sup-8609@pimlott.net","threadId":"34108","inReplyTo":"7vr4fnqnse.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] rebase -i: fixup fixup! fixup!","fromName":"Andrew Pimlott","fromEmail":"andrew@pimlott.net","sentAt":"2013-06-28T14:20:51Z","receivedAt":"2013-06-28T14:20:51Z","isPatch":true,"sender":{"key":"andrew@pimlott.net","avatar":null},"body":"Excerpts from Junio C Hamano's message of Thu Jun 27 13:52:33 -0700 2013:\n> Two issues here, which I'll locally amend (no need to resend):\n\nGreat!  Thank you for your help and patience.\n\n>         cat >expected <<-EOF &&\n>         pick ...\n>         ...\n>                 EOF\n>         test_cmp expected actual\n\nIs that two issues?\n\nAndrew\n"}]}