Git alias with positional parameters

Basically I’m trying to alias:

git files 9fa3

…to execute the command:

  • Adding Git Bash Alias
  • `find -exec` in git alias
  • Cannot create simple Git alias in MinGW32 on windows 7
  • User Input to Bash Alias?
  • How can I define an alias for a Git subcommand (e.g. for `list` in `git stash list`)?
  • How to embed bash script directly inside a git alias
  • 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?

  • Push a tag to a remote repository using Git?
  • Pushing files from one Git repository to another
  • Mercurial convert extension not able to pull from remote Git repository
  • Setting umask in Git / Gitolite
  • Re-doing a reverted merge in Git
  • Git setup remote tracking branch
  • 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.