Hg: Comment faire un rebase comme le rebase de git

207

Dans Git, je peux le faire:

1. Commencez à travailler sur une nouvelle fonctionnalité:
$ git co -b newfeature-123 # (une branche de développement de fonctionnalités locales)
faire quelques commits (M, N, O)

maître A --- B --- C
                \
nouveaut é-123 M --- N --- O

2. Tirez les nouvelles modifications du maître en amont:
$ git pull
(maître mis à jour avec ff-commits)

maître A --- B --- C --- D --- E --- F
                \
nouveaut é-123 M --- N --- O

3. Rebase off master afin que ma nouvelle fonctionnalité 
peut être développé contre les derniers changements en amont:
(à partir de newfeature-123)
$ git rebase master

maître A --- B --- C --- D --- E --- F
                            \
nouveaut é-123 M --- N --- O


Je veux savoir comment faire la même chose dans Mercurial, et j'ai parcouru le Web pour trouver une réponse, mais le meilleur que j'ai pu trouver était: git rebase - can hg do that

Ce lien fournit 2 exemples:
1. J'admets que ceci: (en remplaçant les révisions de l'exemple par celles de mon propre exemple)

hg up -CF  
hg branch -f newfeature-123  
greffe de hg -a -b nouveauté-123 

n'est pas trop mal, sauf qu'il laisse derrière le MNO pré-rebase en tant que tête non fusionnée et crée 3 nouveaux commits M ', N', O 'qui les représentent se ramifiant de la ligne principale mise à jour.

Fondamentalement, le problème est que je me retrouve avec ceci:

maître A --- B --- C --- D --- E --- F
                \ \
newfeature-123 \ M '--- N' --- O '
                  \
nouveaut é-123 M --- N --- O

ce n'est pas bon car cela laisse des commits locaux et indésirables qui devraient être supprimés.

  1. L'autre option du même lien est
hg qimport -r M: O
hg qpop -a
hg up F
hg branch newfeature-123
hg qpush -a
hg qdel -r qbase: qtip

et cela donne le graphique souhaité:

maître A --- B --- C --- D --- E --- F
                            \
nouveaut é-123 M --- N --- O

mais ces commandes (toutes les 6!) semblent tellement plus compliquées que

$ git rebase master

Je veux savoir si c'est le seul équivalent en Hg ou s'il existe une autre méthode disponible aussi simple que Git.

jpswain
la source
7
"ce n'est pas bon car cela laisse des commits locaux et indésirables qui devraient être abandonnés." - en fait, git fait la même chose. Il ne modifie ni ne supprime les validations dans la branche d'origine, il en crée simplement de nouvelles qui appliquent le même ensemble de modifications au-dessus de master. Vous pouvez toujours accéder aux anciens en utilisant git refloget ils ne sont pas totalement partis tant qu'ils n'ont pas récupéré les ordures. Si vous souhaitez les conserver dans une branche nommée afin de ne pas avoir à utiliser le reflog, faites-le git branch feature-123_originalavant de rebaser.
MatrixFrog
5
Question aléatoire: avez-vous tracé vous-même les modifications / branches ou existe-t-il un outil qui le fait?
Amir Rachum
2
Je viens de les faire moi-même avec TextWrangler réglé sur "écraser".
jpswain

Réponses:

235

VonC a la réponse que vous cherchez , l'extension Rebase. Cependant, cela vaut la peine de passer une seconde ou deux à réfléchir à la raison pour laquelle ni mq ni rebase ne sont activés par défaut dans mercurial: car mercurial est une question de changements indélébiles. Lorsque je travaille de la manière que vous décrivez, ce qui est presque quotidien, voici le schéma que je prends:

1. Start working on a new feature:
$ hg clone mainline-repo newfeature-123
do a few commits (M, N, O)

master A---B---C
                \
newfeature-123   M---N---O

2. Pull new changes from upstream mainline:
$ hg pull

master A---B---C---D---E---F
                \
newfeature-123   M---N---O

3. merge master into my clone so that my new feature 
can be developed against the latest upstream changes:
(from newfeature-123)
$ hg merge F

master A---B---C---D---E---F
                \           \
newfeature-123   M---N---O---P

et c'est vraiment tout ce qui est nécessaire. Je me retrouve avec un clone newfeature-123 que je peux facilement repousser vers la ligne principale lorsque j'en suis satisfait. Mais surtout, je n'ai jamais changé l'histoire . Quelqu'un peut regarder mes csets et voir contre quoi ils ont été codés à l'origine et comment j'ai réagi aux changements dans la ligne principale tout au long de mon travail. Tout le monde ne pense pas que cela ait de la valeur, mais je suis fermement convaincu que c'est le travail du contrôle des sources de nous montrer non pas ce que nous aurions souhaité, mais ce qui s'est réellement passé - chaque impasse et chaque refactor devrait laisser une trace indélébile et rebaser et d'autres techniques d'édition de l'histoire cachent cela.

Maintenant, allez chercher la réponse de VonC pendant que je range ma boîte à savon. :)

Ry4an Brase
la source
18
Remarque: bien sûr, Git ne vous permet pas exactement de réécrire l'historique, seulement d'en créer un nouveau facilement ( utcc.utoronto.ca/~cks/space/blog/tech/GitNewHistory ). En ajoutant RebaseExtension, Mercurial fournit le même moyen pratique et exact de remplacer un ancien historique par un nouveau. Pourquoi? Parce qu'une fusion n'est pas toujours la bonne réponse, surtout lorsque votre ensemble de modifications doit être considéré comme des évolutions au-dessus de F, et non l'inverse (P fusionné au-dessus de O)
VonC
19
VonC, je suis d'accord, une fusion n'est pas toujours le bon choix, mais je pense que la différence réside dans ce que l'on veut que l'histoire de VCS puisse leur dire. Je pense que l'histoire devrait toujours être en mesure de répondre à des questions telles que "Comment ai-je essayé de l'intégrer au début qui n'a pas fonctionné et que je pensais inutile à l'époque". Les scientifiques gardent les journaux de bord dans un stylo avec des pages numérotées, et les ingénieurs logiciels de l'AFAIC devraient enregistrer chaque octet qu'ils ont déjà tapé. La réécriture de l'histoire, même la parenté cset, mais certainement CollapseExtension, HistEdit, etc. violent cela. C'est totalement une question de choix personnel.
Ry4an Brase
14
+1 pour choix personnel. Avec Git, j'utilise par conséquent rebase pour les divergences triviales et fusion pour les non triviales. Cela me permet de conserver l'historique des fusions là où je pense que c'est important, mais de garder le journal propre et linéaire la plupart du temps.
Kos
16
Je fais également des tonnes de rebases interactifs parce que j'ai tendance à faire d'abord beaucoup de petits commits, puis à les rejoindre, à les étiqueter et à les nettoyer, puis à les fusionner avec (ou rebaser au-dessus) de la branche principale. J'aime que le codage et la gestion des modifications soient des étapes distinctes.
Kos
6
La philosophie «enregistrer chaque octet sur le chemin du développement» n'est pas vraiment appropriée pour les grands projets open source, où les contributeurs proposent un ensemble de correctifs, puis les retravaillent en fonction des commentaires des responsables. Ensuite, le référentiel de projet principal a juste la version appropriée de toutes les modifications, avec toutes les modifications associées dans un seul commit. git interactive rebase est vraiment bon pour nettoyer les changements de travail en une séquence de validations qui ne laisse pas l'arbre cassé, et avec des messages de validation reflétant ce que vous décidez de dire après avoir fini de travailler sur le tout.
Peter Cordes
104

Vous recherchez peut-être l' extension Rebase . (implémenté dans le cadre du SummerOfCode 2008 )

Dans ces cas, il peut être utile de «détacher» les modifications locales, de synchroniser le référentiel avec le courant dominant, puis d'ajouter les modifications privées en plus des nouvelles modifications à distance. Cette opération est appelée rebase.

En provenance de :

texte alternatif

à:

texte alternatif


Comme commenté ci-dessous par steprobe :

Dans le cas où vous ne récupérez pas les modifications et que vous avez les deux branches dans votre référentiel, vous pouvez faire (en utilisantkeepbranches ):

hg up newfeature-123 
hg rebase -d master --keepbranches

( --keepbranches: Hérite du nom de la branche d'origine.)

Mojca mentionne:

J'aime utiliser hg rebase --source {L1's-sha} --dest {R2's-sha}, mais je ne savais pas que je pourrais ajouter --keepbranchesà la fin.

Comme illustré ci-dessous par Jonathan Blackburn :

 hg rebase -d default --keepbranches
VonC
la source
1
J'ai regardé l'extension Rebase, mais elle n'est toujours pas claire pour moi. Pourriez-vous s'il vous plaît expliquer les étapes pour faire ce que j'ai décrit ci-dessus?
jpswain
6
Dans le cas où vous ne récupérez pas les modifications et que vous avez les deux branches dans votre référentiel, vous pouvez faire: hg up newfeature-123suivi dehg rebase -d master --keepbranches
steprobe
2
Je crois que rien de mal à rebaser, c'est juste une question de choix. Le problème est que le rebase est abusé et je suis avec @ Ry4an à ce sujet ne réécrivez pas l'histoire afin que vous puissiez savoir ce qui se passe et quand.
Jorge Vargas
@steprobe: merci pour l'astuce pour l'utilisation de --keepbranches. J'aime utiliser hg rebase --source {L1's-sha} --dest {R2's-sha}, mais je ne savais pas que je pourrais ajouter --keepbranchesà la fin. Ceci est mentionné dans d'autres réponses de rang inférieur, mais ce serait bien de l'écrire explicitement dans cette réponse également.
Mojca
@Mojca Pas de problème. J'ai modifié la réponse en conséquence.
VonC
44

En supposant que vous disposez d'une installation moderne de Hg, vous pouvez simplement ajouter:

[extensions]
rebase = 

à ~ / .hgrc.

Ensuite , vous pouvez utiliser les commandes hg rebase, hg pull --rebaseou hg help rebase.

sblom
la source
2
Juste pour ajouter à cela en termes de commande, vous devez alors exécuter serait: hg rebase -s o -d f
Simple-Solution
21

Je ne pense pas que les réponses ci-dessus atteignent l'objectif du PO, qui était de maintenir sa branche de tâches, juste rebasée contre un point ultérieur sur la branche parent.

Disons que je commence par ce graphique (généré à l'aide de l'extension graphlog. Un sérieux amour geek pour graphlog).

@  9a4c0eb66429 Feature 3 commit 2 tip feature3
|
| o  af630ccb4a80 default againagainagain  
| |
o |  98bdde5d2185 Feature 3 branch commit 1  feature3
|/
o  e9f850ac41da foo   

Si je suis sur la branche feature3 et que je veux la rebaser hors du commit de nouveau, je comprends que je vais courir hg rebase -d default. Cela a le résultat suivant:

@  89dada24591e Feature 3 commit 2 tip 
|
o  77dcce88786d Feature 3 branch commit 1  
|
o  af630ccb4a80 default againagainagain  
|
o  e9f850ac41da foo  

Mission accomplie? Je ne pense pas. Le problème est que lorsque les validations sur la branche feature3 ont été rebasées à nouveau, la branche feature3 a été supprimée . Mes validations ont été déplacées vers la branche par défaut, ce que j'essayais d'éviter en premier lieu.

Dans Git, le résultat ressemblerait à ceci:

@  9a4c0eb66429 Feature 3 commit 2 tip
|
o  98bdde5d2185 Feature 3 branch commit 1 **feature3**
|
o  af630ccb4a80 default againagainagain
|
o  e9f850ac41da foo

Notez que la branche feature3 existe toujours, les deux validations sont toujours sur la branche feature3 et ne sont pas visibles par défaut. Sans conserver la branche des tâches, je ne vois pas en quoi cela est fonctionnellement différent d'une fusion.

MISE À JOUR : J'ai découvert le --keepbranchesdrapeau pris en charge par hg rebase, et je suis heureux de signaler que tout est okey-dokey. En utilisant hg rebase -d default --keepbranches, je reproduis exactement le comportement de Git dont j'avais envie. Quelques alias plus tard et je rebase comme si personne ne travaillait.

Jonathan Blackburn
la source
7
Je viens de découvrir le drapeau --keepbranches sur rebase. Problème résolu. Si c'était mon code, je ferais cela par défaut, mais c'est juste moi.
Jonathan Blackburn
1
Je pense qu'il serait utile que vous ajoutiez cette information à votre réponse ci-dessus - il m'a fallu un certain temps pour la trouver.
HansMari
3

Étant donné que certaines personnes ont sonné en disant qu'elles pensent qu'il est bon de garder chaque itération de tout, je soulignerai que pour les projets open source plus importants, accepter des changements pleins de fusions et d'itérations de développement ferait un historique de révision désordonné de la ligne principale, et ferait l'historique des révisions moins utile pour voir comment la version actuelle y est arrivée.

Cela fonctionne bien lorsque les modifications soumises sont examinées par des personnes qui ne les ont pas écrites, avant d'être acceptées, de sorte que les modifications qui entrent dans la ligne principale sont généralement déboguées et fonctionnent. Ensuite, lorsque vous revenez à l'origine d'une ligne, vous voyez tous les changements qui vont avec, pas un moment au milieu du développement du changement dont elle fait partie.

La page des contributeurs x265 explique comment réengager un ensemble de modifications sur lesquelles vous travaillez, afin de les préparer pour la soumission au projet x265. (Y compris l'utilisation de TortoiseHG pour valider certaines modifications, mais pas toutes, dans un fichier individuel, comme le morceau diff de stage / unstage de git gui pour la validation).

Le processus consiste à mettre hg à jour vers la pointe en amont, puis à récupérer toutes vos modifications non validées dans le répertoire de travail. Triez ceux qui ne font pas partie de ce que vous souhaitez soumettre, puis divisez le reste en autant de validations distinctes appropriées, avec de beaux messages de validation.

Je suppose que vous copiez / collez, puis modifiez les messages de validation des itérations précédentes d'un ensemble de correctifs que vous révisez. Ou peut-être pourriez-vous greffer vos anciens commits (sélection de cerise dans le langage git), puis les modifier un par un, pour obtenir vos anciens messages de commit comme point de départ pour la modification.

Peter Cordes
la source