Git : comment corriger des erreurs

Anne git

Voilà un aide mémoire pour retrouver comment corriger quelques unes des erreurs que l’on peut faire avec git.

En local

Attention : on ne parle ici que de corrections locales, c’est-à-dire qui n’ont pas été poussées dans le dépôt.

Avant le commit

On a ajouté par erreur un ou plusieurs fichiers dans l’index, on les enlève en faisant :

$ git checkout -- fichier1 fichier2 ...

Si on veut carrément tout des-indexer :

$ git reset HEAD

Modification du dernier commit

La commande suivante modifie le message du dernier commit et ajoute les fichiers présents dans l’index :

$ git commit --amend -m "mon message modifié"

Si on ne précise pas le message, l'éditeur s’ouvre et on peut soit garder le même message, soit corriger l’ancien (typo par exemple).

Annuler le(s) dernier(s) commit(s)

Pour revenir en arrière, et annuler par exemple le dernier commit :

$ git reset HEAD^1

Plus généralement :

$ git reset <commit>

Il y a trois modes :

  • --soft remet les fichiers dans l’index comme avant le commit, les modifications locales sont conservées ;
  • --mixed (mode par défaut) récupère les fichiers dans la copie de travail, mais les modifications locales sont conservées ;
  • --hard récupère l'état au moment du commit : les modifications locales sont perdues.

Il faut noter qu’on peut récupérer un seul fichier :

$ git reset <commit> <fichier>

Pour corriger un commit plus ancien

Il s’agit ici de revenir sur un commit qui n’est pas le dernier, par exemple pour corriger le message, modifier ou ajouter des fichiers, mais sans perdre les commit qu’on a fait depuis.

$ git rebase --interactive <commit>^

Le ^ (signifie -1) et permet de se placer sur le commit juste avant de celui qu’on veut modifier. L'éditeur s’ouvre avec la liste des commit : il faut remplacer pick pour edit devant celui qu’on veut modifier, ou par reword si on veut juste modifier le message.

Si on a choisi edit, on se retrouve alors dans une branche REBASE et l’on peut faire les modifications que l’on souhaite, puis on met à jour le commit (ne pas mettre --no-edit si on veut modifier le message) :

$ git commit -a --amend --no-edit

Et on rejoue les autres commit à partir de cette version modifiée :

$ git rebase --continue

Échanger deux commits

$ git rebase --interactive <commit>

Dans l'éditeur, on laisse pick, mais on peut changer l’ordre des lignes.

Si on a des modification en cours, on peut toujours les sauver avant :

$ git stash

et les restaurer après :

$ git stash pop

Fusionner plusieurs commits (avec rebase)

Ça peut aussi se faire avec le commande rebase, mais il faut cette fois remplacer pick par squash sur une ou plusieurs lignes consécutives.

Supposons qu’on ait:

o - (HEAD, master) fix2
o - fix1
o - new feature
o - (origin/master, origin/HEAD) ...

mais on veut ne pousser qu’un seul commit tout propre. On va faire :

git rebase -i HEAD~3

L'éditeur s’ouvre alors avec les trois derniers commit, et il faut cette fois remplacer pick par squash pour les deux commits que l’on souhaite fusionner:

pick xxx new feature
squash yyy fix1
squash zzz fix2

L'éditeur s’ouvre alors avec la liste des anciens messages de commit et il faut saisir le nouveau message.

S’il y a des conflits, ils se corrigent à l’aide de successions de git add et git rebase --continue, ou bien on peut abandonner l’opération avec git rebase --abort.

Fusionner plusieurs commits (avec reset)

Quand on fait un pull request, il arrive souvent qu’il y ait des demandes de corrections avant l’intégration. A la fin, on peut avoir envie de tout remettre sous la forme d’un seul commit. Voilà comment faire :

$ git checkout nouveau_dev  # doit être pareil que origin/nouveau_dev
$ git status  # doit être propre
$ git reset --soft <commit>  # le commit juste avant le début de nouveau_dev
$ git commit -m 'message'
$ git push -f origin nouveau_dev

Et hop : ça a du effacer tous les commits de cette branche et le remplacer par un seul, tout propre.

Sur un dépôt privé

Quand on utilise un dépôt privé à soi tout seul, on peut se permettre de faire tout ce qui a été dit au sujet des modifications locales. Il faut juste utiliser -f pour pousser ça sur le dépôt sans que git ne râle :

$ git push -f private_repo branch

Sur un dépôt public

Sur un dépôt public, utilisé par d’autre, il est fortement déconseillé de réécrire l’histoire, c’est-à-dire de modifier les commits existants. Dans ce cas, le mieux est de refaire un ou plusieurs commits pour annuler l’erreur. On peut pour cela s’aider avec la commande revert qui construit le commit correspondant. Si par exemple on part de la situation:

A -- B -- C -- D -- E (HEAD)

Pour annuler C, on fait :

$ git revert HEAD~2

On va avoir avoir (en local) :

A -- B -- C -- D -- E -- F

avec F qui est dans le même état que E si on n’avait pas fait C.

Pour annuler plusieurs commits, on peut faire :

$ git revert HEAD~5..HEAD

Il ne reste plus qu'à pousser ce nouveau commit dans le dépôt. Si on veut voir ce qui se passe avant de faire le nouveau commit, on peut utiliser l’option -n.

Documentation

Voir aussi :