Rechercher et restaurer un fichier supprimé dans un référentiel Git


2802

Disons que je suis dans un référentiel Git. Je supprime un fichier et valide cette modification. Je continue de travailler et je fais encore plus de commits. Ensuite, je trouve que je dois 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. Quelle serait la façon la plus simple de remettre ce fichier dans ma copie de travail?

J'espère que je n'ai pas à parcourir manuellement mes journaux, à extraire l'intégralité du projet pour un SHA donné, puis à copier manuellement ce fichier dans mon extraction de projet d'origine.


39
notez que le commentaire précédent répond à la question dans le titre, pas dans le corps - cela inclut de savoir quand le fichier a été supprimé.
avdgaag

8
Pour trouver le commit, un fichier a été supprimé dans:git log --diff-filter=D -- path/to/file
titaniumdecoy



54
@hhh git checkout deletedFilesera rétabli deletedFiles'il a été supprimé mais que cette suppression n'a pas encore été mise en place ou validée . Ce n'est pas ce que la question demande ici; cette question concerne la façon de restaurer un fichier dont la suppression a été validée il y a de nombreuses validations.
Mark Amery

Réponses:


3150

Trouvez le dernier commit qui a affecté le chemin donné. Comme le fichier n'est pas dans la validation HEAD, cette validation doit l'avoir supprimé.

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

Ensuite, extrayez la version lors de la validation avant, en utilisant le ^symbole caret ( ):

git checkout <deleting_commit>^ -- <file_path>

Ou dans une commande, si $filec'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 caret ne fonctionnera pas. Vous pouvez utiliser à la ~1place.

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

94
L'astuce consiste à extraire le commit AVANT, en utilisant le suffixe ^. Merci.
Christian Oudard

4
Pour une raison quelconque, cela ne fonctionnera pas dans 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 cela a bien fonctionné.
zoras

20
De la ligne de commande Windows, j'ai eu une erreur. error: pathspec <filename> did not match any file(s) known to git.. La solution était d'utiliser git bash.
donturner

56
@zoras zsh a sa propre extension sur '^' je crois, mais vous pouvez utiliser la syntaxe alternative de '~ 1': git checkout <deleting-commit>~1 -- <file-path> ~ X vous permet de spécifier X commits avant le commit spécifié, donc ~ 1 est le commit avant, ~ 2 est deux commits avant, etc
Nils Luxton

22
À l'invite Windows cmd, le ^caractère est le caractère d'échappement! Par conséquent, sur cmd, vous devez taper ^^pour dire à cmd que vous voulez un seul littéral ^ et que vous n'échappez pas à autre chose après. Ce qui arrive à beaucoup de gens, c'est que le ^est suivi d'un espace. Donc cmd pense que vous échappez à l'espace - ce qui donne simplement un caractère d'espace. Ainsi, au moment où git obtient les arguments cli, il voit SHA1et non SHA1^ . C'est vraiment agaçant. ~n'est pas un personnage d'échappement, c'est pourquoi cela fonctionne toujours. (PS. Si vous pensez que les googleurs voudront cette information, veuillez voter pour ce commentaire)
Alexander Bird

875
  1. Utilisation 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.extpour restaurer le fichier supprimé.

$commitest la valeur du commit que vous avez trouvé à l'étape 1, par exemplee4cf499627


10
curieux, à quoi fait référence le ~ 1?
tommy chheng

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

5
c'est de loin l'approche la plus simple et la plus intuitive. git log -- *PartOfMyFileName*. Merci pour le$commit~1
bgs

3
la git checkout $commit~1 filenamesyntaxe fonctionne parfaitement pour les fichiers individuels, et fonctionne également pour des répertoires entiers. à savoir: pour restaurer toutes les images supprimées dans ./images de sha 12345: git checkout 12345~1 images. merci pour cette réponse!
noinput

34
@Alexar $commit~1signifie que vous devez ajouter le nom du commit. Quelque chose comme 1d0c9ef6eb4e39488490543570c31c2ff594426c$commitest.
Eugene

319

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

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

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

21
C'est probablement la méthode la plus simple. Son perverti combien difficile gita rendu même la tâche la plus simple.
JWW

git checkout - [fichier] annulera les modifications dans [fichier]. Le tube remplacera [fichier] par le nom des fichiers supprimés.
Manu

6
La ls-filessous-commande est pratique, mais ne semble pas fonctionner pour les fichiers qui ont été supprimés avec git rmie staged, sans parler de commit, ce que l'OP a demandé.
MarkHu

Cela a fonctionné pour restaurer les fichiers supprimés, mais comment puis-je mettre à jour les fichiers qui ont été modifiés et apparaître comme M myChangedFileaprès git checkout?
libby

124

Je suis venu à cette question en cherchant à restaurer un fichier que je viens de supprimer mais je n'avais pas encore validé la modification. Au cas où vous vous trouveriez dans cette situation, tout ce que vous devez faire est le suivant:

git checkout HEAD -- path/to/file.ext


93

Si vous êtes fou, utilisez git-bisect. Voici quoi 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 elle foo.barexiste et 1 sinon. La commande "run" de git-bisectutilisera la recherche binaire pour trouver automatiquement le premier commit où le test échoue. Il commence à mi-chemin dans la plage donnée (de bon à mauvais) et le coupe de moitié en fonction du résultat du test spécifié.

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

Vous êtes maintenant au commit qui l'a supprimé. À partir d'ici, vous pouvez revenir vers le futur et utiliser git-revertpour annuler le changement,

git bisect reset
git revert <the offending commit>

ou vous pouvez revenir en arrière d'un commit et inspecter manuellement les dommages:

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

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

Vous pouvez également utiliser le bon et le mauvais manuellement, si c'est quelque chose qui ne peut être vérifié automatiquement. Voir la page de manuel de bissect.
Josh Lee

1
@avdgaag le git bisect rundit à Git d'automatiser la bissection en exécutant la commande suivant le mot 'run' où la commande doit retourner 0pour une goodversion (voir git help bisectpour plus de détails). Le '[ -e foo.bar ]'est une expression standard pour tester si le fichier foo.barexiste (l'implémentation est généralement dans un fichier /usr/bin/[qui est habituellement lié à /usr/bin/test) et les guillemets simples sont utilisés pour mettre tout cela comme un argument de ligne de commande unique.
Mikko Rantalainen

Bonne idée. J'ai essayé cette approche et elle a identifié un commit avant la suppression, mais pas le commit qui a réellement supprimé le fichier. Et dans un autre test, il a identifié 2 commits avant la suppression.
Michael Osofsky

Insensé? Peut être. Mais la bissectrice est un excellent moyen d'aider à trouver où un bug a été introduit et c'est donc une compétence précieuse à apprendre de toute façon. Donc, bien que ce ne soit peut-être pas la manière «correcte» ou la plus «correcte» ici, c'est toujours une bonne idée et vaut certainement un +1!
Pryftan

77

Mon nouvel alias préféré, basé sur bonyiii s » réponse (upvoted), et ma réponse au sujet de « passer un argument à une commande 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) vient la commande expérimentale nommée 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
Cela restaure l'intégralité de la validation, pas seulement le fichier demandé.
Daniel Bang

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

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

1
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\""
jegan

2
Expansion of alias 'restore' failed; '!git' is not a git command
Karl Morrison

55

Si vous connaissez le nom de fichier, c'est un moyen simple avec les commandes de base:

Liste tous les commits pour ce fichier.

git log -- path/to/file

Le dernier commit (le plus haut) est celui qui a supprimé le fichier. Vous devez donc restaurer l'avant-dernier commit.

git checkout {second to last commit} -- path/to/file

Je viens d'utiliser cette solution et il n'y a pas eu de validation pour la suppression. J'ai cependant pu restaurer le fichier en utilisant l'ID de validation le plus récent.
Adam

L'avant-dernier commit (commit précédent à la suppression) ne contiendrait-il pas la version la plus récente du fichier supprimé? L'avant-dernier (la validation avant la précédente validation de la suppression) pourrait être désespérément dépassé.
Suncat2000

1
C'est la première solution que j'ai vue qui est assez simple pour que je n'aie pas à revenir ici pour la trouver la prochaine fois. Peut être.
Eloff

@ Suncat2000 "avant-dernier" signifie "validation précédente de la suppression", comme "avant-dernier". en.wiktionary.org/wiki/penultimate#Synonyms
wisbucky

Merci un million de fois pour cette réponse !!!!!
Rakesh Bk

29

Pour restaurer un fichier supprimé et validé:

git reset HEAD some/path
git checkout -- some/path

Il a été testé sur Git version 1.7.5.4.


1
Ça n'a pas marché pour moi. Après le paiement, je me suis error: pathspec 'foo' did not match any file(s) known to git.assuré que le nom de fichier était correct. Git version 2.7.0
wisbucky

-1; c'est faux. Ces commandes annuleront une suppression qui n'a pas encore été validée (la première annule la suppression, si elle est intermédiaire, et la seconde rejette les modifications non programmées du fichier), mais vous prétendez ici qu'elles restaureront une validation la suppression du fichier, ce qui n'est tout simplement pas vrai et échouera avec une erreur comme celle dans le commentaire de @ wisbucky ci-dessus.
Mark Amery

@MarkAmery En effet, je pense que cette commande a bien fonctionné pour les développeurs, qui n'ont pas fait de transfert explicite pour la validation des fichiers supprimés avec git add -A, mais le fichier restauré était donc encore en phase non validée.
Fedir RYKHTIK

25

Si vous avez seulement apporté des modifications et supprimé un fichier, mais pas le valider, et maintenant vous avez rompu avec vos modifications

git checkout -- .

mais vos fichiers supprimés ne sont pas revenus, vous exécutez simplement la commande suivante:

git checkout <file_path>

Et hop, votre dossier est de retour.


24

J'ai cette solution .

  1. Obtenez l'id du commit où le fichier a été supprimé en utilisant l'une des méthodes ci-dessous.

    • git log --grep=*word*
    • git log -Sword
    • git log | grep --context=5 *word*
    • git log --stat | grep --context=5 *word* # recommandé si vous ne vous souvenez de rien
  2. Vous devriez obtenir quelque chose comme:

commit bfe68bd117e1091c96d2976c99b3bcc8310bebe7 Auteur: Alexander Orlov Date: jeu 12 mai 23:44:27 2011 +0200

replaced deprecated GWT class
- gwtI18nKeySync.sh, an outdated (?, replaced by a Maven goal) I18n generation script

commit 3ea4e3af253ac6fd1691ff6bb89c964f54802302 Auteur: Alexander Orlov Date: jeu 12 mai 22:10:22 2011 +0200

3 . Maintenant, en utilisant l'ID de validation bfe68bd117e1091c96d2976c99b3bcc8310bebe7, procédez comme suit:

git checkout bfe68bd117e1091c96d2976c99b3bcc8310bebe7^1 yourDeletedFile.java

Comme l'ID de validation fait référence à la validation où le fichier a déjà été supprimé, vous devez référencer la validation juste avant bfe68b, ce que vous pouvez faire en ajoutant ^1. Cela signifie: donnez-moi le commit juste avant bfe68b.


Il s'agit de la même approche que la réponse acceptée, mais avec d'autres moyens de trouver le commit de suppression. J'aime toujours l'approche adoptée dans la réponse acceptée, mais ce sont de bonnes alternatives. Merci!
avdgaag

Je suppose que l'extraction du fichier supprimé puis (sans le modifier) ​​la validation ne crée pas de copie du fichier. Droite? (Je dois le faire avec des images, et une copie rendrait le référentiel plus grand)
Stonecrusher


12

git undelete path/to/file.ext

  1. Mettez ceci dans votre .bash_profile(ou tout autre fichier pertinent qui se charge lorsque vous ouvrez un shell de commande):

    git config --global alias.undelete '!sh -c "git checkout $(git rev-list -n 1 HEAD -- $1)^ -- $1" -'
    
  2. Utilisez ensuite:

    git undelete path/to/file.ext
    

Cet alias vérifie d'abord pour trouver le dernier commit où ce fichier existait, puis effectue une extraction Git de ce chemin de fichier à partir du dernier commit où ce fichier existait. Source .


11

Dans de nombreux cas, il peut être utile d'utiliser des coreutils (grep, sed, etc.) en conjonction avec Git. Je connais déjà assez bien ces outils, mais Git l'est moins. Si je voulais faire une recherche pour un fichier supprimé, je ferais ce qui suit:

git log --raw | grep -B 30 $'D\t.*deleted_file.c'

Quand je trouve la révision / commit:

git checkout <rev>^ -- path/to/refound/deleted_file.c

Tout comme d'autres l'ont dit avant moi.

Le fichier sera maintenant restauré dans son état antérieur à sa suppression. N'oubliez pas de le réengager dans l'arborescence de travail si vous souhaitez le conserver.


7

J'ai dû restaurer un tas de fichiers supprimés à partir d'un commit spécifique, et je l'ai géré avec deux commandes:

git show <rev> --diff-filter=D --summary --name-only --no-commit-id | xargs git checkout <rev>^ -- 
git show <rev> --diff-filter=D --summary --name-only --no-commit-id | xargs git reset HEAD 

(Notez l'espace de fin à la fin de chaque commande.)

Les fichiers ont été ajoutés au fichier .gitignore puis effacés avec git rm. J'avais besoin de restaurer les fichiers, mais de les décompresser. J'avais des centaines de fichiers à restaurer, et taper des choses manuellement pour chaque fichier comme dans les autres exemples allait être beaucoup trop lent.


7

En fait, cette question concerne directement Git, mais quelqu'un comme moi travaille avec des outils GUI comme WebStorm VCS part connaître les commandes CLI de Git.

Je fais un clic droit sur le chemin qui contient le fichier supprimé, puis je vais dans Git, puis je clique sur Afficher l'historique .

Entrez la description de l'image ici

Les outils VCS montrent toutes les révisions et je peux voir tous les commits et les changements de chacun d'eux.

Entrez la description de l'image ici

Ensuite, je sélectionne les commits que mon ami supprime le PostAd.jsfichier. voir maintenant ci-dessous:

Entrez la description de l'image ici

Et maintenant, je peux voir mon fichier supprimé de désir. Je viens de double-cliquer sur le nom du fichier et il récupère.

Entrez la description de l'image ici

Je sais que ma réponse n'est pas les commandes Git, mais elle est rapide, fiable et facile pour les développeurs débutants et professionnels. Les outils WebStorm VCS sont géniaux et parfaits pour travailler avec Git et ne nécessitent aucun autre plugin ou outil.


1
C'est génial! Je vous remercie. Définitivement une solution plus facile pour ceux qui utilisent l'un des IDE JetBrains.
Fabiano Arruda

Comment restaurer le fichier s'il s'agit d'une image?
Nodirabegimxonoyim

Cher @FabianoArruda, les IDE JetBrains sont de puissants outils de développement. Merci pour votre aimable commentaire.
AmerllicA

Merci à toi cher @PeterMortensen pour l'édition.
AmerllicA

6

J'avais la même question. Sans le savoir, j'avais créé un commit pendant .

Liste des validations pendantes

git fsck --lost-found

Inspectez chaque commit pendant

git reset --hard <commit id>

Mes fichiers ont réapparu lorsque je suis passé au commit suspendu.

git status pour la raison:

“HEAD detached from <commit id where it detached>”


2
Merci beaucoup. Vous m'avez aidé à restaurer des milliers de lignes de codes.
Ruben

5
user@bsd:~/work/git$ rm slides.tex
user@bsd:~/work/git$ git pull 
Already up-to-date.
user@bsd:~/work/git$ ls slides.tex
ls: slides.tex: No such file or directory

Restaurez le fichier supprimé:

user@bsd:~/work/git$ git checkout
D       .slides.tex.swp
D       slides.tex
user@bsd:~/work/git$ git checkout slides.tex 
user@bsd:~/work/git$ ls slides.tex
slides.tex

2
La question concernait la restauration d'un fichier après sa suppression et la modification validée. Cette réponse concerne la restauration d'un fichier qui a été supprimé uniquement dans le répertoire de travail.
akaihola

C'est vrai, et c'est ce que je cherchais.
Hola Soy Edu Feliz Navidad

4

Si vous connaissez le commit qui a supprimé le ou les fichiers, exécutez cette commande où <SHA1_deletion>est le commit qui a supprimé le fichier:

git diff --diff-filter=D --name-only <SHA1_deletion>~1 <SHA1_deletion> | xargs git checkout <SHA1_deletion>~1 --

La partie avant le canal répertorie tous les fichiers qui ont été supprimés dans la validation; ils sont tous extraits de la validation précédente pour les restaurer.


4

Recherchez le commit qui a supprimé votre fichier:

git log --diff-filter=D --oneline -- path/to/file | cut -f -d ' '

Exemple de sortie:

4711174

Depuis Git 2.23, il existe en fait une restorecommande. C'est encore expérimental mais pour restaurer quelque chose que vous avez supprimé dans un commit (4711174 dans ce cas) vous pouvez alors taper:

git restore --source=4711174^ path/to/file

Notez le ^ après l'ID de validation car nous voulons restaurer quelque chose à partir du commit avant celui qui a supprimé le fichier.

L' --sourceargument indique aurestore commande où chercher le ou les fichiers à restaurer et il peut s'agir de n'importe quel commit et même de l'index.

Voir: doc git-restore pour git 2.23.0


4

Dans notre cas, nous avons accidentellement supprimé des fichiers dans un commit et certains commits plus tard, nous avons réalisé notre erreur et voulions récupérer tous les fichiers qui ont été supprimés, mais pas ceux qui ont été modifiés.

Basé sur l'excellente réponse de Charles Bailey, voici mon one-liner:

git co $(git rev-list -n 1 HEAD -- <file_path>)~1 -- $(git diff --name-status $(git rev-list -n 1 HEAD -- <file_path>)~1 head | grep '^D' | cut -f 2)

2

Simple et précis-

Tout d'abord, obtenez un dernier commit stable dans lequel vous avez ce fichier par -

git log 

Supposons que vous trouviez $ commitid 1234567 ..., puis

git checkout <$commitid> $fileName

Cela restaurera la version du fichier qui était dans ce commit.


1

Pour la meilleure façon de le faire, essayez-le.


Tout d'abord, recherchez l'ID de validation du commit qui a supprimé votre fichier. Il vous donnera un résumé des validations qui ont supprimé les fichiers.

git log --diff-filter = D --summary

git checkout 84sdhfddbdddf ~ 1

Remarque: 84sdhfddbdddest votrecommit id

Grâce à cela, vous pouvez facilement récupérer tous les fichiers supprimés.


1

Vous pouvez toujours git revertvotre commit qui a supprimé le fichier. ( Cela suppose que la suppression était le seul changement dans la validation. )

> git log
commit 2994bda49cd97ce49099953fc3f76f7d3c35d1d3
Author: Dave <dave@domain.com>
Date:   Thu May 9 11:11:06 2019 -0700

    deleted readme.md

Et si vous avez poursuivi le travail et réalisé plus tard que vous ne vouliez pas valider ce commit de suppression, vous pouvez le rétablir en utilisant:

> git revert 2994bd

git logMontre maintenant :

> git log
Author: Dave <dave@domain.com>
Date:   Thu May 9 11:17:41 2019 -0700

    Revert "deleted readme"

    This reverts commit 2994bda49cd97ce49099953fc3f76f7d3c35d1d3.

Et readme.mda été restauré dans le référentiel.


Étant donné que la question suppose qu'un certain nombre de validations ont été effectuées après la suppression du fichier et qu'il n'y a aucune indication que les validations ultérieures sont indésirables, cela ne semble pas susceptible d'aider le PO dans la situation décrite.
Jonathan Leffler

1
Oui! Vous pouvez effectuer des validations ultérieures et toujours annuler la validation de suppression. Donc, si le commit 111 supprime le fichier, et le commit 222, 333, 444, ajoute / modifie des choses, vous pouvez toujours annuler le commit 111 pour annuler la suppression, et il deviendra le commit 555
Dave Baghdanov

0

J'ai également ce problème en utilisant le code ci-dessous pour récupérer un fichier précédent dans un répertoire local:

git checkout <file path with name>

L'exemple ci-dessous fonctionne pour moi:

git checkout resources/views/usaSchools.blade.php


Veuillez mentionner quel est le problème
Akbor

La suppression a déjà été validée. Dans ce cas, vous devez spécifier le commit à restaurer.
sba


-1

Si la suppression n'a pas été validée, la commande ci-dessous restaurera le fichier supprimé dans l'arborescence de travail.

$ git checkout -- <file>

Vous pouvez obtenir une liste de tous les fichiers supprimés dans l'arborescence de travail à l'aide de la commande ci-dessous.

$ git ls-files --deleted

Si la suppression a été validée, recherchez la validation à l'endroit où elle s'est produite, puis récupérez le fichier à partir de cette validation.

$ git rev-list -n 1 HEAD -- <file>
$ git checkout <commit>^ -- <file>

Dans le cas où vous recherchez le chemin du fichier à récupérer, la commande suivante affichera un résumé de tous les fichiers supprimés.

$ git log --diff-filter=D --summary

-1

Afin de restaurer tous les fichiers supprimés avec Git, vous pouvez également faire:

git checkout $(git ls-files --deleted)

git ls-files --deletedrépertorie tous les fichiers supprimés et git checkout $(git command)restaure la liste des fichiers dans un paramètre.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.