Récupérer son travail
- Leçon 12
- 40 min
- Module 4 : Résoudre et récupérer
Leçon 12 sur 15 · L'essentiel de Git et du développement en équipeRécupérer son travail
1. Comprendre un dépôt
2. Travailler avec les branches et les dépôts distants
3. Collaborer par la revue de code
4. Résoudre et récupérer
5. Mises en production et correctifs urgents
Ce que vous saurez faire
Récupérez votre travail après avoir déplacé une branche vers le mauvais commit ou supprimé une branche. Après cette leçon, vous pourrez retrouver un commit perdu, le récupérer et conserver aussi le travail de votre collègue.
Avant de commencerCe qu’il faut savoir et avoir sous la main
Ce que vous devez savoir
- Ce que fait
git reset, et pourquoi il sert seulement pour les commits (enregistrements) que personne d’autre n’a. Consultez Annuler en toute sécurité : reset : seulement pour les commits que personne d’autre ne possède. - Dans ce module, vous créez des commits, fusionnez et annulez des commits sur
mainpour garder les commandes courtes. En équipe, vous faites chaque changement sur une branche et la fusionnez par une demande de tirage (pull request). Consultez Les étapes d’une demande de tirage.
Ce qu’il vous faut sur votre ordinateur
- Le laboratoire
~/projects/c14-labde ce module, avecticket-notes,sam-notesetteam-remote.git. Dansticket-notes,git statusafficheYour branch is up to date with 'origin/main'(votre branche est à jour avec « origin/main ») etnothing to commit, working tree clean(rien à valider, répertoire de travail propre). - Les commits d’annulation que vous avez poussés dans Annuler en toute sécurité : exercice guidé : annuler un commit, puis réinitialiser dans un clone et Annuler en toute sécurité : à vous de jouer : annuler un commit et garder le suivant. Dans cette leçon, le dernier commit du dépôt distant est le commit d’annulation de cette tâche « À vous de jouer ». Si vous n’avez pas fait cette tâche, votre dernier commit est différent. Les étapes fonctionnent de la même façon.
- Aucun dossier
scratchdansc14-lab. Vous l’avez supprimé à la dernière étape de Annuler en toute sécurité : exercice guidé : annuler un commit, puis réinitialiser dans un clone. S’il est présent, allez dans~/projects/c14-lab, vérifiez avecpwdet exécutezrm -rf scratch. - Si le laboratoire manque ou ne fonctionne plus, exécutez
sh nextia-learning-labs/C14/setup.sh m04 --resetdans~/projects. Refaites ensuite les exemples détaillés de Les conflits, étape par étape et Annuler en toute sécurité. - Vous faites toutes les réinitialisations de cette leçon dans un clone jetable nommé
scratch. Vous le créez avecgit cloneet le supprimez avant de le recréer. Les commits que vous créez ont des empreintes différentes de celles de cette leçon.
Git se souvient des positions de vos branches
Section intitulée « Git se souvient des positions de vos branches »Quand vous réinitialisez une branche, Git ne supprime pas immédiatement les commits qui la quittent. Il les conserve pendant un certain temps. Le problème est de trouver leurs empreintes, car aucune branche ni aucun git log ne les montre maintenant.
Le reflog (journal des références) enregistre les positions de HEAD et de chaque branche, de la plus récente à la plus ancienne. Git ajoute une entrée chaque fois que HEAD se déplace : commit, changement de branche, réinitialisation, fusion, récupération ou clonage. git reflog montre les entrées de HEAD :
2bf80ca HEAD@{0}: reset: moving to HEAD~241faf11 HEAD@{1}: commit: Add ticket 107121ec42 HEAD@{2}: commit: Add ticket 1062bf80ca HEAD@{3}: clone: from /Users/amira/projects/c14-lab/team-remote.gitLisez chaque ligne de gauche à droite :
| Partie | Exemple | Signification |
|---|---|---|
| Empreinte | 41faf11 |
Le commit où se trouvait HEAD après cette action. |
| Nom | HEAD@{1} |
« La position de HEAD il y a un déplacement ». HEAD@{0} est sa position actuelle. |
| Action | commit: Add ticket 107 |
Ce qui a déplacé HEAD, et le message du commit ou la cible. |
Trois limites sont importantes :
- Le reflog est local. Il existe seulement dans votre clone. Git ne le pousse pas, et un nouveau clone commence avec un reflog vide. Votre collègue ne peut pas utiliser votre reflog.
- Les entrées expirent. Par défaut, Git conserve une entrée pendant 90 jours. Pour un commit qu’aucune branche ne contient, il conserve l’entrée pendant 30 jours. Ensuite, Git peut supprimer le commit.
- Il enregistre seulement les commits. Si vous n’avez jamais enregistré un changement dans un commit ou un stash, le reflog ne peut rien récupérer.
git restoreetgit reset --hardsuppriment définitivement ces changements.
Exemple détaillé : annuler une réinitialisation accidentelle
Section intitulée « Exemple détaillé : annuler une réinitialisation accidentelle »Amira crée un clone jetable, puis y crée deux commits locaux :
cd ~/projects/c14-labgit clone team-remote.git scratchcd scratchElle ajoute une ligne pour le billet 106 à tickets.txt et l’enregistre avec git commit -am "Add ticket 106". Elle fait ensuite la même chose pour le billet 107 :
41faf11 (HEAD -> main) Add ticket 107121ec42 Add ticket 1062bf80ca (origin/main, origin/HEAD) Revert "Update the accounts team extension"Elle veut retirer seulement le dernier commit, mais elle tape HEAD~2 au lieu de HEAD~1 :
git reset --hard HEAD~2HEAD is now at 2bf80ca Revert "Update the accounts team extension"Les deux commits ont disparu de main et de git log. Elle consulte le reflog. -5 montre les cinq entrées les plus récentes :
git reflog -52bf80ca HEAD@{0}: reset: moving to HEAD~241faf11 HEAD@{1}: commit: Add ticket 107121ec42 HEAD@{2}: commit: Add ticket 1062bf80ca HEAD@{3}: clone: from /Users/amira/projects/c14-lab/team-remote.gitL’entrée avant la réinitialisation est 41faf11, son commit pour le billet 107. Son parent est le commit pour le billet 106. Donc, 41faf11 comprend ses deux commits.
Elle pourrait exécuter git reset --hard 41faf11 pour remettre main à sa position précédente. Mais si elle copie la mauvaise empreinte, elle fait une deuxième erreur. Il est plus sûr de placer d’abord une nouvelle branche sur le commit. Une branche ne change aucun fichier, et Amira peut l’inspecter :
git branch rescue 41faf11git log --oneline -3 rescue41faf11 (rescue) Add ticket 107121ec42 Add ticket 1062bf80ca (HEAD -> main, origin/main, origin/HEAD) Revert "Update the accounts team extension"Ce sont les bons commits. main est derrière rescue, donc elle avance main avec une fusion en avance rapide. --ff-only signifie « seulement si c’est une avance rapide ». Sinon, Git s’arrête et ne change rien. Elle supprime ensuite la branche de récupération :
git merge --ff-only rescuegit branch -d rescueUpdating 2bf80ca..41faf11Fast-forward tickets.txt | 2 ++ 1 file changed, 2 insertions(+)Deleted branch rescue (was 41faf11).main pointe de nouveau vers 41faf11, et les deux commits sont de retour.
Exemple détaillé : récupérer une branche supprimée
Section intitulée « Exemple détaillé : récupérer une branche supprimée »Amira essaie une idée sur une branche, try-priorities, avec un commit. Elle revient à main et supprime la branche. git branch -d refuse, car la branche contient un commit qui n’est sur aucune autre branche :
error: the branch 'try-priorities' is not fully mergedhint: If you are sure you want to delete it, run 'git branch -D try-priorities'Cet avertissement protège le travail. Elle utilise -D, qui supprime quand même la branche :
git branch -D try-prioritiesDeleted branch try-priorities (was edc37a8).Elle réalise ensuite qu’elle a finalement besoin de cette idée. La sortie ci-dessus montre l’empreinte : (was edc37a8) (était à edc37a8). Si cette sortie n’est plus à l’écran, utilisez le reflog. L’empreinte se trouve dans l’entrée de son commit sur la branche :
41faf11 HEAD@{0}: checkout: moving from try-priorities to mainedc37a8 HEAD@{1}: commit: Add priority levels41faf11 HEAD@{2}: checkout: moving from main to try-prioritiesUne branche est seulement une étiquette sur un commit, alors elle recrée l’étiquette :
git branch try-priorities edc37a8git log --oneline -2 try-prioritiesedc37a8 (try-priorities) Add priority levels41faf11 (HEAD -> main) Add ticket 107Stash : mettre des changements de côté temporairement
Section intitulée « Stash : mettre des changements de côté temporairement »Un stash (réserve temporaire) conserve des changements non enregistrés dans un commit pour vous permettre de travailler sur autre chose. git stash push sauvegarde les changements et remet vos fichiers dans l’état du dernier commit. git stash pop remet les changements en place.
Amira a fait la moitié du travail sur le billet 108 dans tickets.txt. Elle a aussi un nouveau fichier, draft.txt, que Git ne suit pas. Un collègue lui demande de vérifier quelque chose sur un main propre :
git status --shortgit stash push -m "Half-done ticket 108"git status --shortgit stash list M tickets.txt?? draft.txtSaved working directory and index state On main: Half-done ticket 108?? draft.txtstash@{0}: On main: Half-done ticket 108-m donne un message au stash pour qu’elle puisse le reconnaître dans git stash list. Le changement de tickets.txt est dans le stash. Mais draft.txt reste dans le dossier : par défaut, le stash ne prend pas les fichiers non suivis. git stash push -u les prend aussi.
Quand elle est prête, elle récupère le changement :
git stash popDropped refs/stash@{0} (e42f7c918e3b194e6e2218fcf006ec4e10a15bed)Le changement est de retour dans tickets.txt, et le stash est retiré de la liste.
Ce que le stash ne fait pas bien
Section intitulée « Ce que le stash ne fait pas bien »- Les fichiers non suivis restent sur place, sauf si vous utilisez
-u. - Un stash est local.
git pushne l’envoie pas. Si votre ordinateur tombe en panne, le stash est perdu. poppeut causer un conflit. Si les mêmes lignes ont changé après leur mise en stash,pops’arrête avec un conflit. Le stash reste alors dans la liste. Consultez l’erreur courante ci-dessous.- Il est facile d’oublier les stashs. Après une semaine,
stash@{0}: On main: WIP(sur main : travail en cours) vous dit très peu de choses.
Pour du travail qui doit attendre plus de quelques minutes, un commit sur sa propre branche est plus sûr. Le reflog l’enregistre, vous pouvez le pousser, et son message décrit son contenu :
git switch -c wip-ticket-108git commit -am "WIP: ticket 108, cause not found yet"git switch mainExercice guidé : perdre des commits et les récupérer
Section intitulée « Exercice guidé : perdre des commits et les récupérer »-
Si vous avez fait les exemples détaillés dans votre propre clone
scratch, supprimez ce clone. Sinon,git clones’arrête à l’étape suivante, car le dossier existe.rm -rfsupprime un dossier sans possibilité d’annuler. Vérifiez donc le nom avant d’appuyer sur Enter (Entrée) :Fenêtre de terminal cd ~/projects/c14-labrm -rf scratchlsne montre plusscratch. -
Créez un nouveau clone jetable :
Fenêtre de terminal cd ~/projects/c14-labgit clone team-remote.git scratchcd scratch -
Ajoutez une ligne pour le billet 106 à
tickets.txt, sauvegardez et créez un commit :Fenêtre de terminal git commit -am "Add ticket 106" -
Ajoutez une ligne pour le billet 107, sauvegardez et créez un commit :
Fenêtre de terminal git commit -am "Add ticket 107" -
Exécutez
pwd. Puis retirez les deux commits par erreur :Fenêtre de terminal git reset --hard HEAD~2git log --oneline -1montre le commit du dépôt distant. Vos deux commits ne sont pas dans le journal. -
Retrouvez-les :
Fenêtre de terminal git reflog -5L’entrée
HEAD@{1}montrecommit: Add ticket 107(commit : ajouter le billet 107). Notez son empreinte. -
Placez une branche sur cette empreinte et vérifiez-la. Utilisez votre empreinte au lieu de
41faf11:Fenêtre de terminal git branch rescue 41faf11git log --oneline -3 rescueLa sortie montre vos deux commits.
-
Avancez
mainet supprimez la branche de récupération :Fenêtre de terminal git merge --ff-only rescuegit branch -d rescueLa sortie comprend
Fast-forward(avance rapide).git log --oneline -2montre de nouveau les deux commits. -
Créez une branche avec un commit, puis revenez à
main:Fenêtre de terminal git switch -c try-prioritiesecho "Priority levels: low, normal, urgent." > priorities.txtgit add priorities.txtgit commit -m "Add priority levels"git switch main -
Exécutez
pwd. Puis supprimez la branche :Fenêtre de terminal git branch -D try-prioritiesLa sortie est
Deleted branch try-priorities (was …)(branche try-priorities supprimée, était à …), avec une empreinte. -
Recréez la branche avec cette empreinte. Utilisez votre empreinte au lieu de
edc37a8. Puis vérifiez-la :Fenêtre de terminal git branch try-priorities edc37a8git log --oneline -1 try-prioritiesLa sortie montre
Add priority levels(ajouter des niveaux de priorité). -
Ajoutez une ligne pour le billet 108 à
tickets.txt, mais ne créez pas de commit. Mettez-la en stash et vérifiez :Fenêtre de terminal git stash push -m "Half-done ticket 108"git stash listgit status --shortgit stash listmontre votre message.git status --shortn’affiche rien. -
Récupérez le changement avec
git stash pop.git status --shortmontreM tickets.txt(tickets.txt modifié).
À vous : récupérez votre commit et gardez celui de Sam
Section intitulée « À vous : récupérez votre commit et gardez celui de Sam »Voici la vérification de fin du module 4. Vous récupérez votre commit perdu, et le travail de votre collègue doit rester.
- Supprimez l’ancien
scratch, puis créez-en un nouveau. Dans ce dépôt, faites un commit (enregistrement des modifications) d’une ligne pour le billet 106. - Dans
sam-notes, exécutezgit pull. AjoutezNetwork printer: ext. 4520(imprimante réseau : poste 4520) àcontacts.txt, faites un commit avec le messageAdd the network printer contact(ajouter le contact de l’imprimante réseau), puis poussez. - Dans
scratch, exécutezgit fetch. Faites ensuite cette erreur volontairement :git reset --hard origin/main. Vous vouliez « mettre à jour depuis le dépôt distant », et votre commit du billet 106 a quittémain. - Récupérez votre commit pour que
main, dansscratch, contienne à la fois votre commit du billet 106 et le contact d’imprimante de Sam. Ne poussez rien depuisscratch.
Montrez ensuite le résultat avec git log --oneline --graph -4, tail -1 contacts.txt, tail -1 tickets.txt et sh check.sh.
Afficher une réponse modèle
Après l’erreur, le reflog (journal des déplacements des références) montre le commit perdu à HEAD@{1} :
6a64a93 HEAD@{0}: reset: moving to origin/maina89b80c HEAD@{1}: commit: Add ticket 1062bf80ca HEAD@{2}: clone: from /Users/amira/projects/c14-lab/team-remote.gitCréez une branche sur ce commit. Fusionnez-la ensuite dans main. Ce n’est pas une avance rapide, car main contient maintenant le commit de Sam et rescue contient le vôtre. Git crée donc un commit de fusion qui conserve les deux :
git branch rescue a89b80cgit merge --no-edit rescuegit log --oneline --graph -4tail -1 contacts.txttail -1 tickets.txtsh check.shMerge made by the 'ort' strategy. tickets.txt | 1 + 1 file changed, 1 insertion(+)* 50d5765 (HEAD -> main) Merge branch 'rescue'|\| * a89b80c (rescue) Add ticket 106* | 6a64a93 (origin/main, origin/HEAD) Add the network printer contact|/* 2bf80ca Revert "Update the accounts team extension"Network printer: ext. 4520Ticket 106: keyboard missing keys. Ordered a new keyboard.PASS: 6 ticket lines checkedLe graphique montre les deux lignes de travail : votre commit récupéré et celui de Sam. Vous pouvez maintenant supprimer l’étiquette avec git branch -d rescue. Le commit de Sam n’a jamais été en danger dans le dépôt distant. Le danger était à l’étape suivante : si vous aviez ramené main à a89b80c avec reset, votre main ne contiendrait pas le commit de Sam.
Autre réponse correcte : git reset --hard a89b80c, puis git pull --no-rebase --no-edit. Les fichiers sont identiques. Seul le message du commit de fusion diffère.
Erreur courante
git stash pop s'arrête sur un conflit
Symptôme : vous avez mis de côté une modification de tickets.txt, puis fait un commit modifiant les mêmes lignes. git stash pop affiche :
Auto-merging tickets.txtCONFLICT (content): Merge conflict in tickets.txt...The stash entry is kept in case you need it again.git stash list montre encore le stash (modifications mises de côté). tickets.txt contient des marqueurs de conflit. Ici, les noms des côtés diffèrent de ceux d’une fusion :
<<<<<<< Updated upstreamTicket 107: cannot print in colour. Installed the colour driver. Fixed.||||||| Stash baseTicket 107: cannot print in colour. Installed the colour driver.=======Ticket 107: cannot print in colour. Installed the colour driver.Ticket 108: VPN drops every hour.>>>>>>> Stashed changesCause : Updated upstream (version actuelle) correspond à votre commit actuel. Stashed changes (modifications mises de côté) correspond au stash. Les deux ont modifié des lignes voisines depuis la mise de côté. Git ne peut donc pas appliquer le stash seul. Il conserve le stash pour ne pas perdre la modification.
Solution :
-
Résolvez le conflit comme dans Les conflits, étape par étape. Ici, gardez la ligne
Fixed.(corrigé) et ajoutez la ligne du billet 108 après celle-ci. -
Exécutez
git diff --checketsh check.sh. -
Exécutez
git add tickets.txtpour marquer le fichier comme résolu. -
Supprimez le stash, car sa modification se trouve maintenant dans votre fichier :
Fenêtre de terminal git stash dropSortie Dropped refs/stash@{0} (1b54611302688949f349718abca555a35e38fcb9)
Erreur courante
Le reflog ne contient pas ma modification
Symptôme : vous avez exécuté git restore tickets.txt ou git reset --hard, et perdu une modification. git reflog ne montre aucune entrée correspondante.
Cause : la modification n’a jamais été dans un commit ou un stash. Le reflog enregistre seulement les endroits où HEAD et les branches pointaient, et ces références pointent vers des commits. Git n’a jamais enregistré la modification. Il ne peut donc pas la récupérer.
Solution : Git ne peut pas vous aider ici. Cherchez une autre copie : l’historique d’annulation de votre éditeur ou sa fonction d’historique local. Pour éviter cela, faites un commit ou un stash avant toute commande qui supprime des modifications. Lisez aussi git status avant de l’exécuter.
Vérifiez votre compréhension
Prédisez : Amira exécute git reset --hard HEAD~2 dans son clone et perd deux commits locaux. Sam clone le dépôt distant sur son ordinateur. Peut-il trouver les deux commits d'Amira avec git reflog?
Afficher la réponse
Non. Le reflog est local à chaque clone et n’est jamais poussé. Amira n’a jamais poussé les deux commits. Ils existent donc seulement dans son clone. Le nouveau clone de Sam a son propre reflog, qui commence à sa création. Seule Amira peut récupérer les commits depuis son propre reflog, et seulement avant l’expiration des entrées.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Vérification de fin : dans votre clone scratch, montrez que vous avez récupéré votre commit perdu et gardé celui de Sam. Quelle sortie prouve chaque partie?
Afficher la réponse
git log --oneline --graph -4montre un commit de fusion avec deux parents : votreAdd ticket 106(ajouter le billet 106) et leAdd the network printer contactde Sam.tail -1 tickets.txtmontre votre ligne du billet 106.tail -1 contacts.txtmontre la ligne d’imprimante de Sam.sh check.shaffichePASS(réussite).- Vous n’avez rien poussé depuis
scratch, et vous n’avez pas réécritorigin/main:git statusindique une avance surorigin/main, et non une divergence ou un retard.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Récapitulatif
- Le reflog enregistre l’endroit où HEAD pointait après chaque déplacement. Utilisez-le pour trouver l’empreinte d’un commit qui a quitté une branche.
- Pour récupérer le commit, créez d’abord une branche sur son empreinte avec
git branch rescue <hash>. Inspectez-le, puis fusionnez-le ou ramenez la branche à ce commit avec reset. - Une branche supprimée est une étiquette perdue. Recréez-la avec
git branch <name> <hash>, en utilisant l’empreinte indiquée dans(was …)(était…) ou dans le reflog. - Le reflog est local, et ses entrées expirent. Il ne peut pas récupérer des modifications jamais enregistrées dans un commit ou un stash.
- Un stash conserve temporairement les modifications sans commit. Sans
-u, il ignore les fichiers non suivis. Il reste sur votre ordinateur et conserve l’entrée sipop(réappliquer et supprimer) provoque un conflit. Pour un travail plus long, faites un commit sur une branche.
C’est la fin du module 4. Vous pouvez maintenant résoudre un conflit, annuler un commit partagé avec revert et récupérer du travail local perdu. Dans le module 5, vous utilisez ces compétences pour préparer une livraison et la corriger.
Leçon suivante : Branches de version et étiquettes