Eksportuj tylko zmodyfikowane i dodane pliki ze strukturą folderów w Git

83

Chciałbym uzyskać listę zmodyfikowanych i dodanych plików w konkretnym zatwierdzeniu, aby móc je wyeksportować i wygenerować pakiet ze strukturą plików.

Chodzi o to, aby pobrać pakiet i rozpakować go na serwerze. Z wielu powodów nie mogę utworzyć podpięcia do automatycznego ściągania repozytorium, a najłatwiejszym sposobem aktualizowania serwera jest generowanie tego pakietu.

Michael Kuhinica
źródło

Odpowiedzi:

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

    Przenieś różnicę podanego zatwierdzenia do jego rodzica (ów) (włączając wszystkie podkatalogi, a nie tylko główny katalog).

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

    Nie wysyłaj zatwierdzenia SHA1. Wyświetla tylko nazwy plików, na które ma to wpływ, zamiast pełnego pliku różnicowego.

  3. --diff-filter=ACMRT:

    Pokaż tylko pliki dodane, skopiowane, zmodyfikowane, o zmienionej nazwie lub których typ został zmieniony (np. Plik → łącze symboliczne) w tym zatwierdzeniu. To pomija usunięte pliki.


AKTUALIZUJ Z KOMENTARZA:
Na podstawie kontekstu pytania i poniższych komentarzy, za pomocą następującego polecenia możesz pobrać ACMRTpliki jako .tarplik z ich strukturą folderów.

git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | tar -czf file.tgz -T -
Arystoteles Pagaltzis
źródło
Teraz muszę tylko znaleźć sposób na przyklejenie wyniku tego polecenia smołą! Dziękuję Ci!
Michael Kuhinica
8
@Taverneiro Możesz przesłać potokiem do polecenia tar podanego w tej odpowiedzi , np. dla pliku tgz:git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | tar -czf file.tgz -T -
Matthieu Sadouni
68
git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | xargs tar -rf mytarfile.tar

Aby to zakończyć, oto polecenie przesłane potokiem do tar. Spowoduje to wyeksportowanie plików do archiwum tar.

James Ehly
źródło
1
Czy można to zrobić dla wielu zatwierdzeń w jednym pliku tar?
evolutionxbox,
4
Podczas próby dodania wielu zatwierdzeń do tego samego pliku tar, czasami tar ponownie tworzy nazwę_pliku.ext z inną nazwą (nazwa_pliku1.roz). Zdaję sobie sprawę, że jeśli użyję zip, mogę dodać i nadpisać plik, jeśli istnieje w zipie ... Wystarczy zamienić xargs na xargs zip -r0 myzipfile.zip $ 1
Ricardo Martins
Czy potrzebne są tutaj Xargs? Wiem, że dotyczy rzeczy takich jak rm, ale pomyślałem, że tar może obsłużyć dowolną liczbę plików.
Erdal G.
1
Uważaj na tę odpowiedź, to pobiera pliki różnicowe, ale jako aktualna wersja bieżącej gałęzi
memy
1
Podczas wykonywania tego polecenia na komputerze podrzędnym z systemem Windows pojawia się następujący błąd: „Xargs” nie jest rozpoznawany jako polecenie wewnętrzne lub zewnętrzne, program operacyjny lub plik wsadowy. ”
Navin prasad
40

Oto jednowierszowe polecenie, które działa w systemie Windows 7. Uruchom je z folderu najwyższego poziomu repozytorium.

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 odpowiada na nieuniknione pytanie xcopy o to, czy kopiujesz plik lub katalog (plik), i możliwe pytanie o nadpisanie pliku (nadpisz wszystko)
  • usebackq pozwala nam użyć danych wyjściowych z naszego polecenia git jako danych wejściowych do naszej klauzuli do
  • HEAD ~ 1 HEAD pobiera wszystkie różnice między poprzednim zatwierdzeniem a bieżącym HEAD
  • % ~ fA przekształca dane wyjściowe git w w pełni kwalifikowane ścieżki (potrzebne do zmiany ukośników w przód na ukośniki odwrotne)
  • C: \ git_changed_files \ to miejsce, w którym znajdziesz wszystkie różne pliki
MM.
źródło
1
Kiedy zobaczyłem ten bałagan symboli, nie spodziewałem się, że zadziała bez znacznych poprawek ... ale zadziałało za pierwszym razem, dzięki.
Nathan Fig
3
mała uwaga dla wszystkich facetów, którzy (jak ja) nie są zbyt zaznajomieni z plikami wsadowymi systemu Windows: Jeśli chcesz użyć powyższego polecenia w pliku wsadowym, musisz zastąpić% A %% A wewnątrz pętli for
buddybubble
1
Co? LOL. Po prostu użyj archiwum git - git diff combo (odpowiedź Nicolasa). O wiele łatwiejsze i nieskomplikowane. (Szczerze mówiąc, nie wiem, co się tutaj dzieje.)
ADTC
1
Ostrzeżenie: spowoduje to skopiowanie plików z kopii roboczej, które mogą w rzeczywistości nie pasować do tego, co jest w HEAD (lub jakikolwiek skrót zmiany, który go
Simon East
1
Świetna rzecz, ale działa tylko z CMD, a nie z Powershell, -v ° v-
Rodion Bykov
33

jeśli twój skrót zatwierdzenia to na przykład a9359f9, to polecenie:

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

wypakuje pliki zmodyfikowane w zatwierdzeniu i umieści je w patch.zip, zachowując nienaruszoną strukturę katalogów projektu.

trochę rozwlekłe, trzykrotnie wspomniano o skrócie zatwierdzenia, ale wydaje mi się, że działa.

mam to tutaj: http://tosbourn.com/2011/05/git/using-git-to-create-an-archive-of-changed-files/

Nicolas Dermine
źródło
Myślę, że mogę użyć functionw PowerShell, aby zredukować polecenie do nazwy funkcji i użyć $argstylko raz do przekazania skrótu zatwierdzenia. W * nix prawdopodobnie możesz użyć skryptów powłoki, aby osiągnąć ten sam efekt.
ADTC
1
Człowieku ... mam ci piwo :)
deanpodgornik
3
@BenSewards dla zakresu zatwierdzeń między zatwierdzeniem1 i zatwierdzeniem2 (łącznie z późniejszym zatwierdzeniem, bez wcześniejszego zatwierdzenia; kolejność nie ma znaczenia) po prostu zrób git archive -o patch.zip HEAD /**or a commit**/ $(git diff --name-only commit1 commit2). Zatwierdzeniem przekazanym do archivepolecenia może być dowolne zatwierdzenie (najprawdopodobniej zatwierdzenie HEAD lub późniejsze) , a pliki są pobierane tak, jakby były wypisywane na tym etapie zatwierdzania. Commit1 i commit2 przekazywane diffsą wykorzystywane jedynie w celu wygenerowania listy plików do ściągania - nie dotyczy wersji plików ciągniętych.
ADTC
1
Dodatkowa wskazówka: jeśli chcesz połączyć zmienione pliki z wielu zakresów zatwierdzeń w jedno archiwum, po prostu powtórz diffpolecenie ze średnikiem: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
Ze względu na spacje w nazwach ścieżek musiałem użyć potoku do xargs obsługującego NULgit diff -z --name-only commit1 commit2 | xargs -0 git archive -o patch.zip HEAD
Boggin
27

Możesz eksportować różnice za pomocą Tortoise Git do MS Windows:

Klikam prawym przyciskiem myszy i wybieram TortoiseGit > Pokaż dziennik, a komunikaty dziennika zostaną otwarte.

Wybierz dwie wersje i porównaj. Różnica między będzie otwarta.

Wybierz pliki i Eksportuj zaznaczenie do ... do folderu!

wprowadź opis obrazu tutaj

Wendel
źródło
1
Zdecydowanie. Tak wiele odpowiedzi na to pytanie zawierało polecenia tylko dla systemu Linux, a wiele z nich nie obejmowało zmian roboczych, a jedynie zmiany, które zostały zatwierdzone. Cieszę się, że to zostało opublikowane!
dythim
8

Musiałem zaktualizować mój serwer testowy i dodać pliki, które zmieniły się od wersji 2.1.
U mnie zadziałało podobne rozwiązanie, które opublikował James Ehly, ale w moim przypadku chciałem wyeksportować do archiwum pakiet różnic między dwoma starszymi tagami - tag_ver_2.1 i tag_ver_2.2, a nie jedynym zatwierdzeniem.

Na przykład:

tag_ver_2.1 = 1f72b38ad
tag_ver_2.2 = c1a546782

Tutaj jest zmodyfikowany przykład:

git diff-tree -r --no-commit-id --name-only c1a546782 1f72b38ad | xargs tar -rf test.tar
pietr
źródło
Właśnie wykryłem DZIWNY problem: jeśli wypróbuję powyższy kod, działa jak urok. Ale zmieniam plik tar -rf test.tar na tar -zcf test.tar.gz, w wyniku czego brakuje kilku plików. Co może powodować tę różnicę w wyniku?
Alberto Alexander Rodriguez
Podczas wykonywania tego polecenia na komputerze podrzędnym z systemem Windows pojawia się następujący komunikat o błędzie: „Xargs” nie jest rozpoznawany jako polecenie wewnętrzne lub zewnętrzne, program operacyjny lub plik wsadowy. ”
Navin prasad
4

Poniższe polecenia działały dla mnie.

Jeśli chcesz, aby różnica plików zmieniona przez ostatnie zatwierdzenie:

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

lub jeśli chcesz rozróżnić dwa konkretne zatwierdzenia:

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

lub jeśli masz niezatwierdzone pliki, pamiętaj, że sposobem gita jest zatwierdzenie wszystkiego, gałęzie są tanie:

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
źródło
2

Zrobiłem skrypt php, aby wyeksportować zmienione pliki w systemie Windows. Jeśli masz lokalny serwer deweloperski z ustawionym php, możesz go łatwo uruchomić. Zapamięta twoje ostatnie repozytorium i będzie eksportować zawsze do tego samego folderu. Folder eksportu jest zawsze opróżniany przed eksportem. Usunięte pliki będą również widoczne na czerwono, więc wiesz, co usunąć na serwerze.

To tylko dwa pliki, więc umieszczę je tutaj. Załóżmy, że twoje repozytoria znajdują się pod c: / www w ich własnych folderach i tak dalej http: // localhost również wskazuje na c: / www i obsługuje php. Umieśćmy te 2 pliki w 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

A teraz załaduj localhost / git-export / w przeglądarce. Skrypt jest skonfigurowany tak, aby zawsze eksportował do c: / www / export - zmień wszystkie ścieżki, aby pasowały do ​​twojego środowiska lub zmodyfikuj skrypt, aby dopasować go do swoich potrzeb.

To zadziała, jeśli masz zainstalowanego Gita, tak że polecenie git znajduje się w Twojej PATH - można to skonfigurować po uruchomieniu instalatora systemu Windows Git.

Sok cytrynowy
źródło
ten kod jest świetny, ale chcę eksportować pliki i katalogi z podmodułami, twój kod działał tylko z głównym repozytorium i jeśli podmoduły zmieniły twój kod, powiedz, że folder podmodułów został usunięty!
morteza khadem
1

Aby wyeksportować zmodyfikowane pliki zaczynające się od daty:

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

Skrót (użyj aliasu)

  git exportmdf 2016-11-01 11.tar

Alias ​​w .gitconfig

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

Oto mały skrypt bash (Unix), który napisałem, który skopiuje pliki dla podanego skrótu zatwierdzenia ze strukturą folderów:

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

Utwórz plik o nazwie „~ / Scripts / copy-commit.sh”, a następnie nadaj mu uprawnienia do wykonywania:

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

Następnie z katalogu głównego repozytorium git:

~/Scripts/copy-commit.sh COMMIT_KEY ~/Existing/Destination/Folder/
Patrick.SE
źródło
0

Ja też miałem wcześniej podobny problem. Napisałem prosty skrypt Shell.

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

Powyższe polecenie wyświetli Commit Hash (Revision) od commit_HashX do commit_HashY w odwrotnej kolejności.

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

Teraz główny skrypt powłoki używający powyższego polecenia.

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

Dodaj ten kod do pliku export_changes.sh. Teraz przekaż skróty pliku i zatwierdzenia do skryptu.

Upewnij się, że początkowy ciąg znaków_zatwierdzenia musi być pierwszym argumentem, a następnie ostatnim znakiem skrótu_zajtowania, do którego chcesz wyeksportować zmiany.

Przykład:

$ sh export_changes.sh hashX hashY

Umieść ten skrypt w katalogu lokalnym git lub ustaw ścieżkę do katalogu lokalnego git w skrypcie. Mam nadzieję, że to pomoże..!

Raj Hawaldar
źródło
0

Działa to dla mnie zarówno w systemie Unix, jak i Windows:

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

W poleceniu są dwa symbole zastępcze. Musisz je wymienić dla swojego celu:

  • __1__ : zamień na identyfikator zatwierdzenia zmiany tuż przed wszystkimi zatwierdzeniami, które chcesz wyeksportować (np. 997cc7b6 - nie zapomnij zachować tego dubledot po identyfikatorze zatwierdzenia - oznacza to "włącz wszystkie zatwierdzenia nowsze niż to zatwierdzenie")

  • __2__ : zastąp istniejącą ścieżką, do której chcesz wyeksportować pliki (np. ../Export_path/)

W rezultacie otrzymasz pliki w strukturze folderów (bez zip / tars ...), ponieważ ktoś może być przyzwyczajony do korzystania z eksportu svn żółwia w repozytoriach svn.

Jest to na przykład bardzo przydatne, gdy chcesz ręcznie wdrożyć dodane / zmodyfikowane pliki kilku ostatnich zatwierdzeń. Możesz więc po prostu skopiować te pliki przez klienta ftp.

Skybamar
źródło
0

Ja też miałem ten sam problem. Rozwiązanie @NicolasDermine nie działało, ponieważ między porównywalnymi zatwierdzeniami zmieniło się zbyt wiele plików. Wystąpił błąd, że argumenty powłoki są za długie.

W konsekwencji dodałem implementację Pythona. Można to zainstalować za pośrednictwem, pip install gitzipa następnie wykonać za pomocą

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

w katalogu głównym repozytoriów, tworząc plik export.zip plik zawierający wszystkie zmienione pliki z zachowaniem struktury katalogów.

Może ktoś też tego potrzebuje, więc pomyślałem, że podzielę się tym tutaj.

Zastrzeżenie : jestem autorem modułu gitzip .

miile7
źródło