2010-01-11 10 views
8

Lo que quiero hacer:repositorio Git remoto se niegan fusionar compromete el impulso

Quiero no permitir cualquier combinación compromete ser empujado al repositorio central. La única excepción es si la combinación se realiza entre sucursales que existen en el repositorio central. Quiero aplicar esto en el repositorio central.

Explicación de por qué quiero hacer esto:

Nota: Si esta explicación se lanza fuera de la pista de lo que quiero hacer, a continuación, pasar por alto la explicación. Por supuesto, me complace escuchar otras formas de resolver el problema que explico a continuación, pero la respuesta que me interesa es sobre lo que quiero hacer, como se indicó anteriormente.

Tengo un repositorio central de git con una rama que varios desarrolladores rastrean. Cada desarrollador tiene un control remoto configurado para la rama de ese repositorio central.

Seguimos una política de compromiso síncrono para este proyecto, por lo que cada desarrollador debe siempre rebase su último trabajo en la parte superior de la rama remota HEAD antes de presionar. Me gustaría aplicar esta política al no permitir que las confusiones de fusión se envíen al repositorio central. La única excepción es si la combinación se realiza entre sucursales que existen en el repositorio central.

Para simplificar, no quiero que las ramas de seguimiento locales del desarrollador se fusionen con la rama remota. Pero mejor dicho siempre sea rebase en la rama remota.

Hemos implementado en parte esto en la máquina del desarrollador estableciendo branch.NAME.rebase = true que ayuda a evitar problemas si el desarrollador usa git pull, sin embargo, necesitamos una solución para aplicar esto en el repositorio central.

Una solución muy básica sería rechazar confirmaciones con el comentario: "Fusionar rama 'NOMBRE' de GITURL", sin embargo, algo más en la línea de comprobar si todos los padres de una confirmación existen en las rutas de bifurcación del repositorio central se más interesante.

Sugerencias? Soluciones?

Editar:

Esto es lo que tengo hasta ahora:

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

El lugar se vuelve problemático es determinar si la ascendencia de cualquiera de los dos padres se originan a partir de una sola rama. Además, como se llama al gancho de pre-recepción antes de que se actualicen los refs, si un push contiene confirmaciones para dos ramas que existen en el control remoto, incluida una fusión entre esas dos ramas, entonces no tengo idea de cuál sería la solución aquí.

+0

Duplicado exacto: http://stackoverflow.com/questions/1997916/git-post-receive-how-to-check-if-pushed-branch-is-merged-with-master – Dustin

+3

No estoy completamente de acuerdo con que sea un duplicado. Tiene muy poco o nada que ver con lo que he pedido. – chris

Respuesta

10

Una forma de evitar los impulsos que crearían un historial no lineal es configurar un gancho pre-receive que usa git rev-list --parents <OLD>..<NEW> para verificar si hay alguna nueva confirmación que tenga más de un elemento primario y salir en caso de error. Para lidiar con el segundo requisito, puede verificar que si hay más de un padre, esas confirmaciones deben estar todas en las sucursales existentes en el repositorio.No he probado esto mucho, pero esto pre-receive gancho (o alguna variante de la misma) puede ser lo que quiera:

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

espero que sea de alguna utilidad.

+0

gracias! Ciertamente no esperaba una implementación completamente escrita. Ciertamente se ve como lo que quiero, lo ejecutaré en algunas pruebas. – chris

+0

Primero, tengo que portar esto porque no tengo Ruby instalado. Sin embargo, según entiendo esta secuencia de comandos, todavía permitirá el historial no lineal en el caso en que la confirmación consista en un punto de fusión entre las rutas de confirmación divergentes en una sola rama. Lo cual es típicamente lo que verá en las ramas de seguimiento locales no basadas en cambios. Todavía no he verificado esto mediante pruebas, por lo que podría perderme algo en mi lectura del guión. – chris

+0

Si un usuario está trabajando en master, fusionarse entre origen/master y origin/master es lineal, no creo que eso pueda suceder. Si se cumplen esas condiciones, cualquier fusión entre el maestro de ese usuario y su origen/maestro será un avance rápido lineal, excepto en uno de los casos en que el maestro no esté presente en el origen/maestro. Si el usuario intenta impulsar una fusión de este tipo, el enlace verá que uno de los padres aún no está en el maestro y lo rechazará. (Aunque si quisieran ser difíciles podrían haber empujado a su maestro a una nueva sucursal en el servidor para que el padre se conozca allí). –

Cuestiones relacionadas