Git别名与位置参数

use*_*575 244 git parameters alias command position

基本上我想尝试别名:

git files 9fa3
Run Code Online (Sandbox Code Playgroud)

...执行命令:

git diff --name-status 9fa3^ 9fa3
Run Code Online (Sandbox Code Playgroud)

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

[alias]
    files = "!git diff --name-status $1^ $1"
    files = "!git diff --name-status {1}^ {1}"
Run Code Online (Sandbox Code Playgroud)

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

退化的情况是:

$ git echo_reverse_these_params a b c d e
e d c b a
Run Code Online (Sandbox Code Playgroud)

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

Cas*_*bel 350

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

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

不带别名!被视为Git命令; 例如commit-all = commit -a.

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

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

  • 注意,`!`将在存储库的根目录下运行,因此在调用别名时使用相对路径将不会给出您可能期望的结果. (39认同)
  • 谢谢,这看起来完全正确:[alias] files ="!f(){echo $ 3 $ 2 $ 1;}; f"; $ git files abc => cba (8认同)
  • @RobertDailey它没有破坏它,它只是没有实现它.见http://stackoverflow.com/questions/342969/how-do-i-get-bash-completion-to-work-with-aliases如何添加它. (4认同)
  • **注意**:这不引用参数(一般来说是危险的).此外,功能是不必要的.有关详细说明,请参阅[我的回答](http://stackoverflow.com/a/39523506/5353461). (3认同)

mip*_*adi 91

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

[alias]
        files = !sh -c 'git diff --name-status $1^ $1' -
Run Code Online (Sandbox Code Playgroud)

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

  • 我更喜欢`--`到`-`,因为它更熟悉,并且在某些时候不太可能意外地意味着stdin.(bash(1)中的" - 等于 - 的参数"是不可编辑的) (12认同)
  • 如果你正在共享命令,你可能想要使用`sh`,因为它本身就是一个shell,它可以在绝大多数系统上使用.仅当命令的工作方式与所有shell一样时,才使用默认shell. (8认同)
  • 结尾' - '的确切含义是什么?它在哪里记录? (5认同)
  • **注意**:这不引用参数(一般来说是危险的).创建子shell(使用`sh -c`)也是不必要的.有关替代方案,请参阅[我的回答](http://stackoverflow.com/a/39523506/5353461). (4认同)
  • 另请参阅[官方Git Wiki - 带参数的高级别名](https://git.wiki.kernel.org/index.php/Aliases#Advanced_aliases_with_arguments). (3认同)
  • @Zitrax:在此处进行说明:http://stackoverflow.com/a/18128834/28804 (3认同)

Tom*_*ale 76

您正在寻找的别名是:

files = "!git diff --name-status \"$1\"^ \"$1\" #"
Run Code Online (Sandbox Code Playgroud)

使用参数验证:

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

最终#是重要-它可以防止所有的用户提供的参数从由所述外壳(它评论出来)被处理.

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

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

  • 是的,`!`已经暗示`sh -c`(在前面加上'GIT_TRACE = 2`时显示),所以不需要运行另一个子shell.你在更复杂的案件中看到了什么问题? (4认同)
  • 对于最简单的情况,这是正确的答案,实际上没有必要通过将其包装在函数或 sh -c 中来复杂化。 (2认同)
  • 工作了,如果你设置了新的参数,它就可以了,所以这很好:`fp ="!a = $ {1: - $(git headBranch)}; b = $ {2:-up}; git fetch -fu $ b pull/$ a/head:$ a; git checkout $ a; git branch -u $ b#"`. (2认同)

bsb*_*bsb 25

使用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
Run Code Online (Sandbox Code Playgroud)

您的原始命令适用于git版本1.8.3.4(Eimantas注意到这在1.8.2.1中已更改).

这些sh -c '..' --f() {..}; f选项都以不同的方式干净地处理"$ @"参数(请参阅GIT_TRACE).将"#"附加到别名也将允许位置参数而不留下尾随参数.


Pie*_*res 18

上面的Drealmer 所述:

" 小心, !将在存储库的根目录运行,因此在调用别名时使用相对路径将不会给出您可能期望的结果. - Drealmer 2013年8月8日16:28»

GIT_PREFIX 通过git设置到您所在的子目录,您可以通过首先更改目录来避免这种情况:

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


Dan*_*lan 8

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

git checkout $1;
git merge --ff-only $2;
git branch -d $2;
Run Code Online (Sandbox Code Playgroud)

最后,我创建了一个名为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;
Run Code Online (Sandbox Code Playgroud)

这有它的好处很多更清晰,因为它是多条线路上.另外,我喜欢能够用-x和调用bash set -e.你可以把这整件作为一个别名来做,但它会超级丑陋且难以维护.

因为该文件已命名,git-m您可以像这样运行它:git m foo bar

  • 我也更喜欢这个,但我无法弄清楚如何通过这种方法使用我想要的自动完成功能。对于别名,你可以这样做: `'!f() { : gitbranch ; ... }; f'` 它会自动完成别名作为分支,这非常方便。 (2认同)

sda*_*aau 5

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

如果别名扩展以感叹号为前缀,它将被视为一个 shell 命令。例如,定义“alias.new = !gitk --all --not ORIG_HEAD”,调用“git new”相当于运行shell命令“gitk --all --not ORIG_HEAD”。请注意,shell 命令将从存储库的顶级目录执行,该目录不一定是当前目录。[...]

我的看法——如果一个别名在带有感叹号前缀时“将被视为 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 "
Run Code Online (Sandbox Code Playgroud)

这是我运行这些别名的结果:

$ 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/
Run Code Online (Sandbox Code Playgroud)

...或:当您!git别名中的“原样”之后使用“普通”命令时- 然后git自动将参数列表附加到该命令!避免它的一种方法确实是将您的脚本作为函数调用 - 或作为sh -c.

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

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

[alias]
  # ...
  fail = ! \"echo 'A' 'B'\"
Run Code Online (Sandbox Code Playgroud)

... - 然后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
Run Code Online (Sandbox Code Playgroud)

我认为,由于git“看到”整个字符串仅作为一个参数!- 它试图将其作为可执行文件运行;相应地,它无法"echo 'A' 'B'"作为文件找到。

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