Comment ajouter avec git facilement un fichier modifié à un ancien commit non publié

En utilisant git comme gestionnaire de source, je ne pousse pas mes commits au fur et à mesure que je les fais, ni par paquet de douze ! Quand j'ai un ensemble cohérent, je finis par lancer la commande git push. Mais avant de faire cela il m'arrive de temps en temps de voir que j'ai oublié de corriger quelque chose dans un fichier ou que j'ai oublié d'ajouter un fichier dans un commit.

Si le commit auquel je veux rajouter un fichier modifié est le dernier c'est facile et il suffit de taper la commande suivante :

git commit --amend --no-edit path/to/my/updated/file

Cette commande modifie le dernier commit (--amend) ne modifie par le message de commit et ajoute le fichier (ou les fichiers d'ailleurs) qui suit. Si on n'ajoute pas l'option --no-edit, l'éditeur par défaut s'ouvre pour modifier le message de commit.

Si ce commit n'est pas le dernier, c'est un peu plus compliqué mais rien d'insurmontable non plus. Supposons qu'on ait 3 commits non publiés dont on obtient la liste en tapant, par exemple, la commande suivante :

git log origin/master...master

On obtient les commits suivants :

commit 8c278a339665e53cb9d24dd14b3563ae2eacd5e8 message1
commit de368517336f50c0006ba61b8e79b9ce5712a993 message2
commit d01f2220e25dd6a2ddf4878f2c3e2328668d3e9a message3

Pour ajouter le fichier path/to/my/updated/file au commit d01f2220e25dd6a2ddf4878f2c3e2328668d3e9a, on va mettre de côté ce fichier en faisant un simple git stash puis on va utiliser la commande rebase en mode interractif :

git rebase -i

Encore une fois cela va ouvrir l'éditeur de code préféré et le haut du fichier va ressembler à ça : 


pick 8c278a339665e53cb9d24dd14b3563ae2eacd5e8 message1
pick de368517336f50c0006ba61b8e79b9ce5712a993 message2
pick d01f2220e25dd6a2ddf4878f2c3e2328668d3e9a message3

# Commandes :
#  p, pick <commit> = utiliser le commit
#  r, reword <commit> = utiliser le commit, mais reformuler son message
#  e, edit <commit> = utiliser le commit, mais s'arrêter pour le modifier
#  s, squash <commit> = utiliser le commit, mais le fusionner avec le précédent
#  f, fixup <commit> = comme "squash", mais en éliminant son message
#  x, exec <commit> = lancer la commande (reste de la ligne) dans un shell
#  b, break = s'arrêter ici (on peut continuer ensuite avec 'git rebase --continue')
#  d, drop <commit> = supprimer le commit
#  l, label <label> = étiqueter la HEAD courante avec un nom
#  t, reset <label> = réinitialiser HEAD à label
#  m, merge [-C <commit> | -c <commit>] <label> [# <uniligne>]
#          créer un commit de fusion utilisant le message de fusion original
#          (ou l'uniligne, si aucun commit de fusion n'a été spécifié).
#          Utilisez -c <commit> pour reformuler le message de validation.
#
# Vous pouvez réordonner ces lignes ; elles sont exécutées de haut en bas.
#
# Si vous éliminez une ligne ici, LE COMMIT CORRESPONDANT SERA PERDU.
#
# Cependant, si vous effacez tout, le rebasage sera annulé


Sous cette ligne la commande git rebase a le bon goût de mettre en commentaire ce qu'on peut faire. Là je veux modifier le commit "utiliser le commit, mais s'arrêter pour le modifier" c'est-à-dire la commande edit. Il suffit de remplacer, sur la ligne souhaitée, pick par edit. On obtient avant sauvegarde : 


pick 8c278a339665e53cb9d24dd14b3563ae2eacd5e8 message1 
pick de368517336f50c0006ba61b8e79b9ce5712a993 message2 
edit d01f2220e25dd6a2ddf4878f2c3e2328668d3e9a message3 

Ensuite je sauvegarde et je quitte l'éditeur. Je me retrouve sur comme si je venais de faire le commit d01f2220e25dd6a2ddf4878f2c3e2328668d3e9a. Je peux donc ajouter mon fichier à ce commit sans le modifier en le récupérant d'abord depuis la zone tampon en tapant :

git stash pop

Pour ajouter le fichier il suffit ensuite de taper :

git commit --amend --no-edit path/to/my/updated/file

Et pour terminer il suffit de terminer le rebase en tapant la commande suivante : 

git rebase --continue

 

Haut de page