rsync excluir según .gitignore & .hgignore & svn: ignorar como --filter =: C

113

Rsync incluye una opción ingeniosa --cvs-excludepara "ignorar archivos de la misma manera que lo hace CVS", pero CVS ha estado obsoleto durante años. ¿Hay alguna forma de hacer que también excluya archivos que serían ignorados por los sistemas de control de versiones modernos (Git, Mercurial, Subversion)?

Por ejemplo, tengo muchos proyectos de Maven extraídos de GitHub. Por lo general, incluyen una .gitignorelista al menos target, el directorio de compilación predeterminado de Maven (que puede estar presente en el nivel superior o en submódulos). Dado que el contenido de estos directorios es completamente desechable y pueden ser mucho más grandes que el código fuente, me gustaría excluirlos al usar rsync para las copias de seguridad.

Por supuesto que puedo explícitamente, --exclude=target/pero eso suprimirá accidentalmente directorios no relacionados que simplemente tienen un nombre targety no se supone que deben ignorarse.

Y podría proporcionar una lista completa de rutas absolutas para todos los nombres de archivo y los patrones mencionadas en cualquiera de .gitignore, .hgignoreo svn:ignorela propiedad en mi disco, pero esto sería una lista enorme que tendría que ser producida por una especie de guión.

Dado que rsync no tiene soporte incorporado para las comprobaciones de VCS que no sean CVS, ¿hay algún buen truco para alimentarlo con sus patrones de ignorar? ¿O algún tipo de sistema de devolución de llamada mediante el cual se le puede preguntar a un script de usuario si un archivo / directorio determinado debe incluirse o no?

Actualización : --filter=':- .gitignore'como sugiere LordJavac, parece funcionar tan bien para Git como --filter=:Cpara CVS, al menos en los ejemplos que he encontrado, aunque no está claro si la sintaxis es una coincidencia exacta. --filter=':- .hgignore'no funciona muy bien para Mercurial; por ejemplo, rsync no reconoce como expresión regular un que .hgignorecontenga una línea como ^target$(el equivalente Mercurial de Git /target/). Y nada parece funcionar para Subversion, para lo cual tendría que analizar .svn/dir-prop-baseuna copia de trabajo 1.6 o anterior, y levantar las manos consternado por una copia de trabajo 1.7 o posterior.

Jesse Glick
fuente
11
Suena un poco como si fuera una buena idea enviar un parche para rsync que agregue soporte para .gitignore, .hgignore, etc.
ThiefMaster
3
@ThiefMaster: Archivé bugzilla.samba.org/show_bug.cgi?id=9744 como punto de partida.
Jesse Glick
2
solo una nota para otros, el .gitignore debe estar en la jerarquía de carpetas que se está rysnc'd, no en el directorio en el que se está ejecutando el comando
myol
¿Qué :-significa exactamente? ¿Qué significa el colon? ¿Qué diablos?
David
Git ahora tiene un check-ignoresubcomando que puede lidiar con el arduo trabajo de analizar los distintos archivos "ignorar", si desea utilizar la opción "generar una lista de todos los archivos no ignorados". Mi respuesta aquí da detalles de cómo hacer eso.
cjs

Respuestas:

120

Como lo mencionó luksan, puede hacer esto con el --filtercambio a rsync. --filter=':- .gitignore'Logré esto con (hay un espacio antes de ".gitignore") que dice rsynchacer una fusión de directorio con .gitignorearchivos y hacer que se excluyan según las reglas de git. Es posible que también desee agregar su archivo de ignorar global, si tiene uno. Para que sea más fácil de usar, creé un alias en el rsyncque se incluía el filtro.

LordJavac
fuente
Un buen comienzo, aunque dudo en "aceptar" esta respuesta, ya que solo cubre Git.
Jesse Glick
23
Una versión más detallada que también excluye los archivos .git:--exclude='/.git' --filter="dir-merge,- .gitignore"
VasiliNovikov
2
Tengo algo como esto ahora: rsync -rvv --exclude='.git*' --exclude='/rsync-to-dev.sh' --filter='dir-merge,-n /.gitignore' $DIR/ development.foobar.com:~/test/.. pero aunque dice [sender] hiding file .gitignore because of pattern .git*, el archivo todavía se envía a la destinación
rolandow
2
Si también desea utilizar --deletela opción, aquí está la línea de comandos de trabajo: rsync --delete-after --filter=":e- .gitignore" --filter "- .git/" -v -a .... Esto me tomó un tiempo ... een filtro y --delete-afterambos son importantes. Sugiero leer el capítulo "REGLAS PER-DIRECTORIO Y ELIMINAR" de la rsyncpágina de manual.
dbolotin
1
Para sincronizar eliminaciones, adiciones y actualizaciones, simplemente puede agregar --delete-aftera la versión del comando de @ VasiliNovikov. (Esto parece equivalente a la versión del comando de @ dboliton, excepto que @db usa: e, que creo que excluye los archivos .gitignore para que no se copien, que no es lo que yo quería.)
Bampfer
10

Puede utilizar git ls-filespara crear la lista de archivos excluidos por los archivos del repositorio .gitignore. https://git-scm.com/docs/git-ls-files

Opciones:

  • --exclude-standardConsidere todos los .gitignorearchivos.
  • -o No ignore los cambios sin etapas.
  • -i Salida solo archivos ignorados.
  • --directory Solo muestre la ruta del directorio si se ignora todo el directorio.

Lo único que dejé por ignorar fue .git.

rsync -azP --exclude=.git --exclude=`git -C <SRC> ls-files --exclude-standard -oi --directory` <SRC> <DEST>
Jared Deckard
fuente
4
esto no funciona. excluye el primer archivo del subcomando git y luego trata el resto como parte de la lista SRC. esto funciona: rsync -azP --exclude-from="$(git -C SRC ls-files --exclude-standard -oi --directory > /tmp/excludes; echo /tmp/excludes)" SRC DEST
maratón
2
Este es el único método que funciona si ha excluido e incluido líneas en su .gitignore(es decir, líneas que comienzan con !). También rsyncs archivos que --forceagregó a su repositorio, lo que generalmente es algo bueno.
ostrokach
1
De hecho, esta respuesta NO FUNCIONA, así que terminé escribiendo una que funciona: stackoverflow.com/a/50059607/99834
sorin
6

¿qué tal rsync --exclude-from='path/.gitignore' --exclude-from='path/myignore.txt' source destination?
Funcionó para mí.
Creo que también puedes tener más --exclude-fromparámetros.

Ericn
fuente
3
Esto funcionará en la medida en que sus .gitignorearchivos usen una sintaxis compatible con rsync.
Jesse Glick
@JesseGlick tiene razón, rsync no puede analizar archivos .gitignore, consulte stackoverflow.com/a/50059607/99834 workround.
sorin
6

Solución 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 

Detalles: --exclude-from es obligatorio en lugar de --excluir porque el caso probable de que la lista de exclusión no se analizaría como argumento. Excluir de requiere un archivo y no puede funcionar con tuberías.

La solución actual guarda el archivo de exclusión dentro de la carpeta .git para garantizar que no afecte git statusmientras se mantiene autónomo. Si lo desea, puede utilizar / tmp.

sorin
fuente
3
Parece que funcionará si tiene un repositorio de Git en particular que desea sincronizar, el SRCaquí, pero no para el problema original que mencioné, que es un directorio en expansión con miles de repositorios de Git como subdirectorios en varias profundidades, muchos de los cuales tienen idiosincrásico .gitignores.
Jesse Glick
1
Si está utilizando un shell con soporte para la sustitución de procesos (bash, zsh, etc.) puede usar--exclude-from=<(git -C SRC ls-files --exclude-standard -oi --directory)
Roland W
3

Para mercurial podrías usar

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

para recopilar la lista de archivos que NO están bajo control mercurial debido a restricciones .hgignore y luego ejecutar

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

para rsync todos los archivos excepto los ignorados. Observe el indicador -m en rsync que excluirá los directorios vacíos de la sincronización porque hg status -i solo enumeraría los archivos excluidos, no los directorios

festejar
fuente
2

Prueba esto:

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

Puede copiar todos los archivos al directorio remoto excluyendo los archivos en '.gitignore' y eliminar archivos que no estén en su directorio actual.

Shawn Wang
fuente
1

Según la rsyncpágina del manual, además de la lista estándar de patrones de archivo:

los archivos enumerados en $ HOME / .cvsignore se agregan a la lista y cualquier archivo enumerado en la variable de entorno CVSIGNORE

Entonces, mi archivo $ HOME / .cvsignore se ve así:

.git/
.sass-cache/

para excluir .git y los archivos generados por Sass .

Doug Harris
fuente
2
Por el contrario, definitivamente quiero incluir .git/directorios, quizás incluso con más fuerza que la copia de trabajo. Lo que quiero excluir son productos de construcción.
Jesse Glick
Además, esta configuración no es portátil. Es por usuario, no por proyecto.
VasiliNovikov
@JesseGlick Te secundo sobre mantener .git / dirs incluido. Al ser un SCM distribuido, es importante realizar una copia de seguridad de todo el repositorio local.
Johan Boulé
1 / La oración de la rsyncpágina de manual citada en esta respuesta describe la --cvs-excludeopción, por lo que debe usarla explícitamente. 2 / Puede crear .cvsignorearchivos en cualquier directorio para tener ignorados específicos del proyecto, esos también se leen. 3 / .gitya se ignora cuando lo usa --cvs-exclude, según el manual, por lo que tenerlo $HOME/.cvsignoreparece redundante.
Niavlys
1

Tenía varios .gitignorearchivos muy grandes y ninguna de las soluciones de "rsync puro" funcionó para mí. Escribí este script de envoltura rsync , respeta completamente las .gitignorereglas (incluye !excepciones de estilo y .gitignorearchivos en subdirectorios) y ha funcionado como un encanto para mí.

cobbzilla
fuente
Intentando esto vía locate -0e .gitignore | (while read -d '' x; do process_git_ignore "$x"; done), pero tiene muchos problemas. Archivos en el mismo directorio que .gitignoreno están separados correctamente del nombre del directorio con /. Líneas en blanco y comentarios mal interpretados. Se ahoga en .gitignorearchivos en rutas con espacios (no importa el diabólico /opt/vagrant/embedded/gems/gems/rb-fsevent-0.9.4/spec/fixtures/custom 'path/.gitignoredel vagrantpaquete para Ubuntu). Quizás sea mejor hacerlo como un script de Perl.
Jesse Glick
@JesseGlick No estoy seguro de por qué está llamando a la función dentro del script. está destinado a ser utilizado como un reemplazo directo de rsync, por la razón específica de que el manejo de comillas / espacios en blanco es tan complicado. Si tiene un ejemplo de una gsynclínea de comando que está fallando y los .gitignorearchivos asociados con ella, me complacerá verla más de cerca.
cobbzilla
Necesito rsyncun sistema de archivos completo, con varios repositorios Git esparcidos a su alrededor. Quizás su script funcione bien para el caso de sincronizar un solo repositorio.
Jesse Glick
1
sí definitivamente. Lo siento, no lo dejé claro. Con este script, tendrías que invocarlo una vez por repositorio de git, desde el directorio del repositorio.
cobbzilla
0

Consulte la sección REGLAS DEL FILTRO DE FUSIÓN DE ARCHIVOS en rsync (1).

Parece que es posible crear una regla de filtro rsync que incluirá archivos .gitignore a medida que atraviesa la estructura del directorio.

luksan
fuente
0

En lugar de crear filtros de exclusión, puede usar git ls-filespara seleccionar cada archivo 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

Esto funciona aunque git ls-filesdevuelve rutas separadas por saltos de línea. Probablemente no funcionará si ha versionado archivos con espacios en los nombres de archivo.


fuente
0

Alternativas:

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

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

(rsync solo entiende parcialmente .gitignore)

druida62
fuente
0

Respuesta corta

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

Significado de los parámetros:

-r: recursivo

--info=...: muestra el progreso

--filter=...: excluir según las reglas enumeradas en el archivo .gitignore

Adrian
fuente