rsync exclude selon .gitignore & .hgignore & svn: ignore comme --filter =: C

113

Rsync inclut une option astucieuse --cvs-excludepour «ignorer les fichiers de la même manière que CVS», mais CVS est obsolète depuis des années. Existe-t-il un moyen d'exclure également les fichiers qui seraient ignorés par les systèmes de contrôle de version modernes (Git, Mercurial, Subversion)?

Par exemple, j'ai beaucoup de projets Maven extraits de GitHub. En général, ils incluent .gitignoreau moins une liste target, le répertoire de construction par défaut de Maven (qui peut être présent au niveau supérieur ou dans des sous-modules). Étant donné que le contenu de ces répertoires est entièrement jetable et qu'ils peuvent être beaucoup plus volumineux que le code source, je voudrais les exclure lors de l'utilisation de rsync pour les sauvegardes.

Bien sûr, je peux explicitement, --exclude=target/mais cela supprimera accidentellement les répertoires non liés qui se trouvent juste être nommés targetet ne sont pas censés être ignorés.

Et je pourrais fournir une liste complète des chemins absolus pour tous les noms de fichiers et de motifs mentionnés dans tout .gitignore, .hgignoreou svn:ignorebien sur mon disque, mais ce serait une énorme liste qui devrait être produit par une sorte de script.

Puisque rsync n'a pas de support intégré pour les extractions VCS autres que CVS, y a-t-il une bonne astuce pour lui donner leurs modèles d'ignorance? Ou une sorte de système de rappel par lequel un script utilisateur peut être demandé si un fichier / répertoire donné doit être inclus ou non?

Mise à jour : --filter=':- .gitignore'comme suggéré par LordJavac semble fonctionner aussi bien pour Git que --filter=:Cpour CVS, du moins sur les exemples que j'ai trouvés, bien que l'on ne sache pas si la syntaxe correspond exactement. --filter=':- .hgignore'ne fonctionne pas très bien pour Mercurial; par exemple, un .hgignorecontenant une ligne comme ^target$(l'équivalent Mercurial de Git /target/) n'est pas reconnu par rsync comme une expression régulière. Et rien ne semble fonctionner pour Subversion, pour lequel vous auriez à analyser .svn/dir-prop-baseune copie de travail 1.6 ou antérieure, et à lever les mains avec consternation pour une copie de travail 1.7 ou ultérieure.

Jesse Glick
la source
11
On dirait un peu que ce serait une bonne idée de soumettre un correctif pour rsync qui ajoute la prise en charge de .gitignore, .hgignore, etc.
ThiefMaster
3
@ThiefMaster: J'ai déposé bugzilla.samba.org/show_bug.cgi?id=9744 comme point de départ.
Jesse Glick
2
juste une note pour les autres, le .gitignore doit être dans la hiérarchie des dossiers en cours d'exécution, pas dans le répertoire où la commande est exécutée
myol
Que :-signifie exactement? Que signifie le côlon? Qu'est-ce que le tableau de bord?
David
Git a maintenant une check-ignoresous-commande qui peut gérer le dur travail d'analyse des divers fichiers "ignorer", si vous voulez utiliser l'option "générer une liste de tous les fichiers non ignorés". Ma réponse ici donne des détails sur la façon de procéder.
cjs

Réponses:

120

Comme mentionné par luksan, vous pouvez le faire avec le --filterpassage à rsync. J'ai réalisé cela avec --filter=':- .gitignore'(il y a un espace avant ".gitignore") qui dit rsyncde faire une fusion de répertoire avec les .gitignorefichiers et de les exclure par les règles de git. Vous pouvez également ajouter votre fichier ignorer global, si vous en avez un. Pour faciliter son utilisation, j'ai créé un alias rsyncauquel le filtre était inclus.

LordJavac
la source
Un bon début, même si j'hésite à «accepter» cette réponse car elle ne couvre que Git.
Jesse Glick
23
Une version plus verbeuse qui exclut également les fichiers .git:--exclude='/.git' --filter="dir-merge,- .gitignore"
VasiliNovikov
2
J'ai quelque chose comme ça maintenant: rsync -rvv --exclude='.git*' --exclude='/rsync-to-dev.sh' --filter='dir-merge,-n /.gitignore' $DIR/ development.foobar.com:~/test/.. mais bien qu'il dise [sender] hiding file .gitignore because of pattern .git*, le fichier est toujours envoyé à la desintation
rolandow
2
Si vous souhaitez également utiliser l' --deleteoption, voici la ligne de commande de travail: rsync --delete-after --filter=":e- .gitignore" --filter "- .git/" -v -a .... Cela m'a pris du temps ... een filtre et --delete-aftersont tous les deux importants. Je suggère de lire le chapitre "RÈGLES PAR RÉPERTOIRE ET SUPPRIMER" de la rsyncpage de manuel.
dbolotin
1
Pour synchroniser les suppressions ainsi que les ajouts et les mises à jour, vous pouvez simplement ajouter --delete-afterà la version de la commande de @ VasiliNovikov. (Cela semble équivalent à la version de la commande de @ dboliton, sauf que @db utilise: e qui, à mon avis, exclut les fichiers .gitignore de la copie, ce qui n'est pas ce que je voulais.)
Bampfer
10

Vous pouvez utiliser git ls-filespour créer la liste des fichiers exclus par les .gitignorefichiers du référentiel . https://git-scm.com/docs/git-ls-files

Options:

  • --exclude-standardConsidérez tous les .gitignorefichiers.
  • -o N'ignorez pas les changements non organisés.
  • -i Afficher uniquement les fichiers ignorés.
  • --directory Affiche le chemin du répertoire uniquement si le répertoire entier est ignoré.

La seule chose qu'il me restait à ignorer était .git.

rsync -azP --exclude=.git --exclude=`git -C <SRC> ls-files --exclude-standard -oi --directory` <SRC> <DEST>
Jared Deckard
la source
4
cela ne fonctionne pas. il exclut le premier fichier de la sous-commande git et traite ensuite le reste comme faisant partie de la liste SRC. cela fonctionne: rsync -azP --exclude-from="$(git -C SRC ls-files --exclude-standard -oi --directory > /tmp/excludes; echo /tmp/excludes)" SRC DEST
marathon
2
C'est la seule méthode qui fonctionne si vous avez à la fois exclure et inclure des lignes dans votre .gitignore(c'est-à-dire des lignes commençant par !). Il synchronise également les fichiers que vous avez --forceajoutés à votre dépôt, ce qui est généralement une bonne chose.
ostrokach
1
En effet, cette réponse NE FONCTIONNE PAS, alors j'ai fini par en écrire une qui fonctionne: stackoverflow.com/a/50059607/99834
sorin
6

et pourquoi rsync --exclude-from='path/.gitignore' --exclude-from='path/myignore.txt' source destination?
Cela a fonctionné pour moi.
Je pense que vous pouvez aussi avoir plus de --exclude-fromparamètres.

Ericn
la source
3
Cela fonctionnera dans la mesure où vos .gitignorefichiers utilisent une syntaxe compatible avec rsync.
Jesse Glick
@JesseGlick a raison, rsync n'est pas en mesure d'analyser les fichiers .gitignore, consultez la solution de contournement stackoverflow.com/a/50059607/99834 .
sorin
6

Solution 2018 confirmée

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 

Détails: --exclude-fromest obligatoire au lieu de --exclude car le cas probable qui exclut la liste ne serait pas analysé comme un argument. Exclure de nécessite un fichier et ne peut pas fonctionner avec des tuyaux.

La solution actuelle enregistre le fichier d'exclusion dans le dossier .git afin de garantir qu'il n'affectera pas git statustout en le gardant autonome. Si vous le souhaitez, vous pouvez utiliser / tmp.

Sorin
la source
3
Cela semble fonctionner si vous avez un référentiel Git particulier que vous souhaitez synchroniser - l' SRCici - mais pas pour le problème d'origine que j'ai déclaré, qui est un répertoire tentaculaire avec des milliers de référentiels Git comme sous-répertoires à différentes profondeurs, dont beaucoup ont idiosyncratique .gitignores.
Jesse Glick
1
Si vous utilisez un shell prenant en charge la substitution de processus (bash, zsh, etc.), vous pouvez utiliser--exclude-from=<(git -C SRC ls-files --exclude-standard -oi --directory)
Roland W
3

Pour mercurial, vous pourriez utiliser

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

pour collecter la liste des fichiers qui ne sont PAS sous contrôle mercurial en raison des restrictions .hgignore , puis exécutez

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

pour rsync tous les fichiers sauf ceux ignorés. Remarquez l' indicateur -m dans rsync qui exclura les répertoires vides de la synchronisation car hg status -i ne listerait que les fichiers exclus, pas les répertoires

fête
la source
2

Essaye ça:

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

Il peut copier tous les fichiers dans le répertoire distant à l'exception des fichiers dans '.gitignore' et supprimer les fichiers qui ne se trouvent pas dans votre répertoire actuel.

Shawn Wang
la source
1

Selon la rsyncpage de manuel, en plus de la liste standard des modèles de fichiers:

les fichiers répertoriés dans $ HOME / .cvsignore sont ajoutés à la liste et tous les fichiers répertoriés dans la variable d'environnement CVSIGNORE

Donc, mon fichier $ HOME / .cvsignore ressemble à ceci:

.git/
.sass-cache/

pour exclure .git et les fichiers générés par Sass .

Doug Harris
la source
2
Au contraire, je souhaite absolument inclure des .git/répertoires, peut-être même plus fortement que la copie de travail. Ce que je veux exclure, ce sont les produits de construction.
Jesse Glick
En outre, ce paramètre n'est pas portable. C'est par utilisateur, pas par projet.
VasiliNovikov
@JesseGlick Je vous second pour garder .git / dirs inclus. Git étant un SCM distribué, il est important de sauvegarder tout le référentiel local.
Johan Boulé
1 / La phrase de la rsyncpage de manuel citée dans cette réponse décrit l' --cvs-excludeoption, vous devez donc l'utiliser explicitement. 2 / Vous pouvez créer des .cvsignorefichiers dans n'importe quel répertoire pour avoir des ignorés spécifiques au projet, ceux-ci sont également lus. 3 / .gitest déjà ignoré lorsque vous utilisez --cvs-exclude, selon le manuel, donc l'avoir en $HOME/.cvsignoresemble redondant.
Niavlys
1

J'avais un certain nombre de .gitignorefichiers très volumineux et aucune des solutions «rsync pure» ne fonctionnait pour moi. J'ai écrit ce script wrapper rsync , il respecte totalement les .gitignorerègles (incluez les !exceptions de style -style et les .gitignorefichiers dans les sous-répertoires) et a fonctionné comme un charme pour moi.

Cobbzilla
la source
Essayer cela via locate -0e .gitignore | (while read -d '' x; do process_git_ignore "$x"; done), mais a beaucoup de problèmes. Les fichiers dans le même répertoire .gitignorene sont pas correctement séparés du nom du répertoire avec /. Lignes vides et commentaires mal interprétés. Étouffe les .gitignorefichiers dans les chemins avec des espaces (sans parler /opt/vagrant/embedded/gems/gems/rb-fsevent-0.9.4/spec/fixtures/custom 'path/.gitignoredu diabolique du vagrantpaquet pour Ubuntu). Peut-être mieux fait en tant que script Perl.
Jesse Glick
@JesseGlick Je ne sais pas pourquoi vous appelez la fonction dans le script. il est destiné à être utilisé comme un remplacement instantané pour rsync, pour la raison spécifique que la gestion des guillemets / espaces blancs est une telle douleur. Si vous avez un exemple de gsyncligne de commande qui échoue et les .gitignorefichiers qui y sont associés, je serais heureux de l'examiner de plus près.
cobbzilla
J'ai besoin d' rsyncun système de fichiers entier, avec divers dépôts Git dispersés autour de lui. Peut-être que votre script fonctionne correctement dans le cas de la synchronisation d'un seul référentiel.
Jesse Glick
1
Oui définitivement. désolé, je ne l'ai pas précisé. Avec ce script, vous devrez l'invoquer une fois par dépôt git, à partir du répertoire du dépôt.
cobbzilla le
0

Consultez la section RÈGLES DE FILTRE DE FUSION-FILES dans rsync (1).

Il semble qu'il soit possible de créer une règle rsync --filter qui inclura les fichiers .gitignore au fur et à mesure que la structure des répertoires sera parcourue.

Luksan
la source
0

Au lieu de créer des filtres d'exclusion, vous pouvez utiliser git ls-filespour sélectionner chaque fichier à 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

Cela fonctionne même si git ls-filesrenvoie des chemins séparés par une nouvelle ligne. Ne fonctionnera probablement pas si vous avez des fichiers versionnés avec des espaces dans les noms de fichiers.


la source
0

Alternatives:

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

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

(rsync ne comprend que partiellement .gitignore)

druide62
la source
0

Réponse courte

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

Paramètres signifiant:

-r: récursif

--info=...: afficher la progression

--filter=...: exclure selon les règles répertoriées dans le fichier .gitignore

Adrian
la source