web-dev-qa-db-ja.com

GIT: 'master'ブランチでfoxtrotのマージを防ぐにはどうすればよいですか?

フォックストロットマージは、次のように、 'Origin/master'が2番目(またはそれ以降)の親としてマージされるマージです。

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

コミット 'D'は、 'Origin/master'が2番目の親であるため、foxtrotマージです。この時点で、「Origin/master」の最初の親の履歴にコミット「B」が含まれていることに注意してください。

しかし、私のgitリポジトリでは、「Origin/master」を最初の親として維持するために、「Origin/master」を含むすべてのマージが必要です。残念ながらgitは、コミットが早送りに適しているかどうかを評価するときに親の順序を気にしません。これにより、私のマスターブランチの最初の親の履歴で、以前は存在していたコミットが失われることがあります(たとえば、「git log --first-parent」の出力)。

前の図の「D」のコミットがプッシュされると、次のようになります。

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

このプッシュを防ぐにはどうすればよいですか? foxtrotマージがプッシュされた後、「Origin/master」の最初の親の履歴にコミット「B」が含まれなくなりました。

明らかに、コミットや作業が実際に失われることはありません。それは、私の環境では、コミットの安定した累積的なレコードになるために「git log --first-parent」が本当に必要なことです。 "(WORM)データベース。 「git log --first-parent」を使用して変更ログとリリースノートを生成し、チケットシステム(JIRA)でチケットの移行を管理するスクリプトとプロセスがあります。 Foxtrotのマージがスクリプトを壊してしまいます!

Foxtrotのマージがプッシュされないようにするために、gitリポジトリにインストールできる一種の事前受信フックはありますか?

pSこのstackoverflow質問のコミットグラフは http://bit-booster.com/graph.html を使用して生成されました。

31

次の事前受信フックはそれらをブロックします:

#/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にあるかどうかは不明です)。事前受信フックにアクセスできないため、アクセスしたとしても、「マージ」をクリックする人に対処する必要があります。これらの製品のWeb UIに直接ボタンを押します(この場合、「プッシュ」はありません)。

Bitbucket Serverを使用すると、 私が作成したアドオン をインストールできます。

インストールしたら、指定したリポジトリの「フック」設定の「最初の親フックを保護する」で「有効にする」をクリックします。

enter image description here

これは、プッシュおよびBitbucket Server UIの「マージ」ボタンを介してfoxtrotマージをブロックします。これは、ライセンスの有効期限が切れた場合でもこれを実行するため、「保護者の親のフックの保護」は、より大きなアドオンの無料のコンポーネントになります。

これが私の Bit-Booster "Protect First Parent" pre-receiveフックの動作例です。

$ ​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マージの背景については 私はブログ投稿を書きました

16

ここにあなたが求めていることをするフックコードがあります:

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;
7
CodeWizard

フィードバックを早く提供するためにこれを書きました(pre-receiveフックも必要です)。私が使う post-mergeおよびpre-Pushこれをフックします。検出するための情報は後でしか利用できないため、commit-msgフックでfoxtrotマージを防ぐことはできません。 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
3
hIpPy