Git Don';不允许分支合并到另一个分支
我有一个分支,我想允许它从另一个分支合并。但我不允许它合并到另一个分支(例如Dev、master)Git Don';不允许分支合并到另一个分支,git,merge,workflow,branch,git-branch,Git,Merge,Workflow,Branch,Git Branch,我有一个分支,我想允许它从另一个分支合并。但我不允许它合并到另一个分支(例如Dev、master) 感谢您的帮助您将发现没有简单的方法可以做到这一点。我可以粗略地告诉你,你需要做什么;但是,你必须克服一些重大障碍,对于那些障碍,你只能靠自己,因为我没有花足够的时间来充分发展我认为糟糕的想法 唯一可能的解决方案是设置git挂钩。您可以在原始回购上设置预接收挂钩,这样,如果有人尝试包含您不喜欢的合并的推送,推送可能会被拒绝。(挑战在于找出你不喜欢的合并;我会回来讨论。) 当然,当有人推的时候,他们可
感谢您的帮助您将发现没有简单的方法可以做到这一点。我可以粗略地告诉你,你需要做什么;但是,你必须克服一些重大障碍,对于那些障碍,你只能靠自己,因为我没有花足够的时间来充分发展我认为糟糕的想法 唯一可能的解决方案是设置git挂钩。您可以在原始回购上设置预接收挂钩,这样,如果有人尝试包含您不喜欢的合并的推送,推送可能会被拒绝。(挑战在于找出你不喜欢的合并;我会回来讨论。) 当然,当有人推的时候,他们可能有大量的工作要重做。因此,您的开发人员知道他们不能推送违反您规则的引用,可能希望安装与预提交挂钩相同的脚本以避免错误。(也就是说,假设他们不只是认为有这种约束的项目不值得去做。) 棘手的部分是如何检测要进行的合并类型。您的脚本必须分析任何新合并,以查看第一个和第二个父级是什么。因此,您可以这样开始:如果合并的第二个(或后续)父级等于
主级
,则拒绝推送。那么如果你有
x -- x -- A <--(master)
\
x -- x -- B <--(dev)
x -- x -- x -- M -- x <--(master)
\ \ /
\ x --- A <--(hotfix)
\
x -- x -- B <--(dev)
因为脚本看到新合并提交的第二个子项是A
,即master
。所以他们不能推
x ----- x ---- A <--(master)
\ \
x -- x -- B -- M <--(dev)
开发人员必须不遗余力地将master合并到dev,但仍然可以这样做。而且,由于您显然希望将其作为一个规则,这意味着您的开发人员看到了这些合并的价值,即使您没有看到,您可以预期您将需要更严格地执行该规则。因此,脚本实际上需要查找其第二个(或后续)父级可从master
访问的任何合并
因此,您可以使用类似于git rev list
的方法来构建一个无法合并的修订列表,并检查每个新的合并,查找第二个(或后续)父项是否在该列表中
但这可能会产生一些意想不到的后果。如果有呢
x -- x -- A <--(master)
\
x -- x -- B <--(dev)
x -- x -- x -- M -- x <--(master)
\ \ /
\ x --- A <--(hotfix)
\
x -- x -- B <--(dev)
<代码> X-X-X-MX-P>你可以通过在本地修改一个<代码>预推< /COD>钩子来接近一个解决方案(在这种情况下,所有的DVS都需要安装钩子,所以你也可以考虑让你的DEVS安装更容易)。或您可以将解决方案重新格式化为原点上的
预接收
钩子,以便更严格地执行
我们的想法是使用一个标记来识别一个很久以前的“坏”提交(标记“坏”分支上的第一个提交),该提交永远不应该被合并到干净的分支中,然后钩子查看您正在推送的ref和“坏”标记之间的git merge base
,如果它们一致,则拒绝推送
下面是我对该答案的改编,用作预推
挂钩。它包含一些逻辑,允许推送应该包含错误提交的分支,并且不必检查推送标记和其他非分支引用
#!/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://stackoverflow.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
这在一个多开发者的环境中是可以接受的
尽管如此,我同意他的回答,即这不是一个需要通过编程解决的问题,而是一个应该通过其他方式解决的问题(培训、自动化测试、简化的分支工作流程等).您可以在预接收钩子中拒绝此类合并的推送。第一个父级的想法很容易被打破:在一个临时分支上进行一次性提交,然后从中进行合并,这与热修复程序是无法区分的。我认为最好的可能是标记提交到第二个父级祖先的钩子,这些祖先可以到达
master
,但是,请咨询当地回购白名单,这样,如果有人批准了这个不寻常的案例,他们仍然可以推动/承诺,但我得说,我同意这个想法很糟糕。这样的工作流程不仅仅是一场灾难,它已经是一场灾难。注意:我欢迎对此答案进行更改/编辑,以便更好地将我的脚本改编的原始答案属性化。我不确定是否有这样的最佳实践来管理这一点。
#!/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://stackoverflow.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