Equivalente in git di "hg cat" o "svn cat"


84

Voglio estrarre una copia dell'ultima versione di un file contenuto in un repository git e passarlo a uno script per qualche elaborazione. Con svn o hg, utilizzo semplicemente il comando "cat":

Stampa i file specificati come erano alla revisione data. Se non viene fornita alcuna revisione, viene utilizzato il genitore della directory di lavoro o suggerimento se non viene estratta alcuna revisione.

(questo è dalla descrizione di hg cat nella documentazione di hg)

Qual è il comando equivalente per farlo con git?


4
La mia soluzione finale è stata quella di utilizzare prima "git ls-files --full-name <filepath>" per ottenere il percorso completo relativo all'inizio del repository, quindi "git show HEAD: <full filepath>"
Richard Boulton


Risposte:


113
git show rev:path/to/file

Dove rev è la revisione.

Vedi http://git.or.cz/course/svn.html per un confronto tra i comandi git e svn.


1
Questo funziona per me, yay! (Usando HEAD come revisione) Penso di aver bisogno di usare il percorso relativo all'inizio del repository, che è un po 'fastidioso, ma funzionante.
Richard Boulton

@Richard Boulton: questo è meno doloroso se si utilizza il completamento della scheda
ks1322

@pimlottc ha funzionato bene per me senza ./ - potrebbe dipendere dalla versione git
Trejkaz

9

c'è "git cat-file" che puoi eseguire in questo modo:

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

dove puoi sostituire 'v1.0' con il ramo, tag o commit SHA che desideri e poi 'path / to / file' con il percorso relativo nel repository. Puoi anche passare "-s" per vedere la dimensione del contenuto, se lo desideri.

potrebbe essere più vicino ai comandi "cat" a cui sei abituato, anche se "show" menzionato in precedenza farà più o meno la stessa cosa.


6

git showè il comando che stai cercando. Dalla documentazione:

   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.

Questo non sembra funzionare: l'esecuzione di "git show next ~ 1: README" produce fatal: argomento ambiguo 'next ~ 1: README': revisione sconosciuta o percorso non nell'albero di lavoro. Usa "-" per separare i percorsi dalle revisioni
Richard Boulton

1
Hai un ramo chiamato "successivo"? Se vuoi il ramo corrente, usa invece "HEAD".
Andrew Aylett

3

Lavora anche con i nomi dei rami (come HEAD nella prima p):

git show $branch:$filename


2

Ho scritto uno script di shell git cat, che è su github


1
Guardare il codice è stato utile, anche se volevo uno script Python autonomo e non avevo bisogno di tutte le funzionalità di git cat. Grazie.
Richard Boulton

1

Non sembra esserci un sostituto diretto . Questa voce di blog spiega come eseguire l'equivalente determinando l' ultimo commit, quindi determinando l'hash per il file in quel commit e quindi scaricandolo.

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

(la voce del blog ha errori di battitura e utilizza quanto sopra con il comando svn)


Il post sul blog è stato utile, nonostante gli errori di battitura.
Richard Boulton

0

Nessuno dei git showsuggerimenti soddisfa davvero perché (provando come potrei), non riesco a trovare un modo per non ottenere i metadati cruft dalla parte superiore dell'output. Lo spirito del gatto (1) è solo per mostrare il contenuto. Questo (sotto) prende un nome file e un numero opzionale. Il numero è il modo in cui vuoi tornare indietro. (I commit che hanno modificato il file. I commit che non modificano il file di destinazione non vengono conteggiati.)

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

mostra il contenuto di filename.txt a partire dall'ultimo commit di filename.txt e il contenuto di 3 commit precedenti.

#!/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 );
}

0

Per coloro che utilizzano bash, la seguente è una funzione utile:

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

Mettilo nel tuo .bashrcfile (puoi usare qualsiasi nome ti piaccia diverso da gcat.

Utilizzo di esempio:

> gcat
Usage: gcat [rev] file

o

> gcat subdirectory/file.ext

o

> gcat rev subdirectory/file.ext
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.