Once upon a time, there was a file in my project that I would now like to be able to get.
The problem is: I have no idea of when have I deleted it and on which path it was.
How can I locate the commits of this file when it existed?
If you do not know the exact path you may use
git log --all --full-history -- "**/thefile.*" If you know the path the file was at, you can do this:
git log --all --full-history -- <path-to-file> This should show a list of commits in all branches which touched that file. Then, you can find the version of the file you want, and display it with...
git show <SHA> -- <path-to-file> Or restore it into your working copy with:
git checkout <SHA>^ -- <path-to-file>
Note the caret symbol (^), which gets the checkout prior to the one identified, because at the moment of <SHA> commit the file is deleted, we need to look at the previous commit to get the deleted file's contents
git log -- <path> will have no output when you are on a branch in which the file never existed. You should always use git log --all -- <path>, to make sure you don't miss changes that happened on other branches. The command git log -- <path> can be very dangerous if you have more than one branch and tend to forget paths and branches (like me) and it's also dangerous if you work with other developers.**/thefile.* it's usually a good idea to quote it, e.g., '**/thefile.*', to protect the glob * from the shell. (I'm not familiar with WIndows shells and when they eat *s, but if there's an accidental match from the current working directory in bash, that could make trouble.)Get a list of the deleted files and copy the full path of the deleted file
git log --diff-filter=D --summary | grep delete Find the commit id of that commit and copy the commit id
git log --all -- FILEPATH Show diff of the deleted file
git show COMMIT_ID -- FILE_PATH Optionally write output to a file using >
git show COMMIT_ID -- FILE_PATH > deleted.diff unknown revision or path not in the working tree.git log --diff-filter=D --summary | grep -E 'delete|^commit\s+\S+'git-grep-latest(){ result_path=$(git log --diff-filter=D --summary | grep $1 | head -1 | awk '{print $4;}'); latest_commit=$(git log --all -- $result_path | head -1 | awk '{print $2;}'); git show $latest_commit -- $result_path; } and now you can just do: git-grep-latest some_textlinux pipes.. you'll like that.Suppose you want to recover a file called MyFile, but are uncertain of its path (or its extension, for that matter):
A nontrivial project may have multiple directories with similar or identical filenames.
> cd <project-root> > git log --diff-filter=D --summary | grep delete | grep MyFile `delete mode 100644 full/path/to/MyFile.js` full/path/to/MyFile.js is the path & file you're seeking.
> git log --oneline --follow -- full/path/to/MyFile.js `bd8374c Some helpful commit message` `ba8d20e Another prior commit message affecting that file` `cfea812 The first message for a commit in which that file appeared.` If you choose the first-listed commit (the last chronologically, here bd8374c), the file will not be found, since it was deleted in that commit.
> git checkout bd8374c -- full/path/to/MyFile.js `error: pathspec 'full/path/to/MyFile.js' did not match any file(s) known to git.` Just select the preceding (append a caret) commit:
> git checkout bd8374c^ -- full/path/to/MyFile.js git log --diff-filter=D --summary | find "delete" | find "MyFile" And step3, note the quotes around the hash: git checkout "bd8374c^" -- full/path/to/MyFile.jsfind by Select-String. So git log --diff-filter=D --summary | Select-String "delete" | Select-String "MyFile"Could not edit the accepted response so adding it as an answer here,
to restore the file in git, use the following (note the '^' sign just after the SHA)
git checkout <SHA>^ -- /path/to/file <SHA>~1 should work the same without the need to wrap it with quote marks.@Amber gave correct answer! Just one more addition, if you do not know the exact path of the file you can use wildcards! This worked for me.
git log --all -- **/thefile.* Below is a simple command, where a dev or a git user can pass a deleted file name from the repository root directory and get the history:
git log --diff-filter=D --summary | grep filename | awk '{print $4; exit}' | xargs git log --all -- If anybody, can improve the command, please do.
Try using one of the viewers, such as gitk so that you can browse around the history to find that half remembered file. (use gitk --all if needed for all branches)
--all option is critical for both your answer and the accepted answer.size of all deleted fileas well as the associated SHA
git log --all --stat --diff-filter=D --oneline add a -p|--patch to see the contents too
git log --all --stat --diff-filter=D -p To narrow down to any file you have two easy options, you can use a pathspec or you can pipe to grep and search for file name.
Using grep:
git log --all --stat --diff-filter=D --oneline | grep foo Using a pathspec:
git log --all --stat --diff-filter=D --oneline -- '*foo*' A pathspec can work well together with -p|--patch, if you want to see contents:
git log --all --stat --diff-filter=D --oneline --patch -- '*foo*' You might also like this one if you know where the file is
git log --all --full-history -- someFileName Summary:
You search your file full path in history of deleted files git log --diff-filter=D --summary | grep filename
You restore your file from commit before it was deleted
restore () { filepath="$@" last_commit=$(git log --all --full-history -- $filepath | grep commit | head -1 | awk '{print $2; exit}') echo "Restoring file from commit before $last_commit" git checkout $last_commit^ -- $filepath } restore my/file_path Here is my solution:
git log --all --full-history --oneline -- <RELATIVE_FILE_PATH> git checkout <COMMIT_SHA>^ -- <RELATIVE_FILE_PATH> I had this happen where I didn't even know what the file's name was, so I wanted to see all deleted files...
In general, I highly recommend becoming familiar with git-filter-repo. It has many uses for re-writing history, but one of its analysis features includes very quickly identifying all deleted files in a repo, and displaying their sizes and date deleted. (Using it on non-Windows OSes is fairly straight-forward, and here are installation instructions specifically for Windows.)
Once you have it runnable in your path, you simply run:
git filter-repo --analyze This will output some files in your .git folder, and the one you want to look at for this question is called:
.git\filter-repo\analysis\path-deleted-sizes.txt This file shows all deleted files in your repo, reverse sorted by size, and the date it was deleted. Here's an example output:
=== Deleted paths by reverse accumulated size === Format: unpacked size, packed size, date deleted, path name(s) 57151421 44898377 2022-07-22 somdirectory/somefileA 46034619 42929136 2022-01-18 somdirectory/somefileB 65332368 29102439 2020-09-28 somdirectory/somefileC 23686432 21681498 2022-07-22 somdirectory/somefileD 23681272 21678818 2022-07-22 somdirectory/somefileE 23676153 21670502 2022-07-22 somdirectory/somefileF 43232768 21439037 2020-07-10 somdirectory/somefileG 18714315 14299243 2019-01-10 somdirectory/somefileH 16788104 13035176 2019-01-10 somdirectory/somefileI Obviously you can use this to find the file you're looking for, or, in the context of re-writing history, based on the 2nd column sizes, I know if I remove those 9 deleted files from the history I'll reclaim about 220 MB.
Once you've identified the file you're looking for you can use this to find the commits:
git log --all --full-history -- <filename> You can use the git log command in a Terminal. This will search across all branches that you have fetched and stored locally. But you need to be aware of what features and flags you can use with it, so that you can build up the complexity of your search query, to find your items quicker. Here are some examples:
cd path/to/Git-Project-Root git log --all --full-history -- "**/**MyClass*" (Press Enter or Down Arrow to scroll. Press 'q' to exit out) "**/" means "in any directory in the Git project"."**MyClass*" means filenames containing the substring "MyClass", like MyClass.java or FirstVersionOfMyClass.py or LoginMyClassVersion1.txtmyclass.cscd path/to/Git-Project-Root git log --all --diff-filter=D --name-only -- "*/*MyClass*" (Press Enter or Down Arrow to scroll. Press 'q' to exit out) --diff-filter=D means filter for deleted files only (in the Git commits).--name-only means display the path of the filename in each Git commit."*/" means "in any directory in the Git project". You can use either 1 * or 2 **."*MyClass*" means filenames containing the substring "MyClass", like MyClass.java or FirstVersionOfMyClass.py or LoginMyClassVersion1.txtmyclass.cscd path/to/Git-Project-Root git log --all --diff-filter=D --name-only -- ":(icase)*/*myclass*" (Press Enter or Down Arrow to scroll. Press 'q' to exit out) --diff-filter=D means filter for deleted files only (in the Git commits).--name-only means display the path of the file in each Git commit."*/" means "in any directory in the Git project". You can use either 1 * or 2 **.":(icase)" means do a case-insensitive search, so it will find names like MyClass, myclass, MYCLASS, and so on."*myclass*" means filenames containing the substring "myclass", like MyClass.java, first_version_of_myclass.py or Login_MYCLASS_Version1.txtcd path/to/Git-Project-Root git log --all --diff-filter=A --name-only -- ":(icase)*/*myclass*" (Press Enter or Down Arrow to scroll. Press 'q' to exit out) --diff-filter=A means filter for added files only (in the Git commits).Sources and more info: