Ajouter des modifications à un commit
Tu viens de commiter et tu t’aperçois que certaines modifications ou fichiers n’ont pas été intégrées à ton commit. Idéalement, tu aimerais les ajouter au commit initial plutôt que de faire le classique commit intitulé « Oops, j'ai oublié des choses
» dont la valeur pour l’historique est très discutable.
Deux cas de figure peuvent exister :
- Tu viens tout juste de commiter ;
- Tu as fait d’autres commit entre temps.
Chaque situation a sa solution, voyons-les ensemble.
1. Ajouter des choses à mon dernier commit
C’est le cas le plus fréquent. La procédure est plutôt facile à suivre :
- tu ajoutes tes fichiers ou modifications oubliés au stage :
git add …
; - tu “amendes” le commit que tu viens de faire :
git commit --amend --no-edit
.
Et voilà, c’est fini ! Bien évidemment, tu vérifies ton historique avant et après ton opération pour garantir que le résultat est bien celui attendu. Tu peux même pousser en faisant un git show
pour voir le contenu du commit.
Le contexte est important : on parle bien de reprendre le commit en fin de branche. Il est peu probable qu’on ait envie de défaire et continuer le travail d’un commit plus ancien. Si c’était le cas, ça serait certainement dans l’optique d’un des autres scenarios de notre série pour apprendre à annuler, défaire et corriger.
Un exemple avec des schémas
Partons de l’historique suivant :
%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': { 'commitLabelFontSize': '16px' }, 'gitGraph': {'showBranches': false} } }%% gitGraph commit id: "a" commit id: "b" commit id: "c" commit id: "d" commit id: "(HEAD) e" type: HIGHLIGHT
Le dernier commit « e » est celui qu’on souhaite modifier.
On prépare nos modifications, on les ajoute au stage et on fait notre :
git commit --amend --no-edit
On obtient alors l’historique avec un commit en remplacement de « e » que j’ai appelé ici « e’ » :
%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': { 'commitLabelFontSize': '16px' }, 'gitGraph': {'showBranches': false} } }%% gitGraph commit id: "a" commit id: "b" commit id: "c" commit id: "d" commit id: "(HEAD) e’"
2. Ajout des choses à un commit plus ancien
Ça semble complexe en apparence (et même techniquement), mais on verra à la fin de cette démonstration qu’on peut faire aussi simple que la procédure précédente. Si ça ne t’intéresse pas de comprendre le détail, libre à toi d’aller directement à la version “rapide”.
Changer l’historique depuis le commit à modifier
Comme dans la situation précédente, la modification du commit donnera lieu à un nouveau commit remplaçant celui modifié, avec un nouvel identifiant. Et comme l’identifiant de chaque commit est en partie déterminé par l’identité de son ou ses commits parents, les commits qui le suivaient vont devoir être tous recalculés, remplacés. C’est le rôle de la commande rebase qui fera toute l’opération pour nous.
La commande rebase est une commande qui offre un fort potentiel, sorte de super couteau Suisse de Git. Si tu veux en savoir plus, tu peux lire cet article.
Les étapes
Avant de lancer un rebase, on doit d’abord effectuer un commit de correction. L’objectif final est de dire au rebase de regrouper ce commit et le commit à modifier pour n’en faire plus qu’un.
Pour faciliter la suite, on utilise une option spécifique à la commande commit
qui va renseigné un message particulier qui sera interprété par le rebase ensuite.
# On ajoute nos modifications au stage puis on fait le commit
git commit --fixup identifiant-du-commit-à-corriger
Le commit est créé et son message et la combinaison du préfixe fixup!
suivi du message du commit d’origine.
Reste alors à lancer la commande rebase en partant du commit précédent celui à modifier :
git rebase -r -i identifiant
On utilise le mode interactif pour que Git puisse regrouper nos commits, mais en réalité nous n’avons aucune intervention à faire dans la liste des actions que Git ouvre dans notre éditeur. On peut se contenter de fermer l’éditeur.
Comme à chaque fois, on vérifie notre historique une fois que tout est terminé.
La solution en 2 étapes
Si tu as lu toute l’explication, tu dois avoir hâte de voir la solution simple. Ça consiste à créer un alias qui fera l’opération en un appel :
# On renseigne l’alias au global pour pouvoir l’utiliser partout
git config --global alias.autofixup '!git commit --fixup $1 && git rebase --autosquash --interactive --rebase-merges $1~1 && echo "autofixup finished"'
Une fois qu’on a ça, les étapes sont très similaires à la première situation :
- On ajoute au stage nos modifications (
git add …
) ; - On lance la commande :
git autofixup idenfitiant-du-commit-à-modifier
.
Et voilà !
Je regrette que Git n’offre pas nativement cette solution. D’autant qu’on se retrouve contraint à l’utiliser dans le terminal. Il est possible que certaines interfaces graphiques permettent de faire à peu près la même chose, mais je n’en ai aujourd’hui pas connaissance.
Revoyons l’action au ralenti
On reprend l’exemple d’avant, sauf qu’on cherche à modifier le commit « c » :
%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': { 'commitLabelFontSize': '16px' }, 'gitGraph': {'showBranches': false} } }%% gitGraph commit id: "a" commit id: "b" commit id: "c" type: HIGHLIGHT commit id: "d" commit id: "(HEAD) e"
On prépare nos modifis, on les ajoute au stage et on lance l’alias
git autofixup c
Et la magie opère, notre historique final est alors
%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': { 'commitLabelFontSize': '16px' }, 'gitGraph': {'showBranches': false} } }%% gitGraph commit id: "a" commit id: "b" commit id: "c’" commit id: "d’" commit id: "(HEAD) e’"
« c’ » contient bien les modifications du commit initial enrichi des nouvelles, et les commits suivants « d » et « e » ont été répliqués et obtiennent de nouveaux identifiants « d’ » et « e’ ».
Tu peux aussi regarder le programme de notre formation "Comprendre Git" ou nous poser tes questions sur notre forum discord.