[git] How to retrieve a single file from a specific revision in Git?

I have a Git repository and I'd like to see how some files looked a few months ago. I found the revision at that date; it's 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8. I need to see what one file looks like, and also save it as a ("new") file.

I managed to see the file using gitk, but it doesn't have an option to save it. I tried with command-line tools, the closest I got was:

git-show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8 my_file.txt

However, this command shows a diff, and not the file contents. I know I can later use something like PAGER=cat and redirect output to a file, but I don't know how to get to the actual file content.

Basically, I'm looking for something like svn cat.

This question is related to git single-file

The answer is


You need to provide the full path to the file:

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8:full/repo/path/to/my_file.txt

Using git show $REV:$FILE as it was already pointed out by others is probably the right answer. I'm posting another answer because when I tried this method, I sometimes got the following error from git:

fatal: path 'link/foo' exists on disk, but not in 'HEAD'

The problem occurs when parts of the path to the file are a symbolic link. In those cases, the git show $REV:$FILE method will not work. Steps to reproduce:

$ git init .
$ mkdir test
$ echo hello > test/foo
$ ln -s test link
$ git add .
$ git commit -m "initial commit"
$ cat link/foo
hello
$ git show HEAD:link/foo
fatal: path 'link/foo' exists on disk, but not in 'HEAD'

The problem is, that utilities like realpath don't help here, because the symlink might not exist in the current commit anymore. I don't know about a good general solution. In my case, I knew that the symlink could only exist in the first component of the path, so I solved the problem by using the git show $REV:$FILE method twice. This works, because when git show $REV:$FILE is used on a symlink, then its target gets printed:

$ git show HEAD:link
test

Whereas with directories, the command will output a header, followed by the directory content:

$ git show HEAD:test
tree HEAD:test

foo

So in my case, I just checked the output of the first call to git show $REV:$FILE and if it was only a single line, then I replaced the first component of my path with the result to resolve the symlink through git.


If you wish to replace/overwrite the content of a file in your current branch with the content of the file from a previous commit or a different branch, you can do so with these commands:

git checkout 08618129e66127921fbfcbc205a06153c92622fe path/to/file.txt

or

git checkout mybranchname path/to/file.txt

You will then have to commit those changes in order for them to be effective in the current branch.


And to nicely dump it into a file (on Windows at least) - Git Bash:

$ echo "`git show 60d8bdfc:src/services/LocationMonitor.java`" >> LM_60d8bdfc.java

The " quotes are needed so it preserves newlines.


git checkout {SHA1} -- filename

this command get the copied file from specific commit.


The easiest way is to write:

git show HASH:file/path/name.ext > some_new_name.ext

where:

  • HASH is the Git revision SHA-1 hash number
  • file/path/name.ext is name of the file you are looking for
  • some_new_name.ext is path and name where the old file should be saved

Example

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8:my_file.txt > my_file.txt.OLD

This will save my_file.txt from revision 27cf8e as a new file with name my_file.txt.OLD

It was tested with Git 2.4.5.

If you want to retrieve deleted file you can use HASH~1 (one commit before specified HASH).

EXAMPLE:

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8~1:deleted_file.txt > deleted_file.txt

This will help you get all deleted files between commits without specifying the path, useful if there are a lot of files deleted.

git diff --name-only --diff-filter=D $commit~1 $commit | xargs git checkout $commit~1

Get the file from a previous commit through checking-out previous commit and copying file.

  • Note which branch you are on: git branch
  • Checkout the previous commit you want: git checkout 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8
  • Copy the file you want to a temporary location
  • Checkout the branch you started from: git checkout theBranchYouNoted
  • Copy in the file you placed in a temporary location
  • Commit your change to git: git commit -m "added file ?? from previous commit"

In Windows, with Git Bash:

  • in your workspace, change dir to the folder where your file lives
  • git show cab485c83b53d56846eb883babaaf4dff2f2cc46:./your_file.ext > old.ext

In addition to all the options listed by other answers, you can use git reset with the Git object (hash, branch, HEAD~x, tag, ...) of interest and the path of your file:

git reset <hash> /path/to/file

In your example:

git reset 27cf8e8 my_file.txt

What this does is that it will revert my_file.txt to its version at the commit 27cf8e8 in the index while leaving it untouched (so in its current version) in the working directory.

From there, things are very easy:

  • you can compare the two versions of your file with git diff --cached my_file.txt
  • you can get rid of the old version of the file with git restore --staged file.txt (or, prior to Git v2.23, git reset file.txt) if you decide that you don't like it
  • you can restore the old version with git commit -m "Restore version of file.txt from 27cf8e8" and git restore file.txt (or, prior to Git v2.23, git checkout -- file.txt)
  • you can add updates from the old to the new version only for some hunks by running git add -p file.txt (then git commit and git restore file.txt).

Lastly, you can even interactively pick and choose which hunk(s) to reset in the very first step if you run:

git reset -p 27cf8e8 my_file.txt

So git reset with a path gives you lots of flexibility to retrieve a specific version of a file to compare with its currently checked-out version and, if you choose to do so, to revert fully or only for some hunks to that version.


Edit: I just realized that I am not answering your question since what you wanted wasn't a diff or an easy way to retrieve part or all of the old version but simply to cat that version.

Of course, you can still do that after resetting the file with:

git show :file.txt

to output to standard output or

git show :file.txt > file_at_27cf8e8.txt

But if this was all you wanted, running git show directly with git show 27cf8e8:file.txt as others suggested is of course much more direct.

I am going to leave this answer though because running git show directly allows you to get that old version instantly, but if you want to do something with it, it isn't nearly as convenient to do so from there as it is if you reset that version in the index.