{"thread":{"id":"26815","subject":"Weird behavior of shell variables in git aliases","startedAt":"2011-03-21T16:39:43Z","lastAt":"2011-03-23T03:01:25Z","messageCount":20,"participants":["Dun Peal","Jeff King","Junio C Hamano","Lasse Makholm","Ævar Arnfjörð Bjarmason"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"163928","messageId":"d9c38309-c433-476c-bba3-f2c5b7e94a89@k15g2000prk.googlegroups.com","threadId":"26815","inReplyTo":null,"subject":"Weird behavior of shell variables in git aliases","fromName":"Dun Peal","fromEmail":"dunpealer@gmail.com","sentAt":"2011-03-21T16:39:43Z","receivedAt":"2011-03-21T16:39:43Z","isPatch":false,"sender":{"key":"dunpealer@gmail.com","avatar":"https://gravatar.com/avatar/42f3e6a1166eb44e33f24c20ccbe809fa8591413f366dfe3755c8a8d4935ace9?d=mp&s=160"},"body":"It seems that a variable is available only once?!  How can the\nfollowing shell session be explained:\n\n    $ git config alias.test0\n    !echo $1\n    $ git test0 foo\n    foo\n    $ git config alias.test1\n    !echo $1 && echo $1\n    $ git test1 foo\n\n    foo\n    $ git config alias.test2\n    !BRANCH=$1 && echo $BRANCH && echo $BRANCH\n    $ git test2 foo\n\n    foo\n\nThanks, D.\n"},{"id":"163988","messageId":"20110321215310.GA2122@sigill.intra.peff.net","threadId":"26815","inReplyTo":"d9c38309-c433-476c-bba3-f2c5b7e94a89@k15g2000prk.googlegroups.com","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-03-21T21:53:10Z","receivedAt":"2011-03-21T21:53:10Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Mar 21, 2011 at 09:39:43AM -0700, Dun Peal wrote:\n\n> It seems that a variable is available only once?!  How can the\n> following shell session be explained:\n> \n>     $ git config alias.test0\n>     !echo $1\n>     $ git test0 foo\n>     foo\n>     $ git config alias.test1\n>     !echo $1 && echo $1\n>     $ git test1 foo\n> \n>     foo\n>     $ git config alias.test2\n>     !BRANCH=$1 && echo $BRANCH && echo $BRANCH\n>     $ git test2 foo\n> \n>     foo\n\nBecause in v1.7.4 and earlier, we literally just tack the arguments\n(shell-quoted) onto the end of the string. So your alias ends up\nexpanding to:\n\n  /bin/sh -c \"!echo $1 && echo $1 'foo'\"\n\nSo the first echo is empty, and then the second one echos foo. And what\nyou are trying to do doesn't work with a straight alias (at the bottom\nI'll show you what you want).\n\nBut interestingly, that's _not_ the behavior as of Erik's 7f51f8b\n(alias: use run_command api to execute aliases, 2011-01-07), which is in\nmaster but not yet released. With that, we end up executing:\n\n  sh -c 'echo $1 && echo $1 \"$@\"' 'echo $1 && echo $1' 'foo'\n\nwhich prints \"foo foo\". So it is technically a regression. I don't know\nhow much we care; using positional parameters like this was already\nnonsensical, as shown above.\n\nFor reference, what you actually want (in either system) is:\n\n  $ git config alias.test1\n  !sh -c 'echo $1 && echo $1' -\n  $ git test1 foo\n  foo\n  foo\n\nMake sure to include the \"-\" (or some other string) which ends up as $0.\n\n-Peff\n"},{"id":"163994","messageId":"7v8vw8nmh2.fsf@alter.siamese.dyndns.org","threadId":"26815","inReplyTo":"20110321215310.GA2122@sigill.intra.peff.net","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-03-21T22:21:45Z","receivedAt":"2011-03-21T22:21:45Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> But interestingly, that's _not_ the behavior as of Erik's 7f51f8b\n> (alias: use run_command api to execute aliases, 2011-01-07), which is in\n> master but not yet released. With that, we end up executing:\n>\n>   sh -c 'echo $1 && echo $1 \"$@\"' 'echo $1 && echo $1' 'foo'\n>\n> which prints \"foo foo\". So it is technically a regression. I don't know\n> how much we care; using positional parameters like this was already\n> nonsensical, as shown above.\n>\n> For reference, what you actually want (in either system) is:\n>\n>   $ git config alias.test1\n>   !sh -c 'echo $1 && echo $1' -\n\nOh, I should have been paying a bit more attention.  I've been assuming\nthat we were turning \"!anything\" into { \"sh\", \"-c\", \"anything\", \"-\" }\nfollowed by the user supplied arguments.\n"},{"id":"163995","messageId":"7v4o6wnlxm.fsf@alter.siamese.dyndns.org","threadId":"26815","inReplyTo":"7v8vw8nmh2.fsf@alter.siamese.dyndns.org","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-03-21T22:33:25Z","receivedAt":"2011-03-21T22:33:25Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Jeff King <peff@peff.net> writes:\n>\n>> But interestingly, that's _not_ the behavior as of Erik's 7f51f8b\n>> (alias: use run_command api to execute aliases, 2011-01-07), which is in\n>> master but not yet released. With that, we end up executing:\n>>\n>>   sh -c 'echo $1 && echo $1 \"$@\"' 'echo $1 && echo $1' 'foo'\n>>\n>> which prints \"foo foo\". So it is technically a regression. I don't know\n>> how much we care; using positional parameters like this was already\n>> nonsensical, as shown above.\n>>\n>> For reference, what you actually want (in either system) is:\n>>\n>>   $ git config alias.test1\n>>   !sh -c 'echo $1 && echo $1' -\n>\n> Oh, I should have been paying a bit more attention.  I've been assuming\n> that we were turning \"!anything\" into { \"sh\", \"-c\", \"anything\", \"-\" }\n> followed by the user supplied arguments.\n\nThe attached quick hack gives\n\n  $ git config alias.silly\n  !echo hello $1; echo $# args, bye!\n  $ GIT_TRACE=1 ./git silly world funny\n  trace: exec: 'git-silly' 'world' 'funny'\n  trace: run_command: 'git-silly' 'world' 'funny'\n  trace: run_command: 'sh' '-c' 'echo hello $1; echo $# args, bye'\\!'' '-' 'world' 'funny'\n  trace: exec: 'sh' '-c' 'echo hello $1; echo $# args, bye'\\!''\n  '-' 'world' 'funny'\n  hello world\n  2 args, bye!\n\nbut it would penalize a properly written alias that uses \"sh -c <it> -\"\ntrick itself by double forking, which is not very nice and I am unhappy\nabout.\n\n git.c |   11 +++++++----\n 1 files changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/git.c b/git.c\nindex ef598c3..8d54466 100644\n--- a/git.c\n+++ b/git.c\n@@ -183,11 +183,14 @@ static int handle_alias(int *argcp, const char ***argv)\n \t\t\tcommit_pager_choice();\n \n \t\t\t/* build alias_argv */\n-\t\t\talias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1));\n-\t\t\talias_argv[0] = alias_string + 1;\n+\t\t\talias_argv = xmalloc(sizeof(*alias_argv) * (argc + 4));\n+\t\t\talias_argv[0] = \"sh\";\n+\t\t\talias_argv[1] = \"-c\";\n+\t\t\talias_argv[2] = alias_string + 1;\n+\t\t\talias_argv[3] = \"-\";\n \t\t\tfor (i = 1; i < argc; ++i)\n-\t\t\t\talias_argv[i] = (*argv)[i];\n-\t\t\talias_argv[argc] = NULL;\n+\t\t\t\talias_argv[i + 3] = (*argv)[i];\n+\t\t\talias_argv[argc + 3] = NULL;\n \n \t\t\tret = run_command_v_opt(alias_argv, RUN_USING_SHELL);\n \t\t\tif (ret >= 0)   /* normal exit */\n"},{"id":"164032","messageId":"AANLkTimH+eVUh6D5qK-PbNJGg46XJwaCii5zMg7xyZ_6@mail.gmail.com","threadId":"26815","inReplyTo":"7v4o6wnlxm.fsf@alter.siamese.dyndns.org","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Lasse Makholm","fromEmail":"lasse.makholm@gmail.com","sentAt":"2011-03-22T10:38:06Z","receivedAt":"2011-03-22T10:38:06Z","isPatch":false,"sender":{"key":"lasse.makholm@gmail.com","avatar":"https://gravatar.com/avatar/5bc3a34e4fa8ba26b38c333f370b15c034351e18e09b056a2608f3904ae1a4c1?d=mp&s=160"},"body":"On 21 March 2011 23:33, Junio C Hamano <gitster@pobox.com> wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n>> Oh, I should have been paying a bit more attention.  I've been assuming\n>> that we were turning \"!anything\" into { \"sh\", \"-c\", \"anything\", \"-\" }\n>> followed by the user supplied arguments.\n>\n> The attached quick hack gives\n>\n>  $ git config alias.silly\n>  !echo hello $1; echo $# args, bye!\n>  $ GIT_TRACE=1 ./git silly world funny\n>  trace: exec: 'git-silly' 'world' 'funny'\n>  trace: run_command: 'git-silly' 'world' 'funny'\n>  trace: run_command: 'sh' '-c' 'echo hello $1; echo $# args, bye'\\!'' '-' 'world' 'funny'\n>  trace: exec: 'sh' '-c' 'echo hello $1; echo $# args, bye'\\!''\n>  '-' 'world' 'funny'\n>  hello world\n>  2 args, bye!\n\nThat would IMHO be The Right Way to do it. Since the documentation for\naliases promises to to pass my alias to a shell if I prefix it with\n\"!\", I shouldn't have to add the \"sh -c ... -\" myself...\n\n> but it would penalize a properly written alias that uses \"sh -c <it> -\"\n> trick itself by double forking, which is not very nice and I am unhappy\n> about.\n\nA properly written alias that uses a trick? I guess that sums up the\nproblem... :-)\n\nAnyway, doesn't the existing way potentially break when passing funky\narguments containing spaces/quotes/something? We currently pass the\narguments to the alias command as a single quoted string. Passing them\nas seperate elements on argv seems a lot more robust...\n\nDo we risk breaking any existing \"!sh -c ... -\" aliases by applying this?\n\n/Lasse\n"},{"id":"164033","messageId":"AANLkTi=h4Upus2x4MTXXEX=PSrp-4xhEpzifsGXKycf5@mail.gmail.com","threadId":"26815","inReplyTo":"7v4o6wnlxm.fsf@alter.siamese.dyndns.org","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2011-03-22T10:52:00Z","receivedAt":"2011-03-22T10:52:00Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"On Mon, Mar 21, 2011 at 23:33, Junio C Hamano <gitster@pobox.com> wrote:\n\n> but it would penalize a properly written alias that uses \"sh -c <it> -\"\n> trick itself by double forking, which is not very nice and I am unhappy\n> about.\n\nIt would also be bizarre on Solaris where /bin/sh isn't a POSIX shell.\n\nBut we can substitute the relevant user-supplied shell during the build process.\n"},{"id":"164035","messageId":"20110322111844.GA32446@sigill.intra.peff.net","threadId":"26815","inReplyTo":"7v4o6wnlxm.fsf@alter.siamese.dyndns.org","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-03-22T11:18:44Z","receivedAt":"2011-03-22T11:18:44Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Mar 21, 2011 at 03:33:25PM -0700, Junio C Hamano wrote:\n\n> >> But interestingly, that's _not_ the behavior as of Erik's 7f51f8b\n> >> (alias: use run_command api to execute aliases, 2011-01-07), which is in\n> >> master but not yet released. With that, we end up executing:\n> >>\n> >>   sh -c 'echo $1 && echo $1 \"$@\"' 'echo $1 && echo $1' 'foo'\n> [...]\n> >\n> > Oh, I should have been paying a bit more attention.  I've been assuming\n> > that we were turning \"!anything\" into { \"sh\", \"-c\", \"anything\", \"-\" }\n> > followed by the user supplied arguments.\n\nYeah, I think that would be more useful in general, but...\n\n> The attached quick hack gives\n> \n>   $ git config alias.silly\n>   !echo hello $1; echo $# args, bye!\n>   $ GIT_TRACE=1 ./git silly world funny\n>   trace: exec: 'git-silly' 'world' 'funny'\n>   trace: run_command: 'git-silly' 'world' 'funny'\n>   trace: run_command: 'sh' '-c' 'echo hello $1; echo $# args, bye'\\!'' '-' 'world' 'funny'\n>   trace: exec: 'sh' '-c' 'echo hello $1; echo $# args, bye'\\!''\n>   '-' 'world' 'funny'\n>   hello world\n>   2 args, bye!\n> \n> but it would penalize a properly written alias that uses \"sh -c <it> -\"\n> trick itself by double forking, which is not very nice and I am unhappy\n> about.\n\nDoesn't it also break a lot of other more garden-variety aliases that\nrely on the automagic \"$@\", like:\n\n  $ git config alias.log-nopager\n  !git --no-pager log\n\n  $ GIT_TRACE=1 git.v1.7.4 log-nopager --oneline\n  trace: exec: 'git-log-nopager' '--oneline'\n  trace: run_command: 'git-log-nopager' '--oneline'\n  trace: alias to shell cmd: log-nopager => git --no-pager log '--oneline'\n  trace: built-in: git 'log' '--oneline'\n  93c7d44 foo\n\n  $ GIT_TRACE=1 git.master log-nopager --oneline\n  trace: exec: 'git-log-nopager' '--oneline'\n  trace: run_command: 'git-log-nopager' '--oneline'\n  trace: run_command: 'git --no-pager log' '--oneline'\n  trace: exec: 'sh' '-c' 'git --no-pager log \"$@\"' 'git --no-pager log' '--oneline'\n  trace: built-in: git 'log' '--oneline'\n  93c7d44 foo\n\n  $ GIT_TRACE=1 git.jch.shell-alias log-nopager --oneline\n  trace: exec: 'git-log-nopager' '--oneline'\n  trace: run_command: 'git-log-nopager' '--oneline'\n  trace: run_command: 'sh' '-c' 'git --no-pager log' '-' '--oneline'\n  trace: exec: 'sh' '-c' 'git --no-pager log' '-' '--oneline'\n  trace: built-in: git 'log'\n  commit 93c7d44635e8bb56a4fd864d024ce75a2ad4ffcf\n  Author: Jeff King <peff@peff.net>\n  Date:   Tue Mar 22 06:57:02 2011 -0400\n\n      foo\n\nI think the evolution of the alias code was something like:\n\n 1. Let's have alias.foo, so that \"git foo XXX\" can be aliased to \"git\n    log --whatever XXX\", just like shell aliases.\n\n 2. Oops, git doesn't allow some things in (1) that we might want to do,\n    like turning off the pager or choosing a new repo. For that we need\n    to respawn git, so now we have \"!git --whatever1 log --whatever2\".\n    It appends the arguments, just like the form in (1), so it is\n    consistent.\n\n 3. Oops, (2) doesn't allow complex mini-scripts that access the\n    parameters in a non-sequential way. You have to do \"!sh -c 'git\n    --whatever $1 log --whatever2 $2'\".\n\nKnowing that step (3) exists, I think your solution is a better one. The\nquestion is whether it is better enough to be worth breaking the people\nwho were helped by step (2).\n\nAnother way of looking at it is that types (1) and (2) are like shell\naliases. And they suck for complex things, just the way that shell\naliases do. The solution in the shell is to put your complex alias into\na shell function, or to push it into its own script. In git, you can\npush things into their own script, but we have no equivalent to a shell\nfunction. So one solution is:\n\n  git config function.silly '!echo hello $1; echo $# args, bye!'\n\nThe name is of course terrible. But I think the idea is sound that there\nare two different types of interface people may want. On the other hand,\nthis is getting kind of grossly complex. The separate script option is\nalways available.\n\n-Peff\n"},{"id":"164036","messageId":"20110322112832.GB32446@sigill.intra.peff.net","threadId":"26815","inReplyTo":"AANLkTimH+eVUh6D5qK-PbNJGg46XJwaCii5zMg7xyZ_6@mail.gmail.com","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-03-22T11:28:32Z","receivedAt":"2011-03-22T11:28:32Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Mar 22, 2011 at 11:38:06AM +0100, Lasse Makholm wrote:\n\n> > The attached quick hack gives\n> >\n> >  $ git config alias.silly\n> >  !echo hello $1; echo $# args, bye!\n> >  $ GIT_TRACE=1 ./git silly world funny\n> >  trace: exec: 'git-silly' 'world' 'funny'\n> >  trace: run_command: 'git-silly' 'world' 'funny'\n> >  trace: run_command: 'sh' '-c' 'echo hello $1; echo $# args, bye'\\!'' '-' 'world' 'funny'\n> >  trace: exec: 'sh' '-c' 'echo hello $1; echo $# args, bye'\\!''\n> >  '-' 'world' 'funny'\n> >  hello world\n> >  2 args, bye!\n> \n> That would IMHO be The Right Way to do it. Since the documentation for\n> aliases promises to to pass my alias to a shell if I prefix it with\n> \"!\", I shouldn't have to add the \"sh -c ... -\" myself...\n\nIt does pass it to the shell. You can do:\n\n  $ git config alias.autolog --oneline\n  !repo=`find-git-repo-for $PWD` && git --git-dir=\"$repo\" log\n\nfor an example of a shell-based alias. It just doesn't handle positional\nparameters the way you want. The typical solution is to invoke another\nshell, but you can also do:\n\n  $ grep -B1 silly .git/config\n  [alias]\n          silly = \"!foo() { echo hello $1; echo $# args, bye!\\n}\\nfoo\"\n\nwhich unsurprisingly looks like exactly the same solution one would use\nin the shell to avoid the fact that shell aliases suck for handling\npositional parameters.\n\n> > but it would penalize a properly written alias that uses \"sh -c <it> -\"\n> > trick itself by double forking, which is not very nice and I am unhappy\n> > about.\n> \n> A properly written alias that uses a trick? I guess that sums up the\n> problem... :-)\n\nYeah. Though it also penalizes non-tricky aliases. See my other mail in\nthis thread.\n\n> Anyway, doesn't the existing way potentially break when passing funky\n> arguments containing spaces/quotes/something? We currently pass the\n> arguments to the alias command as a single quoted string. Passing them\n> as seperate elements on argv seems a lot more robust...\n\nNo, the arguments in the current scheme are properly shell-quoted before\nthey are appended to the shell snippet. So they are equally robust.\n\n-Peff\n"},{"id":"164045","messageId":"AANLkTi=E3p8ue=-4LLiu4xFEXmC4gpa9moq=T2fQGJwR@mail.gmail.com","threadId":"26815","inReplyTo":"20110322112832.GB32446@sigill.intra.peff.net","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Lasse Makholm","fromEmail":"lasse.makholm@gmail.com","sentAt":"2011-03-22T12:59:54Z","receivedAt":"2011-03-22T12:59:54Z","isPatch":false,"sender":{"key":"lasse.makholm@gmail.com","avatar":"https://gravatar.com/avatar/5bc3a34e4fa8ba26b38c333f370b15c034351e18e09b056a2608f3904ae1a4c1?d=mp&s=160"},"body":"On 22 March 2011 12:28, Jeff King <peff@peff.net> wrote:\n>  $ grep -B1 silly .git/config\n>  [alias]\n>          silly = \"!foo() { echo hello $1; echo $# args, bye!\\n}\\nfoo\"\n>\n> which unsurprisingly looks like exactly the same solution one would use\n> in the shell to avoid the fact that shell aliases suck for handling\n> positional parameters.\n\nAtually, I think that's probably the most elegant way of dealing with\nthe issue... An example like this would be very useful in the alias\ndocumentation along with an explanation on how positional parameters\nare handled...\n\n-- \n/Lasse\n"},{"id":"164046","messageId":"20110322132820.GA14559@sigill.intra.peff.net","threadId":"26815","inReplyTo":"20110322111844.GA32446@sigill.intra.peff.net","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-03-22T13:28:20Z","receivedAt":"2011-03-22T13:28:20Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Mar 22, 2011 at 07:18:44AM -0400, Jeff King wrote:\n\n> Doesn't it also break a lot of other more garden-variety aliases that\n> rely on the automagic \"$@\", like:\n> \n>   $ git config alias.log-nopager\n>   !git --no-pager log\n> [...]\n>   $ GIT_TRACE=1 git.master log-nopager --oneline\n>   trace: exec: 'git-log-nopager' '--oneline'\n>   trace: run_command: 'git-log-nopager' '--oneline'\n>   trace: run_command: 'git --no-pager log' '--oneline'\n>   trace: exec: 'sh' '-c' 'git --no-pager log \"$@\"' 'git --no-pager log' '--oneline'\n>   trace: built-in: git 'log' '--oneline'\n>   93c7d44 foo\n> \n>   $ GIT_TRACE=1 git.jch.shell-alias log-nopager --oneline\n>   trace: exec: 'git-log-nopager' '--oneline'\n>   trace: run_command: 'git-log-nopager' '--oneline'\n>   trace: run_command: 'sh' '-c' 'git --no-pager log' '-' '--oneline'\n>   trace: exec: 'sh' '-c' 'git --no-pager log' '-' '--oneline'\n>   trace: built-in: git 'log'\n>   commit 93c7d44635e8bb56a4fd864d024ce75a2ad4ffcf\n>   Author: Jeff King <peff@peff.net>\n>   Date:   Tue Mar 22 06:57:02 2011 -0400\n> \n>       foo\n\nOne other solution would be to make the \"$@\" more magic by detecting\nwhen the alias uses positional parameters and omitting it in that\ncase.  Something like (on top of your patch):\n\ndiff --git a/git.c b/git.c\nindex 8d54466..1daf89c 100644\n--- a/git.c\n+++ b/git.c\n@@ -161,6 +161,27 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \treturn handled;\n }\n \n+static int alias_uses_positional_parameters(const char *s)\n+{\n+\twhile ((s = strchr(s, '$')))\n+\t\tif (s[1] == '@' || s[1] == '#' ||\n+\t\t   (s[1] >= '1' && s[1] <= '9'))\n+\t\t\treturn 1;\n+\treturn 0;\n+}\n+\n+static char *alias_to_shell(const char *in)\n+{\n+\tstruct strbuf out = STRBUF_INIT;\n+\n+\tif (alias_uses_positional_parameters(in))\n+\t\treturn xstrdup(in);\n+\n+\tstrbuf_addstr(&out, in);\n+\tstrbuf_addstr(&out, \" \\\"$@\\\"\");\n+\treturn strbuf_detach(&out, NULL);\n+}\n+\n static int handle_alias(int *argcp, const char ***argv)\n {\n \tint envchanged = 0, ret = 0, saved_errno = errno;\n@@ -186,7 +207,7 @@ static int handle_alias(int *argcp, const char ***argv)\n \t\t\talias_argv = xmalloc(sizeof(*alias_argv) * (argc + 4));\n \t\t\talias_argv[0] = \"sh\";\n \t\t\talias_argv[1] = \"-c\";\n-\t\t\talias_argv[2] = alias_string + 1;\n+\t\t\talias_argv[2] = alias_to_shell(alias_string + 1);\n \t\t\talias_argv[3] = \"-\";\n \t\t\tfor (i = 1; i < argc; ++i)\n \t\t\t\talias_argv[i + 3] = (*argv)[i];\n\n\nBut I think that is a little too magic for my taste. Although the false\npositives (\"!echo 'literal $#'\") and false negatives (you want \"!foo\" to\n_ignore_ its parameters) are pretty obscure, I would prefer to keep\nthings simple.\n\n-Peff\n"},{"id":"164047","messageId":"AANLkTin3fXkGaC5cTVny5adU=FusQV0WAcPHLUEeEzLi@mail.gmail.com","threadId":"26815","inReplyTo":"20110322132820.GA14559@sigill.intra.peff.net","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Lasse Makholm","fromEmail":"lasse.makholm@gmail.com","sentAt":"2011-03-22T13:35:10Z","receivedAt":"2011-03-22T13:35:10Z","isPatch":false,"sender":{"key":"lasse.makholm@gmail.com","avatar":"https://gravatar.com/avatar/5bc3a34e4fa8ba26b38c333f370b15c034351e18e09b056a2608f3904ae1a4c1?d=mp&s=160"},"body":"On 22 March 2011 14:28, Jeff King <peff@peff.net> wrote:\n> But I think that is a little too magic for my taste. Although the false\n> positives (\"!echo 'literal $#'\") and false negatives (you want \"!foo\" to\n> _ignore_ its parameters) are pretty obscure, I would prefer to keep\n> things simple.\n\nThen how about simply:\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 6468a68..8097480 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -586,9 +586,16 @@ If the alias expansion is prefixed with an\nexclamation point,\n it will be treated as a shell command.  For example, defining\n \"alias.new = !gitk --all --not ORIG_HEAD\", the invocation\n \"git new\" is equivalent to running the shell command\n-\"gitk --all --not ORIG_HEAD\".  Note that shell commands will be\n-executed from the top-level directory of a repository, which may\n-not necessarily be the current directory.\n+\"gitk --all --not ORIG_HEAD\". Note that any arguments you pass\n+when running aliases are simply appended to the shell command.\n+This means that \"alias.foo = !echo $# args: $1, $2 and $3\" will\n+not do what you expect. To use alias arguments as positional\n+parameters, wrap your command in a shell function:\n+\"alias.foo = !foo () { echo $# args: $1, $2 and $3; }; foo\"\n++\n+Shell commands will be executed from the top-level directory\n+of a repository, which may not necessarily be the current\n+directory.\n\n am.keepcr::\n \tIf true, git-am will call git-mailsplit for patches in mbox format\n-- \n/Lasse\n"},{"id":"164050","messageId":"20110322134358.GA19064@sigill.intra.peff.net","threadId":"26815","inReplyTo":"AANLkTin3fXkGaC5cTVny5adU=FusQV0WAcPHLUEeEzLi@mail.gmail.com","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-03-22T13:43:58Z","receivedAt":"2011-03-22T13:43:58Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Mar 22, 2011 at 02:35:10PM +0100, Lasse Makholm wrote:\n\n> On 22 March 2011 14:28, Jeff King <peff@peff.net> wrote:\n> > But I think that is a little too magic for my taste. Although the false\n> > positives (\"!echo 'literal $#'\") and false negatives (you want \"!foo\" to\n> > _ignore_ its parameters) are pretty obscure, I would prefer to keep\n> > things simple.\n> \n> Then how about simply:\n> \n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index 6468a68..8097480 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -586,9 +586,16 @@ If the alias expansion is prefixed with an\n> exclamation point,\n>  it will be treated as a shell command.  For example, defining\n>  \"alias.new = !gitk --all --not ORIG_HEAD\", the invocation\n>  \"git new\" is equivalent to running the shell command\n> -\"gitk --all --not ORIG_HEAD\".  Note that shell commands will be\n> -executed from the top-level directory of a repository, which may\n> -not necessarily be the current directory.\n> +\"gitk --all --not ORIG_HEAD\". Note that any arguments you pass\n> +when running aliases are simply appended to the shell command.\n> +This means that \"alias.foo = !echo $# args: $1, $2 and $3\" will\n> +not do what you expect. To use alias arguments as positional\n> +parameters, wrap your command in a shell function:\n> +\"alias.foo = !foo () { echo $# args: $1, $2 and $3; }; foo\"\n> ++\n> +Shell commands will be executed from the top-level directory\n> +of a repository, which may not necessarily be the current\n> +directory.\n\nYeah, that certainly improves the situation.\n\nA small formatting nit: Those long commands with punctuation get hard to\nread in the middle of a paragraph. Maybe something like this on top:\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 3916665..d2b7515 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -582,10 +582,18 @@ it will be treated as a shell command.  For example, defining\n \"git new\" is equivalent to running the shell command\n \"gitk --all --not ORIG_HEAD\". Note that any arguments you pass\n when running aliases are simply appended to the shell command.\n-This means that \"alias.foo = !echo $# args: $1, $2 and $3\" will\n-not do what you expect. To use alias arguments as positional\n+This means that\n++\n+----------------------\n+alias.foo = !echo $# args: $1, $2 and $3\n+----------------------\n++\n+will not do what you expect. To use alias arguments as positional\n parameters, wrap your command in a shell function:\n-\"alias.foo = !foo () { echo $# args: $1, $2 and $3; }; foo\"\n++\n+----------------------\n+alias.foo = !foo () { echo $# args: $1, $2 and $3; }; foo\n+----------------------\n +\n Shell commands will be executed from the top-level directory\n of a repository, which may not necessarily be the current\n\n-Peff\n\nPS Your patch was wrapped on the @@ line. You might want to check your\n   mailer settings.\n"},{"id":"164051","messageId":"AANLkTin_qX2S+orrj25=GjB6kf8B6GRSE+HVFUAkDGSb@mail.gmail.com","threadId":"26815","inReplyTo":"20110322134358.GA19064@sigill.intra.peff.net","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Lasse Makholm","fromEmail":"lasse.makholm@gmail.com","sentAt":"2011-03-22T13:53:29Z","receivedAt":"2011-03-22T13:53:29Z","isPatch":false,"sender":{"key":"lasse.makholm@gmail.com","avatar":"https://gravatar.com/avatar/5bc3a34e4fa8ba26b38c333f370b15c034351e18e09b056a2608f3904ae1a4c1?d=mp&s=160"},"body":"On 22 March 2011 14:43, Jeff King <peff@peff.net> wrote:\n>\n> Yeah, that certainly improves the situation.\n>\n> A small formatting nit: Those long commands with punctuation get hard to\n> read in the middle of a paragraph. Maybe something like this on top:\n\nThought about it but wasn't sure of the asciidoc formatting... I'll\nfix it and submit a proper patch...\n\n> PS Your patch was wrapped on the @@ line. You might want to check your\n>   mailer settings.\n\nYeah, gmail is sucky in that regard... :-/\n\n-- \n/Lasse\n"},{"id":"164057","messageId":"e81763ce-b933-40ca-b624-adfb26653ac9@s18g2000prg.googlegroups.com","threadId":"26815","inReplyTo":"AANLkTin_qX2S+orrj25=GjB6kf8B6GRSE+HVFUAkDGSb@mail.gmail.com","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Dun Peal","fromEmail":"dunpealer@gmail.com","sentAt":"2011-03-22T15:06:22Z","receivedAt":"2011-03-22T15:06:22Z","isPatch":false,"sender":{"key":"dunpealer@gmail.com","avatar":"https://gravatar.com/avatar/42f3e6a1166eb44e33f24c20ccbe809fa8591413f366dfe3755c8a8d4935ace9?d=mp&s=160"},"body":"Thanks a lot for the explanations, I was really wondering about that.\n\nIncidentally, I had a real reason to prefer using the `!echo...`\nsyntax over the `!sh -c 'echo...'` one;  I need my users to define the\nfollowing alias:\n\n    git checkout $1 && git submodule foreach 'git checkout $sha1'\n\nDue to the necessity of single-quoting the `foreach` expression, I\ncouldn't define it with the `!sh -c` syntax. And because of the\nproblems discussed in this thread, I still don't know how to have my\nusers define this alias.\n\nThanks, D.\n\nP.S. Incidentally, this alias is addressing a pitfall in the use of\nsubmodules that I think should not exist in the first place:\n\nIf a repo has a submodule, and head 'foo' of the repo has the\nsubmodule's HEAD pointing to head 'foo' of its origin, while head\n'bar' of the repo has the submodule HEAD pointing to head 'bar' of its\norigin, checking out bar/foo on the repository leaves the submodule in\nan outdated state (and `submodule update` doesn't help).\n"},{"id":"164065","messageId":"7voc53kqi4.fsf@alter.siamese.dyndns.org","threadId":"26815","inReplyTo":"20110322111844.GA32446@sigill.intra.peff.net","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-03-22T17:35:15Z","receivedAt":"2011-03-22T17:35:15Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n>> > Oh, I should have been paying a bit more attention.  I've been assuming\n>> > that we were turning \"!anything\" into { \"sh\", \"-c\", \"anything\", \"-\" }\n>> > followed by the user supplied arguments.\n>\n> Yeah, I think that would be more useful in general, but...\n> ...\n> Doesn't it also break a lot of other more garden-variety aliases that\n> rely on the automagic \"$@\", like:\n\nOf course, silly me.\n\nBecause the most common usage of the alias mechanism is to package the\ncommand name and common options and allow tacking other command line\narguments after them at runtime, and it is silly to require the user to\nsay \"$@\" (i.e. 'lg = \"log --oneline\"' example), any alias that wants to\nuse positional parameter should do the 'sh -c \"$str\" -' packaging itself.\n\nSo there is nothing to fix after all; I was just confused by seeing the\ninitial report which was just a usage error.\n\nSorry for the noise, and thanks.\n"},{"id":"164066","messageId":"7vk4frkqfz.fsf@alter.siamese.dyndns.org","threadId":"26815","inReplyTo":"20110322132820.GA14559@sigill.intra.peff.net","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-03-22T17:36:32Z","receivedAt":"2011-03-22T17:36:32Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> ... q&d hack to grep for $1 and the like without really parsing...\n> But I think that is a little too magic for my taste.\n\nLet's not go there.\n"},{"id":"164067","messageId":"7vfwqfkphn.fsf@alter.siamese.dyndns.org","threadId":"26815","inReplyTo":"20110322134358.GA19064@sigill.intra.peff.net","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-03-22T17:57:08Z","receivedAt":"2011-03-22T17:57:08Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Tue, Mar 22, 2011 at 02:35:10PM +0100, Lasse Makholm wrote:\n>\n>> On 22 March 2011 14:28, Jeff King <peff@peff.net> wrote:\n>> > But I think that is a little too magic for my taste. Although the false\n>> > positives (\"!echo 'literal $#'\") and false negatives (you want \"!foo\" to\n>> > _ignore_ its parameters) are pretty obscure, I would prefer to keep\n>> > things simple.\n>> \n>> Then how about simply:\n>> \n>> diff --git a/Documentation/config.txt b/Documentation/config.txt\n>> index 6468a68..8097480 100644\n>> --- a/Documentation/config.txt\n>> +++ b/Documentation/config.txt\n>> @@ -586,9 +586,16 @@ If the alias expansion is prefixed with an\n>> exclamation point,\n>>  it will be treated as a shell command.  For example, defining\n>>  \"alias.new = !gitk --all --not ORIG_HEAD\", the invocation\n>>  \"git new\" is equivalent to running the shell command\n>> -\"gitk --all --not ORIG_HEAD\".  Note that shell commands will be\n>> -executed from the top-level directory of a repository, which may\n>> -not necessarily be the current directory.\n>> +\"gitk --all --not ORIG_HEAD\". Note that any arguments you pass\n>> +when running aliases are simply appended to the shell command.\n>> +This means that \"alias.foo = !echo $# args: $1, $2 and $3\" will\n>> +not do what you expect. To use alias arguments as positional\n>> +parameters, wrap your command in a shell function:\n>> +\"alias.foo = !foo () { echo $# args: $1, $2 and $3; }; foo\"\n>> ++\n>> +Shell commands will be executed from the top-level directory\n>> +of a repository, which may not necessarily be the current\n>> +directory.\n>\n> Yeah, that certainly improves the situation.\n\nThe first addition is indeed a huge improvement.\n\n    Note that any argument you pass when running aliases are simply\n    appended to the shell command.\n\nThe original didn't explicitly say it but it really should have.  The\nexample that comes before it, \"alias.new = !...\", should be updated with\nan invocation that takes a parameter, perhaps like this:\n\n    With this alias defined:\n\n\t[alias] since = \"!gitk --all --since\"\n\n    you can view commits in the last week with:\n\n        $ git since 7.days\n\n    because this expands to \"gitk --all --since 7.days\" by concatenating\n    the arguments supplied at runtime to the alias.\n\nThen say that \"Note ...\" to stress that point.  The description at that\npoint has become much better.\n\nWith that understanding already there,\n\n    This means that \"alias.foo = !echo $# args: $1, $2 and $3\" will\n    not do what you expect.\n\nis no longer true; nobody sane would expect that if you made them realize\nthat \"simply appended\" already.  Just dropping that sentence would make\nthe resulting text flow much better.\n\n    If you want to refer to arguments given to the alias, you can\n    wrap it as a shell script, e.g.\n\n\t[alias] reversed = \"!sh -c 'echo $2 $1' -\"\n\n    or a shell function, e.g.\n\n\t[alias] reversed = \"!reversed() { echo $2 $1 } && reversed\"\n\n    and invoke it like so:\n\n\t$ git reversed one two\n        two one\n\nI personally think the former \"sh -c <str> -\" is the more traditional and\nwell understood form (iow, an idiom) for people who breathe shells.\n\n> +----------------------\n> +alias.foo = !echo $# args: $1, $2 and $3\n> +----------------------\n\nWhile I totally agree with the formatting advice you gave here, we may\nwant to avoid this notation; neither \"git config alias.foo = !echo ...\"\nnor writing \"alias.foo = ...\"  in .git/config file would work.\n"},{"id":"164072","messageId":"20110322183201.GA22534@sigill.intra.peff.net","threadId":"26815","inReplyTo":"7vfwqfkphn.fsf@alter.siamese.dyndns.org","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-03-22T18:32:01Z","receivedAt":"2011-03-22T18:32:01Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Mar 22, 2011 at 10:57:08AM -0700, Junio C Hamano wrote:\n\n> The first addition is indeed a huge improvement.\n> \n>     Note that any argument you pass when running aliases are simply\n>     appended to the shell command.\n> \n> The original didn't explicitly say it but it really should have.  The\n> example that comes before it, \"alias.new = !...\", should be updated with\n> an invocation that takes a parameter, perhaps like this:\n> \n>     With this alias defined:\n> \n> \t[alias] since = \"!gitk --all --since\"\n> \n>     you can view commits in the last week with:\n> \n>         $ git since 7.days\n> \n>     because this expands to \"gitk --all --since 7.days\" by concatenating\n>     the arguments supplied at runtime to the alias.\n> \n> Then say that \"Note ...\" to stress that point.  The description at that\n> point has become much better.\n> \n> With that understanding already there,\n> \n>     This means that \"alias.foo = !echo $# args: $1, $2 and $3\" will\n>     not do what you expect.\n> \n> is no longer true; nobody sane would expect that if you made them realize\n> that \"simply appended\" already.  Just dropping that sentence would make\n> the resulting text flow much better.\n\nAgreed, your version is better.\n\n>     If you want to refer to arguments given to the alias, you can\n>     wrap it as a shell script, e.g.\n> \n> \t[alias] reversed = \"!sh -c 'echo $2 $1' -\"\n> \n>     or a shell function, e.g.\n> \n> \t[alias] reversed = \"!reversed() { echo $2 $1 } && reversed\"\n> \n>     and invoke it like so:\n> \n> \t$ git reversed one two\n>         two one\n> \n> I personally think the former \"sh -c <str> -\" is the more traditional and\n> well understood form (iow, an idiom) for people who breathe shells.\n\nYeah, that is probably true. One advantage of the function idiom is that\nit doesn't happen inside single-quotes, so it's one less layer of\nquoting to deal with. And of course it saves a shell invocation. So I\nthink mentioning both is reasonable.\n\n> > +----------------------\n> > +alias.foo = !echo $# args: $1, $2 and $3\n> > +----------------------\n> \n> While I totally agree with the formatting advice you gave here, we may\n> want to avoid this notation; neither \"git config alias.foo = !echo ...\"\n> nor writing \"alias.foo = ...\"  in .git/config file would work.\n\nYeah, I didn't even think about that, but you are right.\n\n-Peff\n"},{"id":"164097","messageId":"AANLkTinvP1TvhNBsN+Y3wyhzg+qampa6J=3NMfwr76a=@mail.gmail.com","threadId":"26815","inReplyTo":"20110322183201.GA22534@sigill.intra.peff.net","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Lasse Makholm","fromEmail":"lasse.makholm@gmail.com","sentAt":"2011-03-22T22:22:04Z","receivedAt":"2011-03-22T22:22:04Z","isPatch":false,"sender":{"key":"lasse.makholm@gmail.com","avatar":"https://gravatar.com/avatar/5bc3a34e4fa8ba26b38c333f370b15c034351e18e09b056a2608f3904ae1a4c1?d=mp&s=160"},"body":"On 22 March 2011 19:32, Jeff King <peff@peff.net> wrote:\n> On Tue, Mar 22, 2011 at 10:57:08AM -0700, Junio C Hamano wrote:\n>> is no longer true; nobody sane would expect that if you made them realize\n>> that \"simply appended\" already.  Just dropping that sentence would make\n>> the resulting text flow much better.\n>\n> Agreed, your version is better.\n\nGood points...\n\n>> I personally think the former \"sh -c <str> -\" is the more traditional and\n>> well understood form (iow, an idiom) for people who breathe shells.\n>\n> Yeah, that is probably true. One advantage of the function idiom is that\n> it doesn't happen inside single-quotes, so it's one less layer of\n> quoting to deal with. And of course it saves a shell invocation. So I\n> think mentioning both is reasonable.\n\nAs one of those shell-breathing people I prefer the function-style for\nits lack of quotes:\n\ngit config alias.foo = !foo () { echo $# args: $1, $2 and $3; }; foo\n\nWhlie we're at it, is \"sh -c ... -\" and \"sh -c ... --\" equally\nportable, I wonder?\n\n>> > +----------------------\n>> > +alias.foo = !echo $# args: $1, $2 and $3\n>> > +----------------------\n>>\n>> While I totally agree with the formatting advice you gave here, we may\n>> want to avoid this notation; neither \"git config alias.foo = !echo ...\"\n>> nor writing \"alias.foo = ...\"  in .git/config file would work.\n>\n> Yeah, I didn't even think about that, but you are right.\n\nGood point, I was wondering about that but decided to take my clue\nfrom existing examples of the same notation...\n\nRight, I'll sleep() on it and cook a patch tomorow, attempting to take\nall of the above into account...\n\n-- \n/Lasse\n"},{"id":"164108","messageId":"7vmxkmilq2.fsf@alter.siamese.dyndns.org","threadId":"26815","inReplyTo":"AANLkTinvP1TvhNBsN+Y3wyhzg+qampa6J=3NMfwr76a=@mail.gmail.com","subject":"Re: Weird behavior of shell variables in git aliases","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-03-23T03:01:25Z","receivedAt":"2011-03-23T03:01:25Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lasse Makholm <lasse.makholm@gmail.com> writes:\n\n> Whlie we're at it, is \"sh -c ... -\" and \"sh -c ... --\" equally\n> portable, I wonder?\n\nI suspect anything would work, but it is customary to pass the dummy $0 by\nwriting \"-\" there.  People who breathe shells would be able to recognize\nthe idiom more easily if you stick to that well-known form.\n"}]}