Git alias with positional parameters

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?

7 Solutions collect form web for “Git alias with positional parameters”

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.

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 = "![ 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 (https://stackoverflow.com/a/3322412/2955802)

« 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 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.

Git Baby is a git and github fan, let's start git clone.