(Traduction du texte original en anglais : Backout)
Backout
hg backout [OPTION]... [-r] REV
Annule les effets du précédent changeset.
Backout fonctionne en appliquant les modifications inverses à celles du dernier changeset. Ce nouveau changeset est validé (commit) au dépôt et éventuellement fusionné (merge).
Référence manuel : http://www.selenic.com/mercurial/hg.1.html#backout
1. Fonctionnement interne
Voici quelques explications complémentaires de Matt sur le fonctionnement interne (légèrement adapté, par expérience) (voir aussi ce fil de discussion e-mail)
Notez que cette page ne dit plus complètement la vérité. Le comportement par défaut a changé dans la 1.7 tel qu'il a été discuté ici.
Soit <rev de départ> la révision à laquelle nous allons faire un backout. Backout est en fait la compilation de quatre étapes en une :
hg update -C -r <rev-pour-le-backout>
hg revert --all -r <parent de rev-pour-le-backout>
hg commit
hg update -C -r <rev de départ>
Il y a une cinquième étape qui se fait automatiquement si vous utilisez l'option --merge :
hg merge (fusionne <rev de départ> avec la révision nouvellement validée à l'étape 3.)
Ainsi qu'une sixième étape, manuelle celle-là :
hg commit (le resultat de la fusion)
Lorsque l'étape 3 (commit) avorte, vous vous retrouvez avec les deux premières étapes terminées et vous pouvez faire :
hg commit et résoudre par vous même les problème, et / ou
hg update -C pour abandonner le processus
L'étape 4 permet de s'assurer que les changesets parents sont fusionnés dans le bon ordre. C'est à dire : parent1 = <rev de départ> et parent2 = <la nouvelle rev backout>.
2. Un exemple:
$ hg init depot $ cd depot $ echo ligne1 > fichier.txt $ echo ligne2 >> fichier.txt $ hg ci -Am "ajout de fichier"
Editons fichier.txt, pour obtenir :
ligne1 ligne1a ligne2
On commit et on fait quelques modifications :
$ hg ci -m "ajout ligne1a" $ echo ligne3 >> fichier.txt $ hg ci -m "ajout ligne3" $ echo ligne4 >> fichier.txt $ hg ci -m "ajout ligne4"
Ce qui produit l'arbre suivant (un brin raccourci) :
@ changeset: 3:36b1c0649d3e | tag: tip | summary: ajout ligne4 | o changeset: 2:2612107e45fe | summary: ajout ligne3 | o changeset: 1:1f33c361852e | summary: ajout ligne1a | o changeset: 0:e3e45b087239 summary: ajout de fichier
Maintenant on fait un backout sur le changeset 1:1f33c361852e.
$ hg backout -r 1
L'arbre est désormais :
o changeset: 4:c3daad6d657d | tag: tip | parent: 1:1f33c361852e | summary: Backed out changeset 1f33c361852e | | @ changeset: 3:36b1c0649d3e | | summary: ajout ligne4 | | | o changeset: 2:2612107e45fe |/ summary: ajout ligne3 | o changeset: 1:1f33c361852e | summary: ajout ligne1a | o changeset: 0:e3e45b087239 summary: ajout de fichier
On fusionne et commit, ce qui donne l'arbre final :
@ changeset: 5:236d8d74edf8 |\ tag: tip | | parent: 3:36b1c0649d3e | | parent: 4:c3daad6d657d | | summary: merge backout | | | o changeset: 4:c3daad6d657d | | parent: 1:1f33c361852e | | summary: Backed out changeset 1f33c361852e | | o | changeset: 3:36b1c0649d3e | | summary: ajout ligne4 | | o | changeset: 2:2612107e45fe |/ summary: ajout ligne3 | o changeset: 1:1f33c361852e | summary: ajout ligne1a | o changeset: 0:e3e45b087239 summary: ajout de fichier
Et fichier.txt est comme suit, s'étant vu joliment éliminé la 'ligne1a' de la rev 1.
ligne1 ligne2 ligne3 ligne4
Quand on essaie cela en séparant les étapes, et qu'on oublie l'étape 4, on obtient un arbre légèrement différent. Notez l'ordre inversé des parents dans le changeset 5:0eeac5ff9c76.
@ changeset: 5:0eeac5ff9c76 |\ tag: tip | | parent: 4:cbca219e80e1 | <--- | | parent: 3:f82e9468d652 | | | summary: merge backout | | | o changeset: 4:cbca219e80e1 | | parent: 1:0cf85b44002c | | summary: Backed out changeset 0cf85b44002c | | o | changeset: 3:f82e9468d652 | | summary: ajout ligne4 | | o | changeset: 2:24ceac6b9018 |/ summary: ajout ligne3 | o changeset: 1:0cf85b44002c | summary: ajout ligne1a | o changeset: 0:73af1be51d81 summary: ajout de fichier
3. Backout d'un Changeset fusionné
Avertissement
Annuler une fusion créera des problèmes si vous souhaiter refaire la fusion. La seule méthode valable avec une mauvaise fusion est d'abandonner la branche.
Imaginez une situation dans laquelle nous fusionnons deux changesets. Le premier changeset contient a b dans un fichier donné, le second contient x y dans ce même fichier. La fusion entraîne un conflit que nous résolvons ainsi :
Nous travaillons un peu sur la branche du dessus pour ajouter c dans ce fichier. Nous découvrons alors que la fusion était mauvaise. Nous faisons un backout dessus :
Le nouveau changeset rouge est le backout, il a supprimé les lignes Y et X de la mauvaise fusion, mais il a gardé la nouvelle ligne c qui a été ajoutée après la fusion.
Nous faisons quelques changements supplémentaires à la branche du dessous et la fusionnons encore :
L'idée est que le nouveau changeset fusionné devrait contenir les lignes x y z parallèlement aux lignes a b c. Cependant ce qui se passe est que la fusion contient un backout sur les lignes x et y !
Le meilleur ancêtre commun des changesets rouge a b c et bleu x y z est le changeset avec x y. La triple façon de fusionner considère ainsi le fichier dans ces configurations :
base : x y
local : a b c
autre : x y z
L'algorithme de fusion conclue que les lignes x et y ont été retirées volontairement, tandis que a b c et z ont été ajoutées. Le résultat est que les lignes x y sont écartées de manière tacite !