【问题标题】:git push --force/-f - how to get each branchgit push --force/-f - 如何获取每个分支
【发布时间】:2015-02-24 19:47:04
【问题描述】:

这与在 git push.default matching 设置有关。此外,在此特定情况下分叉回购也不适用。我们在工作中使用 github,由于它不允许使用 pre-receive 钩子,我们必须尝试通过 pre-push 钩子来缓解这种情况(如果你问我,这有点半途而废,但确实如此)。

无论如何,我正在尝试编写一个pre-push 挂钩,以防止有人意外强制更新我们的master 分支(或我们想要的任何分支)。然而,当我尝试做一个简单的尝试获取分支名称的测试时,我似乎只能得到当前签出的分支。我已经使用了这三个 git“方法”来做到这一点:

  1. $(git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/')
  2. $(git symbolic-ref HEAD)
  3. $(git rev-parse --abbrev-ref HEAD)

这三个都给了我相同的结果。这是一个例子:

[dude (master)] $ git rebase -i head^
# reword a commit to get into a force-push state
[dude (master)] $ git checkout test3
[dude (test3)] $ git push 
branch 2>: test3 # output by the hook
symbolic-ref: refs/heads/test3 # output by the hook
rev-parse: test3 # output by the hook
--- pre-push: nothing to check # output by the hook
To git@github.com:dude/stuff.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'git@github.com:dude/stuff.git'
hint: bla bla bla

现在应用强制更新:

[dude (test3)] $ git push -f
branch 2>: test3 # output by the hook
symbolic-ref: refs/heads/test3 # output by the hook
rev-parse: test3 # output by the hook
--- pre-push: nothing to check # output by the hook
Counting objects: 1, done.
Writing objects: 100% (1/1), 185 bytes | 0 bytes/s, done.
Total 1 (delta 0), reused 0 (delta 0)
To git@github.com:dude/stuff.git
 + 060fa0b...763516d master -> master (forced update)

如您所见,仅引用了 test3 分支。我想知道 git 什么时候会推送 master 分支。有谁知道这里的强制更新在幕后做了什么?

【问题讨论】:

  • 我没有给你答案,但在环顾四周时,我发现了这个:gist.github.com/pixelhandler/5718585 似乎你可以使用它?
  • @loganfsmyth - 我认为这不能解决脚本使用current_branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,') 获取当前分支的问题,根据我上面的示例,该分支不起作用。
  • 还有@loganfsmyth 这个命令push_command=$(ps -ocommand= -p $PPID) 只得到推送命令(git push -f 是我的输出)
  • 您在使用 GitHub Enterprise 吗?如果是这样,他们似乎可以选择禁用强制推送(请参阅Blocking force pushes to a repository)。

标签: git github hook githooks


【解决方案1】:

Git 确实只有“一个当前分支”的概念,即存储在HEAD 中的任何符号引用(如果有)。 (如果你处于“分离 HEAD”模式,那么你根本不在任何分支上,所以完整的答案是在 git 中,你要么根本没有分支,要么是存储在 HEAD 中的一个分支。)

然而,push 命令采用任意的、用户指定的“refspecs”集。简单地说,refspec 只是一对源-目标引用,例如master:masterrefs/heads/master:refs/heads/master。如果您在 push 命令中指定 no refspecs,git 会使用一些默认值,正如您所观察到的那样,它随着时间的推移发生了变化(它曾经是 matching,现在是 simple,但是它可以通过push.default 配置以及remote.<em>name</em>.push 和更多项目进行更改。1

可以使用前导 + 来扩充 Refspecs 以仅为该 refspec 设置 --force,或者当然您可以在 push 命令行上使用 --force 为所有 refspecs 设置它。而且,对于git push,refspec 的“源”端可以是任意提交标识符(例如HEAD 或原始SHA-1),或者为“删除”操作为空;并且“目标”端不需要完全限定(git通常会自己找出分支与标签)。

我认为重现 git push 使用的规则太难了,尤其是在 pre-push 钩子中。而且,不幸的是,预推送挂钩不会为您提供--force 全局设置 per-refspec +-or-from-global force 标志的副本。但是,它确实会在标准输入上为您提供建议更新的完整列表(以及作为参数的远程名称和 URL)。因此:

#! /bin/sh
# sample pre-push hook, not tested
case "$1" in
origin) ;;
*)
    echo "pushing to $1, not doing any checking" 1>&2
    exit 0;;  # allow the push
esac

STATUS=0

disallow() {
    echo "pre-push hook: stopping your push" 1>&2
    echo "reason: $@" 1>&2
    STATUS=1
}

# pushing to remote named "origin": do some simple tests
while read localref localsha remoteref remotesha; do
    case $remoteref in
        refs/heads/master)
            disallow "push goes to master"
            ;;
        # add more tests here as desired
    esac
done

exit $STATUS

为了(完全)推送到服务器上的master,可以简单地将origin 远程复制到另一个名称,然后复制git push Xorigin master:master。或者,您可以使用类似loganfsmyth's comment 中的脚本来尝试猜测是否在命令行中给出了--force(或加号)。

另一种可能性是在推送过程中联系远程(可能通过 URL,在通过 URL 推送的情况下,但这会使一切复杂化),使用 fetch 更新有关其提交的信息图,然后尝试发现所提出的标签移动是否实际上是快进。我不确定这是否可行(git 是否允许在您提议更新推送时读取远程存储库?它可能会,但我不知道),这有点棘手,它仍然无法判断用户是否特别要求 force-push,但它可以解决大部分问题。 p>


1从技术上讲,“匹配”和“简单”都不是参考规范。相反,这些是 git 计算 refspec(s) 的规则。这当然是问题的一部分:在您在命令行上输入命令的点和开始各种数据传输的点之间应用了很多内部魔法。 pre-push 钩子在 refspecs 被计算之后调用,但在你的 git 开始向远程发送更新之前。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-21
    • 2012-09-16
    • 2012-08-31
    • 2016-07-10
    • 1970-01-01
    • 2014-06-19
    相关资源
    最近更新 更多