Git alias with positional parameters

前端 未结 7 837
北荒
北荒 2020-11-22 12:35

Basically I\'m trying to alias:

git files 9fa3

...to execute the command:

git diff --name-status 9fa3^ 9fa3
相关标签:
7条回答
  • 2020-11-22 13:18

    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.

    0 讨论(0)
  • 2020-11-22 13:21

    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.

    0 讨论(0)
  • 2020-11-22 13:23

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

    0 讨论(0)
  • 2020-11-22 13:34

    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

    0 讨论(0)
  • 2020-11-22 13:38

    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'

    0 讨论(0)
  • 2020-11-22 13:40

    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 /;)

    0 讨论(0)
提交回复
热议问题