Экспортировать только измененные и добавленные файлы со структурой папок в Git

Я хотел бы получить список измененных и добавленных файлов в конкретном коммите, чтобы я мог их экспортировать и сгенерировать пакет с файловой структурой.

Идея состоит в том, чтобы получить пакет и распаковать его на сервере. По многим причинам я не могу создать ловушку для автоматического извлечения репо, и самый простой способ поддерживать сервер в актуальном состоянии - это создать этот пакет.


person Michael Kuhinica    schedule 27.12.2010    source источник


Ответы (13)


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 -
person Aristotle Pagaltzis    schedule 27.12.2010
comment
Теперь мне просто нужно найти способ склеить результат этой команды с tar! Спасибо! - person Michael Kuhinica; 05.01.2011
comment
@Taverneiro Вы можете передать его по конвейеру в команду tar , указанную в этом ответе, например. для файла tgz: git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | tar -czf file.tgz -T - - person Matthieu Sadouni; 19.12.2014

git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $commit_id | xargs tar -rf mytarfile.tar

Чтобы завершить это, вот команда, переданная в tar. Это экспортирует файлы в tar-архив.

person James Ehly    schedule 24.09.2011
comment
Можно ли это сделать для нескольких коммитов в один tar-файл? - person evolutionxbox; 14.08.2014
comment
При попытке добавить несколько коммитов к одному и тому же файлу tar иногда tar снова создает filename.ext с другим именем (filename1.ext). Я понимаю, что если я использую zip, я могу добавить и перезаписать файл, если он существует в zip ... Вам просто нужно заменить xargs на xargs zip -r0 myzipfile.zip $ 1 - person Ricardo Martins; 23.09.2014
comment
Здесь xargs нужен? Я знаю, что это для таких вещей, как rm, но я думал, что tar может обрабатывать столько файлов, сколько вы хотите. - person Erdal G.; 07.06.2016
comment
Будьте осторожны с этим ответом, это принимает файлы diff, но как текущую версию текущей ветки - person mems; 20.03.2018
comment
При выполнении этой команды на подчиненном компьютере Windows я получаю следующую ошибку: «xargs» не распознается как внутренняя или внешняя команда, работающая программа или командный файл. - person Navin prasad; 23.01.2019
comment
@mems предполагает, что разница - это fileA, тогда как получить версию $ commit_id для fileA вместо текущей версии fileA? - person user1169587; 28.06.2019

Вот однострочная команда, которая работает в Windows 7. Запустите ее из папки верхнего уровня вашего репозитория.

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 отвечает на неизбежный вопрос xcopy о том, копируете ли вы файл или каталог (файл), а также на возможный вопрос о перезаписи файла (перезаписать все)
  • usebackq позволяет нам использовать выходные данные нашей команды git в качестве входных данных для нашего предложения do.
  • HEAD ~ 1 HEAD получает все различия между предыдущей фиксацией и текущей HEAD.
  • % ~ fA преобразует вывод git в полностью определенные пути (необходимо для замены косой черты на обратную).
  • C: \ git_changed_files \ - это место, где вы найдете все файлы, которые отличаются
person MM.    schedule 22.04.2012
comment
Когда я увидел этот беспорядок символов, я не ожидал, что он будет работать без обширной настройки ... но он сработал с первой попытки, спасибо. - person Nathan Fig; 28.03.2014
comment
Немного внимания всем парням, которые (как и я) не очень знакомы с пакетными файлами Windows: если вы хотите использовать указанную выше команду в пакетном файле, вам нужно будет заменить% A на %% A внутри цикла for - person buddybubble; 17.11.2015
comment
Какие? РЖУ НЕ МОГУ. Просто используйте комбо git archive - git diff (ответ Николаса). Намного проще и понятнее. (честно говоря, я не знаю, что здесь происходит.) - person ADTC; 23.11.2015
comment
Предупреждение: это скопирует файлы из вашей рабочей копии, которые могут фактически не соответствовать тому, что находится в HEAD (или какому-либо хешу фиксации, которым вы его заменяете) - person Simon East; 07.03.2019
comment
Отличный материал, но работает только с CMD, а не с Powershell, -v ° v- - person Rodion Bykov; 30.06.2020

если ваш хеш фиксации, например, 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/

person Nicolas Dermine    schedule 19.12.2013
comment
Я думаю, что могу использовать function в PowerShell, чтобы уменьшить команду до простого имени функции и использовать $args для однократной передачи хэша фиксации. В * nix вы, вероятно, можете использовать сценарии оболочки для достижения того же эффекта. - person ADTC; 28.08.2014
comment
Человек ... у меня есть пиво :) - person deanpodgornik; 02.04.2015
comment
просто обратите внимание, что архив git не работает для ряда ревизий - person Ben Sewards; 12.06.2015
comment
@BenSewards для диапазона коммитов между commit1 и commit2 (включая последующие фиксации, исключая более ранние фиксации; порядок не имеет значения) просто сделайте git archive -o patch.zip HEAD /**or a commit**/ $(git diff --name-only commit1 commit2). Фиксация, переданная команде archive, может быть любой произвольной фиксацией (скорее всего, либо HEAD, либо более поздней фиксации), и файлы извлекаются, как если бы они были извлечены на этой стадии фиксации. commit1 и commit2, переданные в diff, используются только для создания списка файлов для извлечения - они не влияют на версию извлекаемых файлов. - person ADTC; 23.11.2015
comment
Дополнительный совет: если вы хотите объединить измененные файлы из нескольких диапазонов коммитов в один архив, просто повторите команду 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) - person ADTC; 23.11.2015
comment
Из-за пробелов в именах путей мне пришлось использовать канал для xargs, обрабатывающий NUL git diff -z --name-only commit1 commit2 | xargs -0 git archive -o patch.zip HEAD - person Boggin; 02.06.2016
comment
@ADTC не может использовать git archive -o patch.zip HEAD / ** или фиксацию ** / $ (git diff --name-only commit1A..commit3B)? но нужно переставить команду diff с точкой с запятой? - person user1169587; 28.06.2019
comment
@ user1169587 ваша версия предназначена для одного диапазона коммитов (ВСЕ коммиты от 1A до 3B). Я говорил об объединении нескольких диапазонов коммитов (1-й диапазон от 1A до 1B; 2-й диапазон от 2A до 2B; 3-й диапазон от 3A до 3B и т. Д.), Поэтому коммиты за пределами этих диапазонов будут проигнорированы. - person ADTC; 11.08.2019

Вы можете экспортировать diff с помощью Tortoise Git в MS Windows:

Я щелкаю правой кнопкой мыши и выбираю TortoiseGit> Показать журнал, и Сообщения журнала будут открыты.

Выберите две версии и сравните их. Разница между будет открыта.

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

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

person Wendel    schedule 29.10.2016
comment
Определенно. Так много ответов на этот вопрос содержали команды только для Linux, и многие из них не фиксировали рабочие изменения, а только те изменения, которые были зафиксированы. Рад, что это было опубликовано! - person dythim; 17.07.2019

Мне нужно было обновить свой тестовый сервер и добавить файлы, которые изменились с версии 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
person pietr    schedule 13.02.2015
comment
Я только что обнаружил СТРАННУЮ проблему: если я попробую приведенный выше код, он сработает как шарм. Но я меняю tar -rf test.tar на tar -zcf test.tar.gz, тогда в итоговом файле не хватает нескольких файлов. Что может быть причиной такой разницы в результате? - person Alberto Alexander Rodriguez; 19.06.2017
comment
При выполнении этой команды на подчиненном компьютере Windows я получаю следующую ошибку: «xargs» не распознается как внутренняя или внешняя команда, работающая программа или командный файл. - person Navin prasad; 23.01.2019

Ниже команды работали для меня.

Если вам нужна разница файлов, измененных последней фиксацией:

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 способ фиксировать все, ветки дешевы:

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^)
person me_astr    schedule 24.09.2018

Я сделал 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 - это можно настроить при запуске установщика Git для Windows.

person Lemon Juice    schedule 06.01.2011
comment
этот код великолепен, но я хочу экспортировать файлы и каталоги с подмодулями, ваш код работал только с основным репозиторием, и если подмодули изменили ваш код, скажем, папка подмодуля удалена! - person morteza khadem; 12.10.2017

Чтобы экспортировать измененные файлы, начиная с даты:

  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"
person catalinp    schedule 28.11.2016

Вот небольшой сценарий 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/
person Patrick.SE    schedule 23.02.2018

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

Как следствие, я добавил реализацию на Python. Его можно установить с помощью pip install gitzip, а затем выполнить с помощью

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

в корне репозиториев создается export.zip файл, содержащий все измененные файлы, сохраняющие структуру каталогов.

Может кому-то это тоже нужно, поэтому решил поделиться здесь.

Заявление об ограничении ответственности: я являюсь автором модуля gitzip.

person miile7    schedule 08.12.2020

Я тоже сталкивался с подобной проблемой раньше. Я написал простой сценарий 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 в скрипте. Надеюсь, это поможет!

person Raj Hawaldar    schedule 04.10.2018

У меня это работает как для Unix, так и для Windows:

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

В команде есть два заполнителя. Вам необходимо заменить их по вашему назначению:

  • _

    git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT __1__.. | xargs cp --parents -t __2__
    
    _: замените на идентификатор фиксации фиксации прямо перед всеми фиксациями, которые вы хотите экспортировать (например, 997cc7b6 - не забудьте сохранить эту двойную точку после идентификатора фиксации - это означает "задействовать все коммиты новее, чем этот коммит")

  • __2__: замените на существующий путь, по которому вы хотите экспортировать файлы (например, ../export_path/)

В результате вы получите свои файлы в виде структуры папок (без zip / tars ...), поскольку кто-то может использовать экспорт черепахи svn в репозитории svn.

Это, например, очень полезно, когда вы хотите выполнить развертывание вручную добавленных / измененных файлов нескольких последних коммитов. Таким образом, вы можете просто скопировать эти файлы через ftp-клиент.

person Skybamar    schedule 28.02.2019