git - 让远程 git 存储库在推送时拒绝 merge 提交

标签 git git-push githooks

我想做什么:

我想禁止将任何 merge 提交推送到中央存储库。 唯一的异常(exception)是 merge 发生在中央存储库中存在的分支之间。我想在中央存储库中强制执行此操作。

解释我为什么要这样做:

注意:如果此解释使您无法理解我想做的事情,请忽略此解释。当然,我很高兴听到其他方法来解决我在下面解释的问题,但我感兴趣的答案是如上所述我想做的事情。

我有一个中央 git 存储库,其中有一个分支供多个开发人员跟踪。每个开发人员都为该中央存储库的分支配置了一个远程。

我们遵循这个项目的同步提交策略,因此每个开发人员在推送之前必须始终将他们的最新工作 rebase 到远程分支 HEAD 之上。我想通过禁止将任何 merge 提交推送到中央存储库来执行此策略。唯一的异常(exception)是 merge 发生在中央存储库中存在的分支之间。

为简化起见,我不希望开发人员的本地跟踪分支与远程分支 merge 。而是总是基于远程分支。

我们通过设置 branch.NAME.rebase = true 在开发人员的机器上部分强制执行此操作,这有助于避免开发人员使用 git pull 时出现问题,但是我们需要一个解决方案来在中央存储库端强制执行此操作。

一个非常基本的解决方案是拒绝带有注释的提交:“Merge branch 'NAME' of GITURL”,然而,检​​查一个提交的所有父项是否存在于中央存储库的分支路径中的更多内容是更有趣。

建议?解决方案?

编辑:

这是我目前所拥有的:

#!/bin/sh
read sha1old sha1new refname

# check if this is merge commit
merge_commit="`git rev-list --parents --merges --no-walk $sha1new 2> /dev/null`"
if test -n "$merge_commit"
then
  # this was a merge commit
  # $merge_commit contains: sha1new sha1parent_1 ... sha1parent_n
fi
exit 0

麻烦的地方是确定任何两个 parent 的祖先是否来自一个分支。此外,因为在更新任何引用之前调用预接收 Hook ,如果推送包含远程中存在的两个分支的提交,包括这两个分支之间的 merge ,那么我不知道这里的解决方案是什么...... .

最佳答案

防止创建非线性历史的推送的一种方法是设置 pre-receive使用 git rev-list --parents <OLD>..<NEW> 的钩子(Hook)检查是否有任何具有多个父项的新提交,如果是,则错误退出。为了处理第二个要求,您可以改为检查是否有多个父项,这些提交必须全部在存储库中的现有分支上。我没有对此进行太多测试,但是这个 pre-receive钩子(Hook)(或它的一些变体)可能是你想要的:

#!/usr/bin/ruby -w

# A pre-receive hook that should refuse any pushes that would update
# master in such a way that a non-linear history would be created,
# except where it involves a merge from another branch in this
# repository.  This has only had very cursory testing.

# This is a suggested answer to:
#   http://stackoverflow.com/questions/2039773/have-remote-git-repository-refuse-local-branch-merge-commits-on-push

ref_to_check = "refs/heads/master"

rev_old, rev_new, ref = STDIN.read.split(" ")

if ref == ref_to_check
  merge_bases = `git merge-base #{rev_old} #{rev_new}`.strip.split(/\s+/)
  unless $?.success? and merge_bases.length == 1
    STDERR.puts "No unique merge base found between #{rev_old} and #{rev_new}"
    exit(1)
  end
  rev_list_output = `git rev-list --parents #{merge_bases[0]}..#{rev_new}`
  list_of_revs_with_parents = rev_list_output.strip.split(/[\r\n]+/)
  list_of_revs_with_parents.each do |line|
    rev_with_parents = line.strip.split(/\s+/)
    if rev_with_parents.length > 2      
      parents = rev_with_parents.slice(1,rev_with_parents.length)
      # The question says to permit non-linear history if the merge is
      # from another branch in the central repository, so check
      # whether that's the case.  (If you just want to prevent all
      # pushes that add non-linear history, just exit with error
      # here.)
      any_parent_not_on_any_branch = false
      parents.each do |p|
        branches = `git branch --contains #{p} 2> /dev/null`
        if $?.success? and ! branches.strip.empty?
          STDERR.puts "More than one parent of commit #{rev_with_parents[0]}"
          STDERR.puts "... but parent #{p} is on branches:"
          STDERR.puts branches
        else
          STDERR.puts "Parent #{p} not found on any other"
          STDERR.puts "branch in this repository"
          any_parent_not_on_any_branch = true
          break
        end
      end
      if any_parent_not_on_any_branch
        STDERR.puts "Refusing push, since it would create non-linear history"
        STDERR.puts "for #{ref} and the merges don't just involve commits on"
        STDERR.puts "other branches in this repository."
        exit(2)
      end
    end
  end
end

希望对你有用。

关于git - 让远程 git 存储库在推送时拒绝 merge 提交,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2039773/

相关文章:

linux - 使用 git pull 在/var/www/vhosts 上的网络服务器上进行部署

git push 让我登录到 github。致命的 : HttpRequestException encountered

git - 从 git push 远程响应中提取 url

python - 预提交运行 unittest git hooks : ModuleNotFoundError for installed python module in environment

git - 创建一个 BitBucket git 提交 Hook ?

git - 如何使用嵌入式 GIT 重置 GIT

javascript - Heroku site 是对的,local 是错的。如何在本地克隆我的 heroku 代码?

git merge : there is a way to operate on the files involved even if there isn't merge conflict?

git - 如何在 `git commit` 之后在本地和远程撤消 `git push`

git - 将 git checkout 中的子模块包含到钩子(Hook)中的 GIT_WORK_TREE