12k
All articles

5 commandes Git au-delà de commit et push

Cinq commandes Git au-delà de commit et push : stash, reflog, cherry-pick, rebase -i et bisect pour récupérer, nettoyer et traquer les bugs.

OpenReplay Team
OpenReplay Team
5 commandes Git au-delà de commit et push

La plupart des développeurs vivent dans add, commit, push et pull, mais le véritable levier de Git réside dans les commandes de récupération, de réécriture d’historique et de débogage que la boucle quotidienne n’effleure jamais.

On a plutôt tendance à découvrir les autres à la dure. Un reset --hard engloutit un après-midi de travail, ou bien un bug surgit quelque part parmi 200 commits et personne ne sait lequel a tout cassé. Les cinq commandes ci-dessous résolvent des problèmes que les bases ne peuvent pas traiter : mettre de côté un travail à moitié terminé, récupérer des commits que vous croyiez perdus, déplacer un correctif isolé d’une branche à l’autre, nettoyer un historique désordonné avant une PR, et identifier précisément le commit à l’origine d’un bug. Chacune est accompagnée d’un déclencheur mental, de la syntaxe exacte et du piège qui fait trébucher tout le monde.

Points clés à retenir

  • git stash met de côté votre travail en cours et vous rend un répertoire de travail propre, ce qui vous permet de changer de contexte sans créer de commit jetable.
  • git reflog enregistre chaque position occupée par HEAD : après un reset ou un rebase malheureux, vous pouvez retrouver le hash du commit perdu et le restaurer avec git reset --hard <ref>.
  • git cherry-pick <hash> copie un commit unique sur votre branche courante, ce qui est idéal pour transférer un hotfix vers une branche de release sans fusionner tout le reste.
  • Les commandes de réécriture d’historique comme git rebase -i et git reset --hard sont sans danger sur du travail local non poussé, mais dangereuses sur des branches partagées ; git reflog est votre issue de secours en cas de problème.
  • git bisect effectue une recherche binaire dans votre historique pour trouver le commit exact à l’origine d’un bug en log₂(n) étapes.

git stash : mettre le travail de côté pour changer de contexte rapidement

À utiliser quand un rapport de bug arrive alors que votre répertoire de travail est un chantier à moitié terminé et que vous devez changer de branche immédiatement. git stash met vos modifications non commitées à l’abri et vous rend un répertoire de travail propre, identique à votre dernier commit. Aucun commit « WIP » jetable n’est nécessaire.

git stash                  # shelve tracked changes, clean the tree
git stash --include-untracked   # also shelve new, untracked files
git stash list             # see all stashes: stash@{0}, stash@{1}, ...
git stash pop              # reapply the latest stash and delete it
git stash apply stash@{1}  # reapply a specific stash, keep it in the list

Le piège : pop et apply ne sont pas interchangeables. git stash pop applique vos modifications mises de côté puis supprime le stash ; git stash apply les applique mais conserve le stash, ce qui est plus sûr si la réapplication risque de générer des conflits. Si un pop provoque un conflit de fusion, le stash n’est pas supprimé. Résolvez d’abord le conflit, puis vérifiez avant de supposer qu’il a disparu.

git reflog : récupérer un travail que vous pensiez perdu

À utiliser quand un reset --hard malheureux, un rebase raté ou une branche supprimée fait disparaître des commits et que la panique s’installe. git reflog enregistre chaque position occupée par HEAD dans votre dépôt local, si bien que le commit « perdu » existe presque toujours encore. Il vous suffit d’en retrouver le hash.

git reflog
# ab12cd3 HEAD@{0}: reset: moving to HEAD~1
# ef45gh6 HEAD@{1}: commit: the work you thought vanished
git reset --hard ef45gh6   # restore HEAD to that commit
# or, to inspect it on a new branch first:
git switch -c recovery ef45gh6

Repérez la référence correspondant à l’état souhaité, puis faites git reset --hard <ref> pour y revenir, ou récupérez-la sur une nouvelle branche pour l’inspecter en toute sécurité au préalable. C’est le sauveur « oh non, j’ai perdu mon travail » de la boîte à outils.

Le piège : le reflog est strictement local et propre à chaque clone. Il ne voyage pas jusqu’au dépôt distant et n’existe pas dans un clone fraîchement créé : recloner pour « récupérer votre travail » ne servira donc à rien. Les entrées sont également purgées au fil du temps par le garbage collector, alors mieux vaut récupérer tôt que tard.

git cherry-pick : déplacer un commit d’une branche à l’autre

À utiliser quand un unique commit de hotfix doit atterrir sur une branche de release, mais que vous ne voulez pas fusionner l’intégralité de la branche de fonctionnalité dont il provient. git cherry-pick <hash> copie un commit précis sur votre branche courante, en laissant tout le reste de côté.

git switch release-2.4
git cherry-pick 9f3c1a7        # apply one commit here
git cherry-pick 9f3c1a7 3b8e2d0   # apply several, in order

Le piège : le cherry-pick crée un nouveau commit avec un nouveau hash. L’original n’est ni préservé ni déplacé. Si ce même commit atteint plus tard cette branche via une fusion classique, vous risquez de vous retrouver avec des modifications dupliquées dans l’historique. Utilisez-le de manière réfléchie, et non comme substitut routinier à la fusion.

git rebase -i : nettoyer l’historique avant une PR

À utiliser quand votre branche de fonctionnalité comporte cinq commits « fix typo » et « wip » et que vous souhaitez un historique propre et relisible avant d’ouvrir une pull request. git rebase -i <base-branch> ouvre un éditeur dans lequel vous pouvez réordonner, fusionner (squash), modifier ou supprimer des commits.

git rebase -i main
# pick   a1b2c3d Add feature scaffold
# squash e4f5g6h fix typo
# reword h7i8j9k Wire up handler
# drop   k0l1m2n debug logging

Marquer un commit avec squash le fusionne dans celui du dessus, reword en modifie uniquement le message, et drop l’exclut totalement du résultat. Si vous avez seulement besoin de condenser toute une branche en un unique ensemble de modifications indexées, git merge --squash <branch> est l’outil frère. Il indexe les modifications combinées sans créer de commit : c’est donc à vous d’exécuter git commit ensuite.

Le piège, et la règle d’or : réécrivez l’historique librement tant qu’il reste local, mais ne rebasez jamais des commits que vous avez déjà poussés sur une branche partagée. Réécrire un historique partagé impose à tous les autres une réconciliation douloureuse, un danger que Pro Git appelle « Les dangers du rebase ».

git bisect : trouver le commit à l’origine d’un bug

À utiliser quand quelque chose fonctionnait la semaine dernière et ne fonctionne plus aujourd’hui, sans que vous ayez la moindre idée duquel des 200 commits en est la cause. git bisect effectue une recherche binaire dans l’historique : marquez un commit défectueux et un commit sain, testez les points médians que Git extrait, et il identifie le premier commit fautif en log₂(n) étapes.

git bisect start
git bisect bad                 # current commit is broken
git bisect good v2.3.0         # this older tag worked
# Git checks out a midpoint — test it, then tell Git:
git bisect good                # this one is fine
git bisect bad                 # this one is broken
# ...repeat until Git reports "<hash> is the first bad commit"
git bisect reset               # return HEAD to where you started

Vous pouvez automatiser toute la recherche : git bisect run <script> utilise le code de sortie d’un script pour marquer chaque commit comme bon ou mauvais, sans aucune intervention manuelle. Terminez chaque session par git bisect reset pour ramener HEAD à sa position d’avant la recherche.

Le piège : bisect part du principe que chaque commit extrait se compile et exécute votre test correctement. Un commit qui échoue à la compilation pour une raison sans rapport fausse le résultat. Marquez ceux-là avec git bisect skip plutôt que de deviner bon ou mauvais.

Un filet de sécurité pour les commandes destructrices

Deux de ces commandes réécrivent l’historique, et la règle est la même pour les deux : git rebase -i et git reset --hard sont sans danger sur du travail local non poussé, et dangereuses sur des branches partagées. Lorsque vous avez besoin d’une annulation sûre sur une branche que d’autres ont déjà récupérée, préférez git revert, qui enregistre un nouveau commit inversant la modification sans altérer l’historique existant. Et lorsqu’une réécriture locale tourne mal, git reflog est la porte de sortie qui vous rend vos commits.

Ces cinq commandes méritent leur place parce que chacune répond à une question à laquelle commit et push ne peuvent pas répondre : sauvegarder ceci, récupérer cela, en déplacer une, faire le ménage, traquer le bug. Associez chacune à son déclencheur, et dégainez-la dès que la situation se présente.

FAQ

Quelle est la différence entre git reset --hard et git revert ?

git reset --hard fait reculer HEAD et supprime des commits en réécrivant l'historique, ce qui le rend dangereux sur toute branche que d'autres ont déjà récupérée. git revert, à l'inverse, enregistre un nouveau commit qui annule la modification visée tout en laissant l'historique existant intact. N'utilisez reset --hard que sur du travail local non poussé ; utilisez revert comme annulation sûre sur les branches partagées. Si un reset tourne mal, git reflog permet de récupérer les commits supprimés grâce à leur hash.

Comment récupérer un commit supprimé par un git reset --hard ?

Exécutez git reflog, qui enregistre chaque position occupée par HEAD dans votre dépôt local. Repérez l'entrée correspondant à l'état souhaité, notez son hash, puis exécutez git reset --hard <hash> pour le restaurer, ou git switch -c recovery <hash> pour l'inspecter d'abord sur une nouvelle branche. Le reflog est local et propre à chaque clone : recloner ne récupérera donc pas votre travail, et les entrées sont purgées au fil du temps par le garbage collector, alors mieux vaut récupérer tôt que tard.

git switch remplace-t-il git checkout pour changer de branche ?

git switch et git restore ont été introduits dans Git 2.23 (août 2019) pour scinder les rôles surchargés de git checkout : switch gère le passage d'une branche à l'autre et restore la restauration de fichiers. Les deux pages de manuel ont porté un avertissement « expérimental » pendant des années, mais celui-ci a été retiré à partir de Git 2.55.0 : ce sont donc désormais simplement les commandes recommandées pour ces opérations. git checkout continue de fonctionner et n'est pas déprécié, si bien que les workflows existants restent valables.

Quand faut-il utiliser git cherry-pick plutôt qu'une fusion ?

Utilisez git cherry-pick lorsque vous avez besoin d'exactement un commit sur une autre branche (par exemple, pour transférer un unique hotfix vers une branche de release) sans embarquer le reste de l'historique de la branche source. Une fusion apporte l'ensemble des modifications de la branche, ce qui n'est pas le bon outil pour isoler un correctif. Notez que cherry-pick crée un nouveau commit avec un nouveau hash : si la même modification arrive plus tard via une fusion classique, vous risquez de vous retrouver avec des modifications dupliquées dans l'historique.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.