304 votes

Comment séparer un commit Git enterré dans l'historique ?

J'ai gâché mon historique et je veux y apporter quelques modifications. Le problème est que j'ai un commit avec deux changements sans rapport, et ce commit est entouré par d'autres changements dans mon historique local (non poussé).

Je veux diviser ce commit avant de le pousser, mais la plupart des guides que je vois concernent la division du commit le plus récent, ou des changements locaux non validés. Est-il possible de faire cela à un commit qui est un peu enterré dans l'histoire, sans avoir à "refaire" mes commits depuis lors ?

460voto

Jefromi Points 127932

Il existe un guide sur le fractionnement des commits dans la page de manuel de rebase . Le résumé rapide est :

  • Effectuer un rebasement interactif incluant le commit cible (ex. git rebase -i <commit-to-split>^ branch ) et marquez-le pour être édité.

  • Lorsque le rebasement atteint ce commit, utilisez git reset HEAD^ pour revenir à la situation d'avant le commit, mais garder votre arbre de travail intact.

  • Ajoutez incrémentiellement des changements et livrez-les, en faisant autant de livraisons que vous le souhaitez. add -p peut être utile pour ajouter seulement certaines des modifications dans un fichier donné. Utilisez commit -c ORIG_HEAD si vous voulez réutiliser le message de livraison original pour une certaine livraison.

  • Si vous voulez tester ce que vous êtes en train de commettre (bonne idée !), utilisez git stash pour dissimuler la partie que vous n'avez pas commise (ou stash --keep-index avant même de l'engager), tester, puis git stash pop pour remettre le reste dans l'arbre de travail. Continuez à faire des livraisons jusqu'à ce que toutes les modifications soient livrées, c'est-à-dire jusqu'à ce que vous ayez un arbre de travail propre.

  • Exécuter git rebase --continue pour continuer à appliquer les commits après le commit maintenant scindé.

17 votes

... mais n'en faites rien si vous avez déjà poussé l'historique depuis le commit vers split.

29 votes

@wilhelmtell : J'ai omis mon habituel boilerplate "potentiellement dangereux ; voir 'recovering from upstream rebase'" parce que le PO a explicitement dit qu'il n'avait pas poussé cette histoire.

2 votes

Et vous avez fait une lecture parfaite. J'essayais d'éviter le "boilerplate" quand j'ai spécifié que ce n'était pas encore de l'histoire partagée :) En tout cas, j'ai eu du succès avec votre suggestion. Mais c'est très pénible de faire ce genre de choses après coup. J'ai appris une leçon ici, et c'est de s'assurer que les commits sont mis en place correctement pour commencer !

8voto

unhammer Points 646

Voici comment procéder avec Magit .

Disons que le commit ed417ae est celui que vous voulez modifier ; il contient deux changements sans rapport et est enterré sous un ou plusieurs commits. Cliquez sur ll pour afficher le journal, et naviguer jusqu'à ed417ae :

initial log

Ensuite, appuyez sur r pour ouvrir la fenêtre popup de rebasement

rebase popup

et m pour modifier l'engagement à un moment donné.

Remarquez comment le @ il y a maintenant sur le commit que vous voulez scinder - cela signifie que HEAD est maintenant sur ce commit :

modifying a commit

Nous voulons déplacer HEAD vers le parent, donc naviguez vers le parent (47e18b3) et appuyez sur x ( magit-reset-quickly , lié à o si vous utilisez evil-magit ) et entrez pour dire "oui, je voulais dire commettre au point". Votre journal devrait maintenant ressembler à ceci :

log after resetting

Maintenant, tapez q pour passer au statut régulier de Magit, puis utiliser le statut régulier d'unstage u pour déstocker ce qui ne va pas dans le premier commit, commiter c le reste comme d'habitude, puis s tage et c omettre ce qui va dans le deuxième commit, et quand c'est fait : taper sur r pour ouvrir la fenêtre popup de rebasement

rebase popup

et un autre r pour continuer, et vous avez terminé ! ll montre maintenant :

all done log

1voto

ruvim Points 78

Pour diviser un commit <commit> et ajoutez le nouvel engagement avant celui-ci et enregistrer la date de l'auteur de <commit> les étapes sont les suivantes :

  1. Modifier le commit avant <commit>

    git rebase -i <commit>^^

    NB : il faudra peut-être aussi éditer <commit> également.

  2. Cueillette de cerises <commit> dans l'index

    git cherry-pick -n <commit>
  3. Réinitialiser interactivement les modifications inutiles de l'index et réinitialiser l'arbre de travail.

    git reset -p && git checkout-index -f -a

    Comme alternative, il suffit de cacher les changements inutiles de manière interactive : git stash push -p -m "tmp other changes"

  4. Effectuez d'autres changements (s'il y en a) et créez la nouvelle livraison.

    git commit -m "upd something" .

    En option, répétez les points 2-4 pour ajouter plus de commits intermédiaires.

  5. Continuer à rebaser

    git rebase --continue

1voto

MarcH Points 1868

Il existe une version plus rapide si vous ne voulez extraire le contenu que d'un seul fichier. Elle est plus rapide parce que le rebasement interactif n'est plus vraiment interactif (et elle est bien sûr encore plus rapide si vous voulez extraire à partir du dernier commit, alors il n'est pas nécessaire de rebaser du tout).

  1. Utilisez votre éditeur et supprimez les lignes que vous voulez extraire de la base de données. the_file . Fermer the_file . C'est la seule édition dont vous avez besoin, tout le reste n'est que des commandes git.

  2. Mettez en scène cette suppression dans l'index :

    git  add  the_file
  3. Rétablir les lignes que vous venez de supprimer dans le fichier sans affecter l'indice !

    git show HEAD:./the_file > the_file
  4. "SHA1" est le commit dont vous voulez extraire les lignes :

    git commit -m 'fixup! SHA1' 
  5. Créez le deuxième commit, tout nouveau, avec le contenu à extraire restauré par l'étape 3 :

    git commit -m 'second and new commit' the_file 
  6. Ne modifiez pas, n'arrêtez pas, ne continuez pas - acceptez tout :

    git rebase --autosquash -i SHA1~1

Bien sûr, c'est encore plus rapide lorsque le commit à extraire est le dernier commit :

4. git commit -C HEAD --amend
5. git commit -m 'second and new commit' thefile
6. no rebase, nothing

Si vous utilisez magit alors les étapes 4, 5 et 6 sont une seule action : Commit, Fixup instantané

0voto

evilkos Points 377

La correction manuelle de l'historique par le biais du "cherry picking" peut également fonctionner dans certains cas.

Je préfère utiliser mon interface graphique git (au lieu de la ligne de commande), mon commit problématique était seulement 3 commits plus bas, je n'en ai pas encore poussé, et les suivants n'étaient pas exactement ordonnés non plus, donc j'ai opté pour une reconstruction complète de tous les commits par cherry-picking, et c'était plus rapide que d'utiliser les éditions interactives de rebase via la ligne de commande, mais similaire dans l'approche.

Voici comment je l'ai fait dans mon interface graphique git préférée (j'utilise personnellement SourceTree) :

  1. Créer un étiquette sur l'état actuel afin qu'il ne soit pas perdu.
  2. Maintenant déplacer votre local actuel pointeur de branche au commit désordonné.
  3. Remise à zéro (mixte) au précédent de sorte que les fichiers de l'engagement (2) soient conservés.
  4. Vous pouvez maintenant diviser le commit en deux ou plus en mettant à disposition les fichiers qui sont nécessaires et en les validant avec le bon message, jusqu'à ce qu'il n'y ait plus de fichiers non mis à disposition.
  5. Cueillette de cerises le prochain commit en ligne (à partir de l'historique que vous avez marqué). Vous faites cela en faisant un clic droit sur le commit désiré et en choisissant "cherry pick". Allez à (4), faites jusqu'à ce qu'il ne reste plus de commits non comptabilisés.
  6. Ne vous inquiétez pas si, en conséquence, vous avez des engagements qui seraient mieux regroupés en un seul. Vous pouvez les écraser avec une option rebasement interactif dans l'interface graphique. Il suffit de cliquer avec le bouton droit de la souris sur le commit qui précède le désordre et de cliquer sur "Interactive rebase", puis de faire glisser les commits les uns sur les autres pour les écraser (en corrigeant le message du commit pour que cela reste simple), ou de les déplacer vers le haut ou vers le bas comme on le souhaite.
  7. Retirer l'étiquette créé en (1).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X