Have remote git repository refuse merge commits on push

后端 未结 1 870
走了就别回头了
走了就别回头了 2020-12-31 15:29

What I want to do:

I want to disallow any merge commits to be pushed to the central repositor

相关标签:
1条回答
  • 2020-12-31 16:09

    One way of preventing pushes that would create non-linear history is to set up a pre-receive hook which uses git rev-list --parents <OLD>..<NEW> to check for any new commit that has more than one parent, and exit in error if so. To deal with the second requirement, you could instead check that if there's more than one parent, those commits must all be on existing branches in the repository. I haven't tested this very much, but this pre-receive hook (or some variant of it) may be what you want:

    #!/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
    

    I hope that's of some use.

    0 讨论(0)
提交回复
热议问题