Comment ajouter un fichier modifié à un commit plus ancien (pas le dernier) dans Git

461

J'ai changé plusieurs choses au cours de la dernière heure et je les ai validées étape par étape, mais je viens de réaliser que j'ai oublié d'ajouter un fichier modifié il y a quelques validations.

Le journal ressemble à ceci:

    GIT TidyUpRequests u:1 d:0> git log 
    commit fc6734b6351f6c36a587dba6dbd9d5efa30c09ce 
    Author: David Klein <> 
    Date:   Tue Apr 27 09:43:55 2010 +0200

        The Main program now tests both Webservices at once

    commit 8a2c6014c2b035e37aebd310a6393a1ecb39f463 
    Author: David Klein <>
    Date:   Tue Apr 27 09:43:27 2010 +0200

        ISBNDBQueryHandler now uses the XPath functions from XPath.fs too

    commit 06a504e277fd98d97eed4dad22dfa5933d81451f 
    Author: David Klein <> 
    Date:   Tue Apr 27 09:30:34 2010 +0200

        AmazonQueryHandler now uses the XPath Helper functions defined in XPath.fs

    commit a0865e28be35a3011d0b6091819ec32922dd2dd8 <--- changed file should go here
    Author: David Klein <> 
    Date:   Tue Apr 27 09:29:53 2010 +0200

        Factored out some common XPath Operations

Des idées?

leen
la source
1
Essentiellement un doublon de Comment modifier un commit spécifié?
Dan Dascalescu

Réponses:

694

Utilisez git rebase. Plus précisément:

  1. Utilisez git stashpour stocker les modifications que vous souhaitez ajouter.
  2. Utilisez git rebase -i HEAD~10(ou le nombre de validations que vous souhaitez voir).
  3. Marquez le commit en question ( a0865...) pour l'éditer en changeant le mot pickau début de la ligne en edit. Ne supprimez pas les autres lignes car cela supprimerait les validations. [^ Vimnote]
  4. Enregistrez le fichier de rebase, et git reviendra au shell et attendra que vous corrigiez ce commit.
  5. Pop the stash en utilisant git stash pop
  6. Ajoutez votre fichier avec git add <file>.
  7. Modifiez le commit avec git commit --amend --no-edit.
  8. Faites un git rebase --continuequi réécrira le reste de vos commits contre le nouveau.
  9. Répétez à partir de l'étape 2 si vous avez marqué plusieurs validations pour modification.

[^ vimnote]: Si vous utilisez, vimvous devrez appuyer sur la Inserttouche pour modifier, puis Esctaper :wqpour enregistrer le fichier, quitter l'éditeur et appliquer les modifications. Alternativement, vous pouvez configurer un éditeur de validation git convivial avec git config --global core.editor "nano".

Greg Hewgill
la source
23
Que faire si vous avez des modifications non mises en scène que vous souhaitez ajouter à la modification? Si je les cache, je ne pourrais pas git add.
Sam
15
Sam, vous pouvez simplement décaper les modifications pendant le commit en question, cela fonctionnera bien.
omnikron
17
Remarque: lorsque vous marquez la validation avec edit, NE SUPPRIMEZ PAS les autres validations répertoriées dans le fichier. Si vous le faites, les validations seront supprimées et vous devrez suivre ces étapes pour les récupérer.
David Tuite
2
En ce qui concerne ce que @DavidTuite a dit, une de mes habitudes lorsque je fais des trucs sur git que je ne sais pas comment cela se passera est de créer une branche "branchname-ref" pour sauvegarder l'état actuel de la chronologie au cas où je bousillerais les choses. Quand j'ai fini, je le supprime.
Raphael
1
À l'étape 6, incluez le. (point) dans la commande. Commande correcte:git add .
Tom
323

Pour "corriger" un ancien commit avec une petite modification, sans changer le message de commit de l'ancien commit, où OLDCOMMITest quelque chose comme 091b73a:

git add <my fixed files>
git commit --fixup=OLDCOMMIT
git rebase --interactive --autosquash OLDCOMMIT^

Vous pouvez également utiliser git commit --squash=OLDCOMMITpour modifier l'ancien message de validation lors du rebase.


  • git rebase --interactiveaffichera un éditeur de texte (qui peut être configuré ) pour confirmer (ou modifier) ​​la séquence d'instructions de rebase . Le fichier contient des informations sur les modifications des instructions de rebase ; il suffit d' enregistrer et de quitter l'éditeur ( :wqinvim ) pour continuer le rebase.
  • --autosquashmettra automatiquement tous les --fixup=OLDCOMMITcommits dans l'ordre souhaité. Notez que cela --autosquashn'est valide que lorsque l' --interactiveoption est utilisée.
  • L' ^entrée OLDCOMMIT^signifie qu'il s'agit d'une référence au commit juste avant OLDCOMMIT.

Les étapes ci-dessus sont bonnes pour la vérification et / ou la modification de la séquence d'instructions de rebase , mais il est également possible d'ignorer / automatiser l'éditeur de texte de rebase interactif en:

Voir git commit et git rebase . Comme toujours, lors de la réécriture de l'historique git , vous ne devez corriger ou écraser que les commits que vous n'avez pas encore publiés à quelqu'un d'autre (y compris les utilisateurs Internet aléatoires et les serveurs de build).

Joel Purra
la source
18
Beaucoup plus clair que les autres options, et cela a fonctionné comme un charme
Chris Mitchelmore
5
@Jonah: l'éditeur n'est pas ouvert pour modifier le message de validation , mais pour confirmer (ou modifier) ​​les étapes de rebase . Cela ne peut pas être évité; --autosquashn'est valide que lorsque l' --interactiveoption est utilisée .
Joel Purra
5
En utilisant cette solution, je suis coincé en montrant VIM. Mon problème est que je ne sais pas comment utiliser VIM. Comment diable puis-je m'en sortir, comment puis-je contrôler cette chose, c'est si déroutant.
Neon Warge du
2
@NeonWarge: l'éditeur de choix est configurable à l'aide par exemple git config --global core.editor "pico". Il existe plusieurs autres façons de configurer git et / ou de modifier l'éditeur par défaut de votre système, etc.
Joel Purra
2
une ligne alias sympa ici
idanp
61

avec git 1.7, il existe un moyen très simple d'utiliser git rebase:

mettre en scène vos fichiers:

git add $files

créer un nouveau commit et réutiliser le message de commit de votre commit "cassé"

git commit -c master~4

ajouter fixup!au début de la ligne d'objet (ou squash!si vous souhaitez modifier la validation (message)):

fixup! Factored out some common XPath Operations

utiliser git rebase -i --autosquashpour corriger votre commit

knittl
la source
2
+1. Belle utilisation de la nouvelle directive fixup (1.7+): stackoverflow.com/questions/2302736/trimming-git-checkins/…
VonC
@knittl J'essayais votre méthode pour ajouter un autre fichier à un ancien commit du mien (non poussé) mais lors du rebasage, je reçois You asked me to rebase without telling me which branch you want to rebase against, and 'branch.master.merge'et si j'utilise ensuite git rebase -i --autosquashj'obtiens simplement une noopligne d'objet, rebasant le commit sur lui-même. Une idée de ce que je fais mal?
oschrenk
7
@oschrenk: Vous devez fournir un commit auquel vous souhaitez rebaser, par exemplegit rebase -i --autosquash HEAD~10
knittl
1
Bonne réponse mais j'avais également besoin d'ajouter un commit contre lequel rebaser. Ce serait génial si vous pouviez le mettre à jour.
Paul Odeon
@PaulOdeon: Je ne comprends pas votre question. Qu'essayez-vous de faire et où avez-vous des problèmes?
knittl
8

Vous pouvez essayer une rebase --interactivesession pour modifier votre ancien commit (à condition que vous n'ayez pas déjà poussé ces commits vers un autre repo).

Parfois, la chose fixée en b.2. ne peut pas être modifié pour le commit pas tout à fait parfait qu'il corrige, car ce commit est profondément enfoui dans une série de patchs .
C'est exactement à cela que sert le rebase interactif: utilisez-le après de nombreux «a» et «b», en réorganisant et en éditant les commits, et en écrasant plusieurs commits en un seul.

Démarrez-le avec le dernier commit que vous souhaitez conserver tel quel:

git rebase -i <after-this-commit>

Un éditeur sera lancé avec toutes les validations de votre branche actuelle (en ignorant les validations de fusion), qui surviennent après la validation donnée.
Vous pouvez réorganiser les validations de cette liste au contenu de votre cœur et vous pouvez les supprimer. La liste ressemble plus ou moins à ceci:

pick deadbee The oneline of this commit
pick fa1afe1 The oneline of the next commit
...

Les descriptions en ligne sont purement pour votre plaisir; git rebase ne les regardera pas mais les noms de commit ("deadbee" et "fa1afe1" dans cet exemple), donc ne supprimez pas et ne modifiez pas les noms.

En remplaçant la commande "pick" par la commande "edit", vous pouvez dire à git rebase de s'arrêter après avoir appliqué ce commit, afin de pouvoir éditer les fichiers et / ou le message de commit, modifier le commit et continuer le rebasage .

VonC
la source