1048

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.

SherylHohman
  • 16,580
  • 17
  • 88
  • 94
Milan Babuškov
  • 59,775
  • 49
  • 126
  • 179
  • 103
    The key here: `git show` (unhelpfully) uses different syntax with a colon. `git show 2c7cf:my_file.txt` – Steve Bennett May 30 '12 at 03:29
  • 5
    To further clarify, the above command is asking git to show two separate objects, a revision and a file. The accepted answer below, that uses a colon between the two items is asking for a specific file at a specific revision. – jhclark Jul 09 '12 at 15:12
  • 2
    On *nix you don't need PAGER, just shell output redirection with `>` – Konstantin Pelepelin Nov 20 '13 at 09:21
  • 1
    possible duplicate of [Is there a quick git command to see an old version of a file?](http://stackoverflow.com/questions/338436/is-there-a-quick-git-command-to-see-an-old-version-of-a-file) – Ciro Santilli OurBigBook.com Sep 10 '14 at 06:34
  • Checat has an important comment, for those who want the content exported to some file. You need something like this: git show {sha}:my_file.txt > old_my_file.txt – ormurin Sep 18 '14 at 09:30
  • Based on the question title, [this seems like the right answer](http://stackoverflow.com/a/4218823/2343633). Based on the rest of the question content, [this answer](http://stackoverflow.com/a/610315/2343633) seems most useful. Took me a few minutes to realize that the two seem pretty different, hopefully this comment saves time for others. – rbatt Dec 05 '15 at 02:30
  • @rbatt No quite anymore: that would be (today, 2019) `git restore`, not `git checkout`. See [my edited answer](https://stackoverflow.com/posts/610315/revisions). – VonC Nov 06 '19 at 16:00
  • If you're looking for the answer of retrieving all files under a sub-directory in Git history revision, read this: https://stackoverflow.com/a/62342847/94148 – aleung Jun 12 '20 at 11:30

12 Answers12

981

Using git show

To complete your own answer, the syntax is indeed

git show object
git show $REV:$FILE
git show somebranch:from/the/root/myfile.txt
git show HEAD^^^:test/test.py

The command takes the usual style of revision, meaning you can use any of the following:

  1. branch name (as suggested by ash)
  2. HEAD + x number of ^ characters
  3. The SHA1 hash of a given revision
  4. The first few (maybe 5) characters of a given SHA1 hash

Tip It's important to remember that when using "git show", always specify a path from the root of the repository, not your current directory position.

(Although Mike Morearty mentions that, at least with git 1.7.5.4, you can specify a relative path by putting "./" at the beginning of the path. For example:

git show HEAD^^:./test.py

)

Using git restore

With Git 2.23+ (August 2019), you can also use git restore which replaces the confusing git checkout command

git restore -s <SHA1>     -- afile
git restore -s somebranch -- afile

That would restore on the working tree only the file as present in the "source" (-s) commit SHA1 or branch somebranch.
To restore also the index:

git restore -s <SHA1> -SW -- afile

(-SW: short for --staged --worktree)


As noted in the comments by starwarswii

It lets you pipe the contents into a file, which is great if you want to just quickly compare files from a commit.

E.g. you can do:

git show 1234:path/to/file.txt > new.txt 
git show 1234~:path/to/file.txt > old.txt

then compare them.


Using low-level git plumbing commands

Before git1.5.x, this was done with some plumbing:

git ls-tree <rev>
show a list of one or more 'blob' objects within a commit

git cat-file blob <file-SHA1>
cat a file as it has been committed within a specific revision (similar to svn cat). use git ls-tree to retrieve the value of a given file-sha1

git cat-file -p $(git-ls-tree $REV $file | cut -d " " -f 3 | cut -f 1)::

git-ls-tree lists the object ID for $file in revision $REV, this is cut out of the output and used as an argument to git-cat-file, which should really be called git-cat-object, and simply dumps that object to stdout.


Note: since Git 2.11 (Q4 2016), you can apply a content filter to the git cat-file output.

See commit 3214594, commit 7bcf341 (09 Sep 2016), commit 7bcf341 (09 Sep 2016), and commit b9e62f6, commit 16dcc29 (24 Aug 2016) by Johannes Schindelin (dscho).
(Merged by Junio C Hamano -- gitster -- in commit 7889ed2, 21 Sep 2016)

git config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <"
git cat-file --textconv --batch

Note: "git cat-file --textconv" started segfaulting recently (2017), which has been corrected in Git 2.15 (Q4 2017)

See commit cc0ea7c (21 Sep 2017) by Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit bfbc2fc, 28 Sep 2017)

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • How do you save what's returned by git show? I want to essentially do a "save as" on it instead of overwriting my uncommitted local file. – Oscar Jun 26 '12 at 09:13
  • 8
    @Oscar since `git show` essentially dump the content on the `stdout` (standard output), you could simply redirect that output to any file you want (http://tldp.org/LDP/abs/html/io-redirection.html). – VonC Jun 26 '12 at 10:47
  • Isn't there a way without redirection? – utapyngo Feb 01 '13 at 09:53
  • @utapyngo not that I know of. – VonC Feb 01 '13 at 09:55
  • 10
    `git checkout [branch | revision] filepath` is the right command – Gaui Jun 06 '13 at 17:58
  • 21
    @Gaui but `git checkout` would **override** your file by another version, as opposed to `git show`, which allows you to save it under a different name, in order for you to get and see *both* (the current version and the old version). It is unclear from the question if the OP wants to *replace* its current version by an old one. – VonC Jun 06 '13 at 19:38
  • 13
    I would like to note that `^^^` can also be written more generally as `~~~` or, better, `~3`. Using tildes also has the advantage of not triggering the file name matching of some shells (zsh, for instance). – Eric O. Lebigot Jun 19 '13 at 12:00
  • 2
    I don't have a sufficiently old git to check: does pre-1.5.x `git rev-parse` handle `rev:path` syntax? (In a more recent git you can `git cat-file -p $REV:path`. However, `git show` works for directory paths as well, so it's not just shorter, it's usually closer to what one wants.) – torek Jan 11 '14 at 22:50
  • git show produced the diff-like output the question specifically didn't want. Is there a cleaner way than git checkout which overwrites the file and changes the version the local repository points at? – dolphus333 Jul 02 '17 at 13:27
  • @dolphus333 Did you try `git show`? (https://git-scm.com/docs/git-show): It does show the file content, not a "diff-like output". – VonC Jul 02 '17 at 15:31
  • 1
    @dolphus333 That is, when you use git show with a filename, it shows the full content of the file. When used with a commit alone, it shows a diff. – VonC Jul 03 '17 at 04:26
  • 1
    I used git show hash filename(relative path), git show hash filename(full path), then just to be sure I cd'd to the directory the file was in and used git show hash filename(in the directory). All three version returned the same result: diff output, not the file. – dolphus333 Jul 03 '17 at 16:03
  • @dolphus333 Interesting. Please ask a separate question with the exact Git commands typed and Git version used: that will allow other users to see what is going on. – VonC Jul 03 '17 at 21:25
  • @dolphus333 I hope you have already solved this, but for the reference, `git show FileName` produces a diff-like output, but `git show HEAD:FileName` should give the committed file contents. – jmster Oct 25 '17 at 13:27
  • 1
    This is incredibly complicated compared to `git checkout [branch | revision] filepath`. – Andrew Koster Nov 13 '19 at 22:11
  • @AndrewKoster No, after testing git restore, I find it more intuitive than git checkout, which deals with files and branches. Here, restore is only for restoring files. – VonC Nov 13 '19 at 22:12
  • As a super specific example, in Password Store, you can view a previous version of the password you just edited by running `pass git show HEAD~1:yourpasswordname.gpg` – Ale Jul 07 '20 at 11:13
  • Why actually bother with ` -- ` within the examples of `git restore`? – bloody Nov 27 '20 at 12:48
  • 1
    @bloody to separate parameter from path arguments. A good habit to have. I documented it 11 years ago: https://stackoverflow.com/a/1192194/6309 – VonC Nov 27 '20 at 13:36
  • What is restoring the index for a file? – Timo Jan 06 '21 at 18:50
  • 2
    @Timo `git restore -s -SW -- afile` would restore both the working tree, and the index, which means it is like restoring the file, and do a `git add -- file` to add what you have just restored to the index, ready to be committed. – VonC Jan 06 '21 at 18:53
  • 1
    +1 to `git show`. It lets you pipe the contents into a file, which is great if you want to just quickly compare files from a commit. e.g. you can do: `git show 1234:path/to/file.txt > new.txt` `git show 1234~:path/to/file.txt > old.txt`, then compare them. – starwarswii Jul 01 '21 at 15:07
  • @Starwarswii Good point, thank you. I have included your comment in the answer for more visibility. – VonC Jul 01 '21 at 22:13
  • Very helpful. I stumbled over this minor aspect: When using git on windows, make sure to use the forward slash `/` (U+002F) not the backslash ``\`` (U+005C) – Eric Allen Jul 18 '22 at 19:57
555

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.

c-a
  • 5,928
  • 1
  • 17
  • 12
  • 5
    simplest solution and this is what git-checkout is designed for - specifying the pathname means only the matching file is checked out. From git-checkout man page: git checkout master~2 Makefile – RichVel Mar 28 '13 at 14:37
  • 1
    Then, how do you go back to the previous state before you run this command? – Flint Oct 09 '13 at 00:50
  • @Flint if you are coming from the HEAD state it would be as simple as git checkout HEAD -- [full path]. – Tiago Espinha Nov 07 '13 at 17:29
  • 81
    Note that this overwrites the existing file in that path, whereas the `git show SHA1:PATH` solution only prints to stdout. – Flimm Jan 22 '14 at 15:24
  • Nice! I wouldn't have been able to figure this out by looking at `git help checkout`. I had to checkout a subdirectory as of a certain date, and using this approach, I could get this syntax working: `git checkout @{YYYY-MM-DD} sub-dir` – haridsv Jan 25 '18 at 15:32
  • Silly mistake of mine, but if you use windows (sourcetree/mingw) note the path separator is `/` and not `\ ` – BurnsBA Aug 23 '18 at 14:38
169

You need to provide the full path to the file:

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8:full/repo/path/to/my_file.txt
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Milan Babuškov
  • 59,775
  • 49
  • 126
  • 179
  • 10
    doesn't have to be full path. Path from git root directory (those who came in `git show --name-only` is enough too – Mohsen Jun 26 '13 at 20:35
  • 9
    Erm, full path from repository root. Take a better look at example I've given. There is no leading slash before "full". – Milan Babuškov Jun 26 '13 at 22:28
  • 8
    FYI, if you are in a subdir, you can use ./filename.ext successfully too. – Traveler Nov 28 '14 at 17:12
  • I think the point is, if you're in `full/repo/path/to` and you try: `git show 27cf8e84:my_file.txt`, you will be rewarded with a message like: _fatal: Path 'full/repo/path/to/my_file.txt' exists, but not 'my_file.txt'._ _Did you mean '27cf8e84:full/repo/path/to/my_file.txt' aka '27cf8e84:./my_file.txt'?_ It's like, Git could have helped directly, but chose to be pedantic here. – Ed Randall Feb 16 '19 at 16:54
136

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
Community
  • 1
  • 1
jmarceli
  • 19,102
  • 6
  • 69
  • 67
  • 2
    Additional Info: You can get the HASH e.g. with git log – xotix Aug 30 '17 at 14:21
  • @xotix Thanks. I got all the HASH history for a particular file using `git log file/path/name.ext` – Sriram Kannan Jun 04 '19 at 12:37
  • This is the right syntax. Important moments: 1. `:` makes Git output the file at revision (while a space in the same place would show diff). 2. The `file/path/name.ext` path has to be relative to the repository root. – Melebius Mar 08 '23 at 07:59
16
git checkout {SHA1} -- filename

this command get the copied file from specific commit.

jsina
  • 4,433
  • 1
  • 30
  • 28
  • +1, I honestly have no idea - the top answer lists 5 different, vastly more convoluted approaches than a simple `checkout` command... – Tomáš M. Dec 01 '21 at 19:29
13

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
Alessandro Jacopson
  • 18,047
  • 15
  • 98
  • 153
9

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.

prosoitos
  • 6,679
  • 5
  • 27
  • 41
  • What if the file is on another branch? – Chuck Mar 12 '21 at 16:48
  • So I want to retrieve a specific version of a specific file on a specific other branch. is this possible? – Chuck Mar 12 '21 at 16:48
  • 1
    Yes. Specific version and specific branch: you just need to find the corresponding hash first with, for instance `git log --graph --oneline --all`. Specific file: give the file path. – prosoitos Mar 12 '21 at 17:41
  • 1
    Remember that this will revert the file to the version corresponding to the hash *in the index*. Not in the working tree. If you want to restore that version in the working tree, you then need to run `git commit` and `git restore `, as I mention in the answer. – prosoitos Mar 12 '21 at 18:22
8

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361
  • Nice one. +1. Good addition on the `git show` syntax I mention above. – VonC Jan 01 '14 at 19:59
  • 26
    I really don't understand why you would use echo, with or without the quotes. And I don't understand why you would want the append form of output redirection. Wouldn't it be better to simply write: git show 60d8bdfc:src/services/LocationMonitor.java > LM_60d8bdfc.java If for some reason you actually wanted to force dos-style line endings, you could pipe it through unix2dos. But I've never found it the least bit useful to retain dos line-endings on Windows, as any text tools other than notepad that I've used on Windows handle unix-style lines just fine. – sootsnoot Jun 12 '14 at 16:29
  • 4
    git show 60d8bdfc:src/services/LocationMonitor.java >> LM_60d8bdfc.java worked for me. – Mike6679 Jan 13 '15 at 14:27
  • @Mike: are you on windows ? – Mr_and_Mrs_D Jan 14 '15 at 00:14
  • `git show 60d8bdfc:./src/services/LocationMonitor.java > LM_60d8bdfc.java` works for me in Ubuntu bash. Note the `./` after `:` – phuclv Oct 25 '16 at 06:23
  • 2
    do not use double quotes because if your file characters which look like a shell variable, i.e. $LANG, it will be replaced. @LưuVĩnhPhúc is saver. also do not use >> It will append the file if it existed and might to lead to errors – theguy Dec 01 '17 at 17:02
  • Based on the comments above, it looks like ```$ echo '`git show 60d8bdfc:src/services/LocationMonitor.java`' > LM_60d8bdfc.java``` is what the command should look like, to preserve newline format on Windows. – SherylHohman Mar 18 '20 at 06:24
  • @SherylHohman Except now it won't actually run `git show` (if I understand correctly). Git Bash is just plain old bash underneath, so `git show 60d8bdfc:src/services/LocationMonitor.java > LM_60d8bdfc.java` should work just fine. – Ale Jul 07 '20 at 11:18
  • @Ale, Hmm.. I am not sure why I concluded that - I failed to cite my sources correctly. I do not know if any comments have been deleted, or not. I vaguely recall seeing additional info, perhaps from another post? You may be correct. However, there are some caveats to "Git Bash is just plain old bash underneath". Not all bash commands are available in git bash. Also, IIRC, I think on windows an extra slash may be sometimes necessary, or single quotes to accommodate spaces in dir names. I have also run into a difference between using single vs double quotes. Unless that's only for config files? – SherylHohman Jul 07 '20 at 13:49
  • @SherylHohman Yeah, there are some differences in the environment but the shell (and so the syntax and features like output redirect) should work in the same way as on *nix systems ;-) – Ale Jul 09 '20 at 12:51
5

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
Adrian Gunawan
  • 13,979
  • 11
  • 40
  • 41
2

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.

josch
  • 6,716
  • 3
  • 41
  • 49
0

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"
Marvin Pinto
  • 30,138
  • 7
  • 37
  • 54
  • I am not sure why this answer got so many downvotes. It certainly isn't pretty and it clearly isn't the best way to do this. But it does get there. – prosoitos Sep 13 '20 at 22:49
-2

If you prefer to use GUI to do this, the following will work if you know when approximately the file got changed.

On Visual Studio with Git Extension installed:

  1. Git -> View Branch History
  2. Right click on the commit where the file got changed, then choose View Commit Details
  3. On list of changes on the right, click on the file of interest. You'll see side by side comparison of full file content before and after the change. Just select all and copy the file content and paste to anywhere you want.
bobt
  • 411
  • 3
  • 8