Не разрешать ветке сливаться с другой веткой
У меня есть ветвь, которую я хочу разрешить слиться с другой веткой. НО я не позволяю ему сливаться с другой веткой (например, Dev, Master).
Спасибо за помощь
2 ответа
Вы не найдете простой способ сделать это. Я могу примерно сказать вам, что вам нужно сделать; но есть некоторые существенные препятствия, которые вам придется преодолеть, и для тех, кому вы сами по себе, потому что я не трачу достаточно времени, чтобы полностью развить то, что я считаю ужасной идеей.
Единственное потенциальное решение - установить git hooks. Вы можете установить ловушку предварительного получения в репо-источнике, чтобы, если кто-то попытается выполнить пуш, который включает в себя слияние, которое вам не нравится, пуш может быть отклонен. (Задача состоит в том, чтобы определить слияния, которые вам не нравятся; я вернусь к этому.)
Конечно, к тому времени, когда кто-то нажмет, у них может появиться значительный объем работы, который им придется переделать. Поэтому ваши разработчики, зная, что они не могут отправлять ссылки, нарушающие ваше правило, могут захотеть установить тот же сценарий, что и хук предварительной фиксации, чтобы избежать ошибок. (Предполагая, что они не просто решают, что не стоит работать над проектом с такими ограничениями).
Сложность в том, как определить тип слияния, к которому вы стремитесь. Ваш сценарий должен будет проанализировать любые новые слияния, чтобы увидеть, каков 1-й и 2-й родитель. Таким образом, вы можете начать с того, что второй (или последующий) родительский элемент слияния равен master
, отклонить толчок. Итак, если у вас есть
x -- x -- A <--(master)
\
x -- x -- B <--(dev)
кто-то не может просто сказать
git checkout dev
git merge master
git push
потому что ваш сценарий видит, что 2-й дочерний элемент нового коммита слияния A
, который master
, Поэтому они не могут подтолкнуть
x ----- x ---- A <--(master)
\ \
x -- x -- B -- M <--(dev)
Но в этом случае вы, вероятно, также не хотите, чтобы они подталкивали
x ----- x ---- A -- C <--(master)
\ \
x -- x -- B -- M <--(dev)
И если крюк только смотрит на master
, они могли бы добраться до этого, сказав
git checkout master
# stage some changes, or make the following commit with the allow-empty flag
git commit
git push
git checkout dev
git merge master^
git push
Разработчик должен приложить все усилия, чтобы объединить master с dev, но все же может это сделать. И поскольку вы, очевидно, хотите сделать это правилом, а это означает, что ваши разработчики видят значение этих слияний, даже если вы этого не делаете, вы можете ожидать, что вам нужно будет применять правило более строго. Таким образом, ваш скрипт должен искать любое слияние, чей второй (или последующий) родительский элемент доступен из master
,
Так что вы бы использовали что-то вроде git rev-list
составить список ревизий, из которых нельзя объединить, и проверять каждое новое слияние, ища второго (или последующего) родителя, который будет в этом списке.
Но это может иметь некоторые непредвиденные последствия. Что делать, если у вас есть
x -- x -- x -- M -- x <--(master)
\ \ /
\ x --- A <--(hotfix)
\
x -- x -- B <--(dev)
Возможно, вам следует разрешить объединение этого исправления с dev, но на этом рисунке оно доступно из master
, (Вам может потребоваться объединить исправление с dev
сначала, а потом master
, но в лучшем случае это еще одно произвольное ограничение, противоречащее тому, что люди могут сделать.)
Так что, возможно, когда вы используете rev-list
чтобы создать список запрещенных целей слияния, вы бы дали ему first-parent
аргумент. Это становится трудно рассуждать, поэтому неясно, является ли это сейчас пуленепробиваемым, но я полагаю, что это становится все ближе.
За исключением следующего, что если кто-то использует reset
переместить master
Ref вокруг, так что во время толчка вы не можете сказать, что то, что они сливаются, на самом деле из master
ветка? Собираетесь ли вы запретить все форсированные операции? (Чтобы быть справедливым, принудительное нажатие должно использоваться осторожно. Тот факт, что это ограничение может подтолкнуть разработчиков к рассмотрению рабочего процесса, который использует его регулярно, является еще одним признаком того, что это плохая идея. Но полное устранение принудительных толчков может не сработать так хорошо.)
Да, и чем больше становится ваш репо (и тем глубже история master
) тем более ресурсоемким будет ваш крючок (замедляется push
операции). Я думаю, вы могли бы установить максимальное количество коммитов в master
взглянуть на это, исходя из предположения, что никто не пойдет слишком далеко в поисках допустимого сегмента истории, из которого можно слиться master
, Но это еще одна сложность для вашего сценария, которая может привести к тому, что он будет перехитрен.
Смысл всего этого в том, что вы можете потратить огромное количество усилий, пытаясь контролировать своих разработчиков, или вы можете установить согласованные командные практики, которым следуют люди, потому что они хорошие разработчики.
Вы можете приблизиться к решению, адаптировав этот ответ либо к pre-push
перехватывать локально (в этом случае всем вашим разработчикам потребуется установить перехватчик, так что вы можете также написать сценарий, который позволит вам хранить перехватчики в репозитории, чтобы облегчить их установку), или вы можете переформулировать решение в pre-receive
зацепка о происхождении, для более строгого исполнения.
Идея состоит в том, чтобы использовать тег, чтобы идентифицировать давно "плохой" коммит (пометить первый коммит на "плохой" ветке), который никогда не должен быть разрешен для слияния с вашими чистыми ветками, а затем ловушка смотрит на git merge-base
между ссылками, которые вы нажимаете, и "плохими" тегами, отклоняющими толчок, если они совпадают.
Вот моя адаптация этого ответа для использования в качестве pre-push
крюк. Он включает в себя некоторую логику, позволяющую выдвигать ветки, которые должны содержать неверные коммиты, а также не беспокоить проверку нажатия меток и других ссылок, не относящихся к ветвям.
#!/bin/bash
#
# This prevents a push to the named remote if the push has any "forbidden"
# commits (indicated by a tag matching the forbidden_pat variable) as any
# ancestors. It prevents a known-bad branch from having its history
# intermingled with other branches.
#
# Adapted from http://stackru.com/a/13384768
# which remote should we protect?
protected_remote="origin"
# which branch should allow forbidden commits?
skip_branch_regex='^test[1-3]$'
# what do our forbidden tags look like?
forbidden_pat="forbidden/*"
# only check if this is the remote we care about
if [ "$1" != "$protected_remote" ]; then
exit 0
fi
# we might be trying to push multiple branches (e.g. push.default = matching).
# See http://git-scm.com/docs/githooks#_pre_push for the format
while read line
do
words=($line)
branch="${words[0]##refs/heads/}"
ref="${words[1]}"
# don't proceed if this is not a branch (IOW, if our substitution didn't
# change anything; we only want to check pushing branch pointers, not tags or
# other refs
if [ "$branch" = "${words[0]}" ]; then
continue
fi
# don't proceed if we're trying to push a branch that can receive the bad
# commit
if [[ "$branch" =~ $skip_branch_regex ]]; then
continue
fi
# check each forbidden tag to see if it's an ancestor of the ref we're
# trying to push; reject if it is.
for forbidden in $(git tag -l "$forbidden_pat"); do
if [ $(git merge-base "$forbidden" "$ref") = $(git rev-parse "$forbidden") ]; then
echo "Push to $branch contains BAD commit $forbidden." >&2
exit 1
fi
done
done
exit 0
Это работало приемлемо в среде с несколькими разработчиками.
С Mark Adelsberger я согласен с ответом Mark Adelsberger о том, что эта проблема не должна решаться программным путем, а должна решаться другими способами (обучение, автоматизированные тесты, упрощенный рабочий процесс ветвления и т. Д.).