【问题标题】:How can I prevent foxtrot merges in my 'master' branch?如何防止在我的“主”分支中合并狐步舞?
【发布时间】:2016-06-28 00:40:53
【问题描述】:

狐步舞合并是一种合并,其中'origin/master'作为第二个(或更高的)父级合并,如下所示:

Commit 'D' 是一个 foxtrot 合并,因为 'origin/master' 是它的第二个父级。请注意,此时来自“origin/master”的第一父级历史记录如何包含提交“B”。

但在我的 git repo 中,我需要所有涉及“origin/master”的合并,以将“origin/master”保持为第一个父级。不幸的是,git 在评估提交是否有资格进行快进时并不关心父顺序。这会导致我的主分支上的第一个父历史记录有时会丢失曾经存在的提交(例如,“git log --first-parent”的输出)。

当从早期图表中推送提交“D”时会发生以下情况:

如何防止这种推送?在推送 foxtrot 合并后,“origin/master”的第一父级历史不再包含提交“B”!

显然没有提交或工作实际上丢失,只是在我的环境中我真的需要“git log --first-parent”作为提交的稳定累积记录 - 如果你愿意,一种“Write-Once”多读”(WORM)数据库。我有使用“git log --first-parent”的脚本和流程来生成变更日志和发行说明,以及在我的票务系统 (JIRA) 中管理票证转换。 Foxtrot 合并正在破坏我的脚本!

是否可以在我的 git 存储库中安装某种预接收挂钩以防止 foxtrot 合并被推送?

附言此 stackoverflow 问题中的提交图是使用 http://bit-booster.com/graph.html 生成的。

【问题讨论】:

标签: git merge git-merge


【解决方案1】:

以下 pre-receive 钩子将阻止那些:

#/bin/bash

# Copyright (c) 2016 G. Sylvie Davies. http://bit-booster.com/
# Copyright (c) 2016 torek. http://stackoverflow.com/users/1256452/torek
# License: MIT license. https://opensource.org/licenses/MIT
while read oldrev newrev refname
do
if [ "$refname" = "refs/heads/master" ]; then
   MATCH=`git log --first-parent --pretty='%H %P' $oldrev..$newrev |
     grep $oldrev |
     awk '{ print \$2 }'`

   if [ "$oldrev" = "$MATCH" ]; then
     exit 0
   else
     echo "*** PUSH REJECTED! FOXTROT MERGE BLOCKED!!! ***"
     exit 1
   fi
fi
done

如果您使用的是 Github / Gitlab / Bitbucket Cloud,您可能需要考虑在他们的 commit status apis 中创建某种调用(这里的 api 文档:bitbucketgithub;不确定 gitlab 是否有一),因为您无权访问 pre-receive 挂钩,即使您这样做了,您仍然需要处理直接在这些产品的 web ui 中单击“合并”按钮的人(在这种情况下没有“推”)。

使用 Bitbucket 服务器,您可以安装 add-on I created

安装后,您在给定存储库的“挂钩”设置中单击“保护第一个父挂钩”上的“启用”:

它将通过推送和 Bitbucket 服务器 UI 中的“合并”按钮阻止 foxtrot 合并。即使它的许可证过期,它也会这样做,使“保护第一父母挂钩”成为更大附加组件的免费组件。

这是我的Bit-Booster“保护第一父母”预接收挂钩的示例:

$ ​git pull
$ git push

remote: *** PUSH REJECTED BY Protect-First-Parent HOOK ***
remote: 
remote: Merge [1f70043b34d3] is not allowed. *Current* master must appear
remote: in the 'first-parent' position of the subsequent commit. To see how
remote: master is merging into the wrong side (not as 1st parent), try this:
remote: 
remote:   git show --graph -s --pretty='%h %d%n' \
remote:      1f70043b34d3 1f70043b34d3~1 origin/master
remote: 
remote: To fix, there are two traditional solutions:
remote: 
remote:   1. (Preferred) rebase your branch:
remote: 
remote:       git rebase origin/master
remote:       git push origin master
remote: 
remote:   2. Redo the merge in the correct direction:
remote: 
remote:       git checkout master 
remote:       git reset --hard origin/master 
remote:       git merge --no-ff 1f70043b34d3eaedb750~1
remote:       git push origin master
remote: 

有关狐步舞合并的更多背景信息I wrote a blog post

【讨论】:

  • 很好,尽管我会使用 awk '{ print $2 }' 而不是 cut 来避免列计数(并且潜在的损坏应该 git 曾经切换到 SHA-256,尽管这么多 其他东西会破坏,这可能不值得担心......)。
  • 我会试试的!您是否可以根据 MIT 许可向我授予更改许可?
  • 完成!谢谢,@torek
  • 我建议进行细微调整(请参阅gist.github.com/rmandvikar/5486d953fc74b60eba0b46a339f6a16a/…)。从我的快速测试来看,我认为 --pretty='%P'awk '{ print \$1 }' 可以工作,因为只需要父哈希。
  • 你知道backlog.com吗,我们那里有git repos,我不知道怎么配置这个
【解决方案2】:

这是一个钩子代码,可以满足您的要求:

pre-receive hook

#!/bin/sh

# Check to see if this is the first commit in the repository or not
if git rev-parse --verify HEAD >/dev/null 2>&1
then
    # We compare our changes against the previous commit
    against=HEAD^
else
    # Initial commit: diff against an empty tree object
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# Redirect output to screen.
exec 1>&2

# Check to see if we have updated the master branch
if [ "$refname" eq "refs/heads/master" ];
then

    # Output colors
    red='\033[0;31m';
    green='\033[0;32m';
    yellow='\033[0;33m';
    default='\033[0;m';
    
    # personal touch :-)
    echo "${red}"
    echo "                                         "
    echo "                   |ZZzzz                "
    echo "                   |                     "
    echo "                   |                     "
    echo "      |ZZzzz      /^\            |ZZzzz  "
    echo "      |          |~~~|           |       "
    echo "      |        |-     -|        / \      "
    echo "     /^\       |[]+    |       |^^^|     "
    echo "  |^^^^^^^|    |    +[]|       |   |     "
    echo "  |    +[]|/\/\/\/\^/\/\/\/\/|^^^^^^^|   "
    echo "  |+[]+   |~~~~~~~~~~~~~~~~~~|    +[]|   "
    echo "  |       |  []   /^\   []   |+[]+   |   "
    echo "  |   +[]+|  []  || ||  []   |   +[]+|   "
    echo "  |[]+    |      || ||       |[]+    |   "
    echo "  |_______|------------------|_______|   "
    echo "                                         "
    echo "                                         "
    echo "      ${green}You have just committed code ${red}  " 
    echo "      Your code ${yellow}is bad.!!!      "
    echo "      ${red} Do not ever commit again    "
    echo "                                         "
    echo "${default}"
fi;

# set the exit code to 0 or 1 based upon your needs
# 0 = good to push
# 1 = exit without pushing.
exit 0;

【讨论】:

  • 很可爱!但是缺少“while read oldrev newrev refname”和“if”语句需要“=”运算符而不是“-eq”运算符。
  • 关于第一部分你是对的,我把它放到了这样 ASCII 将适合屏幕。关于第二部分它为我工作。你可以同时使用 eq 或 =
  • 我是文章的原作者。
  • 你可以得到赏金,因为没有其他人回答。但是当我尝试它时,你的钩子实际上并没有阻止狐步舞的合并。
  • 好的,所以我修复它。你确实写了一篇很棒的文章,我在 GIT 小组中分享了它:-) 很棒的工作
【解决方案3】:

我写这篇文章是为了尽早提供反馈(还需要pre-receive 钩子)。我为此使用post-mergepre-push 钩子。不可能在 commit-msg 钩子中阻止狐步舞合并,因为检测到的信息只有在之后才可用。在post-merge 钩子中,我只是警告。在pre-push 钩子中,我抛出并阻止了推送。参见d3f1821(“foxtrot:添加子钩子以检测 foxtrot 合并”,2017-08-05)。

~/.git-hooks/helpers/foxtrot-merge-detector:

#!/bin/sh

#usage:
#   foxtrot-merge-detector [<branch>]
#
# If foxtrot merge detected for branch (current branch if no branch),
# exit with 1.

# foxtrot merges:
# See http://bit-booster.blogspot.cz/2016/02/no-foxtrots-allowed.html
# https://stackoverflow.com/questions/35962754/git-how-can-i-prevent-foxtrot-merges-in-my-master-branch

remoteBranch=$(git rev-parse --abbrev-ref "$1"@{u} 2>/dev/null)
# no remote tracking branch, exit
if [[ -z "$remoteBranch" ]]; then
    exit 0
fi
branch=$(git rev-parse --abbrev-ref "${1-@}" 2>/dev/null)
# branch commit does not cover remote branch commit, exit
if ! $(git merge-base --is-ancestor $remoteBranch $branch); then
    exit 0
fi
remoteBranchCommit=$(git rev-parse $remoteBranch)
# branch commit is same as remote branch commit, exit
if [[ $(git rev-parse $branch) == $remoteBranchCommit ]]; then
    exit 0
fi
# remote branch commit is first-parent of branch, exit
if [[ $(git log --first-parent --pretty='%P' $remoteBranchCommit..$branch | \
    cut -d' ' -f1 | \
    grep $remoteBranchCommit | wc -l) -eq 1 ]]; then
    exit 0
fi
# foxtrot merge detected if here
exit 1

然后把它当作

预推钩:

#!/bin/sh

remote="$1"
url="$2"
z40=0000000000000000000000000000000000000000
while read local_ref local_sha remote_ref remote_sha
do
    if [ "$local_sha" = $z40 ]; then
        # handle delete, do nothing
        :
    else
        # ex $local_ref as "refs/heads/dev"
        branch=$(git rev-parse --abbrev-ref "$local_ref")
        ~/.git-hooks/helpers/foxtrot-merge-detector "$branch"
        # check exit code and exit if needed
        exitcode=$?
        if [ $exitcode -ne 0 ]; then
            echo 1>&2 "fatal: foxtrot merge detected, aborting push"
            echo 1>&2 "fatal: branch $branch"
            exit $exitcode
        fi
    fi
done

合并后:

#!/bin/sh

~/.git-hooks/helpers/foxtrot-merge-detector
# check exit code and exit if needed
exitcode=$?
if [ $exitcode -ne 0 ]; then
    echo -e "  ${Yellow}WARNING:${None} foxtrot merge detected"
    # swallow exit code
fi

【讨论】:

    猜你喜欢
    • 2019-08-04
    • 2011-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-15
    • 2019-03-28
    • 1970-01-01
    • 2014-10-28
    相关资源
    最近更新 更多