[PATCH] alias: document caveats and add trace of prepared command

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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





[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux