Git别名:多个命令,可变参数

4

我经常发现自己在打这个:

git push remote1 branch1 branch2 tag1 tag2 tag3..
git push remote2 branch1 branch2 tag1 tag2 tag3..

我希望有一个别名,可以用它代替我需要输入的内容:

git pushall branch1 branch2 tag1 tag2 tag3 ..

注意:我知道我可以创建一个新的远程“all”并使用多个URL。让我们不在这里讨论这个问题,而是专注于别名!我可以硬编码远程名称,因为我有很多项目都有相同的多个远程名称(通常是“drupal”和“github”)。
到目前为止的进展:
我已经想出了一个非可变参数版本:
[alias]
pushall = "!git push github $1; git push drupal $1; #"

这里有两个技巧:

  • 使用双引号来防止 ';' 在 .ini 文件中具有特殊含义
  • # 可以忽略行末的内容。

但这只能一次推送一个分支或标签。所以我需要输入以下命令:

git pushall branch1
git pushall branch2
git pushall tag1
git pushall tag2
git pushall tag3
...

我希望有一个别名,可以输入以下内容:
git pushall branch1 branch2 tag1 tag2 tag3 ..

为什么不能有多个push urls的新远程“all”?

正如所说,让我们专注于别名,以便读者可以找到他们正在寻找的内容。

无论如何,以下是我不创建远程“all”的原因:

  • 我必须在每个项目中都这样做,而不能在全局范围内执行。在我的情况下,将远程名称硬编码到全局别名中实际上是可行的!
  • 据我所知,我会在历史记录中污染引用,如“all/branch1”,而不是或除了“remote1/branch1”和“remote2/branch1”之外。

讨论这个问题的正确地方在这里:从多个远程位置拉取/推送

另请参阅

以下内容与可变参数无关,但它们没有涉及到:

以下内容可能有所帮助,但它涉及到纯shell脚本,而不是特定的git别名:


请参见https://dev59.com/4HA75IYBdhLWcg3wYYBQ,其中包含有关Git别名和位置参数的信息。 - torek
@torek,这个链接属于我已经提到的那些类别之一。出于完整性考虑,我将其添加到“另请参阅”列表中。 - donquixote
我从多个答案中学到的相关信息是,$@ 在 shell 脚本中表示可变参数,在 git 别名中也适用。我之前并不知道这一点。现在我不知道该接受哪个答案了... 我再看看吧。 - donquixote
总的技巧是将您的别名定义为一个shell函数,然后将所有参数传递给该别名作为其参数。然后您可以使用shell脚本,这是一种真正的(名义上的图灵完备)语言。在某些特殊情况下,您不需要诉诸于全功率模式(axiac的答案),但我通常直接转向函数,它们并不那么复杂。 - torek
4个回答

2
这个问题已经在你链接的其他问题中有所回答,但为了更清晰明了,我再解释一遍:
[alias]
    pushall = "!git push github \"$@\"; git push drupal \"$@\"; :"

或者从命令行设置:

git config --global alias.pushall '!git push github "$@"; git push drupal "$@"; :'

我没有在其他答案中看到"$@"的提及,至少不是明确地。我之前并不知道这个结构。记录一下,我刚刚发现这也会插入像-l--version(对于ls)这样的选项。但是--help选项似乎被传递给了git命令。 - donquixote
在一个小的测试运行中,没有双引号包围$@也可以工作。如果我省略它们会有什么可能出错? - donquixote
在本例中,可能并没有太大影响,因为你不能使用带有空格或其他可能会导致参数列表在单词拆分期间发生更改的字符的分支名称。 - CB Bailey

2
将任意脚本打包成git别名的习语是将其放入shell函数中:
pushall = "! f() { git push github \"$@\"; git push drupal \"$@\"; }; f"

我想指出正确使用$@的方法是将其放置在双引号内:"$@"


1
通过扩展您的初始尝试:
[alias]
pushall = "!git push github $@; git push drupal"

这样,git pushall branch1 branch2 branch3 将扩展为:
git push github branch1 branch2 branch3; git push drupal branch1 branch2 branch3
#                  |               |                        |               |
#                  +-------+-------+                        +-------+-------+
# these arguments were     |                                        |
# expanded from $@ --------+                                        |
#                                                                   |
#                  these are the arguments of the original command -+

$@扩展为所有命令行参数。
行末不需要#; 片段git pushall将被别名的值替换,其余参数保留。

如果您有更大的远程存储库列表,可以按照以下方式编写:

[alias]
pushall = "!for repo in github drupal bitbucket; do git push $repo $@; done #"
#                         |               |
#                         +-------+-------+
# put all your repos here         |
# separated by spaces ------------+

这次需要使用 # 标记。它将原始参数转换为注释;否则,命令会出现语法错误并无法运行。
如果您想要推送到存储库的所有远程,则可以编写一个更智能的别名:
pushall = "! for repo in $(git remote); do git push $repo $@; done #"

它运行git remote以查找所有远程,并使用命令替换替换$(...)git remote命令的输出,然后继续执行。
您可以使用以下方法将其定义为全局别名:
$ git config alias.pusha '! for repo in $(git remote); do git push $repo $@; done #'

如果您有一些仓库不想推送到所有远程仓库,您可以将其定义为本地别名,并在每个仓库中使用此命令自定义远程仓库列表:当您在该仓库中时。
$ git config --local alias.pusha '! for repo in github drupal; do git push $repo $@; done #'

我想在结尾省略$@; #可以节省一些字符。但这也使整个代码不太“对称”。所以也许我更喜欢稍微长一点的版本。 - donquixote
我更喜欢 for 版本,它更加灵活。你可以在 dodone 之间挤入更多的命令(以 ; 结尾)。例如,在 push 之后,你可以调用一个程序(如 Ubuntu 上的 notify-send)来显示桌面通知。 - axiac

0
仅供记录,以下的“作弊方法”确实可行。虽然不是完整的答案,但对许多人来说已经足够了。
[alias]
pushall = "!git push github $1 $2 $3 $4 $5; git push drupal $1 $2 $3 $4 $5; :"

是的,这并不是真正的可变参数,因为它仅限于5个参数(或在创建别名时选择的任何数字)。此外,它无法传递像--key=value这样的选项。但正如所说,这可能已经足够好了。

在我的特定用例中,大多数情况下我只推送一个分支和一个发布标签,因此两个参数($1 $2)就足够了。

请注意,末尾的:似乎具有与#相同的效果。我在stackoverflow的其他地方学到了这一点。

我不会“接受”这个答案,因为我想留给其他人提出更好的解决方案的机会。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接