1749 votes

Diviser un commit précédent en plusieurs commits

Sans créer une branche et faire un tas de travail funky sur une nouvelle branche, est-il possible de casser un seul commit en plusieurs commits différents après qu'il ait été commis dans le dépôt local ?

66 votes

Une bonne source pour apprendre à le faire est Pro Git §6.4 Outils Git - Réécrire l'histoire dans la section "Fractionnement d'un engagement".

7 votes

Les documents liés au commentaire ci-dessus sont excellents, mieux expliqués que les réponses ci-dessous.

3 votes

Je suggère l'utilisation de cet alias stackoverflow.com/a/19267103/301717 . Il permet de diviser un commit en utilisant git autorebase split COMMIT_ID

2431voto

Wayne Conrad Points 31052

git rebase -i le fera.

Tout d'abord, commencez par un répertoire de travail propre : git status ne doit pas indiquer de modifications, de suppressions ou d'ajouts en attente.

Maintenant, vous devez décider quel(s) commit(s) vous voulez diviser.

A) Séparation du commit le plus récent

Pour séparer votre plus récent commit, commencez par :

$ git reset HEAD~

Maintenant, livrez les pièces individuellement de la manière habituelle, en produisant autant de livraisons que nécessaire.

B) Diviser un commit plus loin en arrière

Cela nécessite rebasement c'est-à-dire réécrire l'histoire. Pour spécifier le commit correct, vous avez plusieurs choix :

  • Si c'est trois commits en arrière, alors

      $ git rebase -i HEAD~3

    donde 3 c'est le nombre de commits en arrière.

  • S'il est plus loin dans l'arbre que ce que vous voulez compter, alors

      $ git rebase -i 123abcd~

    donde 123abcd est le SHA1 du commit que vous voulez diviser.

  • Si vous êtes sur une branche différente (par exemple, une branche de fonctionnalité) que vous voulez fusionner avec master :

      $ git rebase -i master

Lorsque vous obtenez l'écran d'édition de rebase, trouvez le commit que vous voulez séparer. Au début de cette ligne, remplacez pick avec edit ( e pour faire court). Sauvegardez le tampon et quittez. Rebase va maintenant s'arrêter juste après le commit que vous voulez modifier. Ensuite :

$ git reset HEAD~

Livrez les pièces individuellement de la manière habituelle, en produisant autant de livraisons que nécessaire.

Enfin (à la fois A et B)

$ git rebase --continue

3 votes

Merci pour cette réponse. Je voulais avoir quelques fichiers précédemment livrés dans la zone de transit, donc les instructions pour moi étaient un peu différentes. Avant de pouvoir git rebase --continue j'ai dû en fait git add (files to be added) , git commit entonces git stash (pour les autres dossiers). Après git rebase --continue J'ai utilisé git checkout stash . pour obtenir les fichiers restants

28 votes

La réponse de manojlds a en fait ce lien à la documentation sur git-scm qui explique également très clairement le processus de fractionnement des commits.

89 votes

Vous voudrez également profiter des avantages suivants git add -p pour ajouter uniquement des sections partielles de fichiers, éventuellement avec l'option e option pour éditer les diffs afin de ne livrer qu'une partie d'un morceau. git stash est également utile si vous voulez continuer à travailler, mais en le retirant du commit actuel.

405voto

MBO Points 12516

De git-rebase manuel (section SPLITTING COMMITS)

En mode interactif, vous pouvez marquer les commits avec l'action "edit". Cependant, cela ne signifie pas nécessairement que git rebase s'attend à ce que le résultat de cette édition soit exactement un commit. En effet, vous pouvez annuler le commit, ou vous pouvez ajouter d'autres commits. Ceci peut être utilisé pour diviser un commit en deux :

  • Commencez un rebasement interactif avec git rebase -i <commit>^ , donde <commit> est le commit que vous voulez diviser. En fait, n'importe quelle plage de commit fera l'affaire, tant qu'elle contient ce commit.

  • Marquez le commit que vous voulez diviser avec l'action "edit".

  • Lorsqu'il s'agit d'éditer ce commit, exécutez git reset HEAD^ . L'effet est que le HEAD est rembobiné par un, et l'index suit. Cependant, l'arbre de travail reste le même.

  • Maintenant, ajoutez les changements à l'index que vous voulez avoir dans le premier commit. Vous pouvez utiliser git add (éventuellement de manière interactive) ou git gui (ou les deux) pour le faire.

  • Livrer l'index actuel avec le message de livraison approprié.

  • Répétez les deux dernières étapes jusqu'à ce que votre arbre de travail soit propre.

  • Continuez le rebasement avec git rebase --continue .

20 votes

Sous Windows, vous devez utiliser ~ au lieu de ^ .

26 votes

Attention : avec cette approche, j'ai perdu le message de validation.

19 votes

@user420667 Oui, bien sûr. Nous sommes reset l'engagement, après tout - message compris. La chose prudente à faire, si vous savez que vous allez diviser un commit mais que vous voulez garder une partie ou la totalité de son message, est de prendre une copie de ce message. Donc, git show l'engagement avant rebase ou si vous l'oubliez ou le préférez : revenez-y plus tard via la fonction reflog . Rien de tout cela ne sera réellement "perdu" avant d'être ramassé dans deux semaines ou plus.

49voto

Rose Perrone Points 14478

Utilisez git rebase --interactive pour modifier cette validation antérieure, exécutez git reset HEAD~ et ensuite git add -p pour en ajouter, puis faire un commit, puis en ajouter d'autres et faire un autre commit, autant de fois que vous le souhaitez. Quand vous avez terminé, lancez git rebase --continue et vous aurez tous les commits séparés plus tôt dans votre pile.

Important : Notez que vous pouvez jouer et faire tous les changements que vous voulez, et ne pas avoir à vous soucier de la perte des anciens changements, parce que vous pouvez toujours exécuter la commande git reflog pour trouver le point dans votre projet qui contient les changements que vous voulez, (appelons-le a8c4ab ), et ensuite git reset a8c4ab .

Voici une série de commandes pour montrer comment cela fonctionne :

mkdir git-test; cd git-test; git init

ajoutez maintenant un fichier A

vi A

ajouter cette ligne :

one

git commit -am one

puis ajoutez cette ligne à A :

two

git commit -am two

puis ajoutez cette ligne à A :

three

git commit -am three

maintenant le fichier A ressemble à ceci :

one
two
three

et notre git log ressemble à ce qui suit (en fait, j'utilise git log --pretty=oneline --pretty="%h %cn %cr ---- %s"

bfb8e46 Rose Perrone 4 seconds ago ---- three
2b613bc Rose Perrone 14 seconds ago ---- two
9aac58f Rose Perrone 24 seconds ago ---- one

Disons que nous voulons diviser le deuxième commit, two .

git rebase --interactive HEAD~2

Cela fait apparaître un message qui ressemble à ceci :

pick 2b613bc two
pick bfb8e46 three

Changez le premier pick à un e pour modifier ce commit.

git reset HEAD~

git diff nous montre que nous venons de déstocker le commit que nous avons fait pour le second commit :

diff --git a/A b/A
index 5626abf..814f4a4 100644
--- a/A
+++ b/A
@@ -1 +1,2 @@
 one
+two

Mettons en scène ce changement, et ajoutons "et un troisième" à cette ligne dans le fichier A .

git add .

C'est généralement à ce moment-là, au cours d'un rebasement interactif, que l'on lance la commande git rebase --continue parce que nous voulons généralement revenir en arrière dans notre pile de commits pour modifier un commit précédent. Mais cette fois, nous voulons créer un nouveau commit. Donc nous allons lancer git commit -am 'two and a third' . Maintenant nous éditons le fichier A et ajoutez la ligne two and two thirds .

git add . git commit -am 'two and two thirds' git rebase --continue

Nous avons un conflit avec notre engagement, three alors résolvons-le :

Nous allons changer

one
<<<<<<< HEAD
two and a third
two and two thirds
=======
two
three
>>>>>>> bfb8e46... three

à

one
two and a third
two and two thirds
three

git add .; git rebase --continue

Maintenant, notre git log -p ressemble à ça :

commit e59ca35bae8360439823d66d459238779e5b4892
Author: Rose Perrone <roseperrone@fake.com>
Date:   Sun Jul 7 13:57:00 2013 -0700

    three

diff --git a/A b/A
index 5aef867..dd8fb63 100644
--- a/A
+++ b/A
@@ -1,3 +1,4 @@
 one
 two and a third
 two and two thirds
+three

commit 4a283ba9bf83ef664541b467acdd0bb4d770ab8e
Author: Rose Perrone <roseperrone@fake.com>
Date:   Sun Jul 7 14:07:07 2013 -0700

    two and two thirds

diff --git a/A b/A
index 575010a..5aef867 100644
--- a/A
+++ b/A
@@ -1,2 +1,3 @@
 one
 two and a third
+two and two thirds

commit 704d323ca1bc7c45ed8b1714d924adcdc83dfa44
Author: Rose Perrone <roseperrone@fake.com>
Date:   Sun Jul 7 14:06:40 2013 -0700

    two and a third

diff --git a/A b/A
index 5626abf..575010a 100644
--- a/A
+++ b/A
@@ -1 +1,2 @@
 one
+two and a third

commit 9aac58f3893488ec643fecab3c85f5a2f481586f
Author: Rose Perrone <roseperrone@fake.com>
Date:   Sun Jul 7 13:56:40 2013 -0700

    one

diff --git a/A b/A
new file mode 100644
index 0000000..5626abf
--- /dev/null
+++ b/A
@@ -0,0 +1 @@
+one

20voto

Cupcake Points 22154

git rebase --interactive peut être utilisé pour diviser un commit en plusieurs commits plus petits. L'adresse La documentation Git sur le rebasement contient une description concise du processus - Fractionnement des commits :

En mode interactif, vous pouvez marquer les commits avec l'action "edit". Cependant, cela ne signifie pas nécessairement que git rebase s'attend à ce que le résultat de cette modification soit exactement un commit. En effet, vous pouvez annuler le commit, ou vous pouvez ajouter d'autres commits. Ceci peut être utilisé pour diviser un commit en deux :

  • Commencez un rebasement interactif avec git rebase -i <commit>^ , donde <commit> est le commit que vous voulez diviser. En fait, n'importe quelle plage de commit fera l'affaire, tant qu'elle contient ce commit.

  • Marquez le commit que vous voulez diviser avec l'action "edit".

  • Quand il s'agit d'éditer ce commit, exécutez git reset HEAD^ . L'effet est que le HEAD est rembobiné par un, et l'index suit. Cependant, l'arbre de travail reste le même.

  • Maintenant, ajoutez les changements à l'index que vous voulez avoir dans le premier commit. Vous pouvez utiliser git add (éventuellement de manière interactive) ou git gui (ou les deux) pour le faire.

  • Livrer l'index actuel avec le message de livraison approprié.

  • Répétez les deux dernières étapes jusqu'à ce que votre arbre de travail soit propre.

  • Continuez le rebasement avec git rebase --continue .

Si vous n'êtes pas absolument sûr que les révisions intermédiaires sont cohérentes (elles compilent, passent la testuite, etc.), vous devez utiliser git stash pour mettre de côté les changements non encore validés après chaque validation, les tester, et modifier la validation si des corrections sont nécessaires.

0 votes

Sous Windows, rappelez-vous ^ est un caractère d'échappement pour la ligne de commande : il doit être doublé. Par exemple, émettez git reset HEAD^^ au lieu de git reset HEAD^ .

0 votes

@Frédéric :s Je n'ai jamais rencontré ce problème. En tout cas, dans PowerShell, ce n'est pas le cas. Ensuite, en utilisant ^ réinitialise deux fois deux commits au-dessus du HEAD actuel.

0 votes

@Farway, essayez-le dans une ligne de commande classique. PowerShell est une toute autre bête, son caractère d'échappement est le backtilt.

9voto

manojlds Points 96599

Vous pouvez faire un rebasement interactif git rebase -i . Man page a exactement ce que vous voulez :

http://git-scm.com/docs/git-rebase#_splitting_commits

19 votes

Il serait plus utile de donner un peu plus de contexte sur la façon d'aborder les problèmes plutôt que de donner simplement un RTFM.

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