Експортуйте лише модифіковані та додані файли зі структурою папок у Git


83

Я хотів би отримати список модифікованих та доданих файлів у певному коміті, щоб я міг їх експортувати та сформувати пакет зі структурою файлів.

Ідея полягає в тому, щоб отримати пакет і витягти його на сервері. З багатьох причин я не можу створити гачок для автоматичного витягування репо, і найпростіший спосіб постійно оновлювати сервер - це створення цього пакета.

Відповіді:


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

    Візьміть різницю даного коміту до його батьків (-ів) (включаючи всі підкаталоги, а не лише верхній каталог).

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

    Не виводити коміт SHA1. Виведіть лише імена файлів, на які це впливає, замість повної різниці.

  3. --diff-filter=ACMRT:

    У цьому коміті відображатимуться лише файли, додані, скопійовані, змінені, перейменовані або змінений їх тип (наприклад, файл → символічне посилання). При цьому залишаються видалені файли.


ОНОВЛЕННЯ З КОМЕНТАРУ:
На основі контексту запитання та коментарів нижче, за допомогою наступної команди, ви можете отримати ACMRTфайли як .tarфайл із їх структурою папок.

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

Тепер мені просто потрібно знайти спосіб закріпити результат цієї команди за допомогою tar! Дякую!
Michael Kuhinica

8
@Taverneiro Ви можете направити його до команди tar, вказаної у цій відповіді , наприклад. для файлу 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

Щоб лише округлити це, ось команда, подана до tar. Це експортує файли до архіву tar.


1
Чи можна це зробити для декількох комітів в один файл tar?
evolutionxbox

4
При спробі додати кілька комітів до одного і того ж файлу tar, іноді tar створює filename.ext знову з іншим ім'ям (filename1.ext). Я розумію, що якщо я використовую zip, я можу додати і перезаписати файл, якщо він існує в zip ... Вам просто потрібно замінити xargs на xargs zip -r0 myzipfile.zip $ 1
Рікардо Мартінс

Чи потрібні тут ксарги? Я знаю, що це для таких речей, як rm, але я думав, що tar може обробляти скільки завгодно файлів.
Ердал Г.

1
Будьте обережні з цією відповіддю, це беруть файли diff, але як поточна версія поточної гілки
mems

1
Під час виконання цієї команди у підлеглій машині Windows, я отримую таку помилку: "'xargs' не розпізнається як внутрішня чи зовнішня команда, операційна програма або пакетний файл."
Navin

40

Ось однорядкова команда, яка працює в Windows 7. Запустіть її з папки верхнього рівня вашого сховища.

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

  • echo FA відповідає на невідворотне запитання xcopy про те, чи копіюєте ви файл чи каталог (файл), і можливе запитання про перезапис файлу (перезапис усіх)
  • usebackq дозволяє використовувати вихідні дані нашої команди git як вхідні дані до нашого речення do
  • HEAD ~ 1 HEAD отримує всі відмінності між попереднім комітом і поточним HEAD
  • % ~ fA перетворює вихідні дані git у повністю кваліфіковані шляхи (необхідні для зміни косих рисок уперед і назад)
  • C: \ git_changed_files \ тут ви знайдете всі різні файли

1
Коли я побачив цю безладність символів, я не сподівався, що це спрацює без великих налаштувань ... але це спрацювало з першої спроби, дякую.
Натан Фіг

3
трохи орієнтації на всіх хлопців, які (як і я) не дуже добре знайомі з командними файлами Windows: якщо ви хочете використовувати наведену вище команду в командному файлі, вам потрібно буде замінити% A на %% A всередині циклу for
buddybubble

1
Що? ЛОЛ. Просто скористайтеся архівом git - комбінованим git diff (відповідь Ніколаса). Набагато простіше і зрозуміліше. (Чесно кажучи, я не знаю, що тут відбувається.)
ADTC

1
Попередження: це буде скопіювати файли з вашої робочої копії, які насправді можуть не відповідати тому, що знаходиться в HEAD (або будь-яким хешем
Саймон Іст

1
Чудовий матеріал, але працює лише з CMD, а не Powershell, -v ° v-
Родіон Биков

33

якщо ваш хеш коміту є, наприклад, a9359f9, ця команда:

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

витягне файли, змінені у коміті, та розмістить їх у patch.zip, зберігаючи структуру каталогів проекту незмінною.

трохи багатослівно, хеш коміту згадується тричі, але, здається, це працює для мене.

отримав тут: http://tosbourn.com/2011/05/git/using-git-to-create-an-archive-of-changed-files/


Я думаю, що я можу використовувати functionв PowerShell, щоб зменшити команду до просто імені функції та використовувати $argsдля передачі в хеш коміту лише один раз. У * nix ви можете скористатися сценаріями оболонки, щоб досягти того самого ефекту.
ADTC

1
Чоловіче ... Я володію тобою пивом :)
deanpodgornik,

3
@BenSewards для діапазону комітів між комітом1 та комітом2 (включаючи пізніший коміт , без попереднього коміту ; порядок не має значення) просто виконайте git archive -o patch.zip HEAD /**or a commit**/ $(git diff --name-only commit1 commit2). Фіксація, передана archiveкоманді, може бути будь-яким довільним комітом (швидше за все, HEAD або пізнішим комітом) , і файли витягуються так, ніби їх перевіряють на цьому етапі коміту. Commit1 і commit2 передаються diffтільки використовуються для створення списку файлів , щоб тягнути - вони не впливають на версію файлів витягувалися.
ADTC

1
Додаткова порада: Якщо ви хочете об’єднати змінені файли з декількох діапазонів комітів в один архів, просто повторіть diffкоманду з крапкою з комою: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
Через пробіли в іменах шляхів мені довелося використовувати конвеєр для ксарг, що обробляють NULgit diff -z --name-only commit1 commit2 | xargs -0 git archive -o patch.zip HEAD
Боггін

27

Ви можете експортувати diff за допомогою Tortoise Git у MS Windows:

Я клацаю правою кнопкою миші та вибираю TortoiseGit > Показати журнал, і журнал повідомлень буде відкритим.

Виберіть дві версії та порівняйте їх. Різниця між ними буде відкритою.

Виберіть файли та експортуйте виділення у ... до папки!

введіть тут опис зображення


1
Безумовно. Так багато відповідей на це мали команди лише для Linux, і багато з них не охоплювали робочих змін, а лише зміни, які були здійснені. Радий, що це було опубліковано!
dythim

8

Мені потрібно було оновити свій тестовий сервер і додати файли, які змінилися після версії 2.1.
Для мене працювало подібне рішення, яке розміщував Джеймс Елі, але в моєму випадку я хотів експортувати в архівний пакет різницю між двома старими тегами - tag_ver_2.1 і tag_ver_2.2, не єдиним комітом.

Наприклад:

tag_ver_2.1 = 1f72b38ad
tag_ver_2.2 = c1a546782

Ось модифікований приклад:

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

Я щойно виявив проблему WEIRD: якщо я спробую код вище, це спрацьовує як шарм. Але я міняю tar -rf test.tar на tar -zcf test.tar.gz, тоді в отриманому файлі бракує декількох файлів. Що може спричинити цю різницю в результаті?
Альберто Олександр Родрігес,

Під час виконання цієї команди у підлеглій машині Windows, я отримую таку помилку: "'xargs' не розпізнається як внутрішня чи зовнішня команда, операційна програма або пакетний файл."
Navin

4

Нижче команди працювали для мене.

Якщо ви хочете, щоб різниця між файлами була змінена за останнім комітом:

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

або якщо ви хочете різницю між двома конкретними комітами:

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

або якщо у вас є незакріплені файли, пам’ятайте, git way - це зробити все, гілки дешеві:

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

2

Я створив php-скрипт для експорту змінених файлів у Windows. Якщо у вас є сервер розвитку localhost з налаштованим php, ви можете легко його запустити. Він запам'ятає ваше останнє сховище та експортує його завжди в одну папку. Папка експорту завжди спорожняється перед експортом. Ви також побачите видалені файли червоним кольором, щоб ви знали, що видалити на сервері.

Це лише два файли, тому я розміщу їх тут. Припустимо, ваші сховища знаходяться в папці c: / www у їх власних папках і що http: // localhost також вказує на c: / www і має підтримку php. Давайте помістимо ці 2 файли в 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

А тепер завантажте localhost / git-export / у браузер. Сценарій налаштований на експорт завжди в c: / www / export - змініть усі шляхи відповідно до вашого середовища або змініть сценарій відповідно до ваших потреб.

Це буде працювати, якщо у вас встановлений Git, щоб команда git знаходилась у вашому PATH - це можна налаштувати під час запуску інсталятора Windows Git.


цей код чудовий, але я хочу експортувати файли та каталоги з підмодулями, ваш код працював лише з основним сховищем, і якщо підмодулі змінили ваш код, скажімо, папку підмодуля видалено!
morteza khadem

1

Щоб експортувати змінені файли, починаючи з дати:

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

Ярлик (використовувати псевдонім)

  git exportmdf 2016-11-01 11.tar

Псевдонім у .gitconfig

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

0

Ось невеликий скрипт bash (Unix), який я написав, який буде копіювати файли для заданого хешу коміту зі структурою папок:

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

Створіть файл із назвою '~ / Scripts / copy-commit.sh', а потім надайте йому права на виконання:

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

Потім з кореня сховища git:

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

0

Я теж раніше стикався з подібною проблемою. Я написав простий сценарій Shell.

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

Вищевказана команда відобразить хеш коміту (ревізія) від commit_HashX до commit_HashY у зворотному порядку.

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

Тепер основний сценарій оболонки за допомогою наведеної вище команди.

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

Додайте цей код до export_changes.sh. Тепер передайте файл і зафіксуйте хеші до сценарію.

Переконайтеся, що початковий commit_hash повинен бути першим аргументом, а потім останнім commit_hash, до якого ви хочете експортувати зміни.

Приклад:

$ sh export_changes.sh hashX hashY

Помістіть цей сценарій у локальний каталог git або встановіть шлях до локального каталогу git у сценарії. Сподіваюся, це допоможе ..!


0

Це працює для мене як для Unix, так і для Windows:

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

У команді є два заповнювачі. Вам потрібно замінити їх для своєї мети:

  • __1__ : замінити ідентифікатором коміту коміту безпосередньо перед усіма комітами, які ви хочете експортувати (наприклад, 997cc7b6 - не забудьте зберегти цей подвійний крапка після ідентифікатора коміту - це означає "задіяти всі коміти новіші, ніж цей коміт")

  • __2__ : замінити наявним шляхом, куди ви хочете експортувати файли (наприклад ../export_path/)

В результаті ви отримаєте свої файли у структурі папок (без застібок / тар ...), оскільки хтось може звик використовувати експорт svn черепахи у сховищах svn.

Наприклад, це дуже корисно, коли ви хочете виконати ручне розгортання доданих / модифікованих файлів кількох останніх комітів. Тоді ви можете просто скопіювати ці файли через ftp-клієнт.


0

У мене теж була та сама проблема. Рішення @NicolasDermine не працювало, оскільки між порівняними комітами змінилося занадто багато файлів. Я отримав помилку, що аргументи оболонки занадто довгі.

Як наслідок, я додав реалізацію python. Це можна встановити через, pip install gitzipа потім виконати за допомогою

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

у кореневому каталозі репозиторіїв, що створює файл export.zip файл, що містить усі змінені файли, що зберігають структуру каталогів.

Можливо, це комусь теж потрібно, тому я подумав поділитися тут.

Застереження : Я є автором модуля gitzip .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.