{"thread":{"id":"24555","subject":"[RFC/PATCH] rebase -i: add run command to launch a shell command","startedAt":"2010-07-28T13:29:44Z","lastAt":"2010-08-07T08:56:05Z","messageCount":27,"participants":["Matthieu Moy","Santi Béjar","Marc Branchaud","Neal Kreitzinger","Ævar Arnfjörð Bjarmason","Miles Bader","Jared Hance","Paolo Bonzini","Junio C Hamano","Kris Shannon","Johannes Sixt"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"146620","messageId":"1280323784-27462-1-git-send-email-Matthieu.Moy@imag.fr","threadId":"24555","inReplyTo":null,"subject":"[RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2010-07-28T13:29:44Z","receivedAt":"2010-07-28T13:29:44Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"The typical usage pattern would be to run a test (or simply a compilation\ncommand) at given points in history.\n\nThe shell command is ran, and the rebase is stopped when the command\nfails, to give the user an opportunity to fix the problem before\ncontinuing with \"git rebase --continue\".\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n\nThe name of the command may be subject to discussions. I've chosen\n\"run\", but maybe \"shell\" would be OK too. In both cases, it doesn't\nallow the one-letter version since both \"r\" and \"s\" are already used.\n\n Documentation/git-rebase.txt  |   18 ++++++++++++++++++\n git-rebase--interactive.sh    |   15 +++++++++++++++\n t/lib-rebase.sh               |    2 ++\n t/t3404-rebase-interactive.sh |   31 +++++++++++++++++++++++++++++++\n 4 files changed, 66 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex be23ad2..72e3152 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -459,6 +459,24 @@ sure that the current HEAD is \"B\", and call\n $ git rebase -i -p --onto Q O\n -----------------------------\n \n+Reordering and editing commits usually creates untested intermediate\n+steps.  You may want to check that your history editting did not break\n+anything by running a test, or at least recompiling at intermediate\n+points in history by using the \"run\" command.  You may do so by\n+creating a todo list like this one:\n+\n+-------------------------------------------\n+pick deadbee The oneline of this commit\n+run make\n+pick c0ffeee The oneline of the next commit\n+run make test\n+...\n+-------------------------------------------\n+\n+The interactive rebase will stop when a command fails (i.e. exists\n+with non-0 status) to give you an opportunity to fix the problem.  You\n+can continue with `git rebase --continue`.\n+\n \n SPLITTING COMMITS\n -----------------\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex b94c2a0..ab8bae0 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -537,6 +537,20 @@ do_next () {\n \t\tesac\n \t\trecord_in_rewritten $sha1\n \t\t;;\n+\trun)\n+\t\tread -r command rest < \"$TODO\"\n+\t\tmark_action_done\n+\t\tprintf 'Running command: %s\\n' \"$rest\"\n+\t\tif ! eval \"$rest\"\n+\t\tthen\n+\t\t\twarn \"Command $rest failed\"\n+\t\t\twarn \"You can fix the problem, and then run\"\n+\t\t\twarn\n+\t\t\twarn \"\tgit rebase --continue\"\n+\t\t\twarn\n+\t\t\texit 0\n+\t\tfi\n+\t\t;;\n \t*)\n \t\twarn \"Unknown command: $command $sha1 $rest\"\n \t\tif git rev-parse --verify -q \"$sha1\" >/dev/null\n@@ -957,6 +971,7 @@ first and then run 'git rebase --continue' again.\"\n #  e, edit = use commit, but stop for amending\n #  s, squash = use commit, but meld into previous commit\n #  f, fixup = like \"squash\", but discard this commit's log message\n+#  run <command> = Run a shell command, and stop if it fails\n #\n # If you remove a line here THAT COMMIT WILL BE LOST.\n # However, if you remove everything, the rebase will be aborted.\ndiff --git a/t/lib-rebase.sh b/t/lib-rebase.sh\nindex 6aefe27..81dd17b 100644\n--- a/t/lib-rebase.sh\n+++ b/t/lib-rebase.sh\n@@ -47,6 +47,8 @@ for line in $FAKE_LINES; do\n \tcase $line in\n \tsquash|fixup|edit|reword)\n \t\taction=\"$line\";;\n+\trun*)\n+\t\techo \"$line\" | sed 's/_/ /g' >> \"$1\";;\n \t\"#\")\n \t\techo '# comment' >> \"$1\";;\n \t\">\")\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 9f03ce6..274a767 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -64,6 +64,37 @@ test_expect_success 'setup' '\n \tdone\n '\n \n+test_expect_success 'rebase -i with the run command' '\n+\tgit checkout master\n+\tFAKE_LINES=\"1 run_touch_touch-one 2 run_touch_touch-two run_false run_touch_touch-three 3 4\n+\t\trun_touch_\\\"touch-file__name_with_spaces\\\" 5\" \\\n+\t\tgit rebase -i A &&\n+\tif ! [ -f touch-one ]; then\n+\t\techo file touch-one not created\n+\t\texit 1\n+\tfi &&\n+\tif ! [ -f touch-two ]; then\n+\t\techo file touch-two not created\n+\t\texit 1\n+\tfi &&\n+\tif [ -f touch-three ]; then\n+\t\techo \"file touch-three already created. Should have stopped before\"\n+\t\texit 1\n+\tfi &&\n+\ttest $(git rev-parse C) = $(git rev-parse HEAD) &&\n+\tgit rebase --continue &&\n+\tif ! [ -f touch-three ]; then\n+\t\techo \"file touch-three not created\"\n+\t\texit 1\n+\tfi &&\n+\tif ! [ -f \"touch-file  name with spaces\" ]; then\n+\t\techo \"file \\\"touch-file  name with spaces\\\" not created\"\n+\t\texit 1\n+\tfi &&\n+\ttest $(git rev-parse master) = $(git rev-parse HEAD) &&\n+\trm -f touch-*\n+'\n+\n test_expect_success 'no changes are a nop' '\n \tgit checkout branch2 &&\n \tgit rebase -i F &&\n-- \n1.7.2.21.ge9796\n"},{"id":"146625","messageId":"AANLkTinvVFvu7WvwrRC0sg8vhoepFXKewmuTr7S94LWk@mail.gmail.com","threadId":"24555","inReplyTo":"1280323784-27462-1-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Santi Béjar","fromEmail":"santi@agolina.net","sentAt":"2010-07-28T14:12:36Z","receivedAt":"2010-07-28T14:12:36Z","isPatch":true,"sender":{"key":"santi@agolina.net","avatar":null},"body":"On Wed, Jul 28, 2010 at 3:29 PM, Matthieu Moy <Matthieu.Moy@imag.fr> wrote:\n> The typical usage pattern would be to run a test (or simply a compilation\n> command) at given points in history.\n>\n> The shell command is ran, and the rebase is stopped when the command\n> fails, to give the user an opportunity to fix the problem before\n> continuing with \"git rebase --continue\".\n>\n\nI think this is a useful addition, but I would find it more useful if\nI could run a command (make test) on top of all commits of a patch\nseries, like:\n\n$ git run HEAD^4.. command arguments\n\n(I'm not quite sure about the syntax). Something like \"git bisect run\"\nbut for all the commits in the range.\n\n I know you said \"given points in history\", maybe each approach is\nuseful for each use case.\n\nThanks,\nSanti\n"},{"id":"146628","messageId":"vpq1van3cnl.fsf@bauges.imag.fr","threadId":"24555","inReplyTo":"AANLkTinvVFvu7WvwrRC0sg8vhoepFXKewmuTr7S94LWk@mail.gmail.com","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2010-07-28T14:26:54Z","receivedAt":"2010-07-28T14:26:54Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Santi Béjar <santi@agolina.net> writes:\n\n> $ git run HEAD^4.. command arguments\n>\n> (I'm not quite sure about the syntax). Something like \"git bisect run\"\n> but for all the commits in the range.\n>\n>  I know you said \"given points in history\", maybe each approach is\n> useful for each use case.\n\nYes, I think both approaches make sense.\n\nThe cool thing with my version is that you're already in an\ninteractive rebase, which means:\n\n* You can re-order commits, rebase them on upstream branch, and check\n  the result in one pass.\n\n* You're ready to ammend commits if they need fixing.\n\nAlso, you may not need to re-check everything for each commit. You may\nwant a todo-list like this\n\npick deadbee log.c: do something\nrun make\npick c0ffeee Documentation for something\nrun make doc\n\nor whatever. Note also that this \"git run\" can easily be implemented\non top of my patch:\n\nGIT_EDITOR=\"sed -i 's/^[^#].*/\\0\\nrun make/'\" git rebase -i\n\nwe can also imagine a \"git rebase -i --run=cmd\" that would prepare a\ntodo-list with \"run cmd\" after each pick line.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"146770","messageId":"vpq1valffpz.fsf@bauges.imag.fr","threadId":"24555","inReplyTo":"vpq1van3cnl.fsf@bauges.imag.fr","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2010-07-30T10:04:24Z","receivedAt":"2010-07-30T10:04:24Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:\n\n> Santi Béjar <santi@agolina.net> writes:\n>\n>> $ git run HEAD^4.. command arguments\n>>\n>> (I'm not quite sure about the syntax). Something like \"git bisect run\"\n>> but for all the commits in the range.\n>>\n>>  I know you said \"given points in history\", maybe each approach is\n>> useful for each use case.\n>\n> Yes, I think both approaches make sense.\n\nI started playing with the patch, and I'm already starting to love\nit ;-). For example\n\npick <commit1>\nfixup <commit4>\nrun make\npick <commit2>\npick <commit3>\n...\n\nJust does \"the right thing\": it checks that the fixup doesn't break\nthe commit (which is really not only a new state, but also a new\npatch, so it can really break), but doesn't spend too much time\nre-checking <commit2> and <commit3> (which are new states, but much\nmore trustworthy than the fixup since the patches are really the\nsame. I may check them later, but don't want to lose time with them\nwhile hacking).\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"146780","messageId":"4C52E6E1.20101@xiplink.com","threadId":"24555","inReplyTo":"1280323784-27462-1-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2010-07-30T14:51:13Z","receivedAt":"2010-07-30T14:51:13Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"On 10-07-28 09:29 AM, Matthieu Moy wrote:\n> The typical usage pattern would be to run a test (or simply a compilation\n> command) at given points in history.\n> \n> The shell command is ran, and the rebase is stopped when the command\n> fails, to give the user an opportunity to fix the problem before\n> continuing with \"git rebase --continue\".\n> \n> Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n> ---\n> \n> The name of the command may be subject to discussions. I've chosen\n> \"run\", but maybe \"shell\" would be OK too. In both cases, it doesn't\n> allow the one-letter version since both \"r\" and \"s\" are already used.\n\n\"exec\" with one-letter \"x\"?\n\n\t\tM.\n"},{"id":"146783","messageId":"vpqd3u53sd2.fsf@bauges.imag.fr","threadId":"24555","inReplyTo":"4C52E6E1.20101@xiplink.com","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2010-07-30T15:24:25Z","receivedAt":"2010-07-30T15:24:25Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Marc Branchaud <marcnarc@xiplink.com> writes:\n\n>> The name of the command may be subject to discussions. I've chosen\n>> \"run\", but maybe \"shell\" would be OK too. In both cases, it doesn't\n>> allow the one-letter version since both \"r\" and \"s\" are already used.\n>\n> \"exec\" with one-letter \"x\"?\n\nThanks, that sounds good, yes. Any other thought?\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"146790","messageId":"i2v5hc$6ep$4@dough.gmane.org","threadId":"24555","inReplyTo":"vpqd3u53sd2.fsf@bauges.imag.fr","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Neal Kreitzinger","fromEmail":"neal@rsss.com","sentAt":"2010-07-30T18:26:11Z","receivedAt":"2010-07-30T18:26:11Z","isPatch":true,"sender":{"key":"neal@rsss.com","avatar":null},"body":"\n\"Matthieu Moy\" <Matthieu.Moy@grenoble-inp.fr> wrote in message \nnews:vpqd3u53sd2.fsf@bauges.imag.fr...\n> Marc Branchaud <marcnarc@xiplink.com> writes:\n>\n>>> The name of the command may be subject to discussions. I've chosen\n>>> \"run\", but maybe \"shell\" would be OK too. In both cases, it doesn't\n>>> allow the one-letter version since both \"r\" and \"s\" are already used.\n>>\n>> \"exec\" with one-letter \"x\"?\n>\n> Thanks, that sounds good, yes. Any other thought?\n\n\"call\" with one-letter \"c\"?\n\nv/r,\nNeal \n"},{"id":"146860","messageId":"vpq62zv2342.fsf@bauges.imag.fr","threadId":"24555","inReplyTo":"i2v5hc$6ep$4@dough.gmane.org","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2010-07-31T13:27:25Z","receivedAt":"2010-07-31T13:27:25Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"\"Neal Kreitzinger\" <neal@rsss.com> writes:\n\n> \"Matthieu Moy\" <Matthieu.Moy@grenoble-inp.fr> wrote in message \n> news:vpqd3u53sd2.fsf@bauges.imag.fr...\n>> Marc Branchaud <marcnarc@xiplink.com> writes:\n>>\n>>>> The name of the command may be subject to discussions. I've chosen\n>>>> \"run\", but maybe \"shell\" would be OK too. In both cases, it doesn't\n>>>> allow the one-letter version since both \"r\" and \"s\" are already used.\n>>>\n>>> \"exec\" with one-letter \"x\"?\n>>\n>> Thanks, that sounds good, yes. Any other thought?\n>\n> \"call\" with one-letter \"c\"?\n\n\"call\" would suggest a function or command, but here, you can give\nsomething more elaborate like\n\n  exec cd \"some directory\"; make\n\nso I prefer the \"exec/x\" version.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"146861","messageId":"AANLkTikMW=ueQXfjpXk8G2FLRN55j1aJsnNz2w19BmkH@mail.gmail.com","threadId":"24555","inReplyTo":"vpqd3u53sd2.fsf@bauges.imag.fr","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2010-07-31T13:56:09Z","receivedAt":"2010-07-31T13:56:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"On Fri, Jul 30, 2010 at 15:24, Matthieu Moy\n<Matthieu.Moy@grenoble-inp.fr> wrote:\n> Marc Branchaud <marcnarc@xiplink.com> writes:\n>\n>>> The name of the command may be subject to discussions. I've chosen\n>>> \"run\", but maybe \"shell\" would be OK too. In both cases, it doesn't\n>>> allow the one-letter version since both \"r\" and \"s\" are already used.\n>>\n>> \"exec\" with one-letter \"x\"?\n>\n> Thanks, that sounds good, yes. Any other thought?\n\nI like \"exec\".\n\nI think the docs need to elaborate on the environment the \"exec\"\ncommand gets executed in, what's its current working directory for\ninstance? Wherever I happened to run git-rebase from? the project\nroot?\n\nyour if ! eval .. error message also exits with 0, surely that should\nexit with 1?\n"},{"id":"146862","messageId":"877hkbd8yt.fsf@catnip.gol.com","threadId":"24555","inReplyTo":"AANLkTikMW=ueQXfjpXk8G2FLRN55j1aJsnNz2w19BmkH@mail.gmail.com","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Miles Bader","fromEmail":"miles@gnu.org","sentAt":"2010-07-31T14:25:30Z","receivedAt":"2010-07-31T14:25:30Z","isPatch":true,"sender":{"key":"miles@gnu.org","avatar":"https://gravatar.com/avatar/01069b69593af7bff28e2f97afeb3644ae6fe2f5f56cb3a8cf34c5fb8c36efe5?d=mp&s=160"},"body":"There's also \"invoke\"\n\n-miles\n\n-- \nImmortality, n.  A toy which people cry for, And on their knees apply for,\n      Dispute, contend and lie for, And if allowed Would be right proud\n      Eternally to die for.\n"},{"id":"146863","messageId":"20100731144008.GA10540@localhost.localdomain","threadId":"24555","inReplyTo":"1280323784-27462-1-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Jared Hance","fromEmail":"jaredhance@gmail.com","sentAt":"2010-07-31T14:40:18Z","receivedAt":"2010-07-31T14:40:18Z","isPatch":true,"sender":{"key":"jaredhance@gmail.com","avatar":"https://avatars.githubusercontent.com/u/170192?v=4"},"body":"On Wed, Jul 28, 2010 at 03:29:44PM +0200, Matthieu Moy wrote:\n> +\t\tif ! eval \"$rest\"\n> +\t\tthen\n> +\t\t\twarn \"Command $rest failed\"\n> +\t\t\twarn \"You can fix the problem, and then run\"\n> +\t\t\twarn\n> +\t\t\twarn \"\tgit rebase --continue\"\n> +\t\t\twarn\n> +\t\t\texit 0\n> +\t\tfi\n\nIf the command, stored in $rest, is rather long, perhaps \"Command\n$rest failed\" won't be friendly output. Perhaps:\n\n    Command Failure: $rest\n"},{"id":"146865","messageId":"4C54410B.704@gnu.org","threadId":"24555","inReplyTo":"vpqd3u53sd2.fsf@bauges.imag.fr","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2010-07-31T15:28:11Z","receivedAt":"2010-07-31T15:28:11Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"On 07/30/2010 05:24 PM, Matthieu Moy wrote:\n> Marc Branchaud<marcnarc@xiplink.com>  writes:\n>\n>>> The name of the command may be subject to discussions. I've chosen\n>>> \"run\", but maybe \"shell\" would be OK too. In both cases, it doesn't\n>>> allow the one-letter version since both \"r\" and \"s\" are already used.\n>>\n>> \"exec\" with one-letter \"x\"?\n>\n> Thanks, that sounds good, yes. Any other thought?\n\nI like run, for the short version what about ! (as in vi)?  Maybe with \nan optional space.\n\nPaolo\n"},{"id":"146866","messageId":"AANLkTi=4y4_N04atgt1YvE-DuPg6uXt_n2j95+VhN9oV@mail.gmail.com","threadId":"24555","inReplyTo":"4C54410B.704@gnu.org","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2010-07-31T15:52:26Z","receivedAt":"2010-07-31T15:52:26Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"On Sat, Jul 31, 2010 at 15:28, Paolo Bonzini <bonzini@gnu.org> wrote:\n> On 07/30/2010 05:24 PM, Matthieu Moy wrote:\n>>\n>> Marc Branchaud<marcnarc@xiplink.com>  writes:\n>>\n>>>> The name of the command may be subject to discussions. I've chosen\n>>>> \"run\", but maybe \"shell\" would be OK too. In both cases, it doesn't\n>>>> allow the one-letter version since both \"r\" and \"s\" are already used.\n>>>\n>>> \"exec\" with one-letter \"x\"?\n>>\n>> Thanks, that sounds good, yes. Any other thought?\n>\n> I like run, for the short version what about ! (as in vi)?  Maybe with an\n> optional space.\n\n\"run\" clashes with the short form of \"reword\", \"exec\" also does that,\nbut the \"x\" key for that is more obvious. ! is a shift-combo away.\n\nAnyway, </bikeshedding>\n"},{"id":"146870","messageId":"20100731185429.GA13309@localhost.localdomain","threadId":"24555","inReplyTo":"4C54410B.704@gnu.org","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Jared Hance","fromEmail":"jaredhance@gmail.com","sentAt":"2010-07-31T18:54:29Z","receivedAt":"2010-07-31T18:54:29Z","isPatch":true,"sender":{"key":"jaredhance@gmail.com","avatar":"https://avatars.githubusercontent.com/u/170192?v=4"},"body":"On Sat, Jul 31, 2010 at 05:28:11PM +0200, Paolo Bonzini wrote:\n> I like run, for the short version what about ! (as in vi)?  Maybe\n> with an optional space.\n> \n> Paolo\n\nI disagree. Firstly, \"!\" is very inconsistent with the current shorthand\nforms. I think the \"x\" makes more since from the point of view that its\nvery easy to associate with exec (the first syllable is pronounced as\nan \"x\"). \"x\" is also used by other commands to mean executable (for\nexample, \"chmod +x\").\n"},{"id":"146934","messageId":"vpqwrs9nxi5.fsf@bauges.imag.fr","threadId":"24555","inReplyTo":"AANLkTikMW=ueQXfjpXk8G2FLRN55j1aJsnNz2w19BmkH@mail.gmail.com","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2010-08-02T10:02:10Z","receivedAt":"2010-08-02T10:02:10Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> On Fri, Jul 30, 2010 at 15:24, Matthieu Moy\n> <Matthieu.Moy@grenoble-inp.fr> wrote:\n>> Marc Branchaud <marcnarc@xiplink.com> writes:\n>>\n>>>> The name of the command may be subject to discussions. I've chosen\n>>>> \"run\", but maybe \"shell\" would be OK too. In both cases, it doesn't\n>>>> allow the one-letter version since both \"r\" and \"s\" are already used.\n>>>\n>>> \"exec\" with one-letter \"x\"?\n>>\n>> Thanks, that sounds good, yes. Any other thought?\n>\n> I like \"exec\".\n\nYes, it's the best proposal IMHO. \"x\" is very often associated to\n\"execute\", and I'd rather keep away from punctuation/shift combo.\nThere have been discussions in the past about giving \"!\" a semantics\n(like \"fixup!\" for a variant of \"fixup\"). Let's keep this as an option\nfor the future by not using ! now.\n\n> I think the docs need to elaborate on the environment the \"exec\"\n> command gets executed in, what's its current working directory for\n> instance? Wherever I happened to run git-rebase from? the project\n> root?\n\nThat's a good question. My original patch was running the command from\nthe toplevel, which is the natural way to implement it. I've changed\nmy mind to execute the command from the place where \"git rebase -i\"\nwas started (which means this has to be memorized in a temporary file\nto be persistant accross \"git rebase --continue\"). I think this makes\nmore sense for the user, and I've actually already been biten by the\nold behavior, running \"rebase -i\" from a doc/ subdirectory, and\nwondering why my \"exec make\" was rebuilding the code itself.\n\nThis comes with at least one drawback: if directory from which the\nrebase was started didn't exist in the past, then we can't do a simple\n\"cd\" to it. My implementation re-creates the directory temporarily, so\nthat the command can run, and cleans it up afterwards. The only really\nproblematic case is when the directory can not be created (like\ndirectory/file conflict). It this case, the command is not ran, and\nthe script exits.\n\n> your if ! eval .. error message also exits with 0, surely that should\n> exit with 1?\n\nI'm now exiting with the same status as the command itself in case of\nfailure.\n\nNew version follows. It should hopefully look more like a real patch\nthan an RFC now.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"146935","messageId":"1280743433-27803-1-git-send-email-Matthieu.Moy@imag.fr","threadId":"24555","inReplyTo":"vpqwrs9nxi5.fsf@bauges.imag.fr","subject":"[PATCH] rebase -i: add exec command to launch a shell command","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2010-08-02T10:03:53Z","receivedAt":"2010-08-02T10:03:53Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"The typical usage pattern would be to run a test (or simply a compilation\ncommand) at given points in history.\n\nThe shell command is ran (in the directory where the rebase was started\nby the user), and the rebase is stopped when the command fails, to give\nthe user an opportunity to fix the problem before continuing with \"git\nrebase --continue\".\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n Documentation/git-rebase.txt  |   26 +++++++++++++\n git-rebase--interactive.sh    |   77 +++++++++++++++++++++++++++++++++++++++\n git-rebase.sh                 |    6 +++\n t/lib-rebase.sh               |    2 +\n t/t3404-rebase-interactive.sh |   79 +++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 190 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex be23ad2..121f873 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -459,6 +459,32 @@ sure that the current HEAD is \"B\", and call\n $ git rebase -i -p --onto Q O\n -----------------------------\n \n+Reordering and editing commits usually creates untested intermediate\n+steps.  You may want to check that your history editting did not break\n+anything by running a test, or at least recompiling at intermediate\n+points in history by using the \"exec\" command (shortcut \"x\").  You may\n+do so by creating a todo list like this one:\n+\n+-------------------------------------------\n+pick deadbee Implement feature XXX\n+fixup f1a5c00 Fix to feature XXX\n+exec make\n+pick c0ffeee The oneline of the next commit\n+edit deadbab The oneline of the commit after\n+exec make; make test\n+...\n+-------------------------------------------\n+\n+The interactive rebase will stop when a command fails (i.e. exists\n+with non-0 status) to give you an opportunity to fix the problem.  You\n+can continue with `git rebase --continue`.\n+\n+The \"exec\" command launches the command in a shell (the one specified\n+in `$SHELL`, or the default shell if `$SHELL` is not set), so you can\n+use usual shell commands like \"cd\". The commands are ran from the same\n+directory as \"rebase -i\" was started (this directory is temporarily\n+re-created if needed).\n+\n \n SPLITTING COMMITS\n -----------------\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex b94c2a0..98a54e5 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -104,6 +104,10 @@ AMEND=\"$DOTEST\"/amend\n REWRITTEN_LIST=\"$DOTEST\"/rewritten-list\n REWRITTEN_PENDING=\"$DOTEST\"/rewritten-pending\n \n+# The \"exec\" command needs to know from which directory the\n+# interactive rebase started.\n+START_PREFIX=\"$DOTEST\"/start-prefix\n+\n PRESERVE_MERGES=\n STRATEGY=\n ONTO=\n@@ -448,6 +452,31 @@ record_in_rewritten() {\n \tesac\n }\n \n+# Like \"mkdir -p\", but outputs the largest prefix of \"$1\" which\n+# already exists as a directory\n+mkdir_parent_verbose() {\n+\tdir=$1\n+\tto_mk=.\n+\twhile ! test -d \"$dir\"\n+\tdo\n+\t\tto_mk=$(basename \"$dir\")/\"$to_mk\"\n+\t\tdir=$(dirname \"$dir\")\n+\tdone\n+\t(cd \"$dir\" && mkdir -p \"$to_mk\" && pwd -P)\n+}\n+\n+# Delete directory \"$1\", and its parent, until encountering a\n+# non-empty directory or \"$2\".\n+cleanup_directory() {\n+\tto_rm=\"$1\"\n+\twhile test -d \"$1\" && test -d \"$2\" &&\n+\t\ttest \"$(cd \"$to_rm\"; pwd -P)\" != \"$(cd \"$2\"; pwd -P)\" &&\n+\t\trmdir \"$to_rm\"\n+\tdo\n+\t\tto_rm=$(dirname \"$to_rm\")\n+\tdone\n+}\n+\n do_next () {\n \trm -f \"$MSG\" \"$AUTHOR_SCRIPT\" \"$AMEND\" || exit\n \tread -r command sha1 rest < \"$TODO\"\n@@ -537,6 +566,52 @@ do_next () {\n \t\tesac\n \t\trecord_in_rewritten $sha1\n \t\t;;\n+\tx|\"exec\")\n+\t\tread -r command rest < \"$TODO\"\n+\t\tmark_action_done\n+\t\tprintf 'Executing: %s\\n' \"$rest\"\n+\t\t# \"exec\" command doesn't take a sha1 in the todo-list.\n+\t\t# => can't just use $sha1 here.\n+\t\tgit rev-parse --verify HEAD > \"$DOTEST\"/stopped-sha\n+\t\t# We run the command from the directory in which \"git\n+\t\t# rebase -i\" was first started.\n+\t\tprefix=$(cat \"$START_PREFIX\")\n+\t\tto_rm=\n+\t\told_pwd=$(pwd)\n+\t\tcd \"$prefix\" 2>/dev/null || {\n+\t\t\t# The prefix from which the rebase started\n+\t\t\t# didn't exist in the past.  We create the\n+\t\t\t# directory to let the command run from there,\n+\t\t\t# and remember how to delete it in $to_rm.\n+\t\t\tto_rm=$(mkdir_parent_verbose \"$prefix\") && cd \"$prefix\"\n+\t\t} || {\n+\t\t\twarn \"Cannot create directory $prefix\"\n+\t\t\twarn \"Aborting execution of command $rest\"\n+\t\t\twarn \"To continue, run\"\n+\t\t\twarn\n+\t\t\twarn \"\tgit rebase --continue\"\n+\t\t\twarn\n+\t\t\texit 1\n+\t\t}\n+\t\t${SHELL:-@SHELL_PATH@} -c \"$rest\" # Actual execution\n+\t\tstatus=$?\n+\t\tcd \"$old_pwd\"\n+\t\t# Don't leave empty directory around.\n+\t\t# Since cleanup_directory never deletes non-empty\n+\t\t# directories, this can't lose valuable data.\n+\t\tif [ -n \"$to_rm\" ]; then\n+\t\t\tcleanup_directory \"$prefix\" \"$to_rm\"\n+\t\tfi\n+\t\tif test \"$status\" -ne 0\n+\t\tthen\n+\t\t\twarn \"Execution failed: $rest\"\n+\t\t\twarn \"You can fix the problem, and then run\"\n+\t\t\twarn\n+\t\t\twarn \"\tgit rebase --continue\"\n+\t\t\twarn\n+\t\t\texit \"$status\"\n+\t\tfi\n+\t\t;;\n \t*)\n \t\twarn \"Unknown command: $command $sha1 $rest\"\n \t\tif git rev-parse --verify -q \"$sha1\" >/dev/null\n@@ -851,6 +926,7 @@ first and then run 'git rebase --continue' again.\"\n \t\techo $ONTO > \"$DOTEST\"/onto\n \t\ttest -z \"$STRATEGY\" || echo \"$STRATEGY\" > \"$DOTEST\"/strategy\n \t\ttest t = \"$VERBOSE\" && : > \"$DOTEST\"/verbose\n+\t\techo \"$GIT_PREFIX\" > \"$START_PREFIX\"\n \t\tif test t = \"$PRESERVE_MERGES\"\n \t\tthen\n \t\t\tif test -z \"$REBASE_ROOT\"\n@@ -957,6 +1033,7 @@ first and then run 'git rebase --continue' again.\"\n #  e, edit = use commit, but stop for amending\n #  s, squash = use commit, but meld into previous commit\n #  f, fixup = like \"squash\", but discard this commit's log message\n+#  x <cmd>, exec <cmd> = Run a shell command <cmd>, and stop if it fails\n #\n # If you remove a line here THAT COMMIT WILL BE LOST.\n # However, if you remove everything, the rebase will be aborted.\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex ab4afa7..fff512c 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -32,6 +32,12 @@ OPTIONS_SPEC=\n . git-sh-setup\n set_reflog_action rebase\n require_work_tree\n+\n+# Keep the prefix path in an environment variable so that\n+# git-rebase--interactive can find it.\n+GIT_PREFIX=$(git rev-parse --show-prefix)\n+export GIT_PREFIX\n+\n cd_to_toplevel\n \n LF='\ndiff --git a/t/lib-rebase.sh b/t/lib-rebase.sh\nindex 6aefe27..6ccf797 100644\n--- a/t/lib-rebase.sh\n+++ b/t/lib-rebase.sh\n@@ -47,6 +47,8 @@ for line in $FAKE_LINES; do\n \tcase $line in\n \tsquash|fixup|edit|reword)\n \t\taction=\"$line\";;\n+\texec*)\n+\t\techo \"$line\" | sed 's/_/ /g' >> \"$1\";;\n \t\"#\")\n \t\techo '# comment' >> \"$1\";;\n \t\">\")\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 9f03ce6..649a400 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -64,6 +64,85 @@ test_expect_success 'setup' '\n \tdone\n '\n \n+file_must_exist () {\n+    if ! [ -f \"$1\" ]; then\n+\techo \"file $1 not created.\"\n+\tfalse\n+    fi\n+}\n+\n+file_must_not_exist () {\n+    if [ -f \"$1\" ]; then\n+\techo \"file $1 created while it shouldn't have. $2\"\n+\tfalse\n+    fi\n+}\n+\n+dir_must_exist () {\n+    if ! [ -d \"$1\" ]; then\n+\techo \"dir $1 does not exist.\"\n+\tfalse\n+    fi\n+}\n+\n+dir_must_not_exist () {\n+    if [ -d \"$1\" ]; then\n+\techo \"dir $1 exists while it shouldn't. $2\"\n+\tfalse\n+    fi\n+}\n+\n+test_expect_success 'rebase -i with the exec command' '\n+\tgit checkout master &&\n+\tFAKE_LINES=\"1 exec_touch_touch-one 2 exec_touch_touch-two exec_false exec_touch_touch-three 3 4\n+\t\texec_touch_\\\"touch-file__name_with_spaces\\\";_touch_touch-after-semicolon 5\" \\\n+\t\ttest_must_fail git rebase -i A &&\n+\tfile_must_exist touch-one &&\n+\tfile_must_exist touch-two &&\n+\tfile_must_not_exist touch-three \"(Should have stopped before)\" &&\n+\ttest $(git rev-parse C) = $(git rev-parse HEAD) || {\n+\t\techo \"Stopped at wrong revision:\"\n+\t\techo \"($(git describe --tags HEAD) instead of C)\"\n+\t\tfalse\n+\t} &&\n+\tgit rebase --continue &&\n+\tfile_must_exist touch-three &&\n+\tfile_must_exist \"touch-file  name with spaces\" &&\n+\tfile_must_exist touch-after-semicolon &&\n+\ttest $(git rev-parse master) = $(git rev-parse HEAD) || {\n+\t\techo \"Stopped at wrong revision:\"\n+\t\techo \"($(git describe --tags HEAD) instead of master)\"\n+\t\tfalse\n+\t} &&\n+\trm -f touch-*\n+'\n+\n+test_expect_success 'rebase -i with exec in deleted directory' '\n+\tgit checkout master &&\n+\trm -f pwd.txt &&\n+\tmkdir tmpdir &&\n+\tcd tmpdir &&\n+\tpwd > ../expect &&\n+\tFAKE_LINES=\"1 exec_false_one\n+\t\t      exec_pwd>../actual exec_false_two\n+\t\t      exec_touch_tmpfile exec_false_three\n+\t\t      exec_pwd\" \\\n+\t\ttest_must_fail git rebase -i HEAD^ &&\n+\tcd .. && rm -fr tmpdir &&\n+\t# Should re-create tmpdir temporarily, and clean it up:\n+\ttest_must_fail git rebase --continue &&\n+\ttest_cmp expect actual &&\n+\tdir_must_not_exist tmpdir &&\n+\t# Should not cleanup non-empty directory\n+\ttest_must_fail git rebase --continue &&\n+\tfile_must_exist tmpdir/tmpfile &&\n+\trm -fr tmpdir && touch tmpdir &&\n+\t# Should fail because of D/F conflict\n+\ttest_must_fail git rebase --continue &&\n+\tgit rebase --continue &&\n+\trm -fr tmpdir\n+'\n+\n test_expect_success 'no changes are a nop' '\n \tgit checkout branch2 &&\n \tgit rebase -i F &&\n-- \n1.7.2.1.10.g5cb67a\n"},{"id":"146939","messageId":"20100802123001.GA3500@localhost.localdomain","threadId":"24555","inReplyTo":"1280743433-27803-1-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [PATCH] rebase -i: add exec command to launch a shell command","fromName":"Jared Hance","fromEmail":"jaredhance@gmail.com","sentAt":"2010-08-02T12:30:01Z","receivedAt":"2010-08-02T12:30:01Z","isPatch":true,"sender":{"key":"jaredhance@gmail.com","avatar":"https://avatars.githubusercontent.com/u/170192?v=4"},"body":"On Mon, Aug 02, 2010 at 12:03:53PM +0200, Matthieu Moy wrote:\n> The shell command is ran (in the directory where the rebase was started\n> by the user)\n\nI disagree with how intuitive this behavior is. \"rebase\" is a\nrepository level command - It modifies the objects in the repository.\nAs a side effect, the working tree attached to a (non-bare) repository\nis changed, even outside of where the rebase was ran.\n\nSince the rebase affects the entire repository, I think it makes sense\nfor the commands to be run out of the repository root.\n\nPerhaps a command-line option for toggling this behavior would be a\ngood compromise.\n"},{"id":"146945","messageId":"4C56DE68.7010906@gnu.org","threadId":"24555","inReplyTo":"vpqwrs9nxi5.fsf@bauges.imag.fr","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2010-08-02T15:04:08Z","receivedAt":"2010-08-02T15:04:08Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"On 08/02/2010 12:02 PM, Matthieu Moy wrote:\n> I think this makes more sense for the user, and I've actually already\n> been biten by the old behavior, running \"rebase -i\" from a doc/\n> subdirectory, and wondering why my \"exec make\" was rebuilding the\n> code itself.\n\nI think it's a matter of habits, and I would surely be bitten more by \nthe opposite problem: when I'm usually ready to rebase and test I'm \nlikely to be in src/ (for packages that have one such directory) or tests/.\n\ncd to the top-level repository is a logical choice since rebase is a \nrepository-wide command (even though the particular set of commits might \ntouch only a part of it).  It is easier to implement, does not have any \nproblem with conflicts or otherwise with deletion, and easier to \ndocument as well.\n\nIf you decide to go with the other choice, however, I would _strongly_ \nsuggest failing if the directory not exists.  After all most of the time \nthe command (\"make\" for example) will be pretty unlikely to succeed.\n\nPaolo\n"},{"id":"146950","messageId":"AANLkTik3JQMCoDr==9HV9-sd4Yh7Lta9LmbACByVGpzd@mail.gmail.com","threadId":"24555","inReplyTo":"1280743433-27803-1-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [PATCH] rebase -i: add exec command to launch a shell command","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2010-08-02T15:51:29Z","receivedAt":"2010-08-02T15:51:29Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"On Mon, Aug 2, 2010 at 10:03, Matthieu Moy <Matthieu.Moy@imag.fr> wrote:\n\n> +use usual shell commands like \"cd\". The commands are ran from the same\n\n\"are run from the same\"\n\n> +directory as \"rebase -i\" was started (this directory is temporarily\n> +re-created if needed).\n\n\"was started in\"\n\n> +file_must_exist () {\n> +    if ! [ -f \"$1\" ]; then\n> +       echo \"file $1 not created.\"\n> +       false\n> +    fi\n> +}\n> +\n> +file_must_not_exist () {\n> +    if [ -f \"$1\" ]; then\n> +       echo \"file $1 created while it shouldn't have. $2\"\n> +       false\n> +    fi\n> +}\n> +\n> +dir_must_exist () {\n> +    if ! [ -d \"$1\" ]; then\n> +       echo \"dir $1 does not exist.\"\n> +       false\n> +    fi\n> +}\n> +\n> +dir_must_not_exist () {\n> +    if [ -d \"$1\" ]; then\n> +       echo \"dir $1 exists while it shouldn't. $2\"\n> +       false\n> +    fi\n> +}\n\nLeftover debugging code? Looks useful, but probably something we\nshould have in test-lib.sh as \"git_test\" as a wrapper for the \"test\"\nbuilt-in.\n"},{"id":"146994","messageId":"7vwrs8k978.fsf@alter.siamese.dyndns.org","threadId":"24555","inReplyTo":"vpqwrs9nxi5.fsf@bauges.imag.fr","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-08-02T21:15:23Z","receivedAt":"2010-08-02T21:15:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:\n\n> That's a good question. My original patch was running the command from\n> the toplevel, which is the natural way to implement it. I've changed\n> my mind to execute the command from the place where \"git rebase -i\"\n> was started (which means this has to be memorized in a temporary file\n> to be persistant accross \"git rebase --continue\"). I think this makes\n> more sense for the user, and I've actually already been biten by the\n> old behavior, running \"rebase -i\" from a doc/ subdirectory, and\n> wondering why my \"exec make\" was rebuilding the code itself.\n>\n> This comes with at least one drawback: if directory from which the\n> rebase was started didn't exist in the past, then we can't do a simple\n> \"cd\" to it. My implementation re-creates the directory temporarily, so\n> that the command can run, and cleans it up afterwards. The only really\n> problematic case is when the directory can not be created (like\n> directory/file conflict). It this case, the command is not ran, and\n> the script exits.\n\nSorry to join the discussion after you have already coded it, but I don't\nthink running the external command at a random subdirectory that the\noperation happened to have started makes much sense, as rebase is a\ntree-wide operation.  The user if s/he so chooses can chdir down (if the\ndirectory still exists in the revision in question) in the script, but I\nthink the built-in behaviour should be to just run it from the toplevel.\n"},{"id":"147021","messageId":"vpq4ofcb3sh.fsf@bauges.imag.fr","threadId":"24555","inReplyTo":"7vwrs8k978.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2010-08-03T06:37:02Z","receivedAt":"2010-08-03T06:37:02Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:\n>\n>> That's a good question. My original patch was running the command from\n>> the toplevel, which is the natural way to implement it. I've changed\n>> my mind to execute the command from the place where \"git rebase -i\"\n>> was started (which means this has to be memorized in a temporary file\n>> to be persistant accross \"git rebase --continue\"). I think this makes\n>> more sense for the user, and I've actually already been biten by the\n>> old behavior, running \"rebase -i\" from a doc/ subdirectory, and\n>> wondering why my \"exec make\" was rebuilding the code itself.\n>>\n>> This comes with at least one drawback: if directory from which the\n>> rebase was started didn't exist in the past, then we can't do a simple\n>> \"cd\" to it. My implementation re-creates the directory temporarily, so\n>> that the command can run, and cleans it up afterwards. The only really\n>> problematic case is when the directory can not be created (like\n>> directory/file conflict). It this case, the command is not ran, and\n>> the script exits.\n>\n> Sorry to join the discussion after you have already coded it, but I don't\n> think running the external command at a random subdirectory that the\n> operation happened to have started makes much sense, as rebase is a\n> tree-wide operation.  The user if s/he so chooses can chdir down (if the\n> directory still exists in the revision in question) in the script, but I\n> think the built-in behaviour should be to just run it from the toplevel.\n\nI'm waiting for other people's opinion, since I'm still not 100%\nconvinced, but this seems to reflect the majority's opinion.\n\nAnd the simplicity of the code also is an argument: even if the \"I'm\ntoo lazy to code it\" is not applicable anymore, the simplest version\nwill also be the simplest to maintain.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"147025","messageId":"AANLkTi=uPHiS2S2hR4=0oBC=s3SyEP_qo3+n8FNHkFEV@mail.gmail.com","threadId":"24555","inReplyTo":"AANLkTikMW=ueQXfjpXk8G2FLRN55j1aJsnNz2w19BmkH@mail.gmail.com","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Kris Shannon","fromEmail":"kris@shannon.id.au","sentAt":"2010-08-03T08:47:12Z","receivedAt":"2010-08-03T08:47:12Z","isPatch":true,"sender":{"key":"kris@shannon.id.au","avatar":"https://gravatar.com/avatar/13a7c0b3c50ffacf54f456e543023fd702898bb134165fa805f019930524151f?d=mp&s=160"},"body":"On 31 July 2010 23:56, Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n> On Fri, Jul 30, 2010 at 15:24, Matthieu Moy\n> <Matthieu.Moy@grenoble-inp.fr> wrote:\n> > Marc Branchaud <marcnarc@xiplink.com> writes:\n> >\n> >>> The name of the command may be subject to discussions. I've chosen\n> >>> \"run\", but maybe \"shell\" would be OK too. In both cases, it doesn't\n> >>> allow the one-letter version since both \"r\" and \"s\" are already used.\n> >>\n> >> \"exec\" with one-letter \"x\"?\n> >\n> > Thanks, that sounds good, yes. Any other thought?\n>\n> I like \"exec\".\nor (t)est.\n"},{"id":"147027","messageId":"vpqzkx45a4k.fsf@bauges.imag.fr","threadId":"24555","inReplyTo":"AANLkTi=uPHiS2S2hR4=0oBC=s3SyEP_qo3+n8FNHkFEV@mail.gmail.com","subject":"Re: [RFC/PATCH] rebase -i: add run command to launch a shell command","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2010-08-03T09:16:43Z","receivedAt":"2010-08-03T09:16:43Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Kris Shannon <kris@shannon.id.au> writes:\n\n> On 31 July 2010 23:56, Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>> On Fri, Jul 30, 2010 at 15:24, Matthieu Moy\n>> <Matthieu.Moy@grenoble-inp.fr> wrote:\n>> > Marc Branchaud <marcnarc@xiplink.com> writes:\n>> >\n>> >>> The name of the command may be subject to discussions. I've chosen\n>> >>> \"run\", but maybe \"shell\" would be OK too. In both cases, it doesn't\n>> >>> allow the one-letter version since both \"r\" and \"s\" are already used.\n>> >>\n>> >> \"exec\" with one-letter \"x\"?\n>> >\n>> > Thanks, that sounds good, yes. Any other thought?\n>>\n>> I like \"exec\".\n> or (t)est.\n\ntesting is one possibility, but you may want to do other things, like\n\npick <commit> ...\nexec perl -pi -e 's/foo/bar/g' file.c; git add file.c; git commit --amend\n\nor whatever. \"exec\" is definitely the best proposal.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"147344","messageId":"201008062307.54841.j6t@kdbg.org","threadId":"24555","inReplyTo":"1280743433-27803-1-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [PATCH] rebase -i: add exec command to launch a shell command","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2010-08-06T21:07:54Z","receivedAt":"2010-08-06T21:07:54Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"On Montag, 2. August 2010, Matthieu Moy wrote:\n> The typical usage pattern would be to run a test (or simply a compilation\n> command) at given points in history.\n\nWhat happens if the command modifies the worktree and/or the index?\n\n-- Hannes\n"},{"id":"147379","messageId":"vpqeiealsez.fsf@bauges.imag.fr","threadId":"24555","inReplyTo":"201008062307.54841.j6t@kdbg.org","subject":"Re: [PATCH] rebase -i: add exec command to launch a shell command","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2010-08-07T08:48:36Z","receivedAt":"2010-08-07T08:48:36Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Johannes Sixt <j6t@kdbg.org> writes:\n\n> On Montag, 2. August 2010, Matthieu Moy wrote:\n>> The typical usage pattern would be to run a test (or simply a compilation\n>> command) at given points in history.\n>\n> What happens if the command modifies the worktree and/or the index?\n\nSomething terrible :-(. The next patch application fails, and the\npatch is more or less silently dropped (I thought it would fail giving\nthe user an opportunity to fix and re-apply, but it doesn't).\n\nNew version checks working tree cleanness right after the command\nfinishes, and stops cleanly, in time (restart with \"git rebase\n--continue\"). This comes with a new test.\n\n(But the command can possibly create a new commit or amend the\nprevious one without problem)\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"147383","messageId":"1281171365-15399-1-git-send-email-Matthieu.Moy@imag.fr","threadId":"24555","inReplyTo":"vpqeiealsez.fsf@bauges.imag.fr","subject":"[PATCH 1/2 (new version)] rebase -i: add exec command to launch a shell command","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2010-08-07T08:56:04Z","receivedAt":"2010-08-07T08:56:04Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"The typical usage pattern would be to run a test (or simply a compilation\ncommand) at given points in history.\n\nThe shell command is ran (from the worktree root), and the rebase is\nstopped when the command fails, to give the user an opportunity to fix\nthe problem before continuing with \"git rebase --continue\".\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\nCompared to previous version (sorry, I didn't number them) :\n\n* Test the case where the command stops with a dirty tree, and fix it\n  (thanks to Johannes Sixt)\n\n* Reword the doc (drop \"usual\", and replace \"shell command\" with\n  \"shell features\" to give examples which aren't commands, but yet\n  useful)\n\nThat should adress everyones's comment.\n\n Documentation/git-rebase.txt  |   24 ++++++++++++++++++++\n git-rebase--interactive.sh    |   29 ++++++++++++++++++++++++\n t/lib-rebase.sh               |    2 +\n t/t3404-rebase-interactive.sh |   48 +++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 103 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex be23ad2..9c68b66 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -459,6 +459,30 @@ sure that the current HEAD is \"B\", and call\n $ git rebase -i -p --onto Q O\n -----------------------------\n \n+Reordering and editing commits usually creates untested intermediate\n+steps.  You may want to check that your history editing did not break\n+anything by running a test, or at least recompiling at intermediate\n+points in history by using the \"exec\" command (shortcut \"x\").  You may\n+do so by creating a todo list like this one:\n+\n+-------------------------------------------\n+pick deadbee Implement feature XXX\n+fixup f1a5c00 Fix to feature XXX\n+exec make\n+pick c0ffeee The oneline of the next commit\n+edit deadbab The oneline of the commit after\n+exec cd subdir; make test\n+...\n+-------------------------------------------\n+\n+The interactive rebase will stop when a command fails (i.e. exits with\n+non-0 status) to give you an opportunity to fix the problem. You can\n+continue with `git rebase --continue`.\n+\n+The \"exec\" command launches the command in a shell (the one specified\n+in `$SHELL`, or the default shell if `$SHELL` is not set), so you can\n+use shell features (like \"cd\", \">\", \";\" ...). The command is run from\n+the root of the working tree.\n \n SPLITTING COMMITS\n -----------------\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex b94c2a0..6dd5859 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -537,6 +537,34 @@ do_next () {\n \t\tesac\n \t\trecord_in_rewritten $sha1\n \t\t;;\n+\tx|\"exec\")\n+\t\tread -r command rest < \"$TODO\"\n+\t\tmark_action_done\n+\t\tprintf 'Executing: %s\\n' \"$rest\"\n+\t\t# \"exec\" command doesn't take a sha1 in the todo-list.\n+\t\t# => can't just use $sha1 here.\n+\t\tgit rev-parse --verify HEAD > \"$DOTEST\"/stopped-sha\n+\t\t${SHELL:-@SHELL_PATH@} -c \"$rest\" # Actual execution\n+\t\tstatus=$?\n+\t\tif test \"$status\" -ne 0\n+\t\tthen\n+\t\t\twarn \"Execution failed: $rest\"\n+\t\t\twarn \"You can fix the problem, and then run\"\n+\t\t\twarn\n+\t\t\twarn \"\tgit rebase --continue\"\n+\t\t\twarn\n+\t\t\texit \"$status\"\n+\t\tfi\n+\t\t# Run in subshell because require_clean_work_tree can die.\n+\t\tif ! (require_clean_work_tree)\n+\t\tthen\n+\t\t\twarn \"Commit or stash your changes, and then run\"\n+\t\t\twarn\n+\t\t\twarn \"\tgit rebase --continue\"\n+\t\t\twarn\n+\t\t\texit 1\n+\t\tfi\n+\t\t;;\n \t*)\n \t\twarn \"Unknown command: $command $sha1 $rest\"\n \t\tif git rev-parse --verify -q \"$sha1\" >/dev/null\n@@ -957,6 +985,7 @@ first and then run 'git rebase --continue' again.\"\n #  e, edit = use commit, but stop for amending\n #  s, squash = use commit, but meld into previous commit\n #  f, fixup = like \"squash\", but discard this commit's log message\n+#  x <cmd>, exec <cmd> = Run a shell command <cmd>, and stop if it fails\n #\n # If you remove a line here THAT COMMIT WILL BE LOST.\n # However, if you remove everything, the rebase will be aborted.\ndiff --git a/t/lib-rebase.sh b/t/lib-rebase.sh\nindex 6aefe27..6ccf797 100644\n--- a/t/lib-rebase.sh\n+++ b/t/lib-rebase.sh\n@@ -47,6 +47,8 @@ for line in $FAKE_LINES; do\n \tcase $line in\n \tsquash|fixup|edit|reword)\n \t\taction=\"$line\";;\n+\texec*)\n+\t\techo \"$line\" | sed 's/_/ /g' >> \"$1\";;\n \t\"#\")\n \t\techo '# comment' >> \"$1\";;\n \t\">\")\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 9f03ce6..7b0026e 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -64,6 +64,54 @@ test_expect_success 'setup' '\n \tdone\n '\n \n+test_expect_success 'rebase -i with the exec command' '\n+\tgit checkout master &&\n+\tFAKE_LINES=\"1 exec_touch_touch-one 2 exec_touch_touch-two exec_false exec_touch_touch-three 3 4\n+\t\texec_touch_\\\"touch-file__name_with_spaces\\\";_touch_touch-after-semicolon 5\" \\\n+\t\ttest_must_fail git rebase -i A &&\n+\ttest -f touch-one &&\n+\ttest -f touch-two &&\n+\t! test -f touch-three &&\n+\ttest $(git rev-parse C) = $(git rev-parse HEAD) || {\n+\t\techo \"Stopped at wrong revision:\"\n+\t\techo \"($(git describe --tags HEAD) instead of C)\"\n+\t\tfalse\n+\t} &&\n+\tgit rebase --continue &&\n+\ttest -f touch-three &&\n+\ttest -f \"touch-file  name with spaces\" &&\n+\ttest -f touch-after-semicolon &&\n+\ttest $(git rev-parse master) = $(git rev-parse HEAD) || {\n+\t\techo \"Stopped at wrong revision:\"\n+\t\techo \"($(git describe --tags HEAD) instead of master)\"\n+\t\tfalse\n+\t} &&\n+\trm -f touch-*\n+'\n+\n+test_expect_success 'rebase -i with the exec command runs from tree root' '\n+\tgit checkout master &&\n+\tmkdir subdir && cd subdir &&\n+\tFAKE_LINES=\"1 exec_touch_touch-subdir\" \\\n+\t\tgit rebase -i HEAD^ &&\n+\tcd .. &&\n+\ttest -f touch-subdir &&\n+\trm -fr subdir\n+'\n+\n+test_expect_success 'rebase -i with the exec command checks tree cleanness' '\n+\tgit checkout master &&\n+\tFAKE_LINES=\"exec_echo_foo_>file1 1\" \\\n+\t\ttest_must_fail git rebase -i HEAD^ &&\n+\ttest $(git rev-parse master^) = $(git rev-parse HEAD) || {\n+\t\techo \"Stopped at wrong revision:\"\n+\t\techo \"($(git describe --tags HEAD) instead of master^)\"\n+\t\tfalse\n+\t} &&\n+\tgit reset --hard &&\n+\tgit rebase --continue\n+'\n+\n test_expect_success 'no changes are a nop' '\n \tgit checkout branch2 &&\n \tgit rebase -i F &&\n-- \n1.7.2.1.52.g95e25.dirty\n"},{"id":"147382","messageId":"1281171365-15399-2-git-send-email-Matthieu.Moy@imag.fr","threadId":"24555","inReplyTo":"vpqeiealsez.fsf@bauges.imag.fr","subject":"[PATCH 2/2] test-lib: user-friendly alternatives to test [!] [-d|-f]","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2010-08-07T08:56:05Z","receivedAt":"2010-08-07T08:56:05Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"The helper functions are implemented, documented, and used in a few\nplaces to validate them, but not everywhere to avoid useless code churn.\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n(this one hasn't been modified since last time)\n\n t/README                      |    8 ++++++++\n t/t3404-rebase-interactive.sh |   18 +++++++++---------\n t/t3407-rebase-abort.sh       |    6 +++---\n t/test-lib.sh                 |   32 ++++++++++++++++++++++++++++++++\n 4 files changed, 52 insertions(+), 12 deletions(-)\n\ndiff --git a/t/README b/t/README\nindex 0d1183c..be760b9 100644\n--- a/t/README\n+++ b/t/README\n@@ -467,6 +467,14 @@ library for your script to use.\n    <expected> file.  This behaves like \"cmp\" but produces more\n    helpful output when the test is run with \"-v\" option.\n \n+ - test_file_must_exist <file> [<diagnosis>]\n+   test_file_must_not_exist <file> [<diagnosis>]\n+   test_dir_must_exist <dir> [<diagnosis>]\n+   test_dir_must_not_exist <dir> [<diagnosis>]\n+\n+   check whether a file/directory exists or doesn't. <diagnosis> will\n+   be displayed if the test fails.\n+\n  - test_when_finished <script>\n \n    Prepend <script> to a list of commands to run to clean up\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 7b0026e..aacdc8a 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -69,18 +69,18 @@ test_expect_success 'rebase -i with the exec command' '\n \tFAKE_LINES=\"1 exec_touch_touch-one 2 exec_touch_touch-two exec_false exec_touch_touch-three 3 4\n \t\texec_touch_\\\"touch-file__name_with_spaces\\\";_touch_touch-after-semicolon 5\" \\\n \t\ttest_must_fail git rebase -i A &&\n-\ttest -f touch-one &&\n-\ttest -f touch-two &&\n-\t! test -f touch-three &&\n+\ttest_file_must_exist touch-one &&\n+\ttest_file_must_exist touch-two &&\n+\ttest_file_must_not_exist touch-three \"(Rebase should have stopped before)\" &&\n \ttest $(git rev-parse C) = $(git rev-parse HEAD) || {\n \t\techo \"Stopped at wrong revision:\"\n \t\techo \"($(git describe --tags HEAD) instead of C)\"\n \t\tfalse\n \t} &&\n \tgit rebase --continue &&\n-\ttest -f touch-three &&\n-\ttest -f \"touch-file  name with spaces\" &&\n-\ttest -f touch-after-semicolon &&\n+\ttest_file_must_exist touch-three &&\n+\ttest_file_must_exist \"touch-file  name with spaces\" &&\n+\ttest_file_must_exist touch-after-semicolon &&\n \ttest $(git rev-parse master) = $(git rev-parse HEAD) || {\n \t\techo \"Stopped at wrong revision:\"\n \t\techo \"($(git describe --tags HEAD) instead of master)\"\n@@ -95,7 +95,7 @@ test_expect_success 'rebase -i with the exec command runs from tree root' '\n \tFAKE_LINES=\"1 exec_touch_touch-subdir\" \\\n \t\tgit rebase -i HEAD^ &&\n \tcd .. &&\n-\ttest -f touch-subdir &&\n+\ttest_file_must_exist touch-subdir &&\n \trm -fr subdir\n '\n \n@@ -191,7 +191,7 @@ 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+\ttest_dir_must_not_exist .git/rebase-merge\n '\n \n test_expect_success 'abort with error when new base cannot be checked out' '\n@@ -200,7 +200,7 @@ test_expect_success 'abort with error when new base cannot be checked out' '\n \ttest_must_fail git rebase -i master > output 2>&1 &&\n \tgrep \"Untracked working tree file .file1. would be overwritten\" \\\n \t\toutput &&\n-\t! test -d .git/rebase-merge &&\n+\ttest_dir_must_not_exist .git/rebase-merge &&\n \tgit reset --hard HEAD^\n '\n \ndiff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh\nindex 2999e78..0ca81fe 100755\n--- a/t/t3407-rebase-abort.sh\n+++ b/t/t3407-rebase-abort.sh\n@@ -38,7 +38,7 @@ testrebase() {\n \t\t# Clean up the state from the previous one\n \t\tgit reset --hard pre-rebase &&\n \t\ttest_must_fail git rebase$type master &&\n-\t\ttest -d \"$dotest\" &&\n+\t\ttest_dir_must_exist \"$dotest\" &&\n \t\tgit rebase --abort &&\n \t\ttest $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&\n \t\ttest ! -d \"$dotest\"\n@@ -49,7 +49,7 @@ testrebase() {\n \t\t# Clean up the state from the previous one\n \t\tgit reset --hard pre-rebase &&\n \t\ttest_must_fail git rebase$type master &&\n-\t\ttest -d \"$dotest\" &&\n+\t\ttest_dir_must_exist \"$dotest\" &&\n \t\ttest_must_fail git rebase --skip &&\n \t\ttest $(git rev-parse HEAD) = $(git rev-parse master) &&\n \t\tgit rebase --abort &&\n@@ -62,7 +62,7 @@ testrebase() {\n \t\t# Clean up the state from the previous one\n \t\tgit reset --hard pre-rebase &&\n \t\ttest_must_fail git rebase$type master &&\n-\t\ttest -d \"$dotest\" &&\n+\t\ttest_dir_must_exist \"$dotest\" &&\n \t\techo c > a &&\n \t\techo d >> a &&\n \t\tgit add a &&\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex e8f21d5..694bbe8 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -541,6 +541,38 @@ test_external_without_stderr () {\n \tfi\n }\n \n+# debugging-friendly alternatives to \"test [!] [-f|-d]\"\n+# The commands test the existence or non-existance of $1. $2 can be\n+# given to provide a more precise diagnosis.\n+test_file_must_exist () {\n+\tif ! [ -f \"$1\" ]; then\n+\t\techo \"file $1 doesn't exist. $*\"\n+\t\tfalse\n+\tfi\n+}\n+\n+test_file_must_not_exist () {\n+\tif [ -f \"$1\" ]; then\n+\t\techo \"file $1 exists. $*\"\n+\t\tfalse\n+\tfi\n+}\n+\n+test_dir_must_exist () {\n+\tif ! [ -d \"$1\" ]; then\n+\t\techo \"directory $1 doesn't exist. $*\"\n+\t\tfalse\n+\tfi\n+}\n+\n+test_dir_must_not_exist () {\n+\tif [ -d \"$1\" ]; then\n+\t\techo \"directory $1 exists. $*\"\n+\t\tfalse\n+\tfi\n+}\n+\n+\n # This is not among top-level (test_expect_success | test_expect_failure)\n # but is a prefix that can be used in the test script, like:\n #\n-- \n1.7.2.1.52.g95e25.dirty\n"}]}