ChatGPT解决这个技术问题 Extra ChatGPT

Find when a file was deleted in Git

git

I have a Git repository with n commits.

I have a file that I need, and that used to be in the repository, and that I suddenly look for and think "Oh! Where'd that file go?"

Is there a (series of) Git command(s) that will tell me that "file really_needed.txt was deleted at commit n-13"?

In other words, without looking at every individual commit, and knowing that my Git repo has every change of every file, can I quickly find the last commit that HAS that file, so I can get it back?

The link shared by Pedro had the answer to my question: how to find a deleted file when you don't remember the path.

B
Boris Verkhovskiy

To show the commits that changed a file, even if the file was deleted, run this command:

git log --full-history -- [file path]

If you want to see only the last commit, which deleted the file, use -1 in addition to the command above:

git log --full-history -1 -- [file path]

See also my article: Which commit deleted a file.


is it possible to search for patterns? I forgot the whole name of the file =( maybe it is possible to get a log of all deletions?
Please note, if you're using PowerShell, the hyphens have to be escaped: git log '--' [file path]. Hopefully this can same someone else some teeth-gnashing.
I was able to search using git log -- */<<filename>>.<<file extension>> not knowing the entire file path.
@MERose square brackets are there as a placeholder for the real file path.
N
Nick Heiner

Short answer:

git log --full-history -- your_file

will show you all commits in your repo's history, including merge commits, that touched your_file. The last (top) one is the one that deleted the file.

Some explanation:

The --full-history flag here is important. Without it, Git performs "history simplification" when you ask it for the log of a file. The docs are light on details about exactly how this works and I lack the grit and courage required to try to figure it out from the source code, but the git-log docs have this much to say:

Default mode Simplifies the history to the simplest history explaining the final state of the tree. Simplest because it prunes some side branches if the end result is the same (i.e. merging branches with the same content)

This is obviously concerning when the file whose history we want is deleted, since the simplest history explaining the final state of a deleted file is no history. Is there a risk that git log without --full-history will simply claim that the file was never created? Unfortunately, yes. Here's a demonstration:

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

Notice how git log -- bar in the terminal dump above resulted in literally no output; Git is "simplifying" history down into a fiction where bar never existed. git log --full-history -- bar, on the other hand, gives us the commit that created bar and the commit that deleted it.

To be clear: this issue isn't merely theoretical. I only looked into the docs and discovered the --full-history flag because git log -- some_file was failing for me in a real repository where I was trying to track a deleted file down. History simplification might sometimes be helpful when you're trying to understand how a currently-existing file came to be in its current state, but when trying to track down a file deletion it's more likely to screw you over by hiding the commit you care about. Always use the --full-history flag for this use case.


Note that this only searches history relevant to the current branch (not 'whole repo history')... i.e. if file is not yet deleted in current branch but has been in another branch, this won't find the delete commit. You have to be on any branch where the file has already been deleted. Maybe is obvious when thinking about it, but it caught me out at first.
This answer works. But from the git log output itself, it's not at all obvious that the last commit has deleted the file. I also tried git log --name-status --full-history -- file_name and git log -p --stat --full-history -- file_name, but neither explicitly indicates that the file was removed in the latest commit. This seems like a bug.
@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 -- bar includes D bar and A bar for me in the log output with Git 2.12.2. Do you not see those lines in the output? What version do you have?
git version 2.15.1 Yes, your command sequence does report D bar and A bar. Perhaps my issue is particular to my file's history. I was tracing the history of a .htaccess file that got gitignore'ed and removed. I finally figured that out and added the file back. When I include --name-status in the git log command, I see two A .htaccess entries (since I added it back in the latest commit) but no D .htaccess. So it seems in some cases, even though a file has been removed from the repository, git log will not show an explicit D file_name entry.
@Martin_ATS Curious. I wonder if perhaps the .htaccess got added in a commit X but then not included in the merge commit that brought X onto master? That's the only thing I can think of that I could possibly argue ought to look like a file having been added and never deleted and yet still not being present. Would be interesting to try and figure out an MCVE, then figure out whether it's a Git bug, and if not, whether it's possible to tweak my answer to handle your case.
A
Akif

You can find the last commit which deleted file as follows:

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

Further information is available here


The primary upvoted solution did not work for me, but this one did.
This solution worked for me. The accepted solution didn't.
Same for me, this solution worked well while the accepted answer didn't. Thank you very much, great tip!
Note that pattern matching works too here. Eg: git rev-list -n 1 HEAD -- '*myfile.txt'
This one worked for me too. The upvoted answer included a bunch of commits that didn't actually touched the file for some reason. Anyway, thanks @Akif
f
fedorqui

Git log but you need to prefix the path with --

Eg:

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

C
Community

I've just added a solution here (is there a way in git to list all deleted files in the repository?) for finding the commits of deleted files by using a regexp:

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

This returns everything deleted within a directory named some_dir (cascading). Any sed regexp there where \/some_dir\/ is will do.

OSX (thanks to @triplee and @keif)

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

Nice. Some mismatch under bash in OS X: sed: 1: "/^commit/h;/\/some_dir\ ...": bad flag in substitute command: '}'
@BrentFoust It's a pitty I cannot test that... try adding a space at the end (after the braces but before the single quote), the man page online is not clear about that...
Nice suggestion. But adding a space before the single quote didn't help. Neither did a space before the closing brace.
BSD/OSX sed is apparently not always good with semicolons as command separators. Try changing them into newlines, or switch to sed -n -e '/^commit/h' -e '\:/some_dir/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }
I tested it, and git log --diff-filter=D --summary | sed -n -e '/^commit/h' -e '\:/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e } worked for me on OSX.
F
Fabio Perrella

This command didn't work for me:

git log --full-history -1 -- [file path]

This is the command that worked:

git log --follow -- [file path]

E
Eric Woodruff

Try:

git log --stat | grep file

Grep on its own is not going to be very useful, since it will give only the file name that was deleted. If you want to get the git hash, then you can use grep -C 10 to get +-10 lines around the file name (which should include the git hash).
I had the file name wrong and this helped me find the correct file.
s
sky

This works for me:

git log -- **/Myfile.cs

If include --full-history switch, it will show a lot of changes have nothing to do with the file. Not sure why.