Git别名与位置参数

基本上我在尝试用别名:

git files 9fa3

...执行命令:

git diff --name-status 9fa3^ 9fa3

但是git似乎没有将位置参数传递给alias命令。我试过:

[alias]
files = "!git diff --name-status $1^ $1"
files = "!git diff --name-status {1}^ {1}"

...还有一些其他的,但都没有成功。

简并的情况是:

$ git echo_reverse_these_params a b c d e
e d c b a

...我该怎么做呢?

79889 次浏览

shell函数可以帮助实现这一点:

[alias]
files = "!f() { git diff --name-status \"$1^\" \"$1\"; }; f"

没有!的别名被视为Git命令;例如commit-all = commit -a

使用!,它在shell中作为自己的命令运行,让你使用像这样更强大的魔法。

< p > 乌利希期刊指南
因为命令是在存储库的根执行的,所以在命令

中引用文件名时可以使用${GIT_PREFIX}变量

你也可以直接引用sh(而不是创建一个函数):

[alias]
files = !sh -c 'git diff --name-status $1^ $1' -

(注意行末的破折号——你会需要的。)

只是碰到了类似的东西;希望可以发布我的笔记。关于git别名和参数,有一件事让我困惑,可能来自git help config(我的git版本为1.7.9.5):

如果别名展开以感叹号作为前缀,它将被视为shell命令。例如,定义“别名”。new = !gitk——all——not ORIG_HEAD",调用“;git new"是否等同于运行shell命令 “gitk——all——not orig_head”;注意,shell命令将从存储库的顶级目录执行, 不一定是当前目录。[…]< / p >

在我看来,如果一个别名“将被视为一个shell命令”;当前缀是感叹号时——为什么我需要使用函数,或者sh -c带参数;为什么不按原样编写我的命令呢?

我仍然不知道答案,但我认为实际上结果会有细微的不同。这里有一个小测试——把它扔到你的.git/config~/.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 "

下面是我运行这些别名得到的结果:

$ 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/

... 或者:当你用“plain”;命令在! "as-is"在git别名中-然后git自动将参数列表附加到该命令!避免这种情况的一种方法实际上是,以函数或sh -c的参数的形式调用脚本。

这里(对我来说)另一件有趣的事情是,在shell脚本中,人们通常期望自动变量$0是脚本的文件名。但对于git别名函数,$0参数基本上是指定该命令的整个字符串的内容(如在配置文件中输入的那样)。

这就是为什么,我猜,如果你碰巧引用错了-在下面的情况下,这将是转义外部双引号:

[alias]
# ...
fail = ! \"echo 'A' 'B'\"

... -那么git将失败(至少对我来说)有点神秘的消息:

$ git fail
"echo 'A' 'B'": 1: echo 'A' 'B': not found
fatal: While expanding alias 'fail': ' "echo 'A' 'B'"': No such file or directory

我认为,自从git "saw"作为!的唯一参数的整个字符串-它试图将其作为可执行文件运行;相应地,它无法找到"echo 'A' 'B'"作为文件。

在任何情况下,在上面git help config引用的上下文中,我推测更准确的说法是:"…调用“git new”;等价于运行shell命令"gitk——all——not ORIG_HEAD $@",其中$@是从运行时的命令行传递到git命令别名的参数. ...“。我想这也可以解释,为什么“直接”;OP中的方法不适用于位置参数。

使用git手册中描述的GIT_TRACE=1使别名处理透明:

$ 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

你原来的命令使用git 1.8.3.4版本(Eimantas注意到这在1.8.2.1中有所改变)。

sh -c '..' --f() {..}; f选项都以不同的方式干净地处理“$@”参数(参见GIT_TRACE)。在别名后面附加“#”还可以允许位置参数,而不保留后面的参数。

如Drealmer 以上所述:

“小心,!”将在存储库的根目录下运行,因此在调用别名时使用相对路径将不会得到预期的结果。- Drealmer 13年8月8日16:28»

GIT_PREFIX被git设置为你所在的子目录,你可以通过首先改变目录来规避这个问题:

Git配置——全局别名。ls”!cd " $ {GIT_PREFIX:。}”; ls -al'

我想用一个别名这样做:

git checkout $1;
git merge --ff-only $2;
git branch -d $2;

最后,我创建了一个名为git-m的shell脚本,其内容如下:

#!/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;

这样做的好处是更易读,因为它在多行上。另外,我喜欢能够调用bash与-xset -e。您可能可以使用别名来完成整个操作,但它将非常难看,并且难以维护。

因为文件名为git-m,你可以这样运行它:git m foo bar

你要找的别名是:

files = "!git diff --name-status \"$1\"^ \"$1\" #"

参数验证:

files = "!cd -- \"${GIT_PREFIX:-.}\" && [ x$# != x1 ] && echo commit-ish required >&2 || git diff --name-status \"$1\"^ \"$1\" #"

最后#很重要——它防止所有用户提供的参数被shell处理(它将它们注释掉)。

注意:git将所有用户提供的参数放在命令行的末尾。要查看此操作,请尝试:GIT_TRACE=2 git files a b c d

转义的报价(由于嵌套)对于包含空格或"; rm -rf --no-preserve-root /;的文件名很重要)