不允许分支合并到另一个分支

时间:2017-12-12 13:06:54

标签: git merge workflow branch git-branch

我有分支,我希望允许从另一个分支合并。但我不允许它合并到另一个分支(例如Dev,master)。

感谢您的帮助

2 个答案:

答案 0 :(得分:3)

你会发现没有简单的方法可以做到这一点。我可以粗略地告诉你,你需要做什么;但是你必须克服一些重大的障碍,对于那些你自己的障碍,因为我没有浪费足够的时间来完全发展我认为是一个可怕的想法。

唯一可能的解决方案是设置git hooks。您可以在原始仓库上放置预接收挂钩,这样如果有人尝试包含您不喜欢的合并的推送,则可以拒绝推送。 (挑战是确定你不喜欢的合并;我会回过头来看。)

当然,当有人推动时,他们可能需要做大量的工作才能重做。因此,您的开发人员知道他们无法推送违反规则的引用,可能希望安装与预提交挂钩相同的脚本以避免错误。 (假设他们不仅仅决定使用这种约束来处理项目是不值得的,那就是。)

棘手的部分是你将如何检测你所追求的合并类型。您的脚本必须分析任何新的合并,以查看第一个和第二个父级是什么。因此,您可以先说,如果合并的第二个(或后续)父级等于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可以访问的任何合并。

因此,您可以使用类似git rev-list的内容来构建无法合并的修订列表,并检查每个新合并,以查找该列表中的第二个(或后续)父级。< / p>

但这可能会产生一些意想不到的后果。如果你有

怎么办?
x -- x -- x -- M -- x <--(master)
 \    \       /
  \    x --- A <--(hotfix)
   \
    x -- x -- B <--(dev)

您应该允许将该修补程序合并到dev,但在此图片中,它可以从master到达。 (您可能需要首先将此修补程序合并到dev,然后将然后合并到master,但最好是另一个与人们可能做的相反的任意约束。)

因此,当您使用rev-list构建禁用合并目标列表时,您可以为其指定first-parent参数。这开始变得难以理解,因此目前尚不清楚这是否现在是防弹,但我认为它越来越接近了。

除了接下来的事情之外,如果有人使用reset来移动master参考,那么在推送时您无法确定他们正在合并的是什么来自master分支?你打算禁止所有强制推进操作吗? (公平地说,应该小心使用强制推进。这种限制可能会促使开发人员考虑一个常规使用它的工作流程这一事实是另一个迹象,这是一个坏主意。但完全消除推力可能无法很好地解决。 )

哦,你的repo越大(master的历史越深)你的钩子就会越耗资源(减慢push次操作)。我想你可以在master中设置一个最大提交数量,假设没有人会回来太多远远寻找一个允许的历史片段来合并{{1 }}。但这对于您的脚本来说又复杂一点,这可能会导致它变得非常聪明。

所有这一切的重点是,您可以花费大量精力来控制您的开发者,或者您可以设置人们遵循的商定团队实践,因为他们是优秀的开发者。

答案 1 :(得分:1)

您可以通过在本地调整this answerpre-push挂钩来接近解决方案(在这种情况下,您的所有开发人员都需要安装挂钩,因此您也可以考虑writing a script that lets you keep your hooks in your repo为了让您的开发人员更容易安装),您可以将解决方案重新制定为pre-receive关于原点的钩子,以便更严格地执行。

这个想法是使用一个标签来识别一个很久以前的&#34;坏&#34;提交(标记&#34; bad&#34;分支上的第一个提交),永远不允许将其合并到您的干净分支中,然后钩子在您之后查看引用之间的git merge-base推动和'坏'&#34;标签,如果它们重合就拒绝推送。

这是我对该答案的改编,用作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://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

这在多开发人员环境中可以接受。

所有这一切,我同意Mark Adelsberger的答案,这不是一个需要以编程方式解决的问题,而是应该以其他方式解决的问题(培训,自动化测试,简化的分支工作流程等。)。