首页 > 解决方案 > 使用 husky hook 强制推送到另一个远程分支

问题描述

我想跑步

git push origin --force CURRENT_BRANCH_NAME:sandbox

在预推钩中。

如何得到

CURRENT_BRANCH_NAME 

作为上述命令的一部分?

我知道我必须git branch --show-current返回当前的分支名称。只是不确定如何将其输出与上述 git 命令一起使用。

标签: gitshell

解决方案


首先,我将回答您实际提出的问题:

  • 要获取当前分支名称,请使用git branch --show-currentorgit symbolic-ref --short HEADgit rev-parse --abbrev-ref HEAD请注意,这三个命令做的事情略有不同! 请参阅下面的详细信息。

  • 在与 POSIX 兼容的 shell 中,要将一个命令的输出替换为另一个命令中的某个位置作为参数,请使用反引号或$(and )。我更喜欢这个$(...)序列,因为它可以正确嵌套:也就是说,你可以$(...)在里面放另一个$(...),因为括号嵌套它就可以工作。反引号并非如此(它们可以工作,但更棘手)。

接下来,我会注意到你不应该费心去做这件事。原因很简单:你必须在上面三项中选择一项,其中两项可能是错误的,都是不必要的。只需使用:

git push --force origin HEAD:sandbox

或者,为了在其中两个项目可能错误的情况下严格正确:

git push --force origin HEAD:refs/heads/sandbox

此变体使用完全限定引用 ,refs/heads/sandbox来引用远程 Git 中的分支名称 sandbox,而不管尝试HEAD转换为 Git 提交哈希 ID 的结果如何。

为什么这有效

git push命令将提交(不是文件,不是分支,只是提交和其他支持的 Git 对象)推送到另一个 Git。一旦提交(和/或其他支持 Git 对象)已经提交另一个 Git 并准备好在那里使用,然后 通过询问(非强制推送)或命令(强制推送)另一个 Git 创建、删除来git push 结束,或更新存储库中的某些名称。

要实现这一系列事件,git push需要:

  • 一些提交和/或其他支持 Git 对象的原始哈希 ID,如果 / 需要,它应该发送;
  • 它应该要求/命令其他 Git 设置或删除的名称,以及要执行的操作(设置与删除);和
  • 强制标志(可以是简单的关闭/开启,或者是更复杂的“带租约”样式选项之一)。

您的 Git(您的软件,从您的存储库发送到另一个将软件接收到目标存储库的 Git)从以下位置获取这三个项目:

  • --force像或+in+HEAD:refs/heads/sandbox--deletein之类的标志git push --delete,它们位于明显的位置;
  • 对于应该发送的原始哈希 ID ,从对的左侧开始src:dst
  • 从同一对的右侧更新名称。

Git 调用该对本身,有或没有可选的+强制标志,一个refspec。refspec 也是如此feature:sandboxHEAD:sandbox也是 refspec。事实上,即使是退化形式:

git push origin main

例如,使用 refspec:它只是缺少冒​​号的一个,因此您只提供src一部分。这是一个部分refspec,在这种情况下,Git 使用与您为源提供的名称相同git push的目标名称(因为这git fetch也适用于 refspecs,但它对部分 refspecs 的处理是不同的)。

由于您已经计划使用包含冒号的完整refspec,因此左侧的项目不需要是name。您的 Git 将发送给另一个 Git的名称来自 refspec 的右侧。

现在,这里有一个小问题:发送 Git(即你的)如何知道是否要求接收Git设置分支名称(例如)如果你给你的 Git 一个不合格的名字,比如?答案是您的 Git 和/或他们的 Git 会尽力猜测您的意思是哪种名称,但总的来说,当您这样做时 - 提供完整的refspec,也就是说 - 提供完整的ref是明智的refs/heads/moorefs/tags/moorefs/for/moomoo在右侧,以便您确定您打算要求其他 Git 设置(或删除)什么。脚本尤其如此,脚本可能在没有人工监督的情况下运行。

因此这里有一个一般规则

当您作为人类跑步时:

git push origin feature3 v1.2

您知道这feature3是您自己的本地分支名称,并且v1.2是您自己的本地标记名称,因此您知道这确实是refs/heads/feature3:refs/heads/feature3and refs/tags/v1.2:refs/tags/v1.2。但是在通常是一次写入多次运行的脚本中,明确的做法更明智。

Coda:三个不同的命令

Git 有两种 HEAD 的“模式”:attacheddetached。(注意:Git 将后者称为分离 HEAD 模式;我将附加 HEAD 模式命名为明显的对应物,但它不是正式的 Git 名称。)在附加-HEAD 模式中,名称HEAD是对分支名称的符号引用. 1但是, 在分离模式下,会保存一个原始提交哈希 ID。HEAD

当您处于附加-HEAD 模式时,该命令git branch --show-current将显示附加到的名称,但当您处于分离-HEAD 模式时,它将静默不打印任何内容并以成功状态退出HEAD

该命令在 attach-HEAD 模式下打印附加git symbolic-ref HEAD到的分支的全名,并在 detached-HEAD 模式下产生错误消息、无标准输出和非零退出代码。它打印分支名称的简短版本,但在分离时仍会产生相同的 no-output-but-error-instead。HEAD--short

该命令git rev-parse --abbrev-ref HEAD在 attach-HEAD 模式下打印当前分支的缩短名称,并HEAD在 detached-HEAD 模式下打印单词。

这里还有另一种模式值得一提,虽然它非常罕见:当你在一个“孤儿”或“未出生”分支上时(Git 使用这两个术语来指代这个状态),名称HEAD是一个分支名称的符号引用不存在。在这种状态下,git branch --show-current两者git symbolic-ref HEAD都成功(并打印不存在的分支名称),但都git rev-parse --abbrev-rev HEAD失败了。

要测试您处于哪种模式并获取提交哈希 ID:

detached=false unborn=false
branch=$(git symbolic-ref --short HEAD 2>/dev/null) || detached=true
if ! $detached; then
    git rev-parse -q --verify HEAD >/dev/null || unborn=true
fi

执行完这五行后,$detached保持是否HEAD分离(作为一个简单的布尔结果),如果没有分离$branch则保持分支名称,并包含测试当前是否有提交的结果,或者进行新的提交将创建当前分支. (很少需要。)HEAD$unborn$unborn


1在古老的原始 Git 中,这是通过符号链接实现的:ln -s refs/heads/master HEAD例如。当它被移植到缺少符号链接的 Windows 时,Git 不得不放弃这个特定的快捷方式。


推荐阅读