Rechercher quand un fichier a été supprimé dans Git

1035

J'ai un dépôt Git avec n commits.

J'ai un fichier dont j'ai besoin, qui se trouvait dans le référentiel, et que je cherche soudainement et pense "Oh! Où est passé ce fichier?"

Existe-t-il une (série de) commande (s) Git qui me dira que "le fichier really_needed.txt a été supprimé lors de la validation n-13"?

En d'autres termes, sans regarder chaque commit individuel et sachant que mon dépôt Git a chaque changement de chaque fichier, puis-je trouver rapidement le dernier commit qui a ce fichier, donc je peux le récupérer?

JohnMetta
la source
2
Le lien partagé par Pedro avait la réponse à ma question: comment trouver un fichier supprimé lorsque vous ne vous souvenez pas du chemin.
Gordon Bean

Réponses:

1127

git log --full-history -- [file path] montre les modifications d'un fichier, fonctionne même si le fichier a été supprimé.

Exemple:

git log --full-history  -- myfile

Si vous souhaitez voir uniquement le dernier commit, qui a supprimé un fichier, utilisez -1 en plus, par exemple, git log --full-history -1 -- [file path]

Voir quel commit a supprimé un fichier

vogella
la source
16
est-il possible de rechercher des motifs? J'ai oublié le nom complet du fichier = (peut-être est-il possible d'obtenir un journal de toutes les suppressions?
wutzebaer
6
je l'
ai
6
Veuillez noter que si vous utilisez PowerShell, les tirets doivent être échappés: git log '-' [chemin du fichier]. Espérons que cela puisse même grincer des dents à quelqu'un d'autre.
A. Wilson
68
J'ai pu effectuer une recherche en git log -- */<<filename>>.<<file extension>>ne connaissant pas le chemin d'accès complet au fichier.
Tom Howard
2
Les crochets @MERose sont là comme un espace réservé pour le chemin du fichier réel.
Emile Bergeron
229

Réponse courte:

git log --full-history -- your_file

vous montrera toutes les validations de l'historique de votre dépôt, y compris les validations de fusion, qui ont touché your_file. Le dernier (en haut) est celui qui a supprimé le fichier.

Quelques explications:

Le --full-historydrapeau ici est important. Sans cela, Git effectue une "simplification de l'historique" lorsque vous lui demandez le journal d'un fichier. Les documents sont légers sur les détails de la façon dont cela fonctionne et je n'ai pas le courage et le courage nécessaires pour essayer de le comprendre à partir du code source, mais les documents git-log ont beaucoup à dire:

Mode par défaut

Simplifie l'historique à l'historique le plus simple expliquant l'état final de l'arbre. Plus simple car il taille certaines branches latérales si le résultat final est le même (c'est-à-dire fusionner des branches avec le même contenu)

Cela concerne évidemment le moment où le fichier dont nous voulons l'historique est supprimé , car l'historique le plus simple expliquant l'état final d'un fichier supprimé n'est pas un historique . Y a-t-il un risque que git logsans --full-historyvouloir simplement prétendre que le fichier n'a jamais été créé? Malheureusement oui. Voici une démonstration:

mark@lunchbox:~/example$ git init
Initialised empty Git repository in /home/mark/example/.git/
mark@lunchbox:~/example$ touch foo && git add foo && git commit -m "Added foo"
[master (root-commit) ddff7a7] Added foo
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 foo
mark@lunchbox:~/example$ git checkout -b newbranch
Switched to a new branch 'newbranch'
mark@lunchbox:~/example$ touch bar && git add bar && git commit -m "Added bar"
[newbranch 7f9299a] Added bar
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 bar
mark@lunchbox:~/example$ git checkout master
Switched to branch 'master'
mark@lunchbox:~/example$ git rm foo && git commit -m "Deleted foo"
rm 'foo'
[master 7740344] Deleted foo
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 foo
mark@lunchbox:~/example$ git checkout newbranch
Switched to branch 'newbranch'
mark@lunchbox:~/example$ git rm bar && git commit -m "Deleted bar"
rm 'bar'
[newbranch 873ed35] Deleted bar
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 bar
mark@lunchbox:~/example$ git checkout master
Switched to branch 'master'
mark@lunchbox:~/example$ git merge newbranch
Already up-to-date!
Merge made by the 'recursive' strategy.
mark@lunchbox:~/example$ git log -- foo
commit 77403443a13a93073289f95a782307b1ebc21162
Author: Mark Amery 
Date:   Tue Jan 12 22:50:50 2016 +0000

    Deleted foo

commit ddff7a78068aefb7a4d19c82e718099cf57be694
Author: Mark Amery 
Date:   Tue Jan 12 22:50:19 2016 +0000

    Added foo
mark@lunchbox:~/example$ git log -- bar
mark@lunchbox:~/example$ git log --full-history -- foo
commit 2463e56a21e8ee529a59b63f2c6fcc9914a2b37c
Merge: 7740344 873ed35
Author: Mark Amery 
Date:   Tue Jan 12 22:51:36 2016 +0000

    Merge branch 'newbranch'

commit 77403443a13a93073289f95a782307b1ebc21162
Author: Mark Amery 
Date:   Tue Jan 12 22:50:50 2016 +0000

    Deleted foo

commit ddff7a78068aefb7a4d19c82e718099cf57be694
Author: Mark Amery 
Date:   Tue Jan 12 22:50:19 2016 +0000

    Added foo
mark@lunchbox:~/example$ git log --full-history -- bar
commit 873ed352c5e0f296b26d1582b3b0b2d99e40d37c
Author: Mark Amery 
Date:   Tue Jan 12 22:51:29 2016 +0000

    Deleted bar

commit 7f9299a80cc9114bf9f415e1e9a849f5d02f94ec
Author: Mark Amery 
Date:   Tue Jan 12 22:50:38 2016 +0000

    Added bar

Remarquez comment git log -- barle vidage de terminal ci-dessus n'a entraîné littéralement aucune sortie; Git "simplifie" l'histoire en une fiction qui barn'a jamais existé. git log --full-history -- bar, d'autre part, nous donne le commit qui a créé baret le commit qui l'a supprimé.

Pour être clair: cette question n'est pas seulement théorique. J'ai seulement regardé les documents et découvert le --full-historydrapeau parce que git log -- some_filej'échouais dans un vrai référentiel où j'essayais de retrouver un fichier supprimé. La simplification de l'historique peut parfois être utile lorsque vous essayez de comprendre comment un fichier existant est devenu dans son état actuel, mais lorsque vous essayez de retrouver une suppression de fichier, il est plus probable qu'il vous dérange en masquant la validation qui vous intéresse . Utilisez toujours l' --full-historyindicateur pour ce cas d'utilisation.

Mark Amery
la source
4
Notez que cela ne recherche que l'historique pertinent pour la branche actuelle (pas 'l'historique complet du référentiel') ... c'est-à-dire que si le fichier n'est pas encore supprimé dans la branche actuelle mais a été dans une autre branche, cela ne trouvera pas le commit de suppression. Vous devez être sur n'importe quelle branche où le fichier a déjà été supprimé . Peut-être que c'est évident quand on y pense, mais ça m'a attrapé au début.
Anentropic
1
Cette réponse fonctionne. Mais à partir de la git logsortie elle-même, il n'est pas du tout évident que le dernier commit a supprimé le fichier. J'ai également essayé git log --name-status --full-history -- file_nameet git log -p --stat --full-history -- file_name, mais ni l'un ni l'autre n'indique explicitement que le fichier a été supprimé lors de la dernière validation. Cela semble être un bug.
Martin_W
@Martin_ATS mkdir somedir && cd somedir && git init && touch foo && git add foo && git commit -m "Added foo" && git checkout -b newbranch && touch bar && git add bar && git commit -m "Added bar" && git checkout master && git rm foo && git commit -m "Deleted foo" && git checkout newbranch && git rm bar && git commit -m "Deleted bar" && git checkout master && git merge newbranch && git log --name-status --full-history -- barinclut D baret A barpour moi dans la sortie du journal avec Git 2.12.2. Ne voyez-vous pas ces lignes dans la sortie? Quelle version avez-vous?
Mark Amery
git version 2.15.1Oui, votre séquence de commandes rend compte D baret A bar. Peut-être que mon problème est particulier à l'historique de mon dossier. Je .htaccesstraçais l'historique d'un fichier qui a été gitignoré et supprimé. J'ai finalement compris cela et j'ai rajouté le fichier. Lorsque j'inclus --name-statusdans la git logcommande, je vois deux A .htaccessentrées (depuis que je l'ai ajouté dans le dernier commit) mais non D .htaccess. Il semble donc que dans certains cas, même si un fichier a été supprimé du référentiel, git logil ne présentera pas d' D file_nameentrée explicite .
Martin_W
@Martin_ATS Curieux. Je me demande si peut-être l' .htaccessajout dans un commit X mais non inclus dans le commit de fusion qui a amené X sur master? C'est la seule chose à laquelle je peux penser que je pourrais peut-être affirmer devrait ressembler à un fichier ayant été ajouté et jamais supprimé et pourtant toujours pas présent. Il serait intéressant d'essayer de comprendre un MCVE, puis de déterminer s'il s'agit d'un bogue Git, et sinon, s'il est possible de modifier ma réponse pour gérer votre cas.
Mark Amery
84

Journal Git mais vous devez préfixer le chemin avec --

Par exemple:

dan-mac:test dani$ git log file1.txt
fatal: ambiguous argument 'file1.txt': unknown revision or path not in the working tree.

dan-mac:test dani$ git log -- file1.txt
 commit 0f7c4e1c36e0b39225d10b26f3dea40ad128b976
 Author: Daniel Palacio <[email protected]>
 Date:   Tue Jul 26 23:32:20 2011 -0500

 foo
daniel
la source
31

Je viens d'ajouter une solution ici (existe-t-il un moyen dans git de répertorier tous les fichiers supprimés dans le référentiel?) Pour trouver les validations des fichiers supprimés à l'aide d'une expression rationnelle:

git log --diff-filter=D --summary | sed -n '/^commit/h;/\/some_dir\//{G;s/\ncommit \(.*\)/ \1/gp}'

Cela renvoie tout ce qui a été supprimé dans un répertoire nommé some_dir(en cascade). Tout regexp sed là où il \/some_dir\/est fera l'affaire.

OSX (merci à @triplee et @keif)

git log --diff-filter=D --summary | sed -n -e '/^commit/h' -e '\:/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }
estani
la source
1
Agréable. Quelques disparités sous bash dans OS X:sed: 1: "/^commit/h;/\/some_dir\ ...": bad flag in substitute command: '}'
Brent Faust
@BrentFoust C'est dommage que je ne puisse pas tester cela ... essayez d'ajouter un espace à la fin (après les accolades mais avant la citation unique), la page de manuel en ligne n'est pas claire à ce sujet ...
estani
Belle suggestion. Mais l'ajout d'un espace avant le guillemet simple n'a pas aidé. Pas plus qu'un espace avant l'accolade de fermeture.
Brent Faust
1
BSD / OSX sedn'est apparemment pas toujours bon avec des points-virgules comme séparateurs de commandes. Essayez de les changer en nouvelles lignes, ou passez àsed -n -e '/^commit/h' -e '\:/some_dir/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }
tripleee
1
Je l'ai testé et git log --diff-filter=D --summary | sed -n -e '/^commit/h' -e '\:/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }travaillé pour moi sur OSX.
keif
22

Vous pouvez trouver le dernier commit qui a supprimé le fichier comme suit:

git rev-list -n 1 HEAD -- [file_path]

De plus amples informations sont disponibles ici

Akif
la source
11
La solution primaire votée n'a pas fonctionné pour moi, mais celle-ci l'a fait.
Nick Heiner du
0

Essayer:

git log --stat | grep file
Eric Woodruff
la source