Basically I\'m trying to alias:
git files 9fa3
...to execute the command:
git diff --name-status 9fa3^ 9fa3
but git doesn\'t appear to pass positional parameters to the alias command. I have tried:
[alias]
files = \"!git diff --name-status $1^ $1\"
files = \"!git diff --name-status {1}^ {1}\"
...and a few others but those didn\'t work.
The degenerate case would be:
$ git echo_reverse_these_params a b c d e
e d c b a
...how can I make this work?
The most obvious way is to use a shell function:
[alias]
files = \"!f() { git diff --name-status \\\"$1^\\\" \\\"$1\\\"; }; f\"
An alias without !
is treated as a Git command; e.g. commit-all = commit -a
.
With the !
, it\'s run as its own command in the shell, letting you use stronger magic like this.
UPD
Because commands are executed at the root of repository you may use ${GIT_PREFIX}
variable when referring to the file names in commands
You can also reference sh
directly (instead of creating a function):
[alias]
files = !sh -c \'git diff --name-status $1^ $1\' -
(Note the dash at the end of the line -- you\'ll need that.)
The alias you are looking for is:
files = \"!git diff --name-status \\\"$1\\\"^ \\\"$1\\\" #\"
With argument validation:
files = \"!cd -- \\\"${GIT_PREFIX:-.}\\\" && [ x$# != x1 ] && echo commit-ish required >&2 || git diff --name-status \\\"$1\\\"^ \\\"$1\\\" #\"
The final #
is important - it prevents all the user-supplied arguments from being processed by the shell (it comments them out).
Note: git
puts all user-supplied arguments at the end of the command line. To see this in action, try: GIT_TRACE=2 git files a b c d
The escaped (due to nesting) quotes are important for filenames containing spaces or \"; rm -rf --no-preserve-root /;
)
Use GIT_TRACE=1 described on the git man page to make the alias processing transparent:
$ git config alias.files
!git diff --name-status $1^ $1
$ GIT_TRACE=1 git files 1d49ec0
trace: exec: \'git-files\' \'1d49ec0\'
trace: run_command: \'git-files\' \'1d49ec0\'
trace: run_command: \'git diff --name-status $1^ $1\' \'1d49ec0\'
trace: exec: \'/bin/sh\' \'-c\' \'git diff --name-status $1^ $1 \"$@\"\' \'git diff --name-status $1^ $1\' \'1d49ec0\'
trace: built-in: git \'diff\' \'--name-status\' \'1d49ec0^\' \'1d49ec0\' \'1d49ec0\'
trace: run_command: \'less -R\'
trace: exec: \'/bin/sh\' \'-c\' \'less -R\' \'less -R\'
MM TODO
Your original commands work with git version 1.8.3.4 (Eimantas noted this changed in 1.8.2.1).
The sh -c \'..\' --
and f() {..}; f
options both cleanly handle the \"$@\" parameters in different ways (see with GIT_TRACE). Appending \"#\" to an alias would also allow positional parameters without leaving the trailing ones.
As stated by Drealmer above:
« Be careful, ! will run at the root of the repository, so using relative paths when calling your alias will not give the results you might expect. – Drealmer Aug 8 \'13 at 16:28 »
GIT_PREFIX
being set by git to the subdirectory you\'re in, you can circumvent this by first changing the directory :
git config --global alias.ls \'!cd \"${GIT_PREFIX:-.}\"; ls -al\'
I wanted to do this with an alias that does this:
git checkout $1;
git merge --ff-only $2;
git branch -d $2;
In the end, I created a shell script named git-m that has this content:
#!/bin/bash -x
set -e
#by naming this git-m and putting it in your PATH, git will be able to run it when you type \"git m ...\"
if [ \"$#\" -ne 2 ]
then
echo \"Wrong number of arguments. Should be 2, was $#\";
exit 1;
fi
git checkout $1;
git merge --ff-only $2;
git branch -d $2;
This has the benefit that it\'s much more legible because it\'s on multiple lines. Plus I like being able to call bash with -x
and set -e
. You can probably do this whole thing as an alias, but it would be super ugly and difficult to maintain.
Because the file is named git-m
you can run it like this: git m foo bar
Just bumped into something similar; hope it\'s oK to post my notes. One thing that confuses me about git
aliases with arguments, probably comes from the git help config
(I have git version 1.7.9.5):
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. [...]
The way I see it - if an alias \"will be treated as a shell command\" when prefixed with exclamation point - why would I need to use a function, or sh -c
with arguments; why not just write my command as-is?
I still don\'t know the answer - but I think actually there is a slight difference in outcome. Here\'s a little test - throw this in your .git/config
or your ~/.gitconfig
:
[alias]
# ...
ech = \"! echo rem: \"
shech = \"! sh -c \'echo rem:\' \"
fech = \"! f() { echo rem: ; }; f \" # must have ; after echo!
echargs = \"! echo 0[[\\\"$0\\\"]] 1-\\\"$1\\\"/ A-\"$@\"/ \"
fechargs = \"! f() { echo 0[[\\\"$0\\\"]] 1-\\\"$1\\\"/ A-\"$@\"/ ; }; f \"
Here is what I get running these aliases:
$ git ech word1 word2
rem: word1 word2
$ git shech word1 word2
rem:
$ git fech word1 word2
rem:
$ git echargs word1 word2
0[[ echo 0[[\"$0\"]] 1-\"$1\"/ A-$@/ ]] 1-word1/ A-word1 word2/ word1 word2
$ git fechargs word1 word2
0[[ f() { echo 0[[\"$0\"]] 1-\"$1\"/ A-$@/ ; }; f ]] 1-word1/ A-word1 word2/
... or: when you\'re using a \"plain\" command after the !
\"as-is\" in a git
alias - then git
automatically appends the arguments list to that command! A way to avoid it, is indeed, to call your script as either a function - or as argument to sh -c
.
Another interesting thing here (for me), is that in a shell script, one typically expects the automatic variable $0
to be the filename of the script. But for a git
alias function, the $0
argument is, basically, the content of the entire string specifying that command (as entered in the config file).
Which is why, I guess, if you happen to misquote - in the below case, that would be escaping the outer double quotes:
[alias]
# ...
fail = ! \\\"echo \'A\' \'B\'\\\"
... - then git
would fail with (for me, at least) somewhat cryptic message:
$ git fail
\"echo \'A\' \'B\'\": 1: echo \'A\' \'B\': not found
fatal: While expanding alias \'fail\': \' \"echo \'A\' \'B\'\"\': No such file or directory
I think, since git
\"saw\" a whole string as only one argument to !
- it tried to run it as an executable file; and correspondingly it failed finding \"echo \'A\' \'B\'\"
as a file.
In any case, in context of the git help config
quote above, I\'d speculate that it\'s more accurate to state something like: \" ... the invocation \"git new\" is equivalent to running the shell command \"gitk --all --not ORIG_HEAD $@\", where $@ are the arguments passed to the git command alias from command line at runtime. ... \". I think that would also explain, why the \"direct\" approach in OP doesn\'t work with positional parameters.