在 git 中与“ hg cat”或“ svn cat”等价

我希望提取 git 存储库中保存的文件的最新版本的副本,并将其传递到脚本中进行某些处理。对于 svn 或 hg,我只使用“ cat”命令:

打印指定的文件,如同它们在给定的修订版中一样 如没有修订,则使用工作目录的父, 如果没有检查修订或提示。

(这来自 hg 文档中对 hg cat 的描述)

对 git 执行这种操作的等效命令是什么?

15413 次浏览
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.

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)

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

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.

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

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.

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

git show $branch:$filename

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 );
}

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