Exportieren Sie nur geänderte und hinzugefügte Dateien mit Ordnerstruktur in Git

82

Ich möchte eine Liste der geänderten und hinzugefügten Dateien in einem bestimmten Commit erhalten, damit ich sie exportieren und ein Paket mit der Dateistruktur generieren kann.

Die Idee ist, das Paket zu bekommen und es auf dem Server zu extrahieren. Aus vielen Gründen kann ich keinen Hook erstellen, um das Repo automatisch abzurufen, und der einfachste Weg, den Server auf dem neuesten Stand zu halten, besteht darin, dieses Paket zu generieren.

Michael Kuhinica
quelle

Antworten:

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

    Nehmen Sie einen Unterschied des angegebenen Commits zu seinen Eltern (einschließlich aller Unterverzeichnisse, nicht nur des obersten Verzeichnisses).

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

    Geben Sie das Commit SHA1 nicht aus. Geben Sie nur die Namen der betroffenen Dateien anstelle eines vollständigen Diff aus.

  3. --diff-filter=ACMRT::

    In diesem Commit werden nur Dateien angezeigt, die hinzugefügt, kopiert, geändert, umbenannt wurden oder deren Typ geändert wurde (z. B. Datei → Symlink). Dadurch werden gelöschte Dateien ausgelassen.


UPDATE AUS DEM KOMMENTAR: Basierend
auf dem Fragenkontext und den Kommentaren unten können Sie mit dem folgenden Befehl die ACMRTDateien als .tarDatei mit ihrer Ordnerstruktur abrufen.

git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | tar -czf file.tgz -T -
Aristoteles Pagaltzis
quelle
Jetzt muss ich nur noch einen Weg finden, um das Ergebnis dieses Befehls mit Teer zu verkleben! Danke dir!
Michael Kuhinica
8
@Taverneiro Sie können es an den in dieser Antwort angegebenen Befehl tar weiterleiten , z. für eine tgz-Datei: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

Um dies abzurunden, ist hier der Befehl, der an tar weitergeleitet wird. Dadurch werden die Dateien in ein Teerarchiv exportiert.

James Ehly
quelle
1
Kann dies für mehrere Commits in eine TAR-Datei durchgeführt werden?
Evolutionxbox
4
Wenn Sie versuchen, mehrere Commits zur gleichen TAR-Datei hinzuzufügen, erstellt tar manchmal erneut den Dateinamen.ext mit einem anderen Namen (Dateiname1.ext). Mir ist klar, dass ich bei Verwendung von zip die Datei hinzufügen und überschreiben kann, wenn sie in der Zip-Datei vorhanden ist ... Sie müssen nur die xargs durch xargs ersetzen. Zip -r0 myzipfile.zip $ 1
Ricardo Martins
Werden hier Xargs benötigt? Ich weiß, dass es für Sachen wie rm ist, aber ich dachte, tar könnte so viele Dateien verarbeiten, wie Sie wollen.
Erdal G.
1
Seien Sie vorsichtig mit dieser Antwort, diese nehmen Diff-Dateien, aber als die aktuelle Version des aktuellen Zweigs
Mems
1
Während der Ausführung dieses Befehls auf dem Windows-Slave-Computer wird die folgende Fehlermeldung angezeigt: "'xargs' wird nicht als interner oder externer Befehl, bedienbares Programm oder Batchdatei erkannt."
Navin Prasad
40

Hier ist ein einzeiliger Befehl, der unter Windows 7 funktioniert. Führen Sie ihn aus dem Ordner der obersten Ebene Ihres Repositorys aus.

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

  • echo FA beantwortet die unvermeidliche xcopy-Frage, ob Sie eine Datei oder ein Verzeichnis (eine Datei) kopieren, und die mögliche Frage zum Überschreiben einer Datei (Alle überschreiben).
  • Mit usebackq können wir die Ausgabe unseres Befehls git als Eingabe für unsere do-Klausel verwenden
  • HEAD ~ 1 HEAD erhält alle Unterschiede zwischen dem vorherigen Commit und dem aktuellen HEAD
  • % ~ fA wandelt die Git-Ausgabe in vollständig qualifizierte Pfade um (erforderlich, um Schrägstriche in Schrägstriche umzuwandeln)
  • In C: \ git_changed_files \ finden Sie alle Dateien, die unterschiedlich sind
MM.
quelle
1
Als ich dieses Durcheinander von Symbolen sah, hatte ich nicht erwartet, dass es ohne umfangreiche Optimierungen funktionieren würde ... aber es funktionierte beim ersten Versuch, danke.
Nathan Fig
3
Ein kleines Heads-Up für alle Leute, die (wie ich) mit Windows-Batchdateien nicht sehr vertraut sind: Wenn Sie den obigen Befehl in einer Batchdatei verwenden möchten, müssen Sie% A durch %% A in der for-Schleife ersetzen
Buddybubble
1
Was? LOL. Verwenden Sie einfach die Kombination git archive - git diff (Nicolas 'Antwort). Viel einfacher und unkomplizierter. (Ich weiß ehrlich gesagt nicht, was hier passiert.)
ADTC
1
Warnung: Dadurch werden Dateien von Ihrer Arbeitskopie kopiert, die möglicherweise nicht mit dem Inhalt von HEAD übereinstimmen (oder mit welchem ​​Commit-Hash Sie ihn ersetzen)
Simon East,
1
Tolles Zeug, funktioniert aber nur mit CMD, nicht mit Powershell, -v ° v-
Rodion Bykov
33

Wenn Ihr Commit-Hash beispielsweise a9359f9 lautet, lautet dieser Befehl:

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

extrahiert die im Commit geänderten Dateien und legt sie in patch.zip ab, während die Projektverzeichnisstruktur erhalten bleibt.

ein bisschen ausführlich, der Commit-Hash wird dreimal erwähnt, aber es scheint für mich zu funktionieren.

habe es hier: http://tosbourn.com/2011/05/git/using-git-to-create-an-archive-of-changed-files/

Nicolas Dermine
quelle
Ich denke, ich kann functionin PowerShell den Befehl auf einen Funktionsnamen reduzieren und $argsden Commit-Hash nur einmal übergeben. In * nix können Sie wahrscheinlich Shell-Scripting verwenden, um den gleichen Effekt zu erzielen.
ADTC
1
Mann ... ich
besitze
3
@BenSewards für Reihe von Commits zwischen commit1 und commit2 (einschließlich später verpflichten, ausschließlich früher begehen, um Materie nicht) nur tun git archive -o patch.zip HEAD /**or a commit**/ $(git diff --name-only commit1 commit2). Das an den archiveBefehl übergebene Commit kann ein beliebiges Commit sein (höchstwahrscheinlich entweder das HEAD oder das spätere Commit) , und die Dateien werden so abgerufen, als ob sie in dieser Commit-Phase ausgecheckt wären. Die commit1 und commit2 bestanden diffnur dann verwendet werden , um die Liste der Dateien zu ziehen , um zu generieren - sie haben nicht die Version der Dateien gezogen beeinflussen.
ADTC
1
Zusätzlicher Tipp: Wenn Sie die geänderten Dateien mehrerer diffgit 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)
Commit-
4
Aufgrund von Leerzeichen in Pfadnamen musste ich eine Pipe verwenden, um Xargs mit dem NULgit diff -z --name-only commit1 commit2 | xargs -0 git archive -o patch.zip HEAD
Boggin
24

Sie können diff mit Tortoise Git nach MS Windows exportieren :

Ich klicke mit der rechten Maustaste und wähle TortoiseGit > Protokoll anzeigen und Protokollnachrichten werden geöffnet.

Wählen Sie zwei Revisionen aus und vergleichen Sie sie. Der Unterschied zwischen wird offen sein.

Wählen Sie die Dateien aus und exportieren Sie die Auswahl in ... in einen Ordner!

Geben Sie hier die Bildbeschreibung ein

Wendel
quelle
1
Bestimmt. So viele Antworten darauf enthielten nur Linux-Befehle, und viele erfassten keine funktionierenden Änderungen, sondern nur festgeschriebene Änderungen. Ich bin froh, dass dies gepostet wurde!
Dythim
8

Ich musste meinen Testserver aktualisieren und Dateien hinzufügen, die sich seit Version 2.1 geändert haben.
Für mich funktionierte eine ähnliche Lösung wie für James Ehly, aber in meinem Fall wollte ich ein Paket mit Unterschieden zwischen zwei älteren Tags in das Archiv exportieren - tag_ver_2.1 und tag_ver_2.2, nicht das einzige Commit.

Zum Beispiel:

tag_ver_2.1 = 1f72b38ad
tag_ver_2.2 = c1a546782

Hier ist ein modifiziertes Beispiel:

git diff-tree -r --no-commit-id --name-only c1a546782 1f72b38ad | xargs tar -rf test.tar
pietr
quelle
Ich habe gerade ein WEIRD-Problem festgestellt: Wenn ich den obigen Code ausprobiere, funktioniert er wie ein Zauber. Aber ich ändere die tar -rf test.tar für tar -zcf test.tar.gz, dann fehlen in der resultierenden Datei mehrere Dateien. Was kann diesen Ergebnisunterschied verursachen?
Alberto Alexander Rodriguez
Während der Ausführung dieses Befehls auf dem Windows-Slave-Computer wird die folgende Fehlermeldung angezeigt: "'xargs' wird nicht als interner oder externer Befehl, bedienbares Programm oder Batchdatei erkannt."
Navin Prasad
4

Die folgenden Befehle haben bei mir funktioniert.

Wenn Sie möchten, dass sich die Dateien beim letzten Festschreiben unterscheiden:

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

oder wenn Sie einen Unterschied zwischen zwei bestimmten Commits wünschen:

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

oder wenn Sie nicht festgeschriebene Dateien haben, denken Sie daran, dass git Weg ist, alles festzuschreiben, Zweige sind billig:

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^)
Ashutosh Tripathy
quelle
2

Ich habe ein PHP-Skript erstellt, um geänderte Dateien unter Windows zu exportieren. Wenn Sie einen localhost-Entwicklungsserver mit PHP eingerichtet haben, können Sie ihn problemlos ausführen. Es speichert Ihr letztes Repository und exportiert immer in denselben Ordner. Der Exportordner wird vor dem Export immer geleert. Sie sehen auch gelöschte Dateien in rot, damit Sie wissen, was auf dem Server gelöscht werden muss.

Dies sind nur zwei Dateien, daher werde ich sie hier veröffentlichen. Nehmen wir an, Ihre Repositorys befinden sich unter c: / www in ihren eigenen Ordnern und http: // localhost zeigt ebenfalls auf c: / www und ist PHP-fähig. Lassen Sie uns diese 2 Dateien in 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

Und jetzt laden Sie localhost / git-export / in einen Browser. Das Skript ist so eingerichtet, dass es immer nach c: / www / export exportiert wird. Ändern Sie alle Pfade entsprechend Ihrer Umgebung oder ändern Sie das Skript entsprechend Ihren Anforderungen.

Dies funktioniert, wenn Sie Git so installiert haben, dass sich der Befehl git in Ihrem PATH befindet. Dies kann konfiguriert werden, wenn Sie das Windows Git-Installationsprogramm ausführen.

Zitronensaft
quelle
Dieser Code ist großartig, aber ich möchte Dateien und Verzeichnisse mit Submodulen exportieren. Ihr Code hat nur mit dem Haupt-Repository funktioniert. Wenn Submodule Ihren Code geändert haben, sagen wir, dass der Submodul-Ordner gelöscht wurde!
Morteza Khadem
1

So exportieren Sie geänderte Dateien ab einem Datum:

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

Verknüpfung (Alias ​​verwenden)

  git exportmdf 2016-11-01 11.tar

Alias ​​in .gitconfig

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

Hier ist ein kleines Bash-Skript (Unix), das ich geschrieben habe und das Dateien für einen bestimmten Commit-Hash mit der Ordnerstruktur kopiert:

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

Erstellen Sie eine Datei mit dem Namen '~ / Scripts / copy-commit.sh' und geben Sie ihr Ausführungsberechtigungen:

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

Dann aus dem Stammverzeichnis des Git-Repositorys:

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

Ich hatte auch schon einmal ein ähnliches Problem. Ich habe ein einfaches Shell-Skript geschrieben.

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

Der obige Befehl zeigt Commit Hash (Revision) von commit_HashX zu commit_HashY in umgekehrter Reihenfolge an.

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

Nun das Haupt-Shell-Skript mit dem obigen Befehl.

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

Fügen Sie diesen Code zu export_changes.sh hinzu. Übergeben Sie nun die Datei und schreiben Sie Hashes an das Skript fest.

Stellen Sie sicher, dass initial commit_hash das erste Argument und dann das letzte commit_hash sein muss, bis zu dem Sie Änderungen exportieren möchten.

Beispiel:

$ sh export_changes.sh hashX hashY

Legen Sie dieses Skript in das lokale Verzeichnis git oder legen Sie den Pfad für das lokale Verzeichnis git im Skript fest. Ich hoffe es hilft..!

Raj Hawaldar
quelle
0

Dies funktioniert für mich sowohl unter Unix als auch unter Windows:

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

Der Befehl enthält zwei Platzhalter. Sie müssen sie für Ihren Zweck ersetzen:

  • __1__ : Ersetzen Sie die Commit-ID des Commits direkt vor allen Commits, die Sie exportieren möchten (z. B. 997cc7b6 - vergessen Sie nicht, diesen doppelten Punkt nach der Commit-ID beizubehalten - dies bedeutet "alle Commits einbeziehen, die neuer als dieses Commit sind").

  • __2__ : Durch vorhandenen Pfad ersetzen, in den Sie Ihre Dateien exportieren möchten (z. B. ../export_path/)

Als Ergebnis erhalten Sie Ihre Dateien in einer Ordnerstruktur (keine Reißverschlüsse / Teere ...), da möglicherweise jemand daran gewöhnt ist , Schildkröten-SVN-Exporte in SVN-Repositorys zu verwenden.

Dies ist beispielsweise sehr nützlich, wenn Sie die manuelle Bereitstellung von hinzugefügten / geänderten Dateien mit wenigen letzten Commits durchführen möchten. Sie können diese Dateien dann einfach über den FTP-Client kopieren.

Skybamar
quelle