git/list[1] front-page[2] threads[3] people[4] search[5] about
 

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

From
Ian Wienand <iwienand@redhat.com>
Date
May 22, 2024, 02:41 UTC
Message-ID
<20240522024133.1108005-1-iwienand@redhat.com>

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@redhat.com>
---
 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
Next: Eric Sunshine
Message 1 of 43 in “alias: document caveats and add trace of prepared command”
  1. alias: document caveats and add trace of prepared commandIan Wienand, May 22, 2024
  2. Eric SunshineMay 22, 2024
  3. Junio C HamanoMay 22, 2024
  4. Ian WienandMay 23, 2024
  5. 1/3 Documentation: alias: rework notes into pointsIan Wienand, May 23, 2024
  6. 2/3 Documentation: alias: add notes on shell expansionIan Wienand, May 23, 2024
  7. 3/3 run-command: show prepared commandIan Wienand, May 23, 2024
  8. Eric SunshineMay 23, 2024
  9. Ian WienandMay 23, 2024
  10. 1/3 Documentation: alias: rework notes into pointsIan Wienand, May 23, 2024
  11. 2/3 Documentation: alias: add notes on shell expansionIan Wienand, May 23, 2024
  12. 3/3 run-command: show prepared commandIan Wienand, May 23, 2024
  13. Junio C HamanoMay 23, 2024
  14. Junio C HamanoMay 23, 2024
  15. Junio C HamanoMay 24, 2024
  16. Ian WienandMay 24, 2024
  17. Junio C HamanoMay 24, 2024
  18. Ian WienandMay 24, 2024
  19. Junio C HamanoMay 24, 2024
  20. Ian WienandMay 25, 2024
  21. Junio C HamanoMay 23, 2024
  22. 1/3 Documentation: alias: rework notes into pointsIan Wienand, May 24, 2024
  23. 2/3 Documentation: alias: add notes on shell expansionIan Wienand, May 24, 2024
  24. 3/3 run-command: show prepared commandIan Wienand, May 24, 2024
  25. Junio C HamanoMay 24, 2024
  26. Junio C HamanoMay 24, 2024
  27. Ian WienandMay 25, 2024
  28. 1/3 Documentation: alias: rework notes into pointsIan Wienand, May 25, 2024
  29. 2/3 Documentation: alias: add notes on shell expansionIan Wienand, May 25, 2024
  30. 3/3 run-command: show prepared commandIan Wienand, May 25, 2024
  31. Junio C HamanoMay 25, 2024
  32. Junio C HamanoMay 25, 2024
  33. Ian WienandMay 25, 2024
  34. 1/3 Documentation: alias: rework notes into pointsIan Wienand, May 25, 2024
  35. 2/3 Documentation: alias: add notes on shell expansionIan Wienand, May 25, 2024
  36. Junio C HamanoMay 26, 2024
  37. Ian WienandMay 27, 2024
  38. 3/3 run-command: show prepared commandIan Wienand, May 25, 2024
  39. Junio C HamanoMay 26, 2024
  40. 1/3 Documentation: alias: rework notes into pointsIan Wienand, May 27, 2024
  41. 2/3 Documentation: alias: add notes on shell expansionIan Wienand, May 27, 2024
  42. Junio C HamanoMay 27, 2024
  43. 3/3 run-command: show prepared commandIan Wienand, May 27, 2024

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.