web-dev-qa-db-fra.com

Comment scinder le dernier commit en deux dans Git

J'ai deux branches de travail, maître et forum et je viens de faire quelques modifications dans la branche forum, que j'aimerais bien sélectionner dans maître. Mais malheureusement, le commit que je souhaite sélectionner contient également des modifications que je ne souhaite pas.

La solution consisterait probablement en quelque sorte à supprimer le mauvais commit et à le remplacer par deux validations distinctes, l'une avec les modifications que je veux choisir dans master et les autres qui n'y appartiennent pas.

J'ai essayé de faire

git reset --hard HEAD^

qui a supprimé tous les changements, donc je devais revenir avec

git reset ORIG_HEAD

Ma question est donc la suivante: quel est le meilleur moyen de scinder le dernier commit en deux commits distincts?

271
Jakub Arnold

Vous devriez utiliser l'index. Après avoir effectué une réinitialisation mixte (" git reset HEAD ^"), ajoutez Le premier ensemble de modifications à l’index, puis validez-les. Puis commettez lerest.

Vous pouvez utiliser " git add " pour mettre toutes les modifications apportées dans un fichier à l'index. Si vous ne voulez pas mettre en scène toutes les modifications apportées dans un fichier, seules certaines d'entre elles peuvent utiliser "git add -p".

Voyons un exemple. Supposons que j’ai un fichier appelé myfile, qui contient le texte suivant:

something
something else
something again

Je l'ai modifié dans mon dernier commit pour qu'il ressemble maintenant à ceci:

1
something
something else
something again
2

Maintenant, je décide que je veux le scinder en deux et je veux que l'insertion de la première ligne Soit dans le premier commit, et l'insertion de la dernière ligne pour êtredans le deuxième commit.

Je reviens d'abord au parent de HEAD, mais je veux conserver les modifications dans le système de fichiers, J'utilise donc "git reset" sans argument (ce qui fera un soi-disant "mixte" Reset):

$ git reset HEAD^
myfile: locally modified
$ cat myfile
1
something
something else
something again
2

J'utilise maintenant "git add -p" pour ajouter les modifications que je veux valider à l'index (= les mettre en scène). "git add -p" est un outil interactif qui vous demande quelles modifications du fichier devrait être ajouté à l'index.

$ git add -p myfile
diff --git a/myfile b/myfile
index 93db4cb..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,5 @@
+1
 something
 something else
 something again
+2
Stage this hunk [y,n,a,d,/,s,e,?]? s    # split this section into two!
Split into 2 hunks.
@@ -1,3 +1,4 @@
+1
 something
 something else
 something again
Stage this hunk [y,n,a,d,/,j,J,g,e,?]? y  # yes, I want to stage this
@@ -1,3 +2,4 @@
 something
 something else
 something again
+2
Stage this hunk [y,n,a,d,/,K,g,e,?]? n   # no, I don't want to stage this

Puis je commets ce premier changement:

$ git commit -m "Added first line"
[master cef3d4e] Added first line
 1 files changed, 1 insertions(+), 0 deletions(-)

Maintenant, je peux valider tous les autres changements (à savoir le chiffre "2" dans la dernière ligne):

$ git commit -am "Added last line"
[master 5e284e6] Added last line
 1 files changed, 1 insertions(+), 0 deletions(-)

Examinons le journal pour voir quels commits nous avons:

$ git log -p -n2 | cat
Commit 5e284e652f5e05a47ad8883d9f59ed9817be59d8
Author: ...
Date: ...

    Added last line

Diff --git a/myfile b/myfile
Index f9e1a67..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -2,3 +2,4 @@
 something
 something else
 something again
+2

Commit cef3d4e0298dd5d279a911440bb72d39410e7898
Author: ...
Date: ...

    Added first line

Diff --git a/myfile b/myfile
Index 93db4cb..f9e1a67 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,4 @@
+1
 something
 something else
 something again
325
hcs42

Buts:

  • Je veux diviser un commit passé (splitme) en deux.
  • Je veux conserve le message de validation .

Plan:

  1. rebase interactive d'un avant splitme
  2. modifier splitme
  3. Réinitialisez les fichiers pour les scinder en un deuxième commit. 
  4. Modifie le commit, conserve le message, modifie si nécessaire.
  5. Rajoutez les fichiers séparés du premier commit.
  6. S'engager avec un nouveau message. 
  7. Continuer rebase. 

Les étapes de base (1 et 7) peuvent être ignorées si la variable splitme est la plus récente.

git rebase -i splitme^
# mark splitme commit with 'e'
git reset HEAD^ -- $files
git commit --amend
git add $files
git commit -m "commit with just some files"
git rebase --continue

Si je voulais que les fichiers fractionnés soient validés en premier, je reformerais ensuite -i et changerais l'ordre.

git rebase -i splitme^
# swap order of splitme and 'just some files'
81
spazm

Pour modifier le commit actuel en deux commits, vous pouvez procéder comme suit.

Non plus:

git reset --soft HEAD^

Cela annule le dernier commit mais laisse tout en scène. Vous pouvez ensuite décomposer certains fichiers:

git reset -- file.file

Restaurez éventuellement des parties de ces fichiers:

git add -p file.file

Faites un nouveau premier commit:

git commit

L'étape et commet le reste des modifications dans un deuxième commit:

git commit -a

Ou:

Annule et annule toutes les modifications depuis le dernier commit:

git reset HEAD^

Organisez de manière sélective le premier tour de modifications:

git add -p

Commettre:

git commit

Validez le reste des modifications:

git commit -a

(Dans l'une ou l'autre étape, si vous avez annulé une validation qui ajoute un nouveau fichier et souhaitez l'ajouter à la deuxième validation, vous devrez l'ajouter manuellement en tant que commit -a ne modifie que les modifications des fichiers déjà suivis.)

52
CB Bailey

Exécutez git gui, sélectionnez le bouton radio "Modifier la dernière validation", puis annulez (Validation> Annuler la validation depuis ou Ctrl-U) changements que vous ne voulez pas entrer dans le premier commit. Je pense que c'est le moyen le plus facile de s'y prendre.

Une autre chose que vous pouvez faire est de sélectionner le changement sans valider (git cherry-pick -n), puis manuellement ou avec git gui, de sélectionner les modifications souhaitées avant de les valider.

22
git reset HEAD^

la - dure est ce qui tue vos changements.

15
semanticart

Je suis surpris que personne n'ait suggéré git cherry-pick -n forum. Cela mettra en place les modifications depuis le dernier forum commit mais pas les valider. Vous pouvez alors reset supprimer les modifications dont vous n'avez pas besoin et commettre ce que vous souhaitez conserver.

13
dahlbyk

La méthode double-revert-squash

  1. Effectuez un autre commit qui supprime les modifications indésirables. (Si c'est par fichier, c'est très facile: git checkout HEAD~1 -- files with unwanted changes et git commit. Sinon, les fichiers contenant des modifications mixtes peuvent être partiellement mis en scène par git reset file et git add -p file en tant qu'étape intermédiaire.) Appelez ceci le revert .
  2. git revert HEAD - Effectuez un autre commit, qui rajoute les modifications indésirables. C'est le double-revert
  3. Sur les 2 commits que vous avez maintenant effectués, squash est le premier sur le commit à scinder (git rebase -i HEAD~3). Ce commit est maintenant exempt de modifications indésirables, car celles-ci sont dans le second commit.

Avantages

  • Conserve le message de validation
  • Fonctionne même si le commit à scinder n'est pas le dernier. Cela nécessite seulement que les modifications indésirables ne soient pas en conflit avec les commits ultérieurs
2
user2394284

Puisque vous choisissez les cerises, vous pouvez:

  1. cherry-pick il avec l'option --no-commit ajoutée.
  2. reset et utilisez add --patch, add --edit ou simplement add pour organiser ce que vous souhaitez conserver.
  3. commit les changements mis en scène .
    • Pour réutiliser le message de validation d'origine, vous pouvez ajouter les options --reuse-message=<old-commit-ref> ou --reedit-message=<old-commit-ref> à la commande commit.
  4. Soufflez les modifications non mises en scène avec reset --hard.

Une autre façon, en préservant ou en modifiant le message de validation d'origine:

  1. cherry-pick le commit original comme d'habitude.
  2. Annulez les modifications que vous ne voulez pas et utilisez add pour organiser l'inversion .
    • Cette étape serait facile si vous supprimez ce que vous avez ajouté, mais un peu délicate si vous ajoutez ce que vous avez supprimé ou si vous annulez une modification.
  3. commit --amend pour effectuer l'inversion sur le commit choisi par les cerises .
    • Vous recevrez à nouveau le même message de validation, que vous pourrez conserver ou réviser si nécessaire.
1
ADTC

Cela pourrait être une autre solution ciblée dans les cas où le commit est énorme et qu'un petit nombre de fichiers doit être déplacé dans un nouveau commit. Cela fonctionnera si un ensemble de fichiers <path> doit être extrait du dernier commit à HEAD et tous déplacé vers un nouveau commit. Si plusieurs validations sont nécessaires, les autres solutions peuvent être utilisées.

Commencez par créer des correctifs dans les zones étagées et non étalées contenant les modifications pour rétablir le code avant modification et après modification:

git reset HEAD^ <path>

$ git status
On branch <your-branch>
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   <path>

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   <path>

Pour comprendre ce qui va se passer (la flèche et les commentaires ne font pas partie de la commande):

git diff --cached   -> show staged changes to revert <path> to before HEAD
git diff            -> show unstaged changes to add current <path> changes

Annule les changements de <path> dans le dernier commit:

git commit --amend  -> reverts changes on HEAD by amending with staged changes

Créez un nouveau commit avec les modifications <path>:

git commit -a -m "New Commit" -> adds new commit with unstaged changes

Cela a pour effet de créer un nouveau commit contenant les modifications extraites du dernier commit.

0
Jose Cifuentes