gitgit-restore

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.


Solution

  • 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)


    As noted by Kira in the comments:

    If you are using LFS files, you need to run the output of show through smudge:

    git show master:blends/bigfile.blend | git lfs smudge > blends/bigfile.blend