关于命令:带位置参数的Git别名

Git alias with positional parameters

基本上我想尝试别名:

1
git files 9fa3

...执行命令:

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

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

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

......以及其他一些但不起作用。

退化的情况是:

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

......我怎么能做这个工作?


最明显的方法是使用shell函数:

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

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

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

UPD
因为命令是在存储库的根目录下执行的,所以在引用命令中的文件名时可以使用${GIT_PREFIX}变量


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

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

(注意行尾的短划线 - 你需要它。)


您正在寻找的别名是:

1
files ="!git diff --name-status "$1"^ "$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 /;的文件名很重要


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

1
2
3
4
5
6
7
8
9
10
11
$ 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所述:

? 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由git设置到您所在的子目录中,您可以通过首先更改目录来避免这种情况:

git config --global alias.ls '!cd"${GIT_PREFIX:-.}"; ls -al'


我想用一个执行此操作的别名来执行此操作:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/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;

这样做的好处是它更易读,因为它在多行上。另外,我喜欢用-xset -e来调用bash。你可以把这整件作为一个别名来做,但它会超级丑陋且难以维护。

因为文件名为git-m,所以可以像下面这样运行:git m foo bar


刚碰到类似的东西;希望能发布我的笔记。有一点让我对带有参数的git别名感到困惑,可能来自git help config(我有git版本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. [...]

我看待它的方式 - 如果别名"将被视为shell命令",当以感叹号作为前缀时 - 为什么我需要使用函数,或者sh -c带参数;为什么不按原样写我的命令?

我仍然不知道答案 - 但我认为实际上结果略有不同。这是一个小测试 - 把它放在你的.git/config或你的~/.gitconfig中:

1
2
3
4
5
6
7
[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"

以下是我运行这些别名的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ 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/

...或者:当你在git别名中的!"as-is"之后使用"plain"命令时,git会自动将参数列表附加到该命令!实际上,一种避免它的方法是将脚本称为函数 - 或者作为sh -c的参数。

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

这就是为什么,我想,如果你碰巧错误引用 - 在下面的例子中,那将是逃避外部双引号:

1
2
3
[alias]
  # ...
  fail = ! "echo 'A' 'B'"

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

1
2
3
$ 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"看到"整个字符串只是!的一个参数 - 它试图将其作为可执行文件运行;相应地,它找不到"echo 'A' 'B'"作为文件。

在任何情况下,在上面的git help config引用的上下文中,我推测它更准确地陈述类似:"......调用"git new"等同于运行shell命令"gitk --all - -not ORIG_HEAD $ @",其中$ @是在运行时从命令行传递给git命令别名的参数。"。我认为这也可以解释为什么OP中的"直接"方法不适用于位置参数。