GIT: Как я могу предотвратить слияние foxtrot в моей "основной" ветке?
Объединение foxtrot - это слияние, где "origin/master" сливается в качестве второго (или более позднего) родителя, например:
Commit 'D' - это фокстрот-слияние, потому что "origin/master" является вторым родителем. Обратите внимание, что история первоисточника из "origin/master" содержит фиксацию "B" в данный момент.
Но в моем реестре git мне нужны все слияния с участием "origin/master" , чтобы сохранить "origin/master" в качестве первого родителя. К сожалению, git не заботится о родительском заказе, когда оценивает, имеет ли фиксация право на перемотку вперед. Это приводит к тому, что первая родительская история на моей главной ветке иногда теряет фиксации, которые были там (например, вывод "git log - first-parent" ).
Здесь происходит то, что происходит при фиксации "D" с предыдущей диаграммы:
Как я могу предотвратить это нажатие? Первоначальная история "origin/master" больше не содержит фиксацию "B" после того, как сбрасывается foxtrot merge!
Очевидно, что никакие коммиты или работа на самом деле не потеряны, просто в моей среде мне действительно нужен "git log -first-parent", чтобы быть стабильной накопительной записью коммитов - если хотите, своего рода "Write -Once Read-Many" (WORM). У меня есть сценарии и процессы, которые используют "git log --first-parent" для генерации изменений и примечаний к выпуску, а также для управления переходами билетов в моей системе продажи билетов (JIRA). Слияния Foxtrot нарушают мои скрипты!
Есть ли какой-либо предварительный прием, который я мог бы установить в своих репозиториях git, чтобы предотвратить слияние фокстрота?
<я > p.s. Графы фиксации в этом вопросе stackoverflow генерировались с помощью http://bit-booster.com/graph.html.
Ответы
Ответ 1
Следующий крюк предварительного приема блокирует их:
#/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 docs for: bitbucket, github, не уверен, есть ли у gitlab один), потому что у вас нет доступа к крючкам предварительной отправки, и даже если вы это сделали, вам все равно придется иметь дело с людьми, которые нажимают кнопку "merge" непосредственно в Интернете ui (в этом случае нет "толчка" ).
С Bitbucket Server вы можете установить надстройку, которую я создал.
После установки вы нажимаете "включить" на "Защитите первый родительский крюк" в настройках "крюка" заданного хранилища:
Он блокирует слияния foxtrot через push и через кнопку "merge" в пользовательском интерфейсе Bitbucket. Он делает это, даже если срок действия лицензии истек, что делает "Защитить первый родительский крючок" свободным компонентом большего дополнения.
Вот пример моего 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 Я написал сообщение в блоге.
Ответ 2
Вот код крюка, который будет делать то, что вы просите:
#!/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;
Ответ 3
Я написал это, чтобы обеспечить обратную связь раньше (pre-receive
нужен также крючок). Для этого я использую post-merge
и pre-push
hooks. Невозможно предотвратить объединение foxtrot в hook-commit-msg, поскольку информация для его обнаружения доступна только после. В hook post-merge
я просто предупреждаю. В pre-push
hook я бросаю и блокирую push. См. d3f1821 ( "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/info/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 "${[email protected]}" 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