Exportați doar fișierele modificate și adăugate cu structura de dosare în Git (Programare, Git, Git Diff)

Michael Kuhinica a intrebat.

Aș dori să obțin o listă de fișiere modificate și adăugate într-un anumit commit, astfel încât să le pot exporta și să pot genera un pachet cu structura de fișiere.

Ideea este de a obține pachetul și de a-l extrage pe server. Din mai multe motive nu pot crea un cârlig pentru a extrage repo-ul în mod automat și cel mai simplu mod pe care îl am de a menține serverul actualizat este generarea acestui pachet.

13 răspunsuri
Aristotel Pagaltzis
git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT <i>$commit_id</i>
  1. git diff-tree -r <i>$commit_id</i>:

    Luați un diff al commit-ului dat cu părintele (părinții) său (inclusiv toate subdirectoarele, nu doar directorul de sus).

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

    Nu afișați SHA1 al confirmării. Afișează doar numele fișierelor afectate în loc de o diferență completă.

  3. --diff-filter=ACMRT:

    Afișează numai fișierele adăugate, copiate, modificate, redenumite sau al căror tip a fost schimbat (de exemplu, fișier → legătură simbolică) în această confirmare. Astfel, nu sunt incluse fișierele șterse.


ACTUALIZARE DIN COMENTARIU:
Pe baza contextului întrebării și a comentariilor de mai jos, cu următoarea comandă, puteți obține ACMRT fișiere ca un .tar fișier cu structura lor de dosare.

git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | tar -czf file.tgz -T -

Comentarii

  • Acum trebuie doar să găsesc o modalitate de a lipi rezultatul acestei comenzi cu tar! Vă mulțumesc! –  > Por Michael Kuhinica.
  • @Taverneiro Poți să o duci la comanda tar dată în acest răspuns, de ex. pentru un fișier tgz : git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | tar -czf file.tgz -T - –  > Por Matthieu Sadouni.
James Ehly
git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT <i>$commit_id</i> | xargs tar -rf mytarfile.tar

Doar pentru a completa, iată comanda canalizată către tar. Aceasta exportă fișierele într-o arhivă tar.

Comentarii

  • Se poate face acest lucru pentru mai multe comenzi într-un singur fișier tar? –  > Por evolutionxbox.
  • Atunci când încercați să adăugați mai multe comenzi în același fișier tar, uneori tar creează din nou numele fișierului.ext cu alt nume (numefila1.ext). Îmi dau seama că, dacă folosesc zip, pot adăuga și suprascrie fișierul dacă acesta există în zip… Trebuie doar să înlocuiți xargs cu xargs zip -r0 myzipfile.zip $1 –  > Por Ricardo Martins.
  • Este necesar xargs aici? Știu că este pentru chestii precum rm, dar am crezut că tar poate gestiona atâtea fișiere câte vrei. –  > Por Erdal G..
  • Fiți atenți la acest răspuns, acesta ia fișiere diff, dar ca versiunea curentă a ramurii curente –  > Por mems.
  • În timp ce execut această comandă în mașina sclav Windows, primesc următoarea eroare „‘xargs’ nu este recunoscut ca o comandă internă sau externă, program operabil sau fișier batch.” –  > Por Navin prasad.
MM.

Iată o comandă de un singur rând care funcționează pe Windows 7. Rulați-o din folderul de nivel superior al depozitului dvs.

for /f „usebackq tokens=*” %A in (`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 răspunde la inevitabila întrebare a lui xcopy dacă copiați un fișier sau un director (file), precum și la posibila întrebare despre suprascrierea unui fișier (overwrite All)
  • usebackq ne permite să folosim ieșirea de la comanda git ca intrare pentru clauza do
  • HEAD~1 HEAD obține toate diferențele dintre confirmarea anterioară și HEAD curent
  • %~fA transformă ieșirea git în căi de acces complet calificate (necesare pentru a schimba barele în backslash-uri)
  • C:git_changed_files este locul în care veți găsi toate fișierele care sunt diferite

Comentarii

  • Când am văzut acea încurcătură de simboluri nu mă așteptam să funcționeze fără modificări ample… dar a funcționat din prima încercare, mulțumesc. –  > Por Nathan Fig.
  • o mică atenționare pentru toți cei care nu sunt (ca mine) foarte familiarizați cu fișierele batch din Windows: Dacă doriți să utilizați comanda de mai sus într-un fișier batch, va trebui să înlocuiți %A cu %%A în interiorul buclei for –  > Por buddybubble.
  • Ce? LOL. Folosiți doar comanda arhiva git – git diff combo (răspunsul lui Nicolas). Mult mai ușor și mai simplu. (Sincer, nu știu ce se întâmplă aici). –  > Por ADTC.
  • Avertisment: acest lucru va copia fișiere din copia de lucru, care s-ar putea să nu corespundă de fapt cu ceea ce este în HEAD (sau orice hash de confirmare cu care îl înlocuiți) – -.  > Por Simon East.
  • Lucruri grozave, dar funcționează doar cu CMD, nu și cu Powershell, -v°v- –  > Por Rodion Bykov.
Nicolas Dermine

dacă hash-ul de comitere este, de exemplu, a9359f9, această comandă :

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

va extrage fișierele modificate în commit și le va plasa în patch.zip, păstrând intactă structura directoarelor proiectului.

un pic verbos, hash-ul de comitere este menționat de trei ori, dar pare să funcționeze pentru mine.

Am găsit-o aici : http://tosbourn.com/2011/05/git/using-git-to-create-an-archive-of-changed-files/

Comentarii

  • Cred că pot folosi function în PowerShell să reduc comanda la doar un nume de funcție și să folosesc $args pentru a trece hash-ul de confirmare doar o singură dată. În *nix, probabil că puteți folosi scriptingul de shell pentru a obține același efect. –  > Por ADTC.
  • Omule… Îți ofer o bere 🙂 –  > Por deanpodgornik.
  • @BenSewards pentru intervalul de comenzi între commit1 și commit2 (inclusiv commit-ul ulterior, exclusiv commit-ul anterior; ordinea nu contează) faceți doar git archive -o patch.zip HEAD /**or a commit**/ $(git diff --name-only commit1 commit2). Confirmarea transmisă către archive poate fi orice commit arbitrar (cel mai probabil, fie HEAD, fie commit-ul ulterior), , iar fișierele sunt extrase ca și cum ar fi fost verificate în acea etapă de confirmare. Adresa commit1 și commit2 transmise către diff sunt folosite doar pentru a genera lista de fișiere de extras – nu afectează versiunea fișierelor extrase. –  > Por ADTC.
  • Sfat suplimentar: Dacă doriți să combinați fișierele modificate din mai multe intervale de comenzi într-o singură arhivă, trebuie doar să repetați comanda diff comanda cu punct și virgulă: 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) –  > Por ADTC.
  • Din cauza spațiilor din numele căilor de acces, a trebuit să folosesc o țeavă către xargs care se ocupă de NUL git diff -z --name-only commit1 commit2 | xargs -0 git archive -o patch.zip HEAD –  > Por Boggin.
Wendel

Puteți exporta diff folosind Tortoise Git către MS Windows:

Fac clic dreapta și selectez TortoiseGit > Afișați jurnalul și Mesaje jurnal vor fi deschise.

Selectați două revizuiri și comparați-le. Diferența dintre va fi deschisă.

Selectați fișierele și Exportați selecția în … într-un dosar!

Comentarii

  • Cu siguranță. Atât de multe răspunsuri la această întrebare au avut comenzi doar pentru Linux și multe nu au capturat modificările de lucru, ci doar modificările care au fost confirmate. Mă bucur că acest lucru a fost postat! –  > Por dythim.
pietr

Trebuia să îmi actualizez serverul de testare și să adaug fișiere care s-au schimbat de la versiunea 2.1.
Pentru mine a funcționat o soluție similară cu cea postată de James Ehly, dar în cazul meu am vrut să export în pachetul de arhivă a diferenței dintre două etichete mai vechi – tag_ver_2.1 și tag_ver_2.2, nu doar o singură confirmare.

De exemplu:

tag_ver_2.1 = 1f72b38ad
tag_ver_2.2 = c1a546782

Iată un exemplu modificat:

git diff-tree -r --no-commit-id --name-only c1a546782 1f72b38ad | xargs tar -rf test.tar

Comentarii

  • Tocmai am detectat o problemă EROARE: dacă încerc codul de mai sus, acesta funcționează de minune. Dar dacă schimb tar -rf test.tar cu tar -zcf test.tar.gz, atunci din fișierul rezultat lipsesc mai multe fișiere. Ce poate fi cauza acestei diferențe de rezultat? –  > Por Alberto Alexander Rodriguez.
  • În timp ce execut această comandă în mașina sclav Windows, primesc următoarea eroare „‘xargs’ nu este recunoscut ca o comandă internă sau externă, program operabil sau fișier batch.” –  > Por Navin prasad.
me_astr

Comenzile de mai jos au funcționat pentru mine.

Dacă doriți diferența dintre fișierele modificate de ultima confirmare:

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

sau dacă doriți diferența dintre două comenzi specifice:

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

sau dacă aveți fișiere necomandate, amintiți-vă că metoda git este de a comite totul, ramurile sunt ieftine:

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^)

Lemon Juice

Am realizat un script php pentru a exporta fișierele modificate pe Windows. Dacă aveți un server de dezvoltare localhost cu php configurat, atunci îl puteți rula cu ușurință. Își va aminti ultimul depozit și va exporta întotdeauna în același dosar. Dosarul de export este întotdeauna golit înainte de a exporta. De asemenea, veți vedea fișierele șterse în roșu, astfel încât să știți ce trebuie să ștergeți pe server.

Acestea sunt doar două fișiere, așa că le voi posta aici. Să presupunem că depozitele dvs. sunt localizate sub c:/www în propriile lor foldere și că http://localhost de asemenea, indică spre c:/www și este activat pentru php. Să punem aceste 2 fișiere în 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/>
";


    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/>
";

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

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

Și acum încărcați localhost/git-export/ într-un browser. Scriptul este setat să exporte întotdeauna în c:/www/export – schimbați toate căile de acces pentru a se potrivi mediului dumneavoastră sau modificați scriptul pentru a se potrivi nevoilor dumneavoastră.

Acest lucru va funcționa dacă aveți Git instalat astfel încât comanda git să fie în PATH-ul dumneavoastră – acest lucru poate fi configurat atunci când executați programul de instalare Git pentru Windows.

Comentarii

  • acest cod este grozav, dar vreau să export fișiere și directoare cu submodulele codul dvs. a funcționat doar cu depozitul principal și dacă submodulele s-au schimbat codul dvs. spuneți că dosarul submodulelor a fost șters! –  > Por morteza khadem.
catalinp

Pentru a exporta fișiere modificate începând cu o dată:

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

Scurtătură (utilizați alias)

  git exportmdf 2016-11-01 11.tar

Alias în .gitconfig

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

Patrick.SE

Iată un mic script bash(Unix) pe care l-am scris și care va copia fișierele pentru un anumit hash de commit cu structura de dosare:

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

Creați un fișier numit „~/Scripts/copy-commit.sh”, apoi acordați-i privilegii de execuție:

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

Apoi, din rădăcina depozitului git:

~/Scripts/copy-commit.sh COMMIT_KEY ~/Existing/Destination/Folder/

Raj Hawaldar

M-am confruntat și eu cu o problemă similară înainte. am scris un simplu script Shell.

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

Comanda de mai sus va afișa Commit Hash(Revision) de la commit_HashX la commit_HashY în ordine inversă.

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

Acum, principalul script Shell care utilizează comanda de mai sus.

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

Adăugați acest cod la export_changes.sh. Acum, treceți fișierul și hașurile de confirmare către script.

Asigurați-vă că commit_hash inițial trebuie să fie primul argument, apoi ultimul commit_hash până la care doriți să exportați modificările.

Exemplu:

$sh export_changes.sh hashX hashY

Puneți acest script în directorul local git sau setați calea directorului local git în script.Sper să vă fie de ajutor!

Skybamar

Acest lucru funcționează pentru mine atât pentru Unix, cât și pentru Windows:

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

În comandă există două spații libere. Trebuie să le înlocuiți pentru scopul dvs:

  • __1__: înlocuiți cu ID-ul de comitere al comenzii chiar înainte de toate comenzile pe care doriți să le exportați (de exemplu, 997cc7b6 – nu uitați să păstrați acel punct dublu după id-ul de comitet – aceasta înseamnă „implică toate comitetele mai noi decât acest comitet”)

  • __2__: înlocuiți cu calea existentă unde doriți să exportați fișierele (de exemplu, ../carte_de_export/)

Ca rezultat, veți obține fișierele într-o structură de dosare (fără fișiere zip/tars…) așa cum cineva ar putea fi obișnuit să folosească tortoise svn exports în depozitele svn.

Acest lucru este, de exemplu, destul de util atunci când doriți să efectuați o distribuire manuală a fișierelor adăugate/modificate din ultimele câteva comenzi. Astfel, puteți copia aceste fișiere prin intermediul unui client ftp.

miile7

Și eu am avut aceeași problemă. Soluția lui @NicolasDermine nu a funcționat deoarece prea multe fișiere s-au modificat între comenzi comparate. Am primit o eroare că argumentele shell-ului sunt prea lungi.

În consecință, am adăugat o implementare python. Aceasta poate fi instalată prin pip install gitzip și apoi executată cu

python -m gitzip export.zip <commit id> <commit id>

în rădăcina depozitului, creând un fișier export.zip care conține toate fișierele modificate, păstrând structura directoarelor.

Poate că și cineva are nevoie de ea, așa că m-am gândit să o împărtășesc aici.

Disclaimer: Eu sunt autorul gitzip modul.