不允许分支合并到另一个分支
Don't allow branch to merge to another branch
我有一个分支,我想让它从另一个分支合并。但我不允许它合并到另一个分支(例如 Dev、master)。
感谢帮助
您会发现没有简单的方法可以做到这一点。我可以粗略地告诉你你需要做什么;但是您必须克服一些重大障碍,对于那些您自己一个人,因为我没有投入足够的时间来充分开发我认为是一个糟糕的想法。
唯一可能的解决方案是设置 git 挂钩。您可以在原始存储库上放置一个预接收挂钩,这样如果有人尝试推送包含您不喜欢的合并,则可以拒绝该推送。 (挑战在于确定您不喜欢的合并;我会回来的。)
当然,当有人推动时,他们可能需要重做大量工作。因此,您的开发人员知道他们不能推送违反您规则的引用,可能希望安装与预提交挂钩相同的脚本以避免错误。 (假设他们不只是决定不值得在具有这种约束的项目上工作,也就是说。)
棘手的部分是您将如何检测您所追求的合并类型。您的脚本将必须分析任何新的合并,以查看第一个和第二个父项是什么。因此,您可能首先会说,如果合并的第二个(或后续)父级等于 master
,则拒绝推送。那么如果你有
x -- x -- A <--(master)
\
x -- x -- B <--(dev)
有人不能简单地说
git checkout dev
git merge master
git push
因为您的脚本发现新合并提交的第二个子项是 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
.[=34= 访问的任何合并]
所以你会使用 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
分支?您要禁止所有强制推送操作吗? (公平地说,应该谨慎使用强制推送。这一限制可能会促使开发人员考虑常规使用它的工作流程这一事实是另一个迹象,表明这是一个坏主意。但完全消除强制推送可能效果不佳。 )
哦,你的 repo 越大(master
的历史越深)你的 hook 将变得越消耗资源(减慢 push
操作)。我想你可以在 master
中设置最大提交数来查看,假设没有人会回头 太 远寻找允许的历史片段合并master
。但这会使您的脚本更加复杂,这可能会导致它被打败。
所有这一切的要点是,您可以花费大量精力试图控制您的开发人员,或者您可以设置人们遵循的一致同意的团队实践,因为他们是优秀的开发人员。
您可以通过调整 this answer into either a pre-push
hook locally (in which case all your devs will need the hook installed, so you might also consider writing a script that lets you keep your hooks in your repo 来接近解决方案,使您的开发人员更容易安装),或 您可以将解决方案重新表述为 pre-receive
hook on the origin,为了更严格的执行。
想法是使用标签来识别很久以前的 "bad" 提交(标记 "bad" 分支上的第一个提交),永远不允许将其合并到干净的分支中,然后钩子会查看您正在推送的 ref 和 "bad" 标签之间的 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
# 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 [ "" != "$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 的回答,即这不是一个可以通过编程方式解决的问题,而是应该以其他方式解决的问题(培训、自动化测试、简化的分支工作流程等)。
我有一个分支,我想让它从另一个分支合并。但我不允许它合并到另一个分支(例如 Dev、master)。
感谢帮助
您会发现没有简单的方法可以做到这一点。我可以粗略地告诉你你需要做什么;但是您必须克服一些重大障碍,对于那些您自己一个人,因为我没有投入足够的时间来充分开发我认为是一个糟糕的想法。
唯一可能的解决方案是设置 git 挂钩。您可以在原始存储库上放置一个预接收挂钩,这样如果有人尝试推送包含您不喜欢的合并,则可以拒绝该推送。 (挑战在于确定您不喜欢的合并;我会回来的。)
当然,当有人推动时,他们可能需要重做大量工作。因此,您的开发人员知道他们不能推送违反您规则的引用,可能希望安装与预提交挂钩相同的脚本以避免错误。 (假设他们不只是决定不值得在具有这种约束的项目上工作,也就是说。)
棘手的部分是您将如何检测您所追求的合并类型。您的脚本将必须分析任何新的合并,以查看第一个和第二个父项是什么。因此,您可能首先会说,如果合并的第二个(或后续)父级等于 master
,则拒绝推送。那么如果你有
x -- x -- A <--(master)
\
x -- x -- B <--(dev)
有人不能简单地说
git checkout dev
git merge master
git push
因为您的脚本发现新合并提交的第二个子项是 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
.[=34= 访问的任何合并]
所以你会使用 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
分支?您要禁止所有强制推送操作吗? (公平地说,应该谨慎使用强制推送。这一限制可能会促使开发人员考虑常规使用它的工作流程这一事实是另一个迹象,表明这是一个坏主意。但完全消除强制推送可能效果不佳。 )
哦,你的 repo 越大(master
的历史越深)你的 hook 将变得越消耗资源(减慢 push
操作)。我想你可以在 master
中设置最大提交数来查看,假设没有人会回头 太 远寻找允许的历史片段合并master
。但这会使您的脚本更加复杂,这可能会导致它被打败。
所有这一切的要点是,您可以花费大量精力试图控制您的开发人员,或者您可以设置人们遵循的一致同意的团队实践,因为他们是优秀的开发人员。
您可以通过调整 this answer into either a pre-push
hook locally (in which case all your devs will need the hook installed, so you might also consider writing a script that lets you keep your hooks in your repo 来接近解决方案,使您的开发人员更容易安装),或 您可以将解决方案重新表述为 pre-receive
hook on the origin,为了更严格的执行。
想法是使用标签来识别很久以前的 "bad" 提交(标记 "bad" 分支上的第一个提交),永远不允许将其合并到干净的分支中,然后钩子会查看您正在推送的 ref 和 "bad" 标签之间的 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
# 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 [ "" != "$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 的回答,即这不是一个可以通过编程方式解决的问题,而是应该以其他方式解决的问题(培训、自动化测试、简化的分支工作流程等)。