3002 votes

Comment trouver et restaurer un fichier supprimé dans un dépôt Git ?

Disons que je suis dans un dépôt Git. Je supprime un fichier et je valide cette modification. Je continue à travailler et effectue d'autres livraisons. Puis, je découvre que j'ai besoin de restaurer ce fichier.

Je sais que je peux extraire un fichier en utilisant git checkout HEAD^ foo.bar mais je ne sais pas vraiment quand ce fichier a été supprimé.

  1. Quel serait le moyen le plus rapide de trouver le commit qui a supprimé un nom de fichier donné ?
  2. Quel serait le moyen le plus simple de récupérer ce fichier dans ma copie de travail ?

J'espère ne pas avoir à parcourir manuellement mes journaux, à extraire l'ensemble du projet pour une ZSD donnée, puis à copier manuellement ce fichier dans l'extraction de mon projet original.

41 votes

Notez que le commentaire précédent répond à la question dans le titre, et non dans le corps du texte -- cela inclut le fait de découvrir quand le fichier a été supprimé.

12 votes

Pour trouver l'engagement dans lequel un fichier a été supprimé : git log --diff-filter=D -- path/to/file

2 votes

3329voto

Charles Bailey Points 244082

Trouve le dernier commit qui a affecté le chemin donné. Comme le fichier n'est pas dans le commit HEAD, le commit précédent doit l'avoir supprimé.

git rev-list -n 1 HEAD -- <file_path>

Ensuite, vérifiez la version au commit précédent, en utilisant l'astérisque ( ^ ) symbole :

git checkout <deleting_commit>^ -- <file_path>

Ou en une seule commande, si $file est le fichier en question.

git checkout $(git rev-list -n 1 HEAD -- "$file")^ -- "$file"

Si vous utilisez zsh et que l'option EXTENDED_GLOB est activée, le symbole de l'escargot ne fonctionnera pas. Vous pouvez utiliser ~1 à la place.

git checkout $(git rev-list -n 1 HEAD -- "$file")~1 -- "$file"

102 votes

Le plus difficile est de vérifier le commit BEFORE, en utilisant le suffixe ^. Merci.

4 votes

Pour une raison quelconque, cela ne fonctionne pas avec zsh. ± git checkout $(git rev-list -n 1 HEAD "spec/Sporkfile_example.rb")^ -- "spec/Sporkfile_example.rb" zsh: no matches found: b71c152d8f38dcd23ad7600a93f261a7252c59e9^ Je suis passé à bash et ça a bien marché.

1 votes

J'ai dû utiliser git checkout <deleting_commit>^^ -- <file_path> pour une raison quelconque - peut-être un truc de ligne de commande Windows.

943voto

Robert Munteanu Points 31558
  1. Utilisez git log --diff-filter=D --summary pour obtenir tous les commits qui ont supprimé des fichiers et les fichiers supprimés ;
  2. Utilisez git checkout $commit~1 path/to/file.ext pour restaurer le fichier supprimé.

$commit est la valeur du commit que vous avez trouvé à l'étape 1, par ex. e4cf499627

10 votes

Par curiosité, à quoi fait référence le ~1 ?

8 votes

@tommy - le tilde spec vous donnera le nième petit-enfant du commit nommé . Voir book.git-scm.com/4_git_treeishes.html pour plus de détails .

9 votes

Cette approche est de loin la plus simple et la plus intuitive. git log -- *PartOfMyFileName* . Merci pour le $commit~1

333voto

Manu Points 1678

Pour restaurer tous les fichiers supprimés dans un dossier, entrez la commande suivante.

git ls-files -d | xargs git checkout --

1 votes

Où les fichiers sont-ils acheminés ? Je ne vois aucun changement.

29 votes

C'est probablement la méthode la plus simple. C'est pervers comme c'est difficile git a rendu même la tâche la plus simple.

9 votes

El ls-files est pratique, mais ne semble pas fonctionner pour les fichiers qui ont été supprimés avec la commande git rm c'est-à-dire mis en scène, et encore moins commis, ce qui est la question posée par le PO.

98voto

Josh Lee Points 53741

Si vous êtes fou, utilisez git-bisect . Voici ce qu'il faut faire :

git bisect start
git bisect bad
git bisect good <some commit where you know the file existed>

Il est maintenant temps d'exécuter le test automatisé. La commande shell '[ -e foo.bar ]' retournera 0 si foo.bar existe, et 1 sinon. La commande "run" de git-bisect utilisera la recherche binaire pour trouver automatiquement le premier commit où le test échoue. Elle commence au milieu de la plage donnée (de bon à mauvais) et la coupe en deux en fonction du résultat du test spécifié.

git bisect run '[ -e foo.bar ]'

Maintenant vous êtes au commit qui l'a supprimé. A partir de là, vous pouvez revenir dans le futur et utiliser git-revert pour annuler la modification,

git bisect reset
git revert <the offending commit>

ou vous pouvez revenir en arrière et inspecter manuellement les dégâts :

git checkout HEAD^
cp foo.bar /tmp
git bisect reset
cp /tmp/foo.bar .

2 votes

Pourriez-vous nous en dire plus sur git bisect run '[ -e foo.bar ]' ?

0 votes

Vous pouvez également utiliser les termes "bon" et "mauvais" manuellement, s'il s'agit de quelque chose qui ne peut pas être vérifié automatiquement. Voir la page de manuel bisect.

3 votes

@avdgaag le git bisect run indique à Git d'automatiser la bissection en exécutant la commande qui suit le mot "run" où la commande doit retourner 0 pour un good (voir git help bisect pour plus de détails). Le site '[ -e foo.bar ]' est une expression standard pour tester si le fichier foo.bar existe (l'implémentation se trouve généralement dans le fichier /usr/bin/[ qui est généralement lié à /usr/bin/test ) et les guillemets simples sont utilisés pour mettre tout cela en un seul argument de ligne de commande.

80voto

VonC Points 414372

Mon nouvel alias préféré, basé sur bonyiii 's réponse (upvoted), et ma propre réponse sur " Passer un argument à une commande d'alias Git " :

git config alias.restore '!f() { git checkout $(git rev-list -n 1 HEAD -- $1)~1 -- $(git diff --name-status $(git rev-list -n 1 HEAD -- $1)~1 | grep '^D' | cut -f 2); }; f'

J'ai perdu un fichier, supprimé par erreur il y a quelques commits ?
Rapide :

git restore my_deleted_file

Crise évitée.

Attention, avec Git 2.23 (Q3 2019) arrive la commande expérimentale nommé git restore ( !).
Renommez donc cet alias (comme indiqué ci-dessous).


Robert Dailey propose dans les commentaires l'alias suivant :

restore-file = !git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "$1"

Et jegan ajoute dans les commentaires :

Pour définir l'alias à partir de la ligne de commande, j'ai utilisé cette commande :

git config --global alias.restore "\!git checkout \$(git rev-list -n 1 HEAD -- \"\$1\")^ -- \"\$1\""

7 votes

Ceci restaure l'ensemble du commit, et pas seulement le fichier demandé.

6 votes

Voici mon alias, qui fonctionne à merveille : restore-file = !git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "$1"

1 votes

@RobertDailey Ça a l'air super ! J'ai inclus votre alias dans la réponse pour plus de visibilité.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X