基本上我想尝试别名:
...执行命令:
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 |
......我怎么能做这个工作?
-
请注意,在git 1.8.2.1中,可以在没有shell函数的情况下执行此操作(使用$1的原始方法应该可行)。
-
@Eimantas您是否愿意在答案中详细说明? 它对我不起作用,我找不到任何关于它的文档。
-
@Eimantas虽然在发行说明中没有任何相关内容。
-
我可以确认我可以在Git 2.11中运行带有参数的shell命令,而不需要任何恶作剧。
最明显的方法是使用shell函数:
1 2
| [alias]
files ="!f() { git diff --name-status "$1^" "$1"; }; f" |
没有!的别名被视为Git命令;例如commit-all = commit -a。
使用!,它在shell中作为自己的命令运行,让你使用更强大的魔法。
UPD
因为命令是在存储库的根目录下执行的,所以在引用命令中的文件名时可以使用${GIT_PREFIX}变量
-
谢谢,这看起来完全正确:[alias] files ="!f(){echo $ 3 $ 2 $ 1;}; f"; $ git文件a b c => c b a
-
@jefromi @mipadi我不是一个热门的shell脚本编写者;你能详细说明为什么在函数定义开始时需要!吗?谢谢!
-
@KohnyiRbert:这实际上不是shell脚本问题;这是git配置的特别之处。没有!的别名被视为Git命令;例如commit-all = commit -a。使用!,它在shell中作为自己的命令运行,让你使用更强大的魔法。
-
@Jefromi啊!真的很高兴知道!我偶然发现了这个问题,为已经定义的别名(如sta = status和st = sta)创建一个新的git别名会失败,并出现一些错误消息:"xy不是git命令"。谢谢(你的)信息!
-
哈。我来到这里试图完成你的"文件"功能。希望自从你回答我的初步问题以及我甚至没有问过的问题后,我可以两次投票。
-
注意,!将在存储库的根目录下运行,因此在调用别名时使用相对路径将不会给出您可能期望的结果。
-
此解决方案会中断分支名称的选项卡完成
-
@RobertDailey它没有破坏它,它只是没有实现它。有关如何添加它,请参阅stackoverflow.com/questions/342969/。
-
注意:这不引用参数(一般来说是危险的)。此外,功能是不必要的。请参阅我的回答以获得更多解释
-
没有任何争论这个borks fatal: bad revision '^'。你期望它做的是品味问题。可以说,你希望它在HEAD上运行。这将实现:files ="!f() { git diff --name-status "${1:-HEAD}"^ "${1:-HEAD}"; }; f"。或者,可能更健全,您希望在工作树上运行它:files ="!f() { [ -n "$1" ] && git diff --name-status "$1"^ "$1" || git diff --name-status; }; f"。
您也可以直接引用sh(而不是创建函数):
1 2
| [alias]
files = !sh -c 'git diff --name-status $1^ $1' - |
(注意行尾的短划线 - 你需要它。)
-
......与jefromi的答案相比,有什么明显的好处吗?我的意思是:他的答案中的函数def是"alias-local",意味着你没有bash调用bash,对吧?无论如何,感谢替代实施。
-
我认为任何一种解决方案都不会对另一种解决方案产生好处 - 只有两种不同的方式可以做同样的事情。
-
我怀疑如果你想分享别名或使用多个shell,jefromi的解决方案会更好;如果sh不调用你当前的shell怎么办?
-
如果您正在共享该命令,则可能需要使用sh,因为它本身就是一个shell,并且它在绝大多数系统上都可用。仅当命令的工作方式与所有shell一样时,才使用默认shell。
-
我更喜欢--到-因为它更熟悉并且不太可能在某些时候意外地表示stdin。 (bash(1)中的" - 等于 - 的参数"是不可编辑的)
-
另请参阅官方Git Wiki - 带参数的高级别名。
-
我已经尝试了-和--,并在cygwin上使用bash 4.2.1获得了两行错误sh: -c: line 0: unexpected EOF while looking for matching ''sh: -c: line 1: syntax error: unexpected end of file 。
-
我用这个解决方案修复了它:unix.stackexchange.com/a/239510/394
-
结尾' - '的确切含义是什么?它在哪里记录?
-
@Zitrax:这里解释一下:stackoverflow.com/a/18128834/28804
-
注意:这不引用参数(一般来说是危险的)。创建子shell(使用sh -c)也是不必要的。请参阅我的答案。
-
请注意,这会创建一个额外的shell副本(总共两个);另外,出于兼容性原因,FreeBSD(及其衍生产品)sh已经破坏--处理(并且-是完全错误的)。我同意你最好的选择是从接受的答案中写出表单中的别名,并假设用户的shell与POSIX sh足够兼容。
-
编辑:但这个答案比他们更好。还要记住这个警告。
您正在寻找的别名是:
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 /;的文件名很重要
-
我喜欢'#'技巧
-
对于最简单的情况,这是正确的答案,实际上没有必要通过将其包装在函数或sh -c中来复杂化。
-
是的,!已经暗示sh -c(在预先GIT_TRACE=2时显示),因此不需要运行另一个子shell。你在更复杂的案件中看到了什么问题?
-
如果要设置默认参数,这是否有效?例如我想这样做来获取Github PR:fp ="! 1=${1:-$(git headBranch)}; 2=${2:-up}; git fetch -fu $2 pull/$1/head:$1; git checkout $1; git branch -u $2 #"。如果没有前两个语句,这种方法很有效,但如果使用它们则会失败。 (我也有headBranch = symbolic-ref --short HEAD)。
-
解决了它,如果你设置了新的参数,它就可以工作,所以这很好:fp ="! a=${1:-$(git headBranch)}; b=${2:-up}; git fetch -fu $b pull/$a/head:$a; git checkout $a; git branch -u $b #"。
-
为什么需要"报价?
-
@EugenKonkov引用是必需的,因为变量的扩展可能包含空格,我们希望将它们保留为单个shell标记。
-
这太棒了,谢谢汤姆!我只是用它来定义我的on git cat命令,请参阅stackoverflow.com/a/54819889/24468
使用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)。将"#"附加到别名也将允许位置参数而不留下尾随参数。
-
感谢您的解释:根据您的建议,这些命令对我原始问题起作用:files ="!git diff --name-status $1^ $1 #" files ="!git diff --name-status $1^"
如上面的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'
-
我也遇到了这个问题(命令在存储库的根目录下运行),但这个解决方案似乎没有做任何事情。 (如果重要,我正在使用OS X.)
-
哎呀... git别名是我做的别名。
-
(自git 1.8.2)git config --set alias.alias ='! git config --global别名。$ 1"$ 2"'
-
这最终为我工作:"使用cd ${GIT_PREFIX:-.} &&."为你的git别名(运行shell命令并需要正确的pwd)添加前缀(来源:stackoverflow.com/a/21929373/266309)
-
引用这个。 !cd"${GIT_PREFIX:-.}" && ls -al
-
@mirabilos谢谢,纠正。
我想用一个执行此操作的别名来执行此操作:
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; |
这样做的好处是它更易读,因为它在多行上。另外,我喜欢用-x和set -e来调用bash。你可以把这整件作为一个别名来做,但它会超级丑陋且难以维护。
因为文件名为git-m,所以可以像下面这样运行:git m foo bar
-
我也更喜欢这个,但是我无法弄清楚如何使用这种方法使用我想要的自动完成功能。在别名上你可以这样做:'!f() { : git branch ; ... }; f'它会自动将别名完成为一个非常方便的分支。
-
是的,我认为我更喜欢将非平凡的事情作为路径上的单个脚本文件来完成。不好的一面是肯定的,你放弃了像引用这样的东西的自动完成。您可以通过手动配置自己的自动完成来解决此问题。尽管如此,我喜欢你可以将脚本放到路径上的一个文件夹中它会开始工作,但是对于自动完成,你需要'加载'它,所以通常它在我的.bashrc文件中我资源。但我认为我不会像脚本本身那样改变脚本自动完成参数的方式,而且只是在开发期间。
刚碰到类似的东西;希望能发布我的笔记。有一点让我对带有参数的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中的"直接"方法不适用于位置参数。
-
很好的测试。快速检查所有可能性!
-
fail正在尝试运行一个名为"echo'A''B"的命令(即10个字符长)。来自sh -c"'echo a b'"的相同错误和相同的原因,引号层太多