Exportar solo archivos modificados y agregados con estructura de carpetas en Git

82

Me gustaría obtener una lista de archivos modificados y agregados en una confirmación específica para poder exportarlos y generar un paquete con la estructura de archivos.

La idea es obtener el paquete y extraerlo en el servidor. Por muchas razones, no puedo crear un enlace para extraer el repositorio automáticamente y la forma más fácil que tengo de mantener el servidor actualizado es generando este paquete.

Michael Kuhinica
fuente

Respuestas:

106
git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id
  1. git diff-tree -r $commit_id:

    Tome una diferencia de la confirmación dada a su padre (s) (incluidos todos los subdirectorios, no solo el directorio superior).

  2. --no-commit-id --name-only:

    No genere la confirmación SHA1. Genere solo los nombres de los archivos afectados en lugar de una diferencia completa.

  3. --diff-filter=ACMRT:

    Solo muestre los archivos agregados, copiados, modificados, renombrados o cuyo tipo haya cambiado (por ejemplo, archivo → enlace simbólico) en esta confirmación. Esto deja fuera los archivos eliminados.


ACTUALIZAR A PARTIR DEL COMENTARIO:
Basado en el contexto de la pregunta y los comentarios a continuación, con el siguiente comando, puede obtener los ACMRTarchivos como un .tararchivo con su estructura de carpetas.

git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | tar -czf file.tgz -T -
Aristóteles Pagaltzis
fuente
¡Ahora solo tengo que encontrar una manera de pegar el resultado de este comando con tar! ¡Gracias!
Michael Kuhinica
8
@Taverneiro Puede canalizarlo al comando tar dado en esta respuesta , por ejemplo. para un archivo tgz:git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | tar -czf file.tgz -T -
Matthieu Sadouni
67
git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | xargs tar -rf mytarfile.tar

Solo para redondear esto, aquí está el comando canalizado a tar. Esto exporta los archivos a un archivo tar.

James Ehly
fuente
1
¿Se puede hacer esto para múltiples confirmaciones en un archivo tar?
evolutionxbox
4
Al intentar agregar varias confirmaciones al mismo archivo tar, a veces tar crea filename.ext nuevamente con otro nombre (filename1.ext). Me doy cuenta de que si uso zip, puedo agregar y sobrescribir el archivo si existe en el zip ... Solo necesita reemplazar los xargs con xargs zip -r0 myzipfile.zip $ 1
Ricardo Martins
¿Se necesitan xargs aquí? Sé que es para cosas como rm, pero pensé que tar podría manejar tantos archivos como quieras.
Erdal G.
1
Tenga cuidado con esta respuesta, esto toma archivos diff pero como la versión actual de la rama actual
mems
1
Al ejecutar este comando en la máquina esclava de Windows, recibo el siguiente error "'xargs' no se reconoce como un comando interno o externo, programa operable o archivo por lotes".
Navin prasad
40

Aquí hay un comando de una línea que funciona en Windows 7. Ejecútelo desde la carpeta de nivel superior de su repositorio.

para / f "usebackq tokens = *"% A en (`git diff-tree -r --no-commit-id --name-only --diff-filter = ACMRT HEAD ~ 1 HEAD`) do echo FA | xcopy "% ~ fA" "C: \ git_changed_files \% A"

  • echo FA responde la inevitable pregunta xcopy sobre si está copiando un archivo o un directorio (archivo), y la posible pregunta sobre cómo sobrescribir un archivo (sobrescribir todo)
  • usebackq nos permite usar la salida de nuestro comando git como entrada a nuestra cláusula do
  • HEAD ~ 1 HEAD obtiene todas las diferencias entre la confirmación anterior y la HEAD actual
  • % ~ fA transforma la salida de git en rutas totalmente calificadas (necesarias para cambiar las barras diagonales hacia atrás)
  • C: \ git_changed_files \ es donde encontrará todos los archivos que son diferentes
MM.
fuente
1
Cuando vi ese lío de símbolos, no esperaba que funcionara sin ajustes extensos ... pero funcionó en el primer intento, gracias.
Nathan Fig
3
un pequeño aviso para todos los tipos que (como yo) no están muy familiarizados con los archivos por lotes de Windows: si desea utilizar el comando anterior en un archivo por lotes, deberá reemplazar% A con %% A dentro del bucle for
buddybubble
1
¿Qué? LOL. Simplemente use el archivo git - git diff combo (respuesta de Nicolas). Mucho más fácil y directo. (Honestamente, no sé qué está sucediendo aquí)
ADTC
1
Advertencia: esto copiará archivos de su copia de trabajo, que en realidad puede no coincidir con lo que está en HEAD (o cualquier hash de confirmación con el que lo reemplace)
Simon East
1
Gran material, pero funciona solo con CMD, no con Powershell, -v ° v-
Rodion Bykov
33

si su hash de confirmación es, por ejemplo, a9359f9, este comando:

git archive -o patch.zip a9359f9 $(git diff --name-only a9359f9^..a9359f9)

extraerá los archivos modificados en la confirmación y los colocará en patch.zip manteniendo intacta la estructura del directorio del proyecto.

un poco detallado, el hash de confirmación se menciona tres veces, pero parece funcionar para mí.

lo tengo aquí: http://tosbourn.com/2011/05/git/using-git-to-create-an-archive-of-changed-files/

Nicolas Dermine
fuente
Creo que puedo usar functionen PowerShell para reducir el comando a solo un nombre de función y usar $argspara pasar el hash de confirmación solo una vez. En * nix, probablemente pueda usar shell-scripting para lograr el mismo efecto.
ADTC
1
Hombre ... te tengo una cerveza :)
deanpodgornik
3
@BenSewards para el rango de confirmaciones entre commit1 y commit2 (incluido el compromiso posterior, exclusivo del compromiso anterior; el orden no importa) simplemente hazlo git archive -o patch.zip HEAD /**or a commit**/ $(git diff --name-only commit1 commit2). La confirmación pasada al archivecomando puede ser cualquier confirmación arbitraria (lo más probable es que sea HEAD o la confirmación posterior) , y los archivos se extraen como si estuvieran extraídos en esa etapa de confirmación. El commit1 y el commit2 pasados diffsolo se utilizan para generar la lista de archivos para extraer; no afectan la versión de los archivos extraídos.
ADTC
1
Consejo adicional: si desea combinar los archivos modificados de múltiples rangos de confirmaciones en un archivo, simplemente repita el diffcomando con un punto y coma:git archive -o patch.zip HEAD /**or a commit**/ $(git diff --name-only commit1A commit1B; git diff --name-only commit2A commit2B; git diff --name-only commit3A commit3B)
ADTC
4
Debido a los espacios en los nombres de ruta que tenía que utilizar un tubo a xargs manejo de la NULgit diff -z --name-only commit1 commit2 | xargs -0 git archive -o patch.zip HEAD
Boggin
25

Puede exportar diff usando Tortoise Git a MS Windows:

Hago clic derecho y selecciono TortoiseGit > Mostrar registro y los mensajes de registro se abrirán.

Seleccione dos revisiones y compárelas. La diferencia entre estará abierta.

Seleccione los archivos y Exportar selección a ... ¡ a una carpeta!

ingrese la descripción de la imagen aquí

Wendel
fuente
1
Seguro. Muchas respuestas a esto tenían comandos solo para Linux, y muchas no capturaban los cambios funcionales, solo los cambios que se habían confirmado. ¡Me alegro de que esto haya sido publicado!
dythim
8

Necesitaba actualizar mi servidor de prueba y agregar archivos que cambiaron desde la versión 2.1.
Para mí funcionó una solución similar a la que publicó James Ehly, pero en mi caso quería exportar al paquete de archivo de la diferencia entre dos etiquetas más antiguas: tag_ver_2.1 y tag_ver_2.2, no la única confirmación.

Por ejemplo:

tag_ver_2.1 = 1f72b38ad
tag_ver_2.2 = c1a546782

aquí hay un ejemplo modificado:

git diff-tree -r --no-commit-id --name-only c1a546782 1f72b38ad | xargs tar -rf test.tar
pietr
fuente
Acabo de detectar un problema EXTRAÑO: si pruebo el código anterior, funciona como un encanto. Pero cambio tar -rf test.tar por tar -zcf test.tar.gz, luego faltan varios archivos en el archivo resultante. ¿Qué puede estar causando esta diferencia en el resultado?
Alberto Alexander Rodríguez
Al ejecutar este comando en la máquina esclava de Windows, recibo el siguiente error "'xargs' no se reconoce como un comando interno o externo, programa operable o archivo por lotes".
Navin prasad
4

Los siguientes comandos funcionaron para mí.

Si desea que la diferencia de los archivos cambie con la última confirmación:

git archive -o update.zip HEAD $(git diff --name-only HEAD HEAD^)

o si quieres diferencia entre dos confirmaciones específicas:

git archive -o update.zip sha1 $(git diff --name-only sha1 sha2)

o si tiene archivos no confirmados, recuerde que la forma de git es confirmar todo, las ramas son baratas:

git stash
git checkout -b feature/new-feature
git stash apply
git add --all
git commit -m 'commit message here'
git archive -o update.zip HEAD $(git diff --name-only HEAD HEAD^)
Tripatía Ashutosh
fuente
2

He creado un script php para exportar archivos modificados en Windows. Si tiene un servidor de desarrollo localhost con php configurado, puede ejecutarlo fácilmente. Recordará su último repositorio y exportará siempre a la misma carpeta. La carpeta de exportación siempre se vacía antes de exportar. También verá los archivos eliminados en rojo para que sepa qué eliminar en el servidor.

Estos son solo dos archivos, así que los publicaré aquí. Supongamos que sus repositorios están ubicados en c: / www en sus propias carpetas y que http: // localhost también apunta a c: / www y está habilitado para php. Pongamos estos 2 archivos en c: / www / git-export -

index.php:

<?php
/* create directory if doesn't exist */
function createDir($dirName, $perm = 0777) {
    $dirs = explode('/', $dirName);
    $dir='';
    foreach ($dirs as $part) {
        $dir.=$part.'/';
        if (!is_dir($dir) && strlen($dir)>0) {
            mkdir($dir, $perm);
        }
    }
}

/* deletes dir recursevely, be careful! */
function deleteDirRecursive($f) {

    if (strpos($f, "c:/www/export" . "/") !== 0) {
        exit("deleteDirRecursive() protection disabled deleting of tree: $f  - please edit the path check in source php file!");
    }

    if (is_dir($f)) {
        foreach(scandir($f) as $item) {
            if ($item == '.' || $item == '..') {
                continue;
            }

            deleteDirRecursive($f . "/" . $item);
        }    
        rmdir($f);

    } elseif (is_file($f)) {
        unlink($f);
    }
}

$lastRepoDirFile = "last_repo_dir.txt";
$repo = isset($_POST['repo']) ? $_POST['repo'] : null;


if (!$repo && is_file($lastRepoDirFile)) {
    $repo = file_get_contents($lastRepoDirFile);
}

$range = isset($_POST['range']) ? $_POST['range'] : "HEAD~1 HEAD";


$ini = parse_ini_file("git-export.ini");

$exportDir = $ini['export_dir'];
?>

<html>
<head>
<title>Git export changed files</title>
</head>

<body>
<form action="." method="post">
    repository: <?=$ini['base_repo_dir'] ?>/<input type="text" name="repo" value="<?=htmlspecialchars($repo) ?>" size="25"><br/><br/>

    range: <input type="text" name="range" value="<?=htmlspecialchars($range) ?>" size="100"><br/><br/>

    target: <strong><?=$exportDir ?></strong><br/><br/>

    <input type="submit" value="EXPORT!">
</form>

<br/>


<?php
if (!empty($_POST)) {

    /* ************************************************************** */
    file_put_contents($lastRepoDirFile, $repo); 

    $repoDir = $ini['base_repo_dir'] ."/$repo";
    $repoDir = rtrim($repoDir, '/\\');

    echo "<hr/>source repository: <strong>$repoDir</strong><br/>";
    echo "exporting to: <strong>$exportDir</strong><br/><br/>\n";


    createDir($exportDir);

    // empty export dir
    foreach (scandir($exportDir) as $file) {
        if ($file != '..' && $file != '.') {
            deleteDirRecursive("$exportDir/$file");
        }
    }

    // execute git diff
    $cmd = "git --git-dir=$repoDir/.git diff $range --name-only";

    exec("$cmd 2>&1", $output, $err);

    if ($err) {
        echo "Command error: <br/>";
        echo implode("<br/>", array_map('htmlspecialchars', $output));
        exit;
    }


    // $output contains a list of filenames with paths of changed files
    foreach ($output as $file) {

        $source = "$repoDir/$file";

        if (is_file($source)) {
            if (strpos($file, '/')) {
                createDir("$exportDir/" .dirname($file));
            }

            copy($source, "$exportDir/$file");
            echo "$file<br/>\n";

        } else {
            // deleted file
            echo "<span style='color: red'>$file</span><br/>\n";
        }
    }
}
?>

</body>
</html>

git-export.ini:

; path to all your git repositories for convenience - less typing
base_repo_dir = c:/www

; if you change it you have to also change it in the php script
; in deleteDirRecursive() function - this is for security
export_dir = c:/www/export

Y ahora cargue localhost / git-export / en un navegador. El script está configurado para exportar siempre a c: / www / export: cambie todas las rutas para que se adapten a su entorno o modifique el script para satisfacer sus necesidades.

Esto funcionará si tiene Git instalado para que el comando git esté en su RUTA; esto se puede configurar cuando ejecuta el instalador de Windows Git.

Jugo de limon
fuente
este código es genial, pero quiero exportar archivos y directorios con submódulos, su código solo funcionó con el repositorio principal y si los submódulos cambiaron su código, ¡diga que se eliminó la carpeta del submódulo!
morteza khadem
1

Para exportar archivos modificados que comienzan con una fecha:

  diff --stat @{2016-11-01} --diff-filter=ACRMRT --name-only | xargs tar -cf 11.tar

Atajo (use alias)

  git exportmdf 2016-11-01 11.tar

Alias ​​en .gitconfig

  [alias]
  exportmdf = "!f() { \
    git diff --stat @{$1} --diff-filter=ACRMRT --name-only | xargs tar -cf $2; \
  }; f"
catalinp
fuente
0

Aquí hay un pequeño script bash (Unix) que escribí que copiará archivos para un hash de confirmación dado con la estructura de carpetas:

ARRAY=($(git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $1))
PWD=$(pwd)

if [ -d "$2" ]; then

    for i in "${ARRAY[@]}"
    do
        : 
        cp --parents "$PWD/$i" $2
    done
else
    echo "Chosen destination folder does not exist."
fi

Cree un archivo llamado '~ / Scripts / copy-commit.sh' y luego dele privilegios de ejecución:

chmod a+x ~/Scripts/copy-commit.sh

Luego, desde la raíz del repositorio de git:

~/Scripts/copy-commit.sh COMMIT_KEY ~/Existing/Destination/Folder/
Patrick.SE
fuente
0

También enfrenté un problema similar antes. Escribí un sencillo script de Shell.

$git log --reverse commit_HashX^..commit_HashY --pretty=format:'%h'

El comando anterior mostrará Commit Hash (Revisión) de commit_HashX a commit_HashY en orden inverso.

 456d517 (second_hash)
 9362d03
 5362d03
 226x47a
 478bf6b (six_hash)

Ahora el script de Shell principal usando el comando anterior.

commitHashList=$(git log --reverse $1^..$2 --pretty=format:'%h')
for hash in $commitHashList
do
    echo "$hash"
    git archive -o \Path_Where_you_want_store\ChangesMade.zip $hash
done

Agregue este código a export_changes.sh. Ahora pase el archivo y confirme los hashes al script.

Asegúrese de que el commit_hash inicial debe ser el primer argumento y luego el último commit_hash hasta el que desea exportar los cambios.

Ejemplo:

$ sh export_changes.sh hashX hashY

Coloque este script en el directorio local de git o establezca la ruta del directorio local de git en el script. Espero eso ayude..!

Raj Hawaldar
fuente
0

Esto me funciona tanto para Unix como para Windows:

git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT __1__.. | xargs cp --parents -t __2__

Hay dos marcadores de posición en el comando. Debe reemplazarlos para su propósito:

  • __1__ : reemplace con la identificación de confirmación de la confirmación justo antes de todas las confirmaciones que desea exportar (por ejemplo, 997cc7b6 - no olvide mantener ese punto doble después de la identificación de confirmación - esto significa "involucrar todas las confirmaciones más nuevas que esta confirmación")

  • __2__ : reemplace con la ruta existente donde desea exportar sus archivos (por ejemplo, ../export_path/)

Como resultado, obtendrá sus archivos en una estructura de carpetas (sin zips / tars ...), ya que alguien podría estar acostumbrado a usar exportaciones de svn de tortuga en repositorios de svn.

Esto es, por ejemplo, bastante útil cuando desea realizar una implementación manual de archivos agregados / modificados de algunas últimas confirmaciones. Entonces puede simplemente copiar estos archivos a través del cliente ftp.

Skybamar
fuente