rsync исключить в соответствии с .gitignore & .hgignore & svn: игнорировать, как --filter =: C

114

Rsync включает отличную возможность --cvs-exclude«игнорировать файлы так же, как это делает CVS», но CVS уже много лет устарела. Есть ли способ сделать так, чтобы он также исключал файлы, которые игнорировались бы современными системами контроля версий (Git, Mercurial, Subversion)?

Например, у меня есть много проектов Maven, проверенных на GitHub. Обычно они включают в себя .gitignoreсписок как минимум targetкаталога сборки Maven по умолчанию (который может присутствовать на верхнем уровне или в подмодулях). Поскольку содержимое этих каталогов полностью одноразовое и может быть намного больше исходного кода, я хотел бы исключить их при использовании rsync для резервного копирования.

Конечно, я могу явно, --exclude=target/но это случайно подавит несвязанные каталоги, которые случайно названы targetи не должны игнорироваться.

И я мог бы предоставить полный перечень абсолютных путей для всех имен файлов и шаблонов , упомянутых в любом .gitignore, .hgignoreили svn:ignoreсобственность на моем диске, но это был бы огромный список , который должен был бы быть получен какой - то сценарий.

Поскольку rsync не имеет встроенной поддержки проверок VCS, кроме CVS, есть ли какой-нибудь хороший трюк, чтобы скормить ему их шаблоны игнорирования? Или какая-то система обратного вызова, с помощью которой пользовательский сценарий может быть задан вопрос, следует ли включать данный файл / каталог?

Обновление : --filter=':- .gitignore'как было предложено LordJavac, похоже, работает также для Git, как --filter=:Cи для CVS, по крайней мере, в найденных мной примерах, хотя неясно, является ли синтаксис точным совпадением. --filter=':- .hgignore'не очень хорошо работает для Mercurial; например, .hgignoreсодержащий строку вроде ^target$(Mercurial эквивалент Git /target/) не распознается rsync как регулярное выражение. И, похоже, ничего не работает для Subversion, для которой вам придется разобрать .svn/dir-prop-baseрабочую копию 1.6 или более ранней версии и в ужасе вскинуть руки для рабочей копии 1.7 или более поздней версии.

Джесси Глик
источник
11
Похоже, было бы неплохо отправить патч для rsync, который добавляет поддержку .gitignore, .hgignore и т. Д.
ThiefMaster
3
@ThiefMaster: Я указал bugzilla.samba.org/show_bug.cgi?id=9744 в качестве отправной точки.
Джесси Глик,
2
просто примечание для других, .gitignore должен находиться в иерархии папок, которую выполняет rysnc'd, а не в каталоге, в котором выполняется
команда
Что :-именно значит? Что означает двоеточие? Что за черта?
Дэвид
Git теперь имеет check-ignoreподкоманду, которая может справиться с тяжелой работой по синтаксическому анализу различных «игнорируемых» файлов, если вы хотите использовать опцию «генерировать список всех не игнорируемых файлов». В моем ответе подробно описано, как это сделать.
cjs 07

Ответы:

121

Как упоминал luksan, вы можете сделать это с помощью --filterпереключателя на rsync. Я добился этого с помощью --filter=':- .gitignore'(есть пробел перед ".gitignore"), который сообщает, rsyncчто нужно объединить каталог с .gitignoreфайлами и исключить их по правилам git. Вы также можете добавить свой глобальный файл игнорирования, если он у вас есть. Чтобы упростить использование, я создал псевдоним, в rsyncкоторый включен фильтр.

LordJavac
источник
Хорошее начало, хотя я не решаюсь «принять» этот ответ, поскольку он касается только Git.
Джесси Глик,
24
Более подробная версия, которая также исключает файлы .git:--exclude='/.git' --filter="dir-merge,- .gitignore"
VasiliNovikov
2
У меня сейчас что-то вроде этого: rsync -rvv --exclude='.git*' --exclude='/rsync-to-dev.sh' --filter='dir-merge,-n /.gitignore' $DIR/ development.foobar.com:~/test/.. но хотя там написано [sender] hiding file .gitignore because of pattern .git*, файл все равно отправляется на удаление
rolandow
2
Если вы хотите использовать --deleteвариант, здесь работает из командной строки: rsync --delete-after --filter=":e- .gitignore" --filter "- .git/" -v -a .... Это заняло у меня некоторое время ... eв фильтре, и --delete-afterоба они важны. Я предлагаю прочитать главу «ПРАВИЛА ДЛЯ КАТАЛОГА И УДАЛИТЬ» на rsyncстранице руководства .
dbolotin
1
Для синхронизации --delete-afterудалений, добавлений и обновлений вы можете просто добавить в версию команды @ VasiliNovikov. (Это похоже на версию команды @ dboliton, за исключением того, что @db использует: e, что, как мне кажется, исключает копирование файлов .gitignore, а это не то, что я хотел.)
Bampfer
10

Вы можете использовать git ls-filesдля построения списка файлов, исключенных из файлов репозитория .gitignore. https://git-scm.com/docs/git-ls-files

Параметры:

  • --exclude-standardСчитайте все .gitignoreфайлы.
  • -o Не игнорируйте неустановленные изменения.
  • -i Выводить только игнорируемые файлы.
  • --directory Выводить путь к каталогу, только если игнорируется весь каталог.

Единственное, что я оставил игнорировать, это .git.

rsync -azP --exclude=.git --exclude=`git -C <SRC> ls-files --exclude-standard -oi --directory` <SRC> <DEST>
Джаред Декард
источник
4
это не работает. он исключает первый файл из подкоманды git, а затем обрабатывает остальные как часть списка SRC. это работает: rsync -azP --exclude-from="$(git -C SRC ls-files --exclude-standard -oi --directory > /tmp/excludes; echo /tmp/excludes)" SRC DEST
марафон
2
Это единственный метод, который работает, если в вашем (то есть строки, начинающиеся с ) есть как строки исключения, так и строки включения . Это также файлы rsyncs, которые вы добавили в свое репо, что обычно хорошо. .gitignore!--force
острокач
1
На самом деле этот ответ НЕ РАБОТАЕТ, поэтому в итоге я написал тот, который работает: stackoverflow.com/a/50059607/99834
sorin
6

как насчет rsync --exclude-from='path/.gitignore' --exclude-from='path/myignore.txt' source destination?
У меня это сработало.
Я считаю, что у вас тоже может быть больше --exclude-fromпараметров.

ericn
источник
3
Это будет работать, если ваши .gitignoreфайлы используют синтаксис, совместимый с rsync.
Джесси Глик,
@JesseGlick прав, rsync не может анализировать файлы .gitignore, см. Рабочий процесс stackoverflow.com/a/50059607/99834 .
sorin
6

Решение 2018 подтверждено

rsync -ah --delete 
    --include .git --exclude-from="$(git -C SRC ls-files \
        --exclude-standard -oi --directory >.git/ignores.tmp && \
        echo .git/ignores.tmp')" \
    SRC DST 

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

Текущее решение сохраняет файл исключений внутри папки .git, чтобы гарантировать, что он не повлияет git status, сохраняя его автономным. Если хотите, можете использовать / tmp.

Сорин
источник
3
Похоже, это сработает, если у вас есть конкретный репозиторий Git, который вы хотите синхронизировать - SRCздесь, - но не для исходной проблемы, о которой я говорил, которая представляет собой разрастающийся каталог с тысячами репозиториев Git в качестве подкаталогов на разной глубине, многие из которых имеют идиосинкразический .gitignores.
Джесси Глик
1
Если вы используете оболочку с поддержкой подстановки процессов (bash, zsh и т. Д.), Вы можете использовать--exclude-from=<(git -C SRC ls-files --exclude-standard -oi --directory)
Roland W
3

Для ртутного вы можете использовать

hg status -i | sed 's/^I //' > /tmp/tmpfile.txt

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

rsync -avm --exclude-from=/tmp/tmpfile.txt --delete source_dir/ target_dir/

для rsync всех файлов, кроме игнорируемых. Обратите внимание на флаг -m в rsync, который исключит пустые каталоги из синхронизации, потому что hg status -i будет перечислять только исключенные файлы, а не каталоги

ffeast
источник
2

Попробуй это:

rsync -azP --delete --filter=":- .gitignore" <SRC> <DEST>

Он может копировать все файлы в удаленный каталог, за исключением файлов в '.gitignore', и удалять файлы не в вашем текущем каталоге.

Шон Ван
источник
1

На rsyncстранице руководства в дополнение к стандартному списку шаблонов файлов:

файлы, перечисленные в $ HOME / .cvsignore, добавляются в список, а любые файлы, перечисленные в переменной среды CVSIGNORE

Итак, мой файл $ HOME / .cvsignore выглядит так:

.git/
.sass-cache/

чтобы исключить .git и файлы, созданные Sass .

Дуг Харрис
источник
2
Напротив, я определенно хочу включить .git/каталоги, возможно, даже сильнее, чем рабочая копия. Я хочу исключить продукты сборки.
Джесси Глик,
Кроме того, этот параметр не переносится. Это для каждого пользователя, а не для проекта.
VasiliNovikov
@JesseGlick Я поддерживаю вас по поводу включения .git / dirs. Git - это распределенный SCM, поэтому важно сделать резервную копию всего локального репозитория.
Johan Boulé
1 / Предложение на rsyncстранице руководства, цитируемое в этом ответе, описывает этот --cvs-excludeпараметр, поэтому вы должны использовать его явно. 2 / Вы можете создавать .cvsignoreфайлы в любом каталоге, чтобы игнорировать специфичные для проекта файлы, они также читаются. 3 / .gitуже игнорируется при использовании --cvs-exclude, согласно руководству, поэтому его использование $HOME/.cvsignoreкажется излишним.
Niavlys
1

У меня было несколько очень больших .gitignoreфайлов, и ни одно из решений "чистого rsync" не помогло мне. Я написал этот сценарий оболочки rsync , он полностью соблюдает .gitignoreправила (включая !исключения в стиле и .gitignoreфайлы в подкаталогах) и работал для меня как шарм.

Cobbzilla
источник
Пробую через это locate -0e .gitignore | (while read -d '' x; do process_git_ignore "$x"; done), но есть много проблем. Файлы в том же каталоге .gitignoreнеправильно отделены от имени каталога с помощью /. Пустые строки и комментарии неверно истолкованы. Давится .gitignoreфайлами в путях с пробелами (не говоря уже о мерзавцах /opt/vagrant/embedded/gems/gems/rb-fsevent-0.9.4/spec/fixtures/custom 'path/.gitignoreиз vagrantпакета для Ubuntu). Возможно, лучше сделать это в виде Perl-скрипта.
Джесси Глик,
@JesseGlick Я не уверен, почему вы вызываете функцию в скрипте. он предназначен для использования в качестве замены rsyncпо той причине, что обработка кавычек / пробелов является такой болью. Если у вас есть пример сбойной gsyncкомандной строки и .gitignoreсвязанные с ней файлы, я был бы рад изучить его поближе.
cobbzilla
Мне нужна rsyncцелая файловая система с разбросанными по ней различными репозиториями Git. Возможно, ваш скрипт отлично подойдет для случая синхронизации одного репозитория.
Джесси Глик,
1
определенно да. извините, я не разъяснил это. С помощью этого скрипта вам придется вызывать его один раз для каждого репозитория git из каталога репо.
cobbzilla
0

Ознакомьтесь с разделом ПРАВИЛА ФИЛЬТРА MERGE-FILES в rsync (1).

Похоже, что можно создать правило rsync --filter, которое будет включать файлы .gitignore по мере прохождения структуры каталогов.

Луксан
источник
0

Вместо создания фильтров исключения вы можете использовать git ls-filesдля выбора каждого файла для rsync:

#!/usr/bin/env bash

if [[ ! $# -eq 2 ]] ; then
    echo "Usage: $(basename $0) <local source> <rsync destination>"
    exit 1
fi

cd $1
versioned=$(git ls-files --exclude-standard)
rsync --verbose --links --times --relative --protect-args ${versioned} $2

Это работает, даже если git ls-filesвозвращает пути, разделенные новой строкой. Вероятно, не будет работать, если у вас есть версированные файлы с пробелами в именах файлов.


источник
0

Альтернативы:

git ls-files -zi --exclude-standard |rsync -0 --exclude-from=- ...

git ls-files -zi --exclude-per-directory=".gitignore" |...

(rsync понимает только частично .gitignore)

друид62
источник
0

Короткий ответ

rsync -r --info=progress2 --filter=':- .gitignore' SOURCE DEST/

Значение параметров:

-r: рекурсивный

--info=...: показать прогресс

--filter=...: исключить по правилам, указанным в файле .gitignore

Адриан
источник
0

После нескольких часов исследований я нашел именно то, что мне нужно: синхронизировать папку назначения с исходной папкой (также удаляя файлы в месте назначения, если они были удалены в источнике), а не копировать в место назначения файлы, которые игнорируются .gitignore, но также не удалять эти файлы в месте назначения :

clear && rsync -vhra /source/project/ /destination/project/ --include='**.gitignore' --exclude='/.git' --filter=':- .gitignore' --delete-after

Другими словами, эта команда полностью игнорирует файлы из .gitignore как в источнике, так и в месте назначения . Вы можете опустить --exclude='/.git'часть, если хотите скопировать и .gitпапку.

Вы ДОЛЖНЫ копировать файлы .gitignore из источника. Если вы будете использовать команду LordJavac, файл .gitignore не будет скопирован. И если вы создаете файл в папке назначения, который должен игнорироваться .gitignore, этот файл будет удален, несмотря на .gitignore. Это потому, что у вас нет файлов .gitignore в месте назначения. Но если у вас будут эти файлы, файлы, описанные в .gitignore, не будут удалены, они будут проигнорированы, как ожидается.

Джеймс Бонд
источник