Equivalent in git of "hg cat" or "svn cat"
GitVersion ControlGit Problem Overview
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?
Git Solutions
Solution 1 - Git
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.
Solution 2 - Git
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.
Solution 3 - Git
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.
Solution 4 - Git
Also work with branch names (like HEAD in the 1st p) :
git show $branch:$filename
Solution 5 - Git
Use git show
, as in git show commit_sha_id:path/to/some/file.cs
.
Solution 6 - Git
I wrote a git cat shell script, which is up on github
Solution 7 - Git
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
)
Solution 8 - Git
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 );
}
Solution 9 - Git
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