rsync wyklucz zgodnie z .gitignore & .hgignore & svn: ignore like --filter =: C

114

Rsync zawiera sprytną opcję --cvs-exclude„ignorowania plików w taki sam sposób, jak robi to CVS”, ale CVS był przestarzały od lat. Czy istnieje sposób, aby wykluczyć również pliki, które byłyby ignorowane przez nowoczesne systemy kontroli wersji (Git, Mercurial, Subversion)?

Na przykład mam wiele projektów Mavena pobranych z GitHub. Zazwyczaj zawierają .gitignoreprzynajmniej listę target, domyślny katalog kompilacji Mavena (który może znajdować się na najwyższym poziomie lub w modułach podrzędnych). Ponieważ zawartość tych katalogów jest całkowicie jednorazowa i może być znacznie większa niż kod źródłowy, chciałbym je wykluczyć podczas używania rsync do tworzenia kopii zapasowych.

Oczywiście mogę wyraźnie, --exclude=target/ale spowoduje to przypadkowe zablokowanie niepowiązanych katalogów, które akurat zostały nazwane targeti nie powinny być ignorowane.

I mogę dostarczyć kompletną listę ścieżek bezwzględnych dla wszystkich nazw plików i wzorów wymienionych w dowolnym .gitignore, .hgignorelub svn:ignoremienia na moim dysku, ale to byłaby ogromna lista, która musiałaby być produkowane przez jakiegoś skryptu.

Ponieważ rsync nie ma wbudowanej obsługi wyewidencjonowania VCS innej niż CVS, czy jest jakaś dobra sztuczka do podawania mu wzorców ignorowania? Lub jakiś system wywołań zwrotnych, w którym skrypt użytkownika może zostać zapytany, czy dany plik / katalog powinien być uwzględniony, czy nie?

Aktualizacja : --filter=':- .gitignore'zgodnie z sugestią LordJavaca wydaje się działać tak samo dobrze dla Gita, jak --filter=:Cdla CVS, przynajmniej na przykładach, które znalazłem, chociaż nie jest jasne, czy składnia jest dokładnie zgodna. --filter=':- .hgignore'nie działa zbyt dobrze w przypadku Mercurial; np. .hgignorewiersz zawierający linię taką jak ^target$(Mercurial odpowiednik Git /target/) nie jest rozpoznawany przez rsync jako wyrażenie regularne. Wydaje się, że nic nie działa dla Subversion, dla której musiałbyś przeanalizować .svn/dir-prop-basekopię roboczą 1.6 lub wcześniejszą i wznieść ręce z konsternacją, aby uzyskać kopię roboczą 1.7 lub nowszą.

Jesse Glick
źródło
11
Wygląda na to, że dobrym pomysłem byłoby przesłanie łatki dla rsync, która dodaje obsługę
.gitignore
3
@ThiefMaster: Jako punkt wyjścia podałem bugzilla.samba.org/show_bug.cgi?id=9744 .
Jesse Glick,
2
uwaga dla innych, .gitignore musi znajdować się w hierarchii folderów, która jest rysnc'd, a nie w katalogu, w którym polecenie jest wykonywane
myol
Co to :-dokładnie znaczy? Co oznacza okrężnica? Co za kreska?
David
Git ma teraz check-ignorepodkomendę, która może poradzić sobie z ciężką pracą polegającą na parsowaniu różnych plików „ignoruj”, jeśli chcesz skorzystać z opcji „wygeneruj listę wszystkich niezignorowanych plików”. Moja odpowiedź zawiera szczegóły, jak to zrobić.
cjs

Odpowiedzi:

121

Jak wspomniał luksan, możesz to zrobić, --filterprzełączając się na rsync. Osiągnąłem to z --filter=':- .gitignore'(jest spacja przed ".gitignore"), który mówi, rsyncaby wykonać połączenie katalogów z .gitignoreplikami i wykluczyć je zgodnie z regułami gita. Możesz także dodać swój globalny plik ignorowania, jeśli taki masz. Aby ułatwić korzystanie z niego, utworzyłem alias, do rsyncktórego dołączyłem filtr.

LordJavac
źródło
Dobry początek, choć waham się przed „zaakceptowaniem” tej odpowiedzi, ponieważ dotyczy ona tylko Gita.
Jesse Glick
24
Bardziej szczegółowa wersja, która wyklucza również pliki .git:--exclude='/.git' --filter="dir-merge,- .gitignore"
VasiliNovikov,
2
Mam teraz coś takiego: rsync -rvv --exclude='.git*' --exclude='/rsync-to-dev.sh' --filter='dir-merge,-n /.gitignore' $DIR/ development.foobar.com:~/test/... ale mimo, że jest napisane [sender] hiding file .gitignore because of pattern .git*, plik nadal jest wysyłany do desintacji
rolandow
2
Jeśli chcesz także użyć --deleteopcji, tutaj jest wiersz poleceń pracy: rsync --delete-after --filter=":e- .gitignore" --filter "- .git/" -v -a .... Zajęło mi to trochę czasu ... ew filtrze i --delete-afteroba są ważne. Proponuję przeczytać rozdział "PER-DIRECTORY RULES AND DELETE" na rsyncstronie podręcznika .
dbolotin
1
Aby zsynchronizować usuwanie, a także dodawanie i aktualizacje, możesz po prostu dodać --delete-afterdo wersji polecenia @ VasiliNovikov. (Wydaje się, że jest to równoważne z wersją polecenia @ dboliton, z wyjątkiem @db używa: e, co moim zdaniem wyklucza pliki .gitignore z kopiowania, co nie jest tym, czego chciałem.)
Bampfer
10

Możesz użyć git ls-filesdo zbudowania listy plików wykluczonych przez .gitignorepliki repozytorium . https://git-scm.com/docs/git-ls-files

Opcje:

  • --exclude-standardRozważ wszystkie .gitignorepliki.
  • -o Nie ignoruj ​​zmian niestopowych.
  • -i Wyprowadzaj tylko ignorowane pliki.
  • --directory Podaj ścieżkę katalogu tylko wtedy, gdy cały katalog jest ignorowany.

Jedyne, co mogłem zignorować, to .git.

rsync -azP --exclude=.git --exclude=`git -C <SRC> ls-files --exclude-standard -oi --directory` <SRC> <DEST>
Jared Deckard
źródło
4
to nie działa. wyklucza pierwszy plik z podkomendy git, a resztę traktuje jako część listy SRC. to działa: rsync -azP --exclude-from="$(git -C SRC ls-files --exclude-standard -oi --directory > /tmp/excludes; echo /tmp/excludes)" SRC DEST
maraton
2
Jest to jedyna metoda, która działa, jeśli masz zarówno wykluczanie, jak i dołączanie wierszy .gitignore(tj. Wiersze zaczynające się od !). Rsyntuje również pliki, które --forcedodałeś do repozytorium, co zwykle jest dobrą rzeczą.
ostrokach
1
Rzeczywiście, ta odpowiedź NIE DZIAŁA, więc napisałem taką, która działa: stackoverflow.com/a/50059607/99834
sorin
6

co powiesz na to rsync --exclude-from='path/.gitignore' --exclude-from='path/myignore.txt' source destination?
U mnie to zadziałało.
Uważam, że możesz mieć też więcej --exclude-fromparametrów.

ericn
źródło
3
To zadziała, o ile Twoje .gitignorepliki będą używać składni zgodnej z rsync.
Jesse Glick
@JesseGlick ma rację, rsync nie jest w stanie przeanalizować plików .gitignore , zobacz stackoverflow.com/a/50059607/99834 workround.
sorin
6

Rozwiązanie 2018 potwierdzone

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 

Szczegóły: --exclude-fromjest obowiązkowe zamiast --exclude, ponieważ prawdopodobny przypadek, który zawiera listę wykluczeń, nie zostałby przeanalizowany jako argument. Wyklucz z wymaga pliku i nie może działać z potokami.

Bieżące rozwiązanie zapisuje plik wykluczenia w folderze .git, aby zapewnić, że nie wpłynie git statusto na jego niezależność. Jeśli chcesz, możesz użyć / tmp.

sorin
źródło
3
Wygląda na to, że zadziała, jeśli masz określone repozytorium Git, które chcesz zsynchronizować - SRCtutaj - ale nie w przypadku pierwotnego problemu, który wskazałem, który jest rozległym katalogiem z tysiącami repozytoriów Git jako podkatalogami na różnych głębokościach, z których wiele ma idiosynkratyczne .gitignores.
Jesse Glick,
1
Jeśli używasz powłoki z obsługą zastępowania procesów (bash, zsh itp.), Możesz użyć--exclude-from=<(git -C SRC ls-files --exclude-standard -oi --directory)
Roland W
3

W przypadku rtęci możesz użyć

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

aby zebrać listę plików, które NIE są pod kontrolą rtęci z powodu ograniczeń .hgignore, a następnie uruchomić

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

rsync wszystkie pliki poza ignorowanymi. Ogłoszenie flaga -m w rsync wyklucza puste katalogi z synchronizacji, ponieważ hg status -i wyświetla tylko wykluczone pliki, a nie katalogi

ffeast
źródło
2

Spróbuj tego:

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

Może kopiować wszystkie pliki do zdalnego katalogu z wyjątkiem plików z „.gitignore” i usuwać pliki, których nie ma w bieżącym katalogu.

Shawn Wang
źródło
1

Na rsyncstronie podręcznika , oprócz standardowej listy wzorców plików:

pliki wymienione w $ HOME / .cvsignore są dodawane do listy, a wszystkie pliki wymienione w zmiennej środowiskowej CVSIGNORE

Tak więc mój plik $ HOME / .cvsignore wygląda następująco:

.git/
.sass-cache/

aby wykluczyć .git i pliki wygenerowane przez Sass .

Doug Harris
źródło
2
Wręcz przeciwnie, zdecydowanie chcę dołączyć .git/katalogi, być może nawet silniej niż kopia robocza. Chcę wykluczyć produkty budowlane.
Jesse Glick
Ponadto to ustawienie nie jest przenośne. To jest na użytkownika, a nie na projekt.
VasiliNovikov
@JesseGlick Popieram cię o uwzględnienie plików .git / dirs. Git jest rozproszonym SCM, dlatego ważne jest, aby wykonać kopię zapasową całego lokalnego repozytorium.
Johan Boulé
1 / Zdanie ze strony podręcznika rsynccytowanej w tej odpowiedzi opisuje --cvs-excludeopcję, więc musisz jej użyć jawnie. 2 / Możesz tworzyć .cvsignorepliki w dowolnym katalogu, aby mieć ignorowania specyficzne dla projektu, one również są odczytywane. 3 / .gitjest już ignorowany, gdy używasz --cvs-exclude, zgodnie z instrukcją, więc posiadanie go $HOME/.cvsignorewydaje się zbędne.
Niavlys
1

Miałem wiele bardzo dużych .gitignoreplików i żadne z rozwiązań „czystego rsync” nie działało. Napisałem ten skrypt opakowujący rsync , w pełni szanuje .gitignorereguły ( !wyjątki w stylu dołączania i .gitignorepliki w podkatalogach) i działa dla mnie jak urok.

cobbzilla
źródło
Próbuję tego przez locate -0e .gitignore | (while read -d '' x; do process_git_ignore "$x"; done), ale ma wiele problemów. Pliki w tym samym katalogu, co .gitignoreniepoprawnie oddzielone od nazwy katalogu za pomocą /. Puste wiersze i komentarze zostały błędnie zinterpretowane. Dławiki na .gitignoreplikach w ścieżkach ze spacjami (nie wspominając o diabelskim /opt/vagrant/embedded/gems/gems/rb-fsevent-0.9.4/spec/fixtures/custom 'path/.gitignorez vagrantpakietu dla Ubuntu). Być może lepiej zrobione jako skrypt Perla.
Jesse Glick
@JesseGlick Nie jestem pewien, dlaczego wywołujesz funkcję w skrypcie. jest przeznaczony do użytku jako zastępczy zamiennik rsync, z tego konkretnego powodu, że obsługa cytatów / białych znaków jest tak uciążliwa. Jeśli masz przykład gsyncniedziałającej linii poleceń i .gitignorepowiązanych z nią plików, z przyjemnością przyjrzę się bliżej.
cobbzilla
Potrzebuję rsynccałego systemu plików z rozrzuconymi wokół niego różnymi repozytoriami Git. Być może twój skrypt działa dobrze w przypadku synchronizacji pojedynczego repozytorium.
Jesse Glick,
1
tak, zdecydowanie. przepraszam, nie wyjaśniłem tego jasno. W przypadku tego skryptu musiałbyś wywołać go raz na repozytorium git, z katalogu repo.
cobbzilla
0

Sprawdź sekcję ZASADY FILTROWANIA PLIKÓW MERGE w rsync (1).

Wygląda na to, że można utworzyć regułę rsync --filter, która będzie obejmować pliki .gitignore podczas przeglądania struktury katalogów.

luksan
źródło
0

Zamiast tworzyć filtry wykluczania, możesz użyć git ls-files do wybrania każdego pliku do 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

Działa to nawet wtedy, gdy git ls-fileszwraca ścieżki oddzielone znakiem nowej linii. Prawdopodobnie nie zadziała, jeśli masz wersjonowane pliki ze spacjami w nazwach plików.


źródło
0

Alternatywy:

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

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

(rsync tylko częściowo rozumie .gitignore)

druid62
źródło
0

Krótka odpowiedź

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

Znaczenie parametrów:

-r: rekurencyjny

--info=...: pokaż postęp

--filter=...: wyklucz według reguł wymienionych w pliku .gitignore

Adrian
źródło
0

Po godzinach poszukiwań znalazłem dokładnie to, czego potrzebuję: synchronizację folderu docelowego z folderem źródłowym (również usuwanie plików w miejscu docelowym, jeśli zostały usunięte w źródle) i nie kopiowanie do miejsca docelowego plików, które są ignorowane przez .gitignore, ale też nie usuwać tych plików w miejscu docelowym :

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

Innymi słowy, to polecenie całkowicie ignoruje pliki z .gitignore, zarówno w źródle, jak iw miejscu docelowym . Możesz pominąć --exclude='/.git'część, jeśli chcesz skopiować plik.git folder.

MUSISZ skopiować pliki .gitignore ze źródła. Jeśli użyjesz polecenia LordaJavaca, plik .gitignore nie zostanie skopiowany. A jeśli utworzysz plik w folderze docelowym, który powinien zostać zignorowany przez .gitignore, ten plik zostanie usunięty pomimo .gitignore. Dzieje się tak, ponieważ nie masz plików .gitignore w miejscu docelowym. Ale jeśli będziesz mieć te pliki, pliki opisane w .gitignore nie zostaną usunięte, zostaną zignorowane, po prostu oczekiwane.

James Bond
źródło