Equivalente en git de "hg cat" o "svn cat"


84

Quiero extraer una copia de la última versión de un archivo almacenado en un repositorio de git y pasarlo a un script para su procesamiento. Con svn o hg, solo uso el comando "cat":

Imprima los archivos especificados como estaban en la revisión dada. Si no se proporciona ninguna revisión, se utiliza el padre del directorio de trabajo, o tip si no se extrae ninguna revisión.

(eso es de la descripción de hg cat en la documentación de hg)

¿Cuál es el comando equivalente para hacer esto con git?


4
Mi solución final fue usar primero "git ls-files --full-name <filepath>" para obtener la ruta completa relativa a la parte superior del repositorio, y luego "git show HEAD: <full filepath>"
Richard Boulton


Respuestas:


113
git show rev:path/to/file

Donde rev es la revisión.

Consulte http://git.or.cz/course/svn.html para ver una comparación de los comandos git y svn.


1
¡Esto funciona para mí, sí! (Usando HEAD como revisión) Creo que necesito usar la ruta relativa a la parte superior del repositorio, lo cual es un poco molesto, pero viable.
Richard Boulton

@Richard Boulton: esto es menos doloroso si se usa la completición de pestañas
ks1322

@pimlottc funcionó bien para mí sin el ./ - podría depender de la versión de git
Trejkaz

9

hay "git cat-file" que puede ejecutar así:

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

donde puede reemplazar 'v1.0' con la rama, etiqueta o confirmar SHA que desee y luego 'ruta / a / archivo' con la ruta relativa en el repositorio. También puede pasar '-s' para ver el tamaño del contenido si lo desea.

podría estar más cerca de los comandos 'cat' a los que está acostumbrado, aunque el 'show' mencionado anteriormente hará lo mismo.


6

git showes el comando que busca. De la documentación:

   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.

Eso no parece funcionar: ejecutar "git show next ~ 1: README" produce fatal: argumento ambiguo 'next ~ 1: README': revisión desconocida o ruta que no está en el árbol de trabajo. Use '-' para separar las rutas de las revisiones
Richard Boulton

1
¿Tiene una rama llamada 'siguiente'? Si desea la rama actual, use 'HEAD' en su lugar.
Andrew Aylett

3

También trabaje con nombres de ramas (como HEAD en la primera p):

git show $branch:$filename


2

Escribí un script de shell de git cat, que está en github


1
Ver el código para eso fue útil, aunque quería un script de Python independiente y no necesitaba todas las características de git cat. Gracias.
Richard Boulton

1

No parece haber un sustituto directo . Esta entrada de blog detalla cómo hacer el equivalente determinando la última confirmación, luego determinando el hash para el archivo en esa confirmación y luego volcándolo.

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

(la entrada del blog tiene errores tipográficos y usa lo anterior con el comando svn)


La entrada del blog fue útil, a pesar de los errores tipográficos.
Richard Boulton

0

Ninguna de las git showsugerencias satisface realmente porque (por mucho que lo intente), no puedo encontrar una manera de no obtener los metadatos cruft de la parte superior de la salida. El espíritu del gato (1) es solo para mostrar el contenido. Esto (a continuación) toma un nombre de archivo y un número opcional. El número es la cantidad de confirmaciones a las que desea volver. (Las confirmaciones que cambiaron ese archivo. Las confirmaciones que no cambian el archivo de destino no se cuentan).

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

muestra el contenido de filename.txt a partir de la última confirmación de filename.txt y el contenido de 3 confirmaciones anteriores.

#!/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

Para aquellos que usan bash, la siguiente es una función útil:

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

Ponlo en tu .bashrcarchivo (puedes usar cualquier nombre que no sea gcat.

Uso de ejemplo:

> gcat
Usage: gcat [rev] file

o

> gcat subdirectory/file.ext

o

> gcat rev subdirectory/file.ext
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.