GIT:我怎样才能阻止foxtrot合并到我的主人身上?科?

时间:2016-03-12 20:12:55

标签: git merge git-merge

foxtrot merge是一个合并,其中&origin; master&master'合并为第二个(或更晚的)父级,如下所示:

Commit 'D' is a foxtrot merge because 'origin/master' is its 2nd parent.

承诺' D'是一个狐步合并因为' origin / master'是它的第二个父母。请注意来自&origin; master / master'的第一父历史记录。包含提交' B'此刻。

但是在我的git repo中,我需要所有涉及&origin; master / master'保持原产地/主人的地位作为第一个父母。不幸的是,当git评估提交是否符合快进条件时,git并不关心父级。这导致我的主分支上的第一个父历史记录有时会丢失曾经存在的提交(例如,输出" git log --first-parent")。

以下是提交“D' D'从前面的图表推送:

How can I prevent this push? First-parent history of 'origin/master' no longer contains commit 'B' after the foxtrot merge is pushed!

如何防止这种推动?祖籍/主人的第一父历史'不再包含提交' B'推出foxtrot合并后!

显然,实际上没有任何提交或工作丢失,只是在我的环境中我真的需要" git log --first-parent"成为一个稳定的提交累积记录 - 如果你愿意,可以选择“一次写入多次读取”#34; (WORM)数据库。我有脚本和进程使用" git log --first-parent"生成更改日志和发行说明,以及管理我的票务系统(JIRA)中的票证转换。 Foxtrot合并破坏了我的脚本!

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

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

3 个答案:

答案 0 :(得分:17)

以下预接收挂钩将阻止这些:

#/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是否有一个),因为你无法访问预接收挂钩,即使你这样做了,你仍然需要处理直接在网页上点击“合并”按钮的人。那些产品(在这种情况下没有“推”)。

使用Bitbucket Server,您可以安装add-on I created

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

enter image description here

它将通过推送和Bitbucket Server 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: 

有关foxtrot合并的更多背景I wrote a blog post

答案 1 :(得分:8)

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

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;

答案 2 :(得分:2)

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

<强>〜/ git的钩/助手/狐步合并检测器:

#!/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