【问题标题】:Reset a git branch globally (for all users)全局重置 git 分支(适用于所有用户)
【发布时间】:2015-04-26 20:03:45
【问题描述】:

在我们当前的工作流程中,我们有 2 个主要的 git 分支:

master - 稳定发布分支

testing - 每个人都在测试他们的代码

现在每个开发人员都会为他们开发的每个功能创建新的分支。完成后,他们将其合并到 testing,当我们的 QA 说可以继续时,他们将他们的分支合并到 master,然后部署到生产环境中。

随着时间的流逝,我们的testing 分支会被从未投入生产的提交污染。被遗弃的功能,被重写而不是修复的东西和其他东西。

为了使mastertesting 保持某种一致的状态,我们希望不时“重置”testing。目前,我们通过完全删除 testing 并将其从 master 重新分支来做到这一点。

这里最大的问题是我们需要确保每个开发人员也删除他的本地 testing 分支并检查它的新副本。 如果一位开发人员忘记这样做并再次推动测试,我们试图摆脱的所有脏提交都会回来。

有什么方法可以重置服务器上的分支以将其分发给所有用户?

一个可接受的解决方案是将testing 分支置于一个没有人可以在不进行本地重置的情况下再推送到它的状态。但我想不出办法。

mastertesting 之间创建差异并恢复提交不是一种选择,因为这会阻止这些提交中的每一个再次进入测试。

理想情况下,我会有一个脚本定期执行此重置,并且每个用户本地环境都不需要交互(git pull 除外)。

【问题讨论】:

    标签: git github git-branch remote-branch


    【解决方案1】:

    简短的回答是“不,你不能那样做”。

    请记住,每个克隆都是一个完整的独立实体1,除了它的origin 和(取决于克隆选项)一些的初始分支状态。2 一旦有人选择了一个名为testing 的分支并将其命名为origin/testing

    • 他们有你让他们有的提交;和
    • 他们有一个名为 origin/testing 的引用(“远程跟踪分支”),当他们连接到远程 origin 时,他们的 git 将自动更新,甚至在指示时修剪(删除)。

    到目前为止一切顺利,这个“自动修剪”动作听起来很棒。如果你能说服他们将remote.origin.prune 设置为true

    $ git config remote.origin.prune true
    

    然后,一旦您删除您的名为testing 的分支,他们的origin/testing 将在他们的下一个git fetch origin 上自动消失。

    他们创建一个名为testing 的分支时,问题就出现了。他们的 git 不会 删除这个分支,除非他们 要求这样做。就他们的 git 而言,他们的私有分支就是他们的私有分支。你不能说服他们的 git 删除他们的私人 testing,就像你不能说服他们的 git 删除他们的私人 experiment-22 一样。他们创造了它;这是他们的存储库;他们控制着它。

    (请注意,他们还可以控制自动修剪,因为他们可以随时git configremote.origin.prune 离开,或者false。这个设置是为了他们的方便,而不是你的——它与他们的remote.origin.fetch 设置一起使用,他们更改了这些设置,以便他们的git fetch 改变它的作用;它的初始默认设置是他们在运行git clone 时创建的。)

    可以继续使用此模型,前提是您让所有开发人员自行控制删除或清理此分支标签。但这不是要走的路。相反,您应该使用另一种模型:为您的开发人员创建一个新的不同分支标签,用于您正在进行的新(和不同)开发线。

    例如,您可能将 dev-feature-X 作为一个临时分支,您的开发人员都可以共享它来处理功能 X。当您全部完成它时,您可以随意保留或删除它,然后您的开发人员会接手自动删除(使用修剪设置)或不随意删除。同时,您创建了 dev-feature-Y 作为一个临时分支,您的开发人员都可以共享该分支,用于开发功能 Y,等等。


    1忽略不适用的特殊情况,例如“浅”克隆,至少。

    2如果你在没有--mirror的情况下克隆,源的分支将成为你的远程分支,并且在你检出一个之前你根本没有本地分支(通常是master,通常是最后一个clone 命令的步骤)。另外,clone 看不到源的钩子,所以这些钩子没有被克隆。 .git 目录中也没有任何其他特殊状态,例如 .git/info 中的项目。不过,这些都不会影响普通分支使用的原则。

    【讨论】:

    • 第一行似乎是真的。这根本不可能。告诉大家设置remote.origin.prune是没有问题的,但是由于我会删除服务器上的分支并立即重新创建它,所以它不会有任何效果。下一次推送将推送所有脏提交。我们已经使用了功能分支,但我们需要测试分支来进行持续集成,以便拥有一个可以自动构建和部署并且 QA 可以测试的中心点。
    • 我不确定您是如何实现 CI 的,但如果您只是有多个 CI 分支循环通过(“testing_1”、“testing_2”等)并将其中大部分删除最多有时,只有当开发人员设法不运行“git fetch”(并因此修剪)分支足够长的时间以使其恢复轮换时,您才会遇到问题。与上述基本思想相同,只​​是细节略有不同......
    【解决方案2】:

    随着时间的推移,我们的测试分支会被从未投入生产的提交污染。被遗弃的功能,被重写而不是修复的东西和其他东西。

    这怎么可能?显然,如果某个功能被放弃,那么您也应该将其从测试分支中删除,因为它似乎是您的看门人。基本上,如果您说您的测试分支随着时间的推移而受到污染,那么它就违背了测试分支的全部目的,因为现在您正在测试的东西并不代表您想要推送到生产环境的代码。

    如果某些事情没有成功,那么开发人员应该恢复他的更改并将提交推送到测试分支,在那里更改也会被恢复。

    在您的场景中,您应该全部或全部从测试合并到生产。

    【讨论】:

    • 虽然这是它应该的工作方式,但实际上并非如此。阻止 20 位开发人员“忘记”他们的功能是不可能的。更糟糕的是,当企业决定搁置某些事情并且开发人员不知道这是否仍然需要 3 个月后,或者它是否已经死亡时。
    • 你试过变基吗?也许这可以解决问题:git-scm.com/book/en/v2/Git-Branching-Rebasing
    • 您是否发布了您发现的任何随机链接?变基与我的问题无关。
    • @MrTweek,您的系统中的问题如何解决?也许它们应该保持打开(或处于其他非关闭状态),直到相关代码被合并或从测试中删除
    • 克里斯,这是一个非常敏捷的环境,提交不一定与票证相关。
    【解决方案3】:

    一种选择是通过以特殊方式合并到主分支来重置开发分支的状态。

    git checkout master
    git checkout -b new_testing
    git merge -s ours testing # this creates a merge commit, but
                              # its tree is that of the current work-tree
                              # which in our case is the same as master
    git checkout testing
    git merge ours_testing
    git branch -d new_testing
    

    我们需要创建临时的new_testing 分支,因为合并策略ours 保留当前树而不是其他树,并且没有等效的theirs 策略。

    在这之后你会得到一个像这样的分支结构

    *         (testing) merge
    |\
    | *       (master) last commit on master
    * |       last commit on testing
    | |
    

    但是测试的内容会和master的内容一致。

    这样做的好处是任何有本地提交测试的人 发生在last commit on testing 之后将能够像往常一样将他们的更改重新定位到origin/testing

    由于这不应中断通常的开发流程,因此没有理由不能经常(每晚?)进行。

    【讨论】:

    • 我刚试过这个。虽然它做了我需要做的事情,但它只是不将此信息分发给用户。只要任何用户运行git push,所有脏提交都会回到分支中。
    • 一个简单的推送不会把错误的提交放回去,只有push --force 会。但是,如果您的开发人员使用push --force,一切都会出错,他们将覆盖彼此的更改等。如果他们变基,他们将能够推送,但在这种情况下,错误的提交将会消失。正如@jthill 提到的,您可以通过在远程存储库中设置denynonfastforward 来阻止接受push --force
    【解决方案4】:

    如果一位开发人员忘记 [rebase] 并再次推送到 testing,那么我们试图摆脱的 [来自废弃的 testing 提示] 的所有脏提交都会回来。

    您无法控制其他人的存储库中发生了什么,但您可以控制他们推送到您的存储库中的内容。

    一个可接受的解决方案是将测试分支置于一个没有人可以在不进行本地重置的情况下再推送到它的状态。但我想不出办法。

    此预接收挂钩将拒绝通过合并引入不需要的历史记录的推送:

    #!/bin/sh
    #  Do not permit merges from unwanted history
    #set -x
    err=0
    while read old new ref; do              # for each pushed ref
    
            [[ ${old%[^0]*} = $old ]] && continue # new branches aren't checked.
    
            nomerge=$(git for-each-ref refs/do-not-merge --format='%(objectname)^!')
    
            if [[ $( git rev-list --count --ancestry-path --boundary $old..$new $nomerge
             ) != $( git rev-list --count --ancestry-path --boundary $old..$new ) ]]; then
                    echo "$ref doesn't allow merges from outdated history"
                    err=1
            fi
    done
    exit $err
    
    # why it works:
    
    # if adding nomerge commits' parents as ancestors has any effect, then the
    # nomerge commits are reachable without going through $old, i.e. they're 
    # in some merged history. So check whether adding the abandoned commits as
    # explicit ancestors to the push makes them show up, and refuse it if so.
    

    要标记不需要的提交,请在refs/do-not-merge 下引用它们,例如

    git config alias.no-further-merges-from \
      '!f() { git update-ref "refs/do-not-merge/$1-@`date +%Y-%m-%dT%H%M%S`" "$1"; }; f'
    

    所以放弃testing的仪式是

    git no-further-merges-from testing
    git checkout -B testing master
    

    如果您想标记以前放弃的提示,您可以通过 sha 或任何其他表达方式引用它们,比如

    git no-further-merges-from 'testing@{last october 31}'
    

    【讨论】:

    • git config receive.denynonfastforward true 似乎对此行为没有任何影响。它仍然将所有脏提交从本地分支推送到新的干净的远程分支。
    猜你喜欢
    • 2013-08-07
    • 1970-01-01
    • 2019-04-04
    • 2012-08-21
    • 2021-06-11
    • 2012-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多