web-dev-qa-db-fra.com

Git squash s'engage au milieu d'une branche

Je veux écraser plusieurs commits ensemble au milieu d'une branche sans modifier les commits avant et après.

J'ai :

A -- B -- C -- D -- E -- F -- G
|                             |
master                        dev
Origin/master

Je veux écraser ça dans

A -- H -- E -- F -- G
|                   |
master              dev
Origin/master

H est équivalent à B -- C -- D. Et je veux pouvoir spécifier le message de commit de H. A est le dernier commit qui a été poussé, donc tous les commit après cela peuvent être réécrits sans gâcher le serveur. L'idée est de nettoyer l'historique avant d'avancer rapidement master.

Comment puis je faire ça ?

PS: Notez que dans mon cas, j'ai en fait beaucoup plus de 3 commits de squash au milieu, mais si je peux le faire avec 3, je devrais pouvoir le faire avec plus.

PPS: Aussi, si possible, je préférerais une solution où E, F et G restent intacts (principalement en ce qui concerne la date de validation).

29
deadbeef

Vous pouvez effectuer un rebase interactif et sélectionner manuellement les validations que vous souhaitez écraser. Cela réécrira l'historique de votre branche dev, mais comme vous n'avez pas poussé ces validations, il ne devrait pas y avoir de conséquence négative en plus de ce qui pourrait arriver sur votre propre ordinateur.

Commencez par ce qui suit:

git checkout dev
git rebase -i HEAD~6

Cela devrait faire apparaître une fenêtre vous montrant la liste suivante de 7 validations, en remontant 6 étapes depuis la HEAD de votre branche dev:

pick 07c5abd message for commit A
pick dl398cn message for commit B
pick 93nmcdu message for commit C
pick lst28e4 message for commit D
pick 398nmol message for commit E
pick 9kml38d message for commit F
pick 02jmdmp message for commit G

Le premier commit affiché (A ci-dessus) est le le plus ancien et le dernier est le plus récent. Vous pouvez voir que par défaut, l'option pour chaque commit est pick. Si vous avez terminé le rebase maintenant, vous conserveriez simplement chaque commit tel quel, ce qui est en fait un no-op. Mais comme vous voulez écraser certaines validations intermédiaires, modifiez et changez la liste en ceci:

pick 07c5abd message for commit A
pick dl398cn new commit message for "H" goes here
squash 93nmcdu message for commit C
squash lst28e4 message for commit D
pick 398nmol message for commit E
pick 9kml38d message for commit F
pick 02jmdmp message for commit G

Notez attentivement ce qui se passe ci-dessus. En tapant squash, vous dites à Git de fusionner ce commit dans celui ci-dessus it, qui est le commit venu immédiatement avant it. Donc, cela dit de squash commit D en arrière dans commit C, puis de squash C dans B, vous laissant avec un seul commit pour les commits B, C et D. Les autres commits restent tels quels.

Enregistrez le fichier (: wq sur Git Bash dans Windows), et le rebase est terminé. Gardez à l'esprit que vous pouvez obtenir des conflits de fusion comme vous pouvez vous y attendre, mais il n'y a rien de spécial à résoudre et vous pouvez continuer comme vous le feriez avec n'importe quel rebase ou fusion régulier.

Si vous inspectez la branche après le rebase, vous remarquerez que les validations E, F et G ont désormais de nouveaux hachages, dates, etc. En effet, ces validations ont en fait été remplacés par de nouveaux commits. La raison en est que vous avez réécrit l'histoire, et donc les commits en général ne peuvent plus être les mêmes qu'avant.

38
Tim Biegeleisen