90

I want to extract a copy of the latest version of a file held in a git repository, and pass it into a script for some processing. With svn or hg, I just use the "cat" command:

Print the specified files as they were at the given revision. If no revision is given, the parent of the working directory is used, or tip if no revision is checked out.

(that's from the description of hg cat in the hg documentation)

What's the equivalent command to do this with git?

Rob Sobers
  • 20,737
  • 24
  • 82
  • 111
Richard Boulton
  • 1,368
  • 1
  • 9
  • 9
  • 4
    My final solution was to first use "git ls-files --full-name " to get the full path relative to the top of the repository, and then "git show HEAD:" – Richard Boulton Jan 15 '10 at 14:13
  • 1
    possible duplicate of [How to retrieve a single file from specific revision in Git?](http://stackoverflow.com/questions/610208/how-to-retrieve-a-single-file-from-specific-revision-in-git) – ks1322 Feb 12 '13 at 16:35
  • 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

9 Answers9

122
git show rev:path/to/file

Where rev is the revision.

See http://git.or.cz/course/svn.html for a comparison of git and svn commands.

Tor Valamo
  • 33,261
  • 11
  • 73
  • 81
11

there is "git cat-file" which you can run like this:

$ git cat-file blob v1.0:path/to/file

where you can replace 'v1.0' with the branch, tag or commit SHA you want and then 'path/to/file' with the relative path in repository. You can also pass '-s' to see the size of the content if you want.

might be closer to the 'cat' commands you are used to, though the previously mentioned 'show' will do much the same thing.

Scott Chacon
  • 2,756
  • 19
  • 14
7

git show is the command you are looking for. From the documentation:

   git show next~10:Documentation/README
          Shows the contents of the file Documentation/README as they were
          current in the 10th last commit of the branch next.
Andrew Aylett
  • 39,182
  • 5
  • 68
  • 95
  • That doesn't seem to work: running "git show next~1:README" produces fatal: ambiguous argument 'next~1:README': unknown revision or path not in the working tree. Use '--' to separate paths from revisions – Richard Boulton Jan 15 '10 at 12:42
  • 1
    Do you have a branch called 'next'? If you want the current branch, use 'HEAD' instead. – Andrew Aylett Jan 15 '10 at 14:53
3

Use git show, as in git show commit_sha_id:path/to/some/file.cs.

John Feminella
  • 303,634
  • 46
  • 339
  • 357
3

Also work with branch names (like HEAD in the 1st p) :

git show $branch:$filename
RzR
  • 3,068
  • 29
  • 26
2

I wrote a git cat shell script, which is up on github

RyanWilcox
  • 13,890
  • 1
  • 36
  • 60
  • 1
    Looking at the code for that was helpful, though I wanted a standalone python script, and didn't need all the features of git cat. Thanks. – Richard Boulton Jan 15 '10 at 14:07
1

There doesn't appear to be a direct substitute. This blog entry details how to do the equivalent by determining the latest commit, then determining the hash for the file in that commit, and then dumping it out.

git log ...
git ls-tree ...
git show -p ...

(the blog entry has typos and uses the above with the command svn)

Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
0

None of the git show suggestions truly satisfy because (try as I might), I can not find a way not to get the metadata cruft from the top of the output. The spirit of cat(1) is just to show the contents. This (below) takes a file name and an optional number. The number is how commits you want to go back. (Commits that changed that file. Commits that do not change the target file are not counted.)

gitcat.pl filename.txt
gitcat.pl -3 filename.txt

shows the contents of filename.txt as of the latest commit of filename.txt and the contents from 3 commits before that.

#!/usr/bin/perl -w

use strict;
use warnings;
use FileHandle;
use Cwd;

# Have I mentioned lately how much I despise git?

(my $prog = $0) =~ s!.*/!!;
my $usage = "Usage: $prog [revisions-ago] filename\n";

die( $usage ) if( ! @ARGV );
my( $revision, $fname ) = @ARGV;

if( ! $fname && -f $revision ) {
    ( $fname, $revision ) = ( $revision, 0 );
}

gitcat( $fname, $revision );

sub gitcat {
    my( $fname, $revision ) = @_;

    my $rev = $revision;
    my $file = FileHandle->new( "git log --format=oneline '$fname' |" );

    # Get the $revisionth line from the log.
    my $line;
    for( 0..$revision ) {
        $line = $file->getline();
    }

    die( "Could not get line $revision from the log for $fname.\n" ) 
        if( ! $line );

    # Get the hash from that.
    my $hash = substr( $line, 0, 40 );
    if( ! $hash =~ m/ ^ ( [0-9a-fA-F]{40} )/x ) {
        die( "The commit hash does not look a hash.\n" );
    }

    # Git needs the path from the root of the repo to the file because it can
    # not work out the path itself.
    my $path = pathhere();
    if( ! $path ) {
        die( "Could not find the git repository.\n" );
    }

    exec( "git cat-file blob $hash:$path/'$fname'" );
}


# Get the path from the git repo to the current dir.
sub pathhere {
    my $cwd = getcwd();
    my @cwd = split( '/', $cwd );
    my @path;

    while( ! -d "$cwd/.git" ) {
        my $path = pop( @cwd );
        unshift( @path, $path );
        if( ! @cwd ) {
            die( "Did not find .git in or above your pwd.\n" );
        }
        $cwd = join( '/', @cwd );
    }
    return join( '/', map { "'$_'"; } @path );
}
neniu
  • 419
  • 3
  • 8
0

For those using bash the following is a useful function:

gcat () { if [ $# -lt 1 ]; then echo "Usage: $FUNCNAME [rev] file"; elif [ $# -lt 2 ]; then git show HEAD:./$*; else git show $1:./$2; fi }

Put it in your .bashrc file (you can use whatever name you like other than gcat.

Example usage:

> gcat
Usage: gcat [rev] file

or

> gcat subdirectory/file.ext

or

> gcat rev subdirectory/file.ext
Matt
  • 4,261
  • 4
  • 39
  • 60