Trova quando un file è stato eliminato in Git


1035

Ho un repository Git con n commit.

Ho un file di cui ho bisogno, che era nel repository, e che improvvisamente cerco e penso "Oh! Dove sarebbe andato quel file?"

C'è un (serie di) comando (i) Git che mi dirà che "il file really_needed.txt è stato cancellato al commit n-13"?

In altre parole, senza guardare ogni singolo commit e senza sapere che il mio repository Git ha tutte le modifiche di ogni file, posso trovare rapidamente l'ultimo commit che ha quel file, così posso recuperarlo?



2
Il link condiviso da Pedro ha dato la risposta alla mia domanda: come trovare un file cancellato quando non ricordi il percorso.
— Gordon Bean,

Risposte:


1127

git log --full-history -- [file path] mostra le modifiche di un file, funziona anche se il file è stato eliminato.

Esempio:

git log --full-history  -- myfile

Se vuoi vedere solo l'ultimo commit, che ha eliminato un file usa -1 in aggiunta, ad es. git log --full-history -1 -- [file path]

Vedi Quale commit ha eliminato un file


16
è possibile cercare modelli? Ho dimenticato il nome completo del file = (forse è possibile ottenere un registro di tutte le eliminazioni?
— wutzebaer,


6
Nota: se si utilizza PowerShell, è necessario eseguire l'escape dei trattini: git log '-' [percorso file]. Spero che questo possa uguagliare qualcun altro a digrignare i denti.
— A. Wilson,

68
Sono stato in grado di cercare utilizzando git log -- */<<filename>>.<<file extension>>non conoscendo l'intero percorso del file.
— Tom Howard,

2
Le parentesi quadre @MERose sono presenti come segnaposto per il percorso del file reale.
— Emile Bergeron,

229

Risposta breve:

git log --full-history -- your_file

ti mostrerò tutti i commit nella storia del tuo repository, inclusi i commit di unione, che sono stati toccati your_file. L'ultimo (in alto) è quello che ha eliminato il file.

Qualche spiegazione:

La --full-historybandiera qui è importante. Senza di essa, Git esegue la "semplificazione della cronologia" quando viene richiesta la registrazione di un file. I documenti sono chiari sui dettagli di come funziona esattamente e mi manca la grinta e il coraggio necessari per cercare di capirlo dal codice sorgente, ma i documenti di git-log hanno molto da dire:

Modalità di default

Semplifica la storia alla storia più semplice spiegando lo stato finale dell'albero. Più semplice perché elimina alcuni rami laterali se il risultato finale è lo stesso (cioè unendo i rami con lo stesso contenuto)

Questo ovviamente riguarda quando il file di cui vogliamo la cronologia viene eliminato , poiché la cronologia più semplice che spiega lo stato finale di un file eliminato non è cronologia . Esiste il rischio che git logsenza --full-historyvolontà si possa semplicemente affermare che il file non è mai stato creato? Sfortunatamente sì. Ecco una dimostrazione:

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

Si noti come git log -- barnel dump del terminale sopra riportato non abbia prodotto letteralmente output; Git sta "semplificando" la storia in una finzione dove barnon è mai esistita. git log --full-history -- bard'altra parte, ci dà il commit che ha creato bare il commit che lo ha eliminato.

Per essere chiari: questo problema non è solo teorico. Ho solo esaminato i documenti e scoperto il--full-history bandiera perché git log -- some_filenon riuscivo per me in un vero repository in cui stavo cercando di rintracciare un file eliminato. La semplificazione della cronologia a volte può essere utile quando stai cercando di capire come un file attualmente esistente è arrivato al suo stato attuale, ma quando cerchi di rintracciare una cancellazione di file è più probabile che ti rovini nascondendo il commit che ti interessa . Usa sempre la --full-historybandiera per questo caso d'uso.


4
Si noti che questo cerca solo la cronologia rilevante per il ramo corrente (non "tutta la cronologia repository") ... vale a dire se il file non è ancora stato cancellato nel ramo corrente ma è stato in un altro ramo, non verrà trovato il commit di eliminazione. Devi essere su qualsiasi ramo in cui il file è già stato eliminato . Forse è ovvio quando ci penso, ma all'inizio mi ha sorpreso.
— Anentropico

1
Questa risposta funziona. Ma git logdall'output stesso, non è affatto ovvio che l'ultimo commit abbia eliminato il file. Ho anche provato git log --name-status --full-history -- file_namee git log -p --stat --full-history -- file_name, ma nessuno dei due indica esplicitamente che il file è stato rimosso nell'ultimo commit. Sembra 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 -- barinclude D bare A barper me nell'output del registro con Git 2.12.2. Non vedi quelle righe nell'output? Quale versione hai?
— Mark Amery,

git version 2.15.1Sì, la sequenza di comandi riporta D bare A bar. Forse il mio problema è particolare della storia del mio file. Stavo tracciando la storia di un .htaccessfile che è stato rimosso e rimosso da Gitignore. Alla fine l'ho capito e ho aggiunto il file. Quando includo --name-statusil git logcomando, vedo due A .htaccessvoci (da quando l'ho aggiunto nell'ultimo commit) ma no D .htaccess. Così sembra che in alcuni casi, anche se un file è stato rimosso dal repository, git lognon mostrerà una D file_namevoce esplicita .
— Martin_W,

@Martin_ATS Curioso. Mi chiedo se forse sono .htaccessstati aggiunti in un commit X ma non inclusi nel commit di merge che ha portato X sul master? Questa è l'unica cosa a cui riesco a pensare che potrei sostenere che dovrebbe apparire come un file che è stato aggiunto e mai cancellato e che tuttavia non è ancora presente. Sarebbe interessante provare a capire un MCVE, quindi capire se si tratta di un bug Git e, in caso contrario, se è possibile modificare la mia risposta per gestire il tuo caso.
— Mark Amery,

84

Accedi a Git ma devi aggiungere il prefisso al percorso --

Per esempio:

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 <danpal@gmail.com>
 Date:   Tue Jul 26 23:32:20 2011 -0500

 foo

31

Ho appena aggiunto una soluzione qui (c'è un modo in git per elencare tutti i file eliminati nel repository?) Per trovare i commit dei file eliminati usando un regexp:

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

Ciò restituisce tutto ciò che è stato cancellato all'interno di una directory denominata some_dir(a cascata). Qualsiasi sed regexp là dove \/some_dir\/è farà.

OSX (grazie a @triplee e @keif)

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

1
Bello. Qualche discrepanza sotto bash in OS X:sed: 1: "/^commit/h;/\/some_dir\ ...": bad flag in substitute command: '}'
— Brent Faust

@BrentFoust È un peccato che non riesco a testarlo ... prova ad aggiungere uno spazio alla fine (dopo le parentesi graffe ma prima della singola citazione), la pagina man online non è chiara al riguardo ...
— estani

Bel suggerimento. Ma l'aggiunta di uno spazio prima della singola citazione non ha aiutato. Né ha fatto uno spazio prima della parentesi graffa di chiusura.
— Brent Faust,

1
sedApparentemente BSD / OSX non è sempre buono con i punti e virgola come separatori di comandi. Prova a cambiarli in newline o passa ased -n -e '/^commit/h' -e '\:/some_dir/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }
— tripleee

1
L'ho provato e ha git log --diff-filter=D --summary | sed -n -e '/^commit/h' -e '\:/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }lavorato per me su OSX.
— keif

21

È possibile trovare l'ultimo commit che ha eliminato il file come segue:

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

Ulteriori informazioni sono disponibili qui


11
La soluzione primaria votata non ha funzionato per me, ma questa ha funzionato.
— Nick Heiner,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.