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
Get a list of the deleted files and copy the full path of the deleted file
git log --diff-filter=D --summary | grep delete
Execute the next command to find commit id of that commit and copy the commit id
git log --all -- FILEPATH
Show diff of deleted file
git show COMMIT_ID -- FILE_PATH
Remember, you can write output to a file using >
like
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_text
linux 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):
Preliminary: Avoid confusion by stepping to the git root
A nontrivial project may have multiple directories with similar or identical filenames.
> cd <project-root>
Find the full path 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.
Determine all the commits that affected that file 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. Checkout the file
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.js
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.
If you prefer to see the size of all deleted file
as 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:
Step 1
You search your file full path in history of deleted files git log --diff-filter=D --summary | grep filename
Step 2
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>
Success story sharing
git log -- <path>
will have no output when you are on a branch in which the file never existed. You should always usegit log --all -- <path>
, to make sure you don't miss changes that happened on other branches. The commandgit 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.--all
(thanks Philip) to yourgit log
answer, so that people don't miss changes and files on other branches. It would save forgetful people like me a lot of grief.git checkout <SHA>^ -- <path-to-file>
(note the ^ symbol), 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**/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.)