Puis-je squash commits dans Mercurial?


110

J'ai une paire de commits qui ne devraient vraiment en être qu'un. Si j'utilisais git, j'utiliserais:

git rebase -i <some-commit-before>

puis écrasez-les.

Puis-je faire ça dans Mercurial? Si c'est le cas, comment?

Réponses:


88

Oui, vous pouvez le faire en utilisant mercurial sans aucune extension en concaténant les ensembles de modifications .

Alternativement, si vous souhaitez utiliser une extension, vous pouvez utiliser:


Oui, j'ai pêché cette réponse parmi les questions de dupe que j'aimais dans mon commentaire sur la question générale. Je pense que c'est votre réponse à ce sujet.
Ry4an Brase

4
Petite remarque: l'extension Histedit est distribuée avec Mercurial 2.3 et versions ultérieures. Il vous suffit de l'activer.
Paidhi le

1
Dans le document Concatenating Changesets, le doc utilise des concepts abstraits de "repos", comment les référencer? Par exemple: hg -R oldrepo export ... produit "abort: repository oldrepo not found!
Aleksandr Levchuk

13
J'essaye juste d'écraser 2 commits. Ai-je vraiment besoin d'une page wiki avec plus de 10 commandes ou des extensions alternatives?
Aleksandr Levchuk

3
Voir les commentaires. Histedit est maintenant intégré, il vous suffit de l'activer (car aucune commande par défaut ne modifiera l'historique)
Ry4an Brase

43

Mon préféré est la hg strip --keepcommande. Et puis je valide tous les changements en un seul commit.

C'est le moyen le plus rapide et le plus confortable pour moi, car j'aime faire de nombreux petits engagements au cours de mon travail quotidien;)


Remarque 1: stripnécessite une extension intégrée mqpour être activée.
Remarque 2: Mon client Git / Mercurial préféré (SmartGit / Hg) ajoute par défaut un --keepparamètre pendant strip. Et ce qui est encore plus pratique: il propose une option appelée join commits:]


4
La commande complète pour hg strip est: hg strip --keep --rev [rev]revest le numéro de révision du premier commit que vous voulez écraser avec le dernier
Nicolas Forney

3
@NicolasForney Pas exactement, --revest facultatif, la commande complète esthg strip --keep [rev]
G.Demecki

La révision est obligatoire pour moi en 3.3.3: hg help stripdonne hg strip [-k] [-f] [-n] [-B bookmark] [-r] REV..., et omettre la révision me donne abort: empty revision set.
Roman Starkov le

3
L'utilisation hg stripn'est pas la meilleure idée. Ce n'est pas vraiment sûr. Essayez hg histedit, peut-être même essayez d'utiliser l'extension evolve.
Martin Thomson

Semble le moyen le plus naturel pour les gens git;)
foo

32

L' extension Rebase a fonctionné comme un charme. Pour écraser 2 commits:

$ hg rebase --dest .~2 --base . --collapse

Le point est un raccourci pour la révision actuelle.

C'est encore plus facile lorsque vous avez quelques commits sur une branche et que vous souhaitez les réduire en un seul:

$ hg rebase --dest {destination branch (e.g. master)} --base . --collapse

Comment ça marche:

entrez la description de l'image ici

(à partir de http://mercurial-scm.org/wiki/RebaseExtension#Collapsing )


1
Où avez-vous trouvé le "~ 2" pour deux commits?
Mi-La

1
C'est expliqué dans la rubrique revsets, voir hg help revsets.
markand

13

Si vous lisez cette réponse, vous pouvez oublier toutes les autres options mentionnées dans cette réponse et utiliser la foldcommande de l' extension evolve .

evolveest une extension de mercurial qui nous aide à avoir une histoire mutable sûre, mais elle est encore expérimentale. Vous pouvez l'utiliser en le clonant à partir de son dépôt et en l'ajoutant dans votre .hgrc comme ceci.

[extensions]
evolve = ~/evolve/hgext/evolve.py

En supposant que vous avez cloné le dépôt évolutif dans votre répertoire personnel. Maintenant, vous êtes prêt à partir. Vous pouvez également rechercher de l'aide par hg help fold.

Commande de pliage

Vous dites foldd'écraser / plier une chaîne linéaire de commits qui n'est pas interrompue. Ce que fait fold, c'est qu'il crée un nouvel ensemble de modifications qui contient les modifications de tous les ensembles de modifications et marque toutes ces validations comme obsolètes. Vous pouvez avoir une vue plus approfondie de cela dans docs .

Supposons maintenant que vous ayez l'historique suivant.

a -> b -> c -> d -> e -> f -> g

Vous voulez écraser e, fet g. Tu peux faire

hg up g
hg fold -r e

Le résultat sera

a -> b -> c -> d -> h

hest l'ensemble de modifications contenant les modifications des trois validations e, fet g.

Vous pouvez également plier les changesets à partir du milieu de l'historique, c'est-à-dire que vous ne devez pas nécessairement choisir une chaîne qui inclut la pointe. Supposons que vous vouliez plier b, cet d. Tu peux faire

hg up d
hg fold -r b
hg evolve --all

Cela entraînera

a -> i -> j

iest pliée de l' ensemble de modifications b, c, det jest le même que ChangeSet h. Le guide de l'utilisateur Evolve est une lecture incontournable.


On dirait que rebase couvre la plupart (peut-être tous?) Des cas d'utilisation de cette extension, et certainement celui qui est posé dans cette question. La fonctionnalité tueur de cette extension est de masquer (plutôt que de supprimer) les révisions que vous remplacez, mais l' --keepoption de rebase couvre cela (suivi en marquant les révisions comme secrètes, ou en utilisant une bande dessus une fois que vous avez vérifié le résultat). Même déplacer des révisions entre d'autres révisions est possible avec une séquence de deux commandes de rebase.
Arthur Tacca

... de plus, si vous faites quelque chose de vraiment compliqué, vous pouvez toujours simplement cloner le dépôt local en premier pour l'utiliser comme sauvegarde. Compte tenu de la rareté (espérons-le!), C'est moins d'effort que d'apprendre à utiliser une extension totalement nouvelle.
Arthur Tacca

"NameError: le nom 'execfile' n'est pas défini" - ce qui signifie évoluer est écrit en Python 2, qui est fondamentalement l'âge de pierre.
Neil G

1
@NeilG mercurial ne prend pas encore en charge Python 3.
Pulkit Goyal

2
@NeilG ouais, la communauté mercurial travaille dur pour obtenir le support py3 dès que possible.
Pulkit Goyal

1

Avec Mercurial 4.8 (novembre 2018, 9 ans plus tard), vous pouviez envisager la nouvelle commande hg absorb(c'était une fonctionnalité expérimentale auparavant ).

Voir " Absorption des changements de validation dans Mercurial 4.8 "

L'extension absorber prendra chaque changement dans votre répertoire de travail, déterminera quels commits de votre série ont modifié cette ligne et modifieront automatiquement le changement de ce commit.
S'il y a une ambiguïté (c'est-à-dire que plusieurs commits ont été modifiés sur la même ligne), absorber ignorera simplement ce changement et le laissera dans votre répertoire de travail pour qu'il soit résolu manuellement.

Au niveau technique, hg absorbtrouve toutes les modifications non validées et tente de mapper chaque ligne modifiée à une validation antérieure sans ambiguïté.
Pour chaque modification qui peut être mappée proprement, les modifications non validées sont absorbées dans la validation antérieure appropriée. Les validations affectées par l'opération sont rebasées automatiquement.
Si une modification ne peut pas être mappée à une validation antérieure sans ambiguïté, elle n'est pas validée et les utilisateurs peuvent revenir à un flux de travail existant (par exemple en utilisant hg histedit).

La logique de réécriture automatique de hg absorbest implémentée en suivant l'historique des lignes: Ceci est fondamentalement différent de l'approche adoptée par hg histeditou git rebase, qui a tendance à s'appuyer sur des stratégies de fusion basées sur la fusion à 3 voies pour dériver une nouvelle version d'un fichier avec plusieurs entrées versions.

Cette approche combinée au fait que hg absorbe saute les changements avec un commit d'application ambigu signifie que hg absorbe ne rencontrera jamais de conflits de fusion!

Maintenant, vous pensez peut-être que si vous ignorez les lignes avec des cibles d'application ambiguës, le correctif s'appliquerait toujours proprement en utilisant une fusion à 3 voies classique. Cette déclaration semble logiquement correcte. Mais ce n'est pas le cas: hg absorbpeut éviter les conflits de fusion lorsque la fusion est effectuée hg histeditou git rebase -iéchoue.


0

Je pense que chistedit(intégré depuis Mercurial 2.3) est le plus proche de rebase -icelui qui est purement Mercurial ( chisteditest la version interactive de histedit). Une fois dans histedit, la foldcommande correspond aux rebases squashet les rollcommandes aux rebases fixup. Consultez la documentation histedit pour plus d'informations.

Voici un exemple simple. Supposons que vous ayez les éléments suivants et que vous souhaitiez déplacer toutes les modifications de 1e21c4b1 dans la révision précédente tout en conservant le message de la révision précédente.

@  1e21c4b1 drees tip
|  A commit you want to squash
o  b4a738a4 drees
|  A commit
o  788aa028 drees
|  Older stuff

Vous pouvez exécuter hg chistedit -r b4a738a4pour modifier l'historique vers b4a738a4. Dans chistedit, vous passez ensuite le curseur sur 1e21c4b1 et appuyez sur rpour indiquer que vous souhaitez lancer cette révision. Notez que l'ordre dans histedit (du plus ancien au plus récent) est inversé de hg log(du plus récent au plus ancien).

#0  pick   160:b4a738a49916   A commit
#1  ^roll  161:1e21c4b1500c

Après avoir choisi vos modifications, vous choisissez cde les valider. Le résultat est le suivant:

@ bfa4a3be drees pointe | Un engagement o 788aa028 drees | Trucs plus anciens

Si vous êtes relativement nouveau pour eux, alors histeditpeut être un meilleur choix que chisteditparce qu'il fournit les descriptions de commande dans le fichier histedit pour référence. Il faut juste un peu plus d'édition pour définir les commandes en utilisant l'édition de texte normale (tout comme le rebase normal).

Remarque, pour utiliser l'un histeditou l' autre ou chisteditvous devez ajouter histedità vos extensions dans votre ~ / .hgrc:

[extensions]
histedit =

J'ai suggéré chisteditcar il est le plus proche rebase -iet fonctionne n'importe où dans l'histoire. Si vous voulez vraiment simplement subsumer / modifier la révision actuelle dans la précédente, alors @G. La stripsuggestion de Demecki peut être bonne car ce qui se passe est clair. Il est intégré depuis Mercuria 2.8. Pour obtenir les résultats équivalents ci-dessus, vous pouvez effectuer les opérations suivantes:

hg strip .
hg add
hg commit --amend

Remarque strip, comme histedit, doit être activé dans votre ~ / .hgrc:

[extensions]
strip =

0

Supposons que vous vouliez écraser (unir) les 2 derniers commits.

  1. Trouver un numéro de révision

    hg log -G -l 3
    

    sortie possible:

    @  changeset:   156:a922d923cf6f
    |  branch:      default
    |  tag:         tip
    |  user:        naXa!
    |  date:        Thu Dec 13 15:45:58 2018 +0300
    |  summary:     commit message 3
    |
    o  changeset:   155:5feb73422486
    |  branch:      default
    |  user:        naXa!
    |  date:        Thu Dec 13 15:22:15 2018 +0300
    |  summary:     commit message 2
    |
    o  changeset:   154:2e490482bd75
    |  branch:      default
    ~  user:        naXa!
       date:        Thu Dec 13 03:28:27 2018 +0300
       summary:     commit message 1
    
  2. Branche de réinitialisation logicielle

    hg strip --keep -r 155
    
  3. Valider à nouveau les modifications

    hg commit -m "new commit message"
    

Remarques

stripnécessite une extension intégrée pour être activée. Créez / modifiez le ~/.hgrcfichier de configuration avec le contenu suivant:

[extensions]
strip = 

-1

J'utilise:

hg phase --draft --force -r 267
...
hg rebase --dest 282 --source 267 --collapse
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.