rsync mengecualikan menurut .gitignore & .hgignore & svn: ignore like --filter =: C

113

Rsync menyertakan opsi bagus --cvs-excludeuntuk "mengabaikan file dengan cara yang sama seperti CVS", tetapi CVS sudah usang selama bertahun-tahun. Apakah ada cara untuk membuatnya juga mengecualikan file yang akan diabaikan oleh sistem kontrol versi modern (Git, Mercurial, Subversion)?

Misalnya, saya memiliki banyak proyek Maven yang diperiksa dari GitHub. Biasanya mereka menyertakan .gitignoredaftar setidaknya target, direktori build Maven default (yang mungkin ada di tingkat atas atau di submodul). Karena isi dari direktori ini sepenuhnya dapat dibuang, dan mereka bisa jauh lebih besar dari kode sumber, saya ingin mengecualikannya saat menggunakan rsync untuk backup.

Tentu saja saya bisa secara eksplisit --exclude=target/tetapi itu akan secara tidak sengaja menekan direktori yang tidak terkait yang kebetulan diberi nama targetdan tidak seharusnya diabaikan.

Dan saya bisa memasok daftar lengkap path absolut untuk semua nama file dan pola disebutkan dalam .gitignore, .hgignoreatau svn:ignoreproperti pada disk saya, tetapi ini akan menjadi daftar besar yang harus diproduksi oleh semacam script.

Karena rsync tidak memiliki dukungan bawaan untuk pemeriksaan VCS selain CVS, apakah ada trik bagus untuk memberinya pola pengabaian mereka? Atau sejenis sistem panggilan balik di mana skrip pengguna dapat ditanyai apakah file / direktori tertentu harus disertakan atau tidak?

Pembaruan : --filter=':- .gitignore'seperti yang disarankan oleh LordJavac tampaknya berfungsi dengan baik untuk Git seperti --filter=:Chalnya untuk CVS, setidaknya pada contoh yang saya temukan, meskipun tidak jelas apakah sintaksnya sama persis. --filter=':- .hgignore'tidak bekerja dengan baik untuk Mercurial; misalnya sebuah .hgignorebaris yang mengandung seperti ^target$(Mercurial equivalent dari Git /target/) tidak dikenali oleh rsync sebagai ekspresi reguler. Dan sepertinya tidak ada yang berhasil untuk Subversion, yang harus Anda parse .svn/dir-prop-baseuntuk copy pekerjaan 1.6 atau sebelumnya, dan angkat tangan Anda dengan cemas untuk copy pekerjaan 1.7 atau yang lebih baru.

Jesse Glick
sumber
11
Kedengarannya lebih baik mengirimkan patch untuk rsync yang menambahkan dukungan untuk .gitignore, .hgignore, dll.
ThiefMaster
3
@ThiefMaster: Saya mengajukan bugzilla.samba.org/show_bug.cgi?id=9744 sebagai titik awal.
Jesse Glick
2
hanya sebuah catatan untuk orang lain, .gitignore harus berada dalam hierarki folder yang sedang rysnc'd, bukan di direktori perintah sedang dijalankan
myol
Apa sebenarnya :-artinya? Apa maksud dari titik dua? Apa dasbornya?
David
Git sekarang memiliki check-ignoresubperintah yang dapat menangani kerja keras parsing berbagai file "abaikan", jika Anda ingin menggunakan opsi "buat daftar semua file yang tidak diabaikan". Jawaban saya di sini memberikan detail bagaimana melakukan itu.
cjs

Jawaban:

120

Seperti yang disebutkan oleh luksan, Anda dapat melakukan ini dengan --filtertombol ke rsync. Saya mencapai ini dengan --filter=':- .gitignore'(ada spasi sebelum ".gitignore") yang memberitahu rsyncuntuk melakukan penggabungan direktori dengan .gitignorefile dan meminta mereka mengecualikan sesuai aturan git. Anda mungkin juga ingin menambahkan file abaikan global Anda, jika ada. Untuk membuatnya lebih mudah digunakan, saya membuat alias rsyncyang menyertakan filter.

LordJavac
sumber
Awal yang baik, meskipun saya ragu untuk "menerima" jawaban ini karena hanya mencakup Git.
Jesse Glick
23
Versi yang lebih bertele-tele yang juga mengecualikan file .git:--exclude='/.git' --filter="dir-merge,- .gitignore"
VasiliNovikov
2
Saya memiliki sesuatu seperti ini sekarang: rsync -rvv --exclude='.git*' --exclude='/rsync-to-dev.sh' --filter='dir-merge,-n /.gitignore' $DIR/ development.foobar.com:~/test/.. tetapi meskipun dikatakan [sender] hiding file .gitignore because of pattern .git*, file tersebut masih dikirim ke tujuan
rolandow
2
Jika Anda juga ingin menggunakan --deletepilihan, di sini adalah baris perintah kerja: rsync --delete-after --filter=":e- .gitignore" --filter "- .git/" -v -a .... Ini butuh waktu beberapa saat ... edalam filter dan --delete-afterkeduanya penting. Saya sarankan membaca bab "PER-DIRECTORY RULES AND DELETE" dari rsynchalaman manual.
dbolotin
1
Untuk menyinkronkan penghapusan serta penambahan & pembaruan, Anda cukup menambahkan --delete-afterke versi perintah @ VasiliNovikov. (Ini tampaknya setara dengan versi perintah @ dboliton, kecuali @db menggunakan: e yang menurut saya tidak termasuk file .gitignore dari penyalinan, yang bukan itu yang saya inginkan.)
Bampfer
10

Anda dapat menggunakan git ls-filesuntuk membuat daftar file yang dikecualikan oleh file repositori .gitignore. https://git-scm.com/docs/git-ls-files

Pilihan:

  • --exclude-standardPertimbangkan semua .gitignorefile.
  • -o Jangan abaikan perubahan tidak bertahap.
  • -i Hanya mengeluarkan file yang diabaikan.
  • --directory Hanya mengeluarkan jalur direktori jika seluruh direktori diabaikan.

Satu-satunya hal yang saya abaikan adalah .git.

rsync -azP --exclude=.git --exclude=`git -C <SRC> ls-files --exclude-standard -oi --directory` <SRC> <DEST>
Jared Deckard
sumber
4
ini tidak berhasil. itu mengecualikan file pertama dari subperintah git dan kemudian memperlakukan sisanya sebagai bagian dari daftar SRC. ini berhasil: rsync -azP --exclude-from="$(git -C SRC ls-files --exclude-standard -oi --directory > /tmp/excludes; echo /tmp/excludes)" SRC DEST
maraton
2
Ini adalah satu-satunya metode yang bekerja jika Anda memiliki keduanya mengecualikan dan menyertakan baris di Anda .gitignore(yaitu baris yang dimulai dengan !). Ini juga menyinkronkan file yang Anda --forcetambahkan ke repo, yang biasanya merupakan hal yang baik.
ostrokach
1
Memang jawaban ini TIDAK BEKERJA, jadi saya akhirnya menulis yang berfungsi: stackoverflow.com/a/50059607/99834
sorin
6

bagaimana rsync --exclude-from='path/.gitignore' --exclude-from='path/myignore.txt' source destination?
Itu berhasil untuk saya.
Saya yakin Anda juga dapat memiliki lebih banyak --exclude-fromparameter.

ericn
sumber
3
Ini akan bekerja selama .gitignorefile Anda kebetulan menggunakan sintaks yang kompatibel dengan rsync.
Jesse Glick
@JesseGlick benar, rsync tidak dapat mengurai file .gitignore, lihat stackoverflow.com/a/50059607/99834 workround.
sorin
6

Solusi 2018 dikonfirmasi

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 

Detail: --exclude-fromadalah wajib daripada --exclude karena kemungkinan kasus yang mengecualikan daftar tidak akan diuraikan sebagai argumen. Kecualikan dari membutuhkan file dan tidak dapat bekerja dengan pipa.

Solusi saat ini menyimpan file kecualikan di dalam folder .git untuk memastikan itu tidak akan mempengaruhi git statussementara menyimpannya sendiri. Jika mau, Anda dapat menggunakan / tmp.

sorin
sumber
3
Sepertinya ini akan berfungsi jika Anda memiliki repositori Git tertentu yang ingin Anda sinkronkan — di SRCsini — tetapi tidak untuk masalah asli yang saya nyatakan, yang merupakan direktori luas dengan ribuan repositori Git sebagai subdirektori di berbagai kedalaman, banyak di antaranya memiliki .gitignores keistimewaan .
Jesse Glick
1
Jika Anda menggunakan shell dengan dukungan untuk substitusi proses (bash, zsh, dll.), Anda dapat menggunakan--exclude-from=<(git -C SRC ls-files --exclude-standard -oi --directory)
Roland W
3

Untuk lincah Anda mungkin menggunakan

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

untuk mengumpulkan daftar file yang TIDAK berada di bawah kendali lincah karena pembatasan .hgignore dan kemudian jalankan

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

untuk rsync semua file kecuali yang diabaikan. Perhatikan -m flag di rsync yang akan mengecualikan direktori kosong dari sinkronisasi karena hg status -i hanya akan mencantumkan file yang dikecualikan, bukan dirs

pesta pesta
sumber
2

Coba ini:

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

Itu dapat menyalin semua file ke direktori jarak jauh tidak termasuk file di '.gitignore', dan menghapus file yang tidak ada di direktori Anda saat ini.

Shawn Wang
sumber
1

Sesuai rsynchalaman manual, selain daftar standar pola file:

file yang terdaftar di $ HOME / .cvsignore ditambahkan ke daftar dan file apa pun yang terdaftar di variabel lingkungan CVSIGNORE

Jadi, file $ HOME / .cvsignore saya terlihat seperti ini:

.git/
.sass-cache/

untuk mengecualikan .git dan file yang dihasilkan oleh Sass .

Doug Harris
sumber
2
Sebaliknya, saya pasti ingin memasukkan .git/direktori, bahkan mungkin lebih kuat daripada copy pekerjaan. Yang ingin saya kecualikan adalah produk build.
Jesse Glick
Selain itu, pengaturan ini tidak portabel. Ini per pengguna, bukan per proyek.
VasiliNovikov
@JesseGlick Saya mendukung Anda tentang menjaga .git / dirs disertakan. Git sebagai SCM terdistribusi, penting untuk membuat cadangan seluruh repositori lokal.
Johan Boulé
1 / Kalimat dari rsynchalaman manual yang dikutip dalam jawaban ini menjelaskan --cvs-excludeopsi, jadi Anda harus menggunakannya secara eksplisit. 2 / Anda dapat membuat .cvsignorefile di direktori manapun untuk mendapatkan pengabaian khusus proyek, itu juga dibaca. 3 / .gitsudah diabaikan saat Anda menggunakan --cvs-exclude, menurut manual, jadi memilikinya di $HOME/.cvsignoretampaknya mubazir.
Niavlys
1

Saya memiliki sejumlah .gitignorefile yang sangat besar dan tidak ada solusi "rsync murni" yang berhasil untuk saya. Saya menulis skrip pembungkus rsync ini , itu sepenuhnya menghormati .gitignoreaturan (termasuk !pengecualian -style dan .gitignorefile di subdirektori) dan telah bekerja seperti pesona bagi saya.

cobbzilla.dll
sumber
Mencoba ini melalui locate -0e .gitignore | (while read -d '' x; do process_git_ignore "$x"; done), tetapi memiliki banyak masalah. File dalam direktori yang sama .gitignoretidak dipisahkan dengan benar dari nama direktori dengan /. Baris kosong dan komentar salah ditafsirkan. Tersedak .gitignorefile di jalur dengan spasi (apalagi yang jahat /opt/vagrant/embedded/gems/gems/rb-fsevent-0.9.4/spec/fixtures/custom 'path/.gitignoredari vagrantpaket untuk Ubuntu). Mungkin lebih baik dilakukan sebagai skrip Perl.
Jesse Glick
@JesseGlick Saya tidak yakin mengapa Anda memanggil fungsi di dalam skrip. ini dimaksudkan untuk digunakan sebagai pengganti drop-in rsync, karena alasan spesifik bahwa menangani quoting / whitespace sangat menyebalkan. Jika Anda memiliki contoh gsyncbaris perintah yang gagal, dan .gitignorefile yang terkait dengannya, saya akan dengan senang hati melihat lebih dekat.
cobbzilla
Saya membutuhkan rsyncseluruh sistem file, dengan berbagai repositori Git yang tersebar di sekitarnya. Mungkin skrip Anda berfungsi dengan baik untuk kasus sinkronisasi satu repositori.
Jesse Glick
1
iya tentu saja. maaf saya tidak menjelaskannya. Dengan skrip ini, Anda harus memanggilnya sekali per git repo, dari dalam direktori repo.
cobbzilla
0

Lihat bagian ATURAN FILTER GABUNGAN di rsync (1).

Sepertinya dimungkinkan untuk membuat aturan rsync --filter yang akan menyertakan file .gitignore saat melintasi struktur direktori.

luksan
sumber
0

Alih-alih membuat filter pengecualian, Anda dapat menggunakan git ls-filesuntuk memilih setiap file untuk 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

Ini berfungsi meskipun git ls-filesmengembalikan jalur yang dipisahkan baris baru. Mungkin tidak akan berfungsi jika Anda memiliki file berversi dengan spasi di nama file.


sumber
0

Alternatif:

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

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

(rsync hanya memahami sebagian .gitignore)

druid62
sumber
0

Jawaban singkat

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

Parameter arti:

-r: rekursif

--info=...: tunjukkan kemajuan

--filter=...: kecualikan menurut aturan yang tercantum pada file .gitignore

Adrian
sumber