There are a number of hidden caveats when using command/shell expansion ("!") in an alias. The command is exec'd, unless it can be split, when it is then run as an argument to "sh -c". Split commands have "$@" appended. Firstly this updates the documentation to explain this a little clearer. Secondly, this adds a trace point in prepare_cmd where this substitution is done, so we can see the command being run without having to resort to strace/code inspection. e.g. "test = !echo" you will show: $ GIT_TRACE=1 git test hello 10:58:56.877234 git.c:755 trace: exec: git-test hello 10:58:56.877382 run-command.c:657 trace: run_command: git-test hello 10:58:56.878655 run-command.c:657 trace: run_command: echo hello 10:58:56.878747 run-command.c:437 trace: prepare_cmd: /usr/bin/echo hello hello For a "split" alias, e.g. test = "!echo $*" you will see $ GIT_TRACE=1 git test hello 11:00:45.959420 git.c:755 trace: exec: git-test hello 11:00:45.959737 run-command.c:657 trace: run_command: git-test hello 11:00:45.961424 run-command.c:657 trace: run_command: 'echo $*' hello 11:00:45.961528 run-command.c:437 trace: prepare_cmd: /bin/sh -c 'echo $* "$@"' 'echo $*' hello hello hello which clearly shows you the appended "$@". This can be very helpful when an alias is giving you an unexpected synatx error that is very difficult figure out from only the run_command trace point, e.g. test = "!for i in 1 2 3; do echo $i; done" $ GIT_TRACE=1 test hello 11:02:39.813030 git.c:755 trace: exec: git-test hello 11:02:39.813233 run-command.c:657 trace: run_command: git-test hello 11:02:39.814384 run-command.c:657 trace: run_command: 'for i in 1 2 3; do echo $i; done' hello 11:02:39.814468 run-command.c:437 trace: prepare_cmd: /bin/sh -c 'for i in 1 2 3; do echo $i; done "$@"' 'for i in 1 2 3; do echo $i; done' hello for i in 1 2 3; do echo $i; done: -c: line 1: syntax error near unexpected token `"$@"' for i in 1 2 3; do echo $i; done: -c: line 1: `for i in 1 2 3; do echo $i; done "$@"' Signed-off-by: Ian Wienand <iwienand@xxxxxxxxxx> --- Documentation/config/alias.txt | 26 +++++++++++++++++++++----- run-command.c | 1 + 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Documentation/config/alias.txt b/Documentation/config/alias.txt index 01df96fab3..a3f090d79d 100644 --- a/Documentation/config/alias.txt +++ b/Documentation/config/alias.txt @@ -21,8 +21,24 @@ If the alias expansion is prefixed with an exclamation point, it will be treated as a shell command. For example, defining `alias.new = !gitk --all --not ORIG_HEAD`, the invocation `git new` is equivalent to running the shell command -`gitk --all --not ORIG_HEAD`. Note that shell commands will be -executed from the top-level directory of a repository, which may -not necessarily be the current directory. -`GIT_PREFIX` is set as returned by running `git rev-parse --show-prefix` -from the original current directory. See linkgit:git-rev-parse[1]. +`gitk --all --not ORIG_HEAD`. Note: ++ +* Shell commands will be executed from the top-level directory of a + repository, which may not necessarily be the current directory. +* `GIT_PREFIX` is set as returned by running `git rev-parse --show-prefix` + from the original current directory. See linkgit:git-rev-parse[1]. +* Single commands will be executed directly. Commands that can be "split" + (contain whitespace or shell-reserved characters) will be run as shell + commands via an argument to `sh -c`. +* When arguments are present to a "split" command running in a shell, + the shell command will have `"$@"` appended. The first non-command + argument to `sh -c` (i.e. `$0` to the command) is always the alias + string, and other user specified arguments will follow. +** This may initially be confusing if your command references argument + variables or is not expecting the presence of `"$@"`. For example: + `alias.echo = "!echo $1"` when run as `git echo arg` will execute + `sh -c "echo $1 $@" "echo $1" "1"` resulting in output `1 1`. + An alias `alias.for = "!for i in 1 2 3; do echo $i; done"` will fail + if any arguments are specified to `git for` as the appended `"$@"` will + create invalid shell syntax. Setting `GIT_TRACE=1` can help debug + the command being run. diff --git a/run-command.c b/run-command.c index 1b821042b4..36b2b2f194 100644 --- a/run-command.c +++ b/run-command.c @@ -435,6 +435,7 @@ static int prepare_cmd(struct strvec *out, const struct child_process *cmd) } } + trace_argv_printf(&out->v[1], "trace: prepare_cmd:"); return 0; } -- 2.45.1