2010-01-11 12 views
8

Push fusionieren Was ich tun möchte:haben Remote-git-Repository verweigern Commits auf

Ich möchte nicht zulassen jede merge verpflichtet an das zentrale Repository geschoben werden. Die einzige Ausnahme ist, wenn die Zusammenführung zwischen Zweigen ist, die im zentralen Repository vorhanden sind. Ich möchte dies am zentralen Repository erzwingen.

Erklärung, warum ich dies tun wollen:

Hinweis: Wenn diese Erklärung Sie aus der Spur, was wirft ich tun möchte, dann die Erklärung ignorieren. Natürlich bin ich glücklich, andere Möglichkeiten zu kennen, das Problem zu lösen, das ich unten erkläre, aber die Antwort, die mich interessiert, ist, was ich tun möchte, wie oben erwähnt.

Ich habe ein zentrales Git Repository mit einem Zweig, den mehrere Entwickler verfolgen. Jeder Entwickler hat eine Remote für den Zweig dieses zentralen Repository konfiguriert.

Wir verfolgen eine synchrone Festschreibungsrichtlinie für dieses Projekt, so dass jeder Entwickler seine neuesten Arbeiten immer auf den Remote-Zweig HEAD zurückstellen muss, bevor er pusht. Ich möchte diese Richtlinie erzwingen, indem ich die Übertragung von Zusammenführungs-Commits in das zentrale Repository verbiete. Die einzige Ausnahme ist, wenn die Zusammenführung zwischen Zweigen erfolgt, die im zentralen Repository vorhanden sind.

Zur Vereinfachung möchte ich nicht, dass die lokalen Tracking-Zweige des Entwicklers jemals mit der Remote-Zweigstelle zusammengeführt werden. Aber immer wieder auf die Remote-Branche rebasiert werden.

Wir haben dies teilweise auf dem Entwicklungscomputer durch die Einstellung von branch.NAME.rebase = true erzwungen, was zur Vermeidung von Problemen beiträgt, wenn der Entwickler git pull verwendet, aber wir benötigen eine Lösung, um dies auf der zentralen Repository-Seite zu erzwingen.

Eine sehr grundlegende Lösung wäre es, Commits mit dem Kommentar zu verweigern: "Verzweigung 'NAME' von GITURL", jedoch etwas mehr im Sinne der Überprüfung, ob alle Eltern eines Commits in den Zweigpfaden des zentralen Repositorys existieren interessanter sein.

Vorschläge? Lösungen?

Edit:

Dies ist, was ich bisher:

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

Der Ort es mühsam wird ermittelt, ob die Abstammung von irgendwelchen zwei Eltern aus einem einzigen Zweig stammen. Da pre-receive hook aufgerufen wird, bevor refs aktualisiert werden, wenn ein Push Commits für zwei Zweige enthält, die in der remote existieren, einschließlich einer Zusammenführung zwischen diesen beiden Zweigen, dann habe ich keine Ahnung, was die Lösung hier wäre. .

+0

Genaues Duplikat: http://StackOverflow.com/Questions/1997916/Git-Post-Receive-How-To-Check-If-Pushed-Branch-is-merged-with-master – Dustin

+3

Ich stimme völlig nicht zu, dass es ist ein Duplikat. Es hat sehr wenig zu tun mit dem, was ich gefragt habe. – chris

Antwort

10

Eine Möglichkeit, um Push-Vorgänge zu verhindern, die nichtlinearen Verlauf erstellen, besteht darin, einen pre-receive Hook einzurichten, der git rev-list --parents <OLD>..<NEW> verwendet, um nach neuen Commits zu suchen, die mehr als ein Elternteil haben. Um die zweite Anforderung zu erfüllen, können Sie stattdessen überprüfen, ob diese Commits bei vorhandenen mehreren Zweigen im Repository vorhanden sein müssen.Ich habe nicht sehr viel getestet, aber diese pre-receive Haken (oder eine Variante davon) kann sein, was Sie wollen:

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

Ich hoffe, dass von Nutzen ist.

+0

danke! Ich habe sicherlich keine vollständig schriftliche Umsetzung erwartet. Es sieht sicherlich wie ich will, ich werde es durch einige Tests laufen. – chris

+0

Zuerst muss ich dies portieren, da ich Ruby nicht installiert habe. Allerdings, wie ich dieses Skript verstehe, wird es immer noch nicht lineare Geschichte in dem Fall, in dem das Commit aus einem Zusammenführungspunkt zwischen divergierenden Commit-Pfade auf einem einzelnen Zweig besteht. Welches ist normalerweise das, was Sie von nicht rebasierten lokalen Tracking-Zweigstellen sehen? Ich habe dies noch nicht durch Tests verifiziert, so dass mir beim Lesen des Skripts etwas fehlt. – chris

+0

Wenn ein Benutzer am Master arbeitet, ist das Zusammenführen von Ursprung/Master und Ursprung/Master linear, ich glaube nicht, dass das passieren kann. Wenn diese Bedingungen erfüllt sind, ist eine Zusammenführung zwischen dem Master dieses Benutzers und seinem Ursprung/Master ein linearer Fast-Forward, außer in einem der Fälle, in denen der Master im Ursprung/Master nicht vorhanden ist. Wenn der Benutzer dann versucht, eine solche Zusammenführung zu schieben, wird der Hook sehen, dass einer der Eltern nicht bereits im Master ist und ihn ablehnen. (Obwohl, wenn sie schwierig sein wollten, hätten sie ihren Master in einen neuen Zweig auf dem Server schieben können, so dass der Elternteil dort bekannt ist.) –

Verwandte Themen