2011-05-16 15 views
8

Estoy usando la rama default para el desarrollo continuo, y ahora voy a crear una nueva rama con nombre para marcar una versión. Todo el desarrollo ulterior estará en la rama por defecto, todas las correcciones de errores de producción se hará sobre la nueva (con la posterior fusión a default), así:Mercurial: permite combinar una rama de versión con la predeterminada, pero no al revés

#>hg branches 
    aristotle 42:dbd... 
    default  41:da5... 
#>hg branch 
    default 
#>echo "Feature #1 for the next release" >> feature1.txt 
#>hg add 
#>hg commit -m "Implement feature #1 for the next release" 

...... eek, need to make an urgent fix on Production ..... 

#>hg update aristotle 
#>echo "Fixed urgent bug #123 on Production" >> fix123.txt 
#>hg add 
#>hg commit -m "Fixed bug #123 on Production" 
    created new head 
#>hg update default 
#>hg merge aristotle 
    1 files updated, 0 files merged, 0 files removed, 0 files unresolved 
    (branch merge, dont forget to commit) 
#>hg commit -m "Merge in the fix for bug #123" 
#>hg push 

Lo anterior parece el camino a seguir, Sin embargo, parece fácil desordenar las cosas y combinar al revés (desde default hasta aristotle lo que significa que todas las nuevas características aparecerán en la rama de producción).

Quizás mis temores carezcan de fundamento porque uno notará el desorden antes de enviar la confirmación al repositorio central, pero me gustaría ver si es posible hacer que el enfoque sea más infalible.

Así que me puse a buscar en los ganchos:

[hooks] 
pretxnchangegroup.branch = hg heads --template "{branches} " | find "aristotle" && exit 1 || exit 0 

..pero luego se dio cuenta que no es lo que necesito, porque esto no va a permitir que empuje cambios Aristóteles en absoluto.

Así que no estoy seguro de qué hacer. Idealmente, quiero que los desarrolladores vean el mensaje "fusionarse de manera incorrecta" cuando intentan realizar una fusión de default a aristotlelocalmente (obviamente, debe haber una doble verificación en el repositorio central), mientras se fusionan de la rama de producción a el predeterminado debe ser posible.

Gracias!

+0

Quizás pueda hacer algo donde si la confirmación está en la rama de producción, y esa confirmación tiene más de un padre (una confirmación de fusión), y uno de esos padres está en la rama predeterminada, falla. Es posible que desee consultar la extensión contiene para ayudar con algo de esto: http://mercurial.selenic.com/wiki/ContainsExtension No tenemos ninguna protección real contra esto, pero tenemos un gancho post-commit que imprime lo que sucedió realmente durante la confirmación (está fusionando branch1 en branch2), que el desarrollador puede leer para verificar que hicieron lo correcto. –

+0

Marca, muchas gracias por el comentario, pero "hg update default" seguido de "hg merge aristotle" parece exactamente como "fusionar branch1 en branch2" (y, por desgracia, un mensaje no impide el push/commit incorrecto). Además, no estoy seguro de cómo "contiene la extensión" puede ayudar en mi caso :(¿Tal vez pueda explicar cómo implementó exactamente su gancho post-commit? – andreister

+0

Mi enlace post-commit no evita que ocurra nada, pero proporciona información a la persona que realiza el compromiso que explica la acción que está sucediendo. Es como cuando lees un número de teléfono a alguien y luego le pides que se lo lea de nuevo para asegurarte de que te entendieron. Realizas una combinación y luego Mercurial le explica la acción nuevamente para que pueda tomar un error. –

Respuesta

0

Esto es más o menos un duplicado exacto de una pregunta de hace unos días Ensuring a merge between branches happens in one direction

pero no me gustó mi respuesta a eso, así que ¿qué tal ésta:

Mantener clones independientes para lanzamientos anteriores. Cuando, en su ejemplo anterior, se decide que tiene que hacer una solución de emergencia sobre Aristóteles hacer esto en su lugar:

cd .. 
hg clone -r aristotle myrepo myrepo-aristotle 

entonces usted tiene un clon con solamente Aristóteles en ella y no se puede accidentalmente fusionarla al defecto en ese clon

Dicho esto, todavía no es una gran respuesta. El único consuelo real es que si te unes en una dirección que no te gusta, siempre puedes volver a clonar las dos cabezas y fundirte en la otra dirección.

+0

Sí, yo ver. Gracias. El problema es que decidí buscar ramas en lugar de clonar, desde el principio :) Aún quiero intentarlo ... ¿Crees que "pretxncommit" podría ayudar? – andreister

+0

Puede utilizar ramas anónimas, clones-como-ramas y ramas con nombre en cualquier combinación, por lo que no es demasiado tarde. Solo haz el comando de clonación que doy más arriba y tendrás un clon con solo esa cabeza de rama con nombre, así que no puedes fusionar accidentalmente en la otra dirección. Un enganche pretxncommit puede ralentizarlo, pero no puede almacenarlo en el repositorio (.hg/hgrc no está clonado) para que otra persona simplemente cometa el error por usted. –

0

Encontré esta pregunta mientras buscaba una solución a un problema relacionado, sé que es una vieja pregunta, pero pensé que compartiría nuestra solución.

Tenemos ramas de lanzamiento llamadas release-x.y.z y desarrollo continuo en el valor predeterminado. Usamos un enlace basado en el ejemplo precommit_badbranch_badmerge que se encuentra en https://www.mercurial-scm.org/wiki/HookExamples.

Básicamente, extraemos el número de versión de cada rama involucrada en cualquier fusión y nos aseguramos de que vaya por el camino correcto (por defecto se trata como 9999.9999.9999 cualquier cosa que no sea una versión o la rama predeterminada obtenga -1, -1 , -1 (se puede combinar en cualquier cosa).

Tenga en cuenta que esto solo hace una simple comprobación "inmediata", no detecta problemas donde alguien se fusiona de forma predeterminada a la rama blah y luego a una rama de versión.

Como nota al margen, también tenemos una política que la fusión a la predeterminada debe ser presionada al mismo tiempo que el cambio original en la rama de la versión, esto evita que alguien tenga que hacer la fusión para fusionar su propia cambios - está imponiendo aquello para lo que estaba buscando una solución.

Nota, se acoge a este uso de la sección de ganchos del servidor repo/central debajo de

[hooks] 
pretxnchangegroup.prevent_bad_merges = python:<path_to_hook>\prevent_bad_merges.py:prevent_bad_merges 

pitón gancho:

# based on precommit_badbranch_badmerge 
# at https://www.mercurial-scm.org/wiki/HookExamples 
import re 

# this isnt a proper compare, it will just return numbers > 0 if source is after target 
def compare_versions(source_version, target_version): 
    # if either side is -1, ignore it 
    if (source_version[0] == -1) or (target_version[0] == -1): 
     return 0; 

    if source_version[0] > target_version[0]: 
     return 1 
    elif source_version[0] == target_version[0]: 
     if source_version[1] > target_version[1]: 
      return 2 
     elif source_version[1] == target_version[1]: 
      if source_version[2] > target_version[2]: 
       return 3   
    return 0 

def get_version(branch): 
    if branch == 'default': 
     major=9999 
     minor=9999 
     revision=9999   
    else:  
     # note python uses ?P for named matches 
     match = re.match('(release-(?P<major>\d)\.(?P<minor>\d)\.(?P<revision>\d))', branch) 
     if match: 
      major = int(match.group('major')) 
      minor = int(match.group('minor')) 
      revision = int(match.group('revision')) 
     else: 
      major = -1 
      minor = -1 
      revision = -1 

    return [major,minor,revision] 

def prevent_bad_merges(ui, repo, node, **kwargs): 
    ui.debug("in hook\n") 
    for rev in xrange(repo[node].rev(), len(repo)): 
     ui.debug("in loop\n") 
     # get context (change) 
     ctx = repo[rev] 
     ui.debug("got ctx\n") 
     if len(ctx.parents()) > 1: 
      ui.debug("got a merge\n") 
      branch = ctx.branch() 
      ui.debug(branch +"\n") 
      parent1 = ctx.parents()[0] 
      ui.debug("got parent1\n") 
      parent2 = ctx.parents()[1] 
      ui.debug("got parent2\n") 

      target_branch = repo[parent1.node()].branch() 
      ui.debug("got parent1 branch\n") 
      target_version = get_version(target_branch) 
      ui.debug("got parent1 version\n") 

      source_branch = repo[parent2.node()].branch() 
      ui.debug("got parent2 branch\n") 
      source_version = get_version(source_branch) 
      ui.debug("got parent2 version\n") 

      # This could happen if someone does 
      # hg update 1.1-branch 
      # hg branch 1.2-branch 
      # hg merge 1.0-branch 
      # which is a strange thing to do. So disallow it. 
      if target_branch != branch: 
       ui.warn('PREVENT BAD MERGE HOOK FAILED : \n' 
         'merging to a different branch from first parent ' 
         'is just weird: please don\'t do that\n') 
       return True 

      ui.debug(source_branch, "\n") 
      ui.debug(str(source_version[0]), "\n") 
      #ui.debug("major:", source_version[0], "\n") 
      #ui.debug("minor:", source_version[1], "\n") 
      #ui.debug("revn :", source_version[2], "\n") 

      ui.debug(target_branch, "\n") 
      ui.debug(str(target_version[0]), "\n") 
      #ui.debug("major:", target_version[0], "\n") 
      #ui.debug("minor:", target_version[1], "\n") 
      #ui.debug("revn :", target_version[2], "\n") 

      # Check for backwards merge. 
      if compare_versions(source_version, target_version) > 0: 
       ui.warn('PREVENT BAD MERGE HOOK FAILED : \n' 
         'invalid backwards merge from %r to %r\n' 
         % (source_branch, target_branch)) 
       return True 
     else: 
      ui.debug("Not a merge\n") 
      # Not merging: nothing more to check. 


    return False 
1

Esto debe hacerlo. Utiliza un revset query para encontrar fusiones en aristotle desde default.

hg log -r 'children(p2(::aristotle and ::default and merge()) and branch(default)) and branch(aristotle)' 
  • ::aristotle and ::default and merge() encuentra todas las combinaciones que son ancestros de ambas ramas aristotle y default
  • p2(...) and branch(default) agarra la segunda matriz (conjunto de cambios entrante) que se encuentran en la rama default.
  • children(...) and branch(aristotle) luego toma el conjunto de cambios de fusión real que está en la rama aristotle.

I recently needed to figure this out myself sino también necesario para asegurar que no se defaultindirectamente fusionaron en mi rama de lanzamiento, es decir, por defecto -> función -> liberación.

Cuestiones relacionadas