rsync excluir de acordo com .gitignore & .hgignore & svn: ignore como --filter =: C

113

O Rsync inclui uma opção bacana --cvs-excludepara “ignorar os arquivos da mesma forma que o CVS”, mas o CVS está obsoleto há anos. Existe alguma maneira de fazer com que ele também exclua arquivos que seriam ignorados pelos sistemas de controle de versão modernos (Git, Mercurial, Subversion)?

Por exemplo, eu tenho muitos projetos Maven retirados do GitHub. Normalmente, eles incluem uma .gitignorelista, pelo menos target, o diretório padrão de construção do Maven (que pode estar presente no nível superior ou em submódulos). Como o conteúdo desses diretórios é totalmente descartável e podem ser muito maiores do que o código-fonte, gostaria de excluí-los ao usar o rsync para backups.

Claro que posso explicitamente, --exclude=target/mas isso suprimirá acidentalmente diretórios não relacionados que apenas foram nomeados targete não devem ser ignorados.

E eu poderia fornecer uma lista completa de caminhos absolutos para todos os nomes de arquivos e padrões mencionados em qualquer .gitignore, .hgignoreou svn:ignorepropriedade no meu disco, mas isso seria uma lista enorme que teria de ser produzida por algum tipo de script.

Visto que o rsync não tem suporte embutido para checkouts VCS além do CVS, existe algum bom truque para alimentá-lo com seus padrões de ignorar? Ou algum tipo de sistema de retorno de chamada por meio do qual um script de usuário pode ser perguntado se um determinado arquivo / diretório deve ser incluído ou não?

Atualização : --filter=':- .gitignore'como sugerido por LordJavac, parece funcionar tão bem para Git quanto --filter=:Cpara CVS, pelo menos nos exemplos que encontrei, embora não esteja claro se a sintaxe é uma correspondência exata. --filter=':- .hgignore'não funciona muito bem para o Mercurial; por exemplo, um .hgignorecontendo uma linha como ^target$(o Mercurial equivalente do Git /target/) não é reconhecido pelo rsync como uma expressão regular. E nada parece funcionar para o Subversion, para o qual você teria que analisar .svn/dir-prop-basepara uma cópia de trabalho 1.6 ou anterior, e desanimar para uma cópia de trabalho 1.7 ou posterior.

Jesse Glick
fonte
11
Parece que seria uma boa ideia enviar um patch para rsync que adiciona suporte para .gitignore, .hgignore, etc.
ThiefMaster
3
@ThiefMaster: Arquivei bugzilla.samba.org/show_bug.cgi?id=9744 como ponto de partida.
Jesse Glick
2
apenas uma observação para os outros, o .gitignore precisa estar na hierarquia de pastas sendo rysnc'd, não no diretório em que o comando está sendo executado
myol
O que :-significa exatamente? O que significa o cólon? Qual é o traço?
David
O Git agora tem um check-ignoresubcomando que pode lidar com o trabalho duro de analisar os vários arquivos "ignorar", se você quiser ir com a opção "gerar uma lista de todos os arquivos não ignorados". Minha resposta aqui dá detalhes de como fazer isso.
cjs

Respostas:

120

Conforme mencionado por luksan, você pode fazer isso com a --filterchave para rsync. Eu consegui isso com --filter=':- .gitignore'(há um espaço antes de ".gitignore") que diz rsyncpara fazer uma fusão de diretório com .gitignorearquivos e excluí-los de acordo com as regras do git. Você também pode adicionar seu arquivo global para ignorar, se tiver um. Para facilitar o uso, criei um alias rsyncque incluía o filtro.

LordJavac
fonte
Um bom começo, embora eu hesite em “aceitar” essa resposta, pois ela cobre apenas o Git.
Jesse Glick
23
Uma versão mais detalhada que também exclui arquivos --exclude='/.git' --filter="dir-merge,- .gitignore"
.git
2
Eu tenho algo assim agora: rsync -rvv --exclude='.git*' --exclude='/rsync-to-dev.sh' --filter='dir-merge,-n /.gitignore' $DIR/ development.foobar.com:~/test/.. mas embora diga [sender] hiding file .gitignore because of pattern .git*, o arquivo ainda é enviado para a desintização
rolandow
2
Se você também quiser usar --deleteopção, aqui é a linha de comando de trabalho: rsync --delete-after --filter=":e- .gitignore" --filter "- .git/" -v -a .... Isso me levou um tempo ... eno filtro e --delete-aftersão importantes. Eu sugiro ler o capítulo "REGRAS POR DIRETÓRIO E EXCLUIR" da rsyncpágina de manual.
dbolotin
1
Para sincronizar exclusões, bem como adições e atualizações, você pode simplesmente adicionar --delete-aftera versão do comando de @VasiliNovikov. (Isso parece equivalente à versão do comando de @ dboliton, exceto que @db usa: e que eu acho que exclui os arquivos .gitignore de serem copiados, o que não é o que eu queria.)
Bampfer
10

Você pode usar git ls-filespara construir a lista de arquivos excluídos pelos arquivos do repositório .gitignore. https://git-scm.com/docs/git-ls-files

Opções:

  • --exclude-standardConsidere todos os .gitignorearquivos.
  • -o Não ignore as mudanças não planejadas.
  • -i Produzir apenas arquivos ignorados.
  • --directory Somente imprima o caminho do diretório se todo o diretório for ignorado.

A única coisa que deixei para ignorar foi .git.

rsync -azP --exclude=.git --exclude=`git -C <SRC> ls-files --exclude-standard -oi --directory` <SRC> <DEST>
Jared Deckard
fonte
4
isso não funciona. ele exclui o primeiro arquivo do subcomando git e, em seguida, trata o resto como parte da lista SRC. isso funciona: rsync -azP --exclude-from="$(git -C SRC ls-files --exclude-standard -oi --directory > /tmp/excludes; echo /tmp/excludes)" SRC DEST
maratona de
2
Este é o único método que funciona se você excluir e incluir linhas em seu .gitignore(ou seja, linhas que começam com !). Ele também sincroniza os arquivos que você --forceadicionou ao repo, o que geralmente é uma coisa boa.
ostrokach
1
Na verdade, essa resposta NÃO FUNCIONA, então acabei escrevendo uma que funciona: stackoverflow.com/a/50059607/99834
sorin
6

que tal rsync --exclude-from='path/.gitignore' --exclude-from='path/myignore.txt' source destination?
Funcionou para mim
Eu acredito que você pode ter mais --exclude-fromparâmetros também.

Ericn
fonte
3
Isso funcionará na medida em que seus .gitignorearquivos usem uma sintaxe compatível com rsync.
Jesse Glick
@JesseGlick está certo, o rsync não consegue analisar arquivos .gitignore, consulte stackoverflow.com/a/50059607/99834 workround.
Sorin
6

Solução 2018 confirmada

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 

Detalhes: --exclude-fromé obrigatório em vez de --exclude porque o caso provável dessa lista de exclusões não seria analisado como um argumento. Excluir de requer um arquivo e não pode funcionar com tubos.

A solução atual salva o arquivo de exclusão dentro da pasta .git para garantir que ele não afetará git status, mantendo-o independente. Se você quiser, pode usar / tmp.

Sorin
fonte
3
Parece que funcionará se você tiver um repositório Git específico que deseja sincronizar, o SRCaqui, mas não para o problema original que afirmei, que é um diretório extenso com milhares de repositórios Git como subdiretórios em várias profundidades, muitos dos quais têm idiossincrático .gitignores.
Jesse Glick
1
Se você estiver usando um shell com suporte para substituição de processos (bash, zsh, etc.), você pode usar--exclude-from=<(git -C SRC ls-files --exclude-standard -oi --directory)
Roland W
3

Para mercurial, você pode usar

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

para coletar a lista de arquivos que NÃO estão sob controle mercurial por causa das restrições .hgignore e, em seguida, execute

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

para sincronizar todos os arquivos, exceto os ignorados. Observe -m sinalizador em rsync que excluirá diretórios vazios da sincronização porque hg status -i listaria apenas arquivos excluídos, não dirs

festa
fonte
2

Experimente isto:

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

Ele pode copiar todos os arquivos para um diretório remoto, excluindo arquivos em '.gitignore', e excluir arquivos que não estejam em seu diretório atual.

Shawn Wang
fonte
1

De acordo com a rsyncpágina do manual, além da lista padrão de padrões de arquivo:

os arquivos listados em $ HOME / .cvsignore são adicionados à lista e quaisquer arquivos listados na variável de ambiente CVSIGNORE

Então, meu arquivo $ HOME / .cvsignore se parece com isto:

.git/
.sass-cache/

para excluir .git e os arquivos gerados pelo Sass .

Doug Harris
fonte
2
Ao contrário, eu definitivamente quero incluir .git/diretórios, talvez ainda mais fortemente do que a cópia de trabalho. O que eu quero excluir são produtos de construção.
Jesse Glick
Além disso, essa configuração não é portátil. É por usuário, não por projeto.
VasiliNovikov
@JesseGlick Concordo com você sobre como manter .git / dirs incluídos. Por ser um SCM distribuído, é importante fazer backup de todo o repositório local.
Johan Boulé
1 / A frase da rsyncpágina de manual citada nesta resposta descreve a --cvs-excludeopção, portanto, você deve usá-la explicitamente. 2 / Você pode criar .cvsignorearquivos em qualquer diretório para ignorar específicos do projeto, eles também são lidos. 3 / .gitjá é ignorado quando você usa --cvs-exclude, de acordo com o manual, então tê-lo $HOME/.cvsignoreativado parece redundante.
Niavlys
1

Eu tinha vários .gitignorearquivos muito grandes e nenhuma das soluções de "rsync puro" funcionou para mim. Eu escrevi este script de wrapper rsync , ele respeita totalmente as .gitignoreregras ( !exceções de estilo de inclusão e .gitignorearquivos em subdiretórios) e tem funcionado perfeitamente para mim.

Cobbzilla
fonte
Tentando isso via locate -0e .gitignore | (while read -d '' x; do process_git_ignore "$x"; done), mas tem muitos problemas. Os arquivos no mesmo diretório .gitignorenão foram separados corretamente do nome do diretório com /. Linhas em branco e comentários mal interpretados. Bloqueia em .gitignorearquivos em caminhos com espaços (não importa o diabólico /opt/vagrant/embedded/gems/gems/rb-fsevent-0.9.4/spec/fixtures/custom 'path/.gitignoredo vagrantpacote para Ubuntu). Talvez seja melhor executado como um script Perl.
Jesse Glick
@JesseGlick Não sei por que você está chamando a função dentro do script. destina-se a ser usado como um substituto imediato para rsync, pelo motivo específico de que lidar com citações / espaços em branco é uma dor. Se você tiver um exemplo de uma gsynclinha de comando que está falhando e os .gitignorearquivos associados a ela, ficaria feliz em dar uma olhada mais de perto.
cobbzilla
Eu preciso de rsyncum sistema de arquivos inteiro, com vários repositórios Git espalhados por ele. Talvez seu script funcione bem no caso de sincronização de um único repositório.
Jesse Glick
1
sim definitivamente. desculpe, eu não deixei isso claro. Com este script, você teria que invocá-lo uma vez por git repo, de dentro do diretório repo.
cobbzilla
0

Verifique a seção MERGE-FILES FILTER RULES em rsync (1).

Parece que é possível criar uma regra rsync --filter que incluirá arquivos .gitignore conforme atravessa a estrutura de diretório.

luksan
fonte
0

Em vez de criar filtros de exclusão, você pode usar git ls-filespara selecionar cada arquivo para 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

Isso funciona mesmo que git ls-filesretorne caminhos separados por nova linha. Provavelmente não funcionará se você tiver arquivos versionados com espaços nos nomes dos arquivos.


fonte
0

Alternativas:

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

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

(o rsync entende apenas parcialmente .gitignore)

druid62
fonte
0

Resposta curta

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

Significado dos parâmetros:

-r: recursivo

--info=...: mostrar progresso

--filter=...: exclui pelas regras listadas no arquivo .gitignore

Adrian
fonte