2011-05-16 16 views
8

Sto utilizzando il ramo default per lo sviluppo in corso e ora creerò un nuovo ramo con nome per contrassegnare un rilascio. Tutti ulteriore sviluppo sarà sul ramo di default, tutte le correzioni di produzione sarà fatto sul nuovo (con conseguente fusione per default), in questo modo:Mercurial: consenti l'unione da un ramo di rilascio a quello predefinito, ma non viceversa

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

Il sembra la strada da percorrere sopra, Tuttavia sembra facile da rovinare e unire l'altro modo intorno a (da default a aristotle che significa che tutte le nuove funzionalità appariranno nel ramo di produzione).

Forse le mie paure sono infondate perché si noterà il disordine prima di spingere il commit al repository centrale, ma mi piacerebbe vedere se è possibile rendere l'approccio più infallibile.

Così ho iniziato a guardare in ganci:

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

..ma poi si rese conto che non è quello che mi serve, perché questo non mi permette di spingere le modifiche Aristotele a tutti.

Quindi non sono sicuro di cosa fare. Idealmente, voglio che gli sviluppatori vedano il messaggio "unione errata" quando tentano di eseguire un'unione da default a aristotlelocalmente (ovviamente, ci dovrebbe essere un doppio controllo sul repository centrale), mentre si uniscono dal ramo di produzione a quello di default dovrebbe essere possibile.

Grazie!

+0

Forse si può fare qualcosa dove se il commit è sul ramo di produzione, e quel commit ha più di un genitore (un merge commit), e uno di quei genitori è sul ramo predefinito, fallire. Potresti voler cercare l'estensione contiene per aiutarti con questo: http://mercurial.selenic.com/wiki/ContainsExtension Non abbiamo alcuna protezione reale contro questo, ma abbiamo un hook post-commit che stampa ciò che è realmente accaduto durante il commit (stai unendo branch1 a branch2), che lo sviluppatore può leggere per verificare di aver fatto la cosa giusta. –

+0

Mark, molte grazie per il commento ma "hg update default" seguito da "hg merge aristotle" sembra esattamente come il messaggio "merging branch1 into branch2" (e ahimè, un messaggio non impedisce il push/commit sbagliato). Inoltre non sono sicuro di come "contiene l'estensione" possa aiutare nel mio caso :(Forse puoi approfondire come hai implementato esattamente il tuo hook post-commit? – andreister

+0

Il mio hook post-commit non impedisce effettivamente che accada qualcosa, ma fornisce informazioni alla persona che esegue il commit che spiega l'azione che sta accadendo. È come quando leggi un numero di telefono a qualcuno e poi chiedi loro di leggerlo di nuovo per assicurarti che ti capiscano. e poi Mercurial ti spiega l'azione in modo da avere la possibilità di cogliere un errore –

risposta

0

Questo è più o meno un duplicato esatto di una domanda da un paio di giorni fa Ensuring a merge between branches happens in one direction

ma non mi piaceva la mia risposta a quella, così come su questa:

Tenere cloni separati per versioni precedenti. Quando, nel tuo esempio sopra, si decide che devi fare una correzione di emergenza su Aristotele fare questo, invece:

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

allora hai un clone con solo Aristotele in esso e non si può accidentalmente fondersi per difetto in quel clone.

Detto questo, non è ancora una grande risposta. L'unica vera consolazione è che se ti unisci in una direzione che non ti piace puoi sempre ripetere la clonazione delle due teste e fonderti nell'altra direzione.

+0

Sì, I vedere. Grazie. Il problema è che ho deciso di andare per i rami invece della clonazione, all'inizio: voglio ancora provarci .. Pensi che "pretxncommit" potrebbe aiutarti? – andreister

+0

È possibile utilizzare rami anonimi, cloni-come-rami e rami denominati in qualsiasi combinazione, quindi non è troppo tardi. Basta fare il comando clone che ho dato sopra e avrai un clone con solo la testa del ramo con quel nome, quindi non puoi accidentalmente unire l'altro modo. Un hook pretxncommit potrebbe rallentarti, ma non puoi memorizzarlo nel repository (.hg/hgrc non è clonato) in modo che qualcun altro possa fare l'errore per te. –

0

Ho trovato questa domanda mentre cercavo una soluzione a un problema correlato, so che è una vecchia domanda, ma ho pensato di condividere la nostra soluzione.

Abbiamo i rami di rilascio denominati release-x.y.z e lo sviluppo in corso su impostazione predefinita. Usiamo un hook basato sull'esempio precommit_badbranch_badmerge trovato a https://www.mercurial-scm.org/wiki/HookExamples.

Fondamentalmente estraiamo il numero di versione per ogni ramo coinvolto in qualsiasi unione e assicuriamo che stia procedendo nel modo giusto (il valore predefinito è 9999,999,99999, quello che non è un rilascio o il ramo predefinito ottiene -1, -1 , -1 (può essere unito a qualsiasi cosa)

Si noti che questo fa solo un semplice controllo 'immediato', non rileva problemi dove qualcuno si fonde dal default al ramo blah e quindi su un ramo di rilascio.

Come nota a margine, abbiamo anche una politica che l'unione di default deve essere spinta contemporaneamente alla modifica originale sul ramo di rilascio, questo evita che qualcun altro debba fare l'unione per unire i propri cambiamenti - sta facendo rispettare quello che stavo cercando una soluzione per.

nota, si richiama questo usando la sezione ganci del server/repository centrale

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

gancio pitone di seguito:

# 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

Questo dovrebbe farlo. Utilizza uno revset query per trovare unioni in aristotle da default.

hg log -r 'children(p2(::aristotle and ::default and merge()) and branch(default)) and branch(aristotle)' 
  • ::aristotle and ::default and merge() trova tutti fusioni che sono antenati di entrambi aristotle e default rami
  • p2(...) and branch(default) afferra il secondo genitore (changeset in entrata) che sono sul ramo default.
  • children(...) and branch(aristotle) quindi acquisisce il set di modifiche unione esistente nel ramo aristotle.

I recently needed to figure this out myself ma anche necessario per assicurare che non era defaultindirettamente fuse nel mio ramo di release, vale a dire di default -> funzione -> rilascio.

Problemi correlati