.gitignore Składnia: bin vs bin / vs bin / * vs bin / **

89

Jaka jest różnica między dodaniem bin, bin/, bin/*a bin/**w moim pliku .gitignore? Używałem bin/, ale patrząc na inne pliki .gitignore (w pliku zaćmień podwójna i pojedyncza gwiazda są nawet używane razem w ten sposób: o tmp/**/*co w tym chodzi?) Widzę, że pierwsze dwa wzorce są również szeroko używane. Czy ktoś mógłby wyjaśnić różnice między tymi trzema?

chandsie
źródło
4
@unutbu: Przyjęta odpowiedź na to pytanie jest najwyraźniej kwestionowana. Jeden z najpopularniejszych komentarzy twierdzi, że odpowiedź jest w rzeczywistości kompletnym mitem.
chandsie
Zachowanie jest całkowicie określone na stronie podręcznika i jestem pewien, że w pobliżu znajduje się pytanie / odpowiedź (lub dziesięć), które zawierają wszystkie te informacje.
Cascabel,
3
W odniesieniu do **: stackoverflow.com/questions/1470572/…
Cascabel

Odpowiedzi:

84

bindopasowuje wszystkie pliki lub katalogi o nazwie „bin”.

bin/dopasowuje wszystkie katalogi o nazwie „bin”, co w efekcie oznacza całą jego zawartość, ponieważ Git nie śledzi samych katalogów.

bin/*dopasowuje wszystkie pliki i katalogi bezpośrednio w dowolnym bin/. Zapobiega to automatycznemu znajdowaniu przez Git jakichkolwiek plików w jego podkatalogach, ale jeśli, powiedzmy, bin/fooutworzony zostanie podkatalog, ta reguła nie będzie pasować foodo zawartości.

bin/**dopasowuje wszystkie pliki i katalogi w dowolnym bin/katalogu i wszystkich jego podkatalogach.

Słowo „any” jest tutaj krytyczne, ponieważ reguły nie odnoszą się do katalogu głównego repozytorium i nie mają zastosowania dowolnym miejscu w drzewie systemu plików. Musisz zacząć reguły od /(lub!/ cofnąć ignorowanie), co oznacza katalog główny repozytorium, a nie katalog główny systemu, aby dopasować tylko to, co było zamierzone.

OSTRZEŻENIE: Nigdy nie powinieneś używać samych reguł, takich jak dir/*, /dir/**itd., Chyba że usuniesz ignorowanie czegoś, co istnieje w tym katalogu . Pomija gwiazdka lub można trwale stracić dużo danych z pewnych wywołań git gc, git stashi więcej.

Naprawdę nie wiem, co tmp/**/*mam zrobić. Początkowo myślałem, że można go użyć do dopasowania plików w podkatalogach, tmp/ale nie plików bezpośrednio obecnych w tmp/sobie. Ale prosty test wydaje się sugerować, że ignoruje to wszystkie pliki w tmp/.

Siddhartha Reddy
źródło
10
żeby wyjaśnić, jaka jest różnica między bin/i bin/**?
chandsie
1
Podejrzewam, bin/że zignoruje katalog bin, podczas gdy bin/**będzie zawierał katalog bin, ale nie zawiera żadnej z jego zawartości
Robin Winslow
1
Wydaje się to niezgodne z odpowiedzią Siddharthy. Czerpiąc z odpowiedzi, bin/zignoruje sam katalog (w tym wszystkie podkatalogi i pliki), podczas gdy bin/**zignoruje wszystkie pliki w katalogu bin i jego podkatalogach, ale nie sam katalog bin. Nie jestem pewien, czy to prawda.
Christopher Berman
3
zwróć uwagę, że jeśli chcesz śledzić wszystkie pliki w katalogu bin /, ale zignorować wszystkie pliki w jego podkatalogach, możesz to zrobić (w kolejnych wierszach) bin/** \n !bin/*(ponieważ nie widzę, jak wymusić
podział wiersza
9
Ta odpowiedź jest błędna z wielu powodów. Po pierwsze, git nie śledzi katalogów, więc wpis .gitignore może zawsze pasować tylko do zawartości katalogu, nigdy do katalogu jako takiego. Po drugie, bindopasowuje zarówno plik o nazwie, jak bin i zawartość binfolderu. Po trzecie, bin/* nie pasuje do żadnych plików w swoich podkatalogach. Czy w ogóle to przetestowaliście?
ThomasR
46

bini bin/różnią się tylko tym, że ten ostatni będzie pasował tylko do katalogu.

bin/**/*jest taki sam jak bin/**(najwyraźniej od 1.8.2, zgodnie z odpowiedzią @ VonC).

Trudne jeden, że ja właśnie spędziliśmy godzinę albo tak zgrywania moich włosów na to, że bin/i bin/**nie są zupełnie takie same! Ponieważ wcześniejszy ignoruje katalog jako całość, a drugi ignoruje każdy z plików w nim zawartych, a git w prawie wszystkich przypadkach nie dba o katalogi, zwykle nie ma różnicy. Jeśli jednak spróbujesz użyć !do cofnięcia ignorowania ścieżki podrzędnej, zauważysz, że git (ahem) zignoruje ją, jeśli zignorowałeś katalog nadrzędny! (znowu zamiast zawartości katalogu)

Jest to najlepiej widoczne na przykładzie, więc dla nowo zainicjowanego repozytorium skonfiguruj:

$ cat .gitignore
ignored-file
or-dir
dir-only/
!dir-only/cant-reinclude
dir-contents/**
!dir-contents/can-reinclude

$ mkdir or-dir dir-only dir-contents

$ touch file ignored-file or-dir/ignored-file dir-only/cant-reinclude dir-contents/can-reinclude

Istnieją następujące nieśledzone pliki:

$ git ls-files --other
.gitignore
dir-contents/can-reinclude
dir-only/cant-reinclude
file
ignored-file
or-dir/ignored-file

Ale możesz zobaczyć, że następujące pliki nie są ignorowane:

$ git ls-files --other --exclude-standard
.gitignore
dir-contents/can-reinclude
file

A jeśli spróbujesz dodać, otrzymasz:

$ git add dir-only/cant-reinclude
The following paths are ignored by one of your .gitignore files:
dir-only/cant-reinclude
Use -f if you really want to add them.
fatal: no files added

Uważam to zachowanie za błąd. (To wszystko jest włączone git version 1.8.4.msysgit.0)

Simon Buchan
źródło
1
+1 rzeczywiście. Powinieneś rozważyć złożenie aktualnego raportu o błędzie, ponieważ zachowanie wydaje się nieoczekiwane.
chandsie
1
Dokładnie mój przypadek użycia. Dzięki!
Sebastian Graf
3
Różne zachowania dir/i dir/**ponownie. cofnięcie ignorowania z !ma miejsce, ponieważ „Nie można ponownie dołączyć pliku, jeśli katalog nadrzędny tego pliku jest wykluczony” [źródło ]. Mylące, ale zrobione ze względu na wydajność. Zobacz powiązane pytanie SO .
tanius
23

Zauważ, że ściśle rzecz biorąc, git nie śledzi katalogów, tylko pliki. Dlatego nie jest możliwe dodanie katalogu, tylko jego zawartość .

Jednak w kontekście .gitignoregit udaje, że rozumie katalogi tylko z tego powodu

Nie można ponownie dołączyć pliku, jeśli katalog nadrzędny tego pliku jest wykluczony.
https://git-scm.com/docs/gitignore#_pattern_format

Co to oznacza dla wzorców wykluczania? Przyjrzyjmy się im szczegółowo:

bin

To ignoruje

  • pliki o nazwie bin.
  • zawartość folderów o nazwie bin

Możesz umieścić na białej liście ignorowane binpliki i foldery, dodając kolejne !wpisy, ale nie możesz dodać do białej listy zawartości folderów o nazwachbin

bin

!bin/file_in_bin # has no effect, since bin/ is blacklisted!
!bin/* # has no effect, since bin/ is blacklisted!
!file_in_bin # has no effect, since bin/ is blacklisted!

!bin # this works

bin/

Tak samo jak powyżej, z tą różnicą, że nie pasuje do plików o nazwie bin. Dodanie końca /mówi gitowi, aby dopasował tylko katalogi.

bin/*

To ignoruje

  • pliki zawarte w folderze o nazwiebin
  • zawartość bezpośrednich podfolderów folderów o nazwach bin
bin/*  # blacklists bin/file_in_bin and bin/subfolder/

!bin/subfolder/file_in_sub # has no effect, since bin/subfolder is blacklisted!
!bin # whitelists files named bin/bin, since bin/ itself is not blacklisted
!bin/ # has no effect, since bin/ itself is not blacklisted


!bin/file_in_bin # works since bin/ itself is not blacklisted
!file_in_bin # works too
!bin/subfolder # works (so implicitly whitelists bin/subfolder/file_in_sub)
!bin/subfolder/ # works just as well
!bin/* # works for file_in_bin and subfolder/

bin/**

To ignoruje

  • zawartość bin
  • zawartość podfolderów (dowolny poziom zagnieżdżenia) w ramach bin
bin/**  # blacklists bin/file_in_bin and
        # bin/subfolder/ and bin/subfolder/file_in_sub and
        # bin/subfolder/2/ and bin/subfolder/2/file_in_sub_2

!bin/subfolder/file_in_sub # has no effect, since bin/subfolder is blacklisted
!bin/subfolder/2/ # has no effect, since bin/subfolder is blacklisted
!bin/subfolder/2/file_in_sub_2 # has no effect, since bin/subfolder is blacklisted

!bin/subfolder # works only in combinations with other whitelist entries,
               # since all contents of subfolder are blacklisted (1)

!bin/file_in_bin # works since bin itself is not blacklisted
!bin/* # works for file_in_bin and subfolder; see (1)
Thomas R.
źródło
9

Właśnie utworzyłem nowe repozytorium i wypróbowałem kilka rzeczy. Oto moje wyniki:

NOWE WYNIKI

git w wersji 2.10.1. windows.1

  1. Zainicjuj prawie puste repozytorium. Tylko plik README
  2. Wypełnij binkatalog na kilka warstw
    • bin.txt
    • Test.txt
    • bin/a/b/bin.txt
    • bin/a/b/Test.txt
    • bin/a/bin/bin.txt
    • bin/a/bin/Test.txt
    • bin/a/bin.txt
    • bin/a/Test.txt
    • bin/bin.txt
    • bin/Test.txt
  3. Dodaj bindo gitignore: Wyniki
    • Wszystko poniżej binkatalogu (i głębiej) jest teraz ignorowane
    • Poziom roota nie jest ignorowany (/bin.txt i /Test.txt nadal się pokazują)
  4. Edit binaby bin/w gitignore: Wyniki
    • Brak zmiany
  5. Edytuj bin/dobin/*
    • Brak zmiany
  6. Edytuj bin/*dobin/**
    • Brak zmiany
  7. Edytuj bin/**dobin/**/
    • bin/bin.txti bin/Test.txtnie są już ignorowane
  8. Edytuj bin/**/dobin/**/*
    • bin/bin.txti bin/Test.txtznów są ignorowani

STARE WYNIKI

wersja git: 2.7.0.windows.1

  1. Zainicjuj prawie puste repozytorium. Tylko plik README
  2. Wypełnij binkatalog na kilka warstw
    • bin/a/b/Test.txt
    • bin/a/bin/Test.txt
    • bin/a/Test.txt
    • bin/Test.txt
  3. Dodaj bindo gitignore: Wyniki
    • Wszystko poniżej binkatalogu (i głębiej) jest teraz ignorowane
  4. Edit binaby bin/w gitignore: Wyniki
    • Wszystko poniżej binkatalogu (i głębiej) jest nadal ignorowane (bez zmian)
  5. Edytuj bin/dobin/*
    • Wszystko poniżej binkatalogu (i głębiej) jest nadal ignorowane (bez zmian)
  6. Edytuj bin/*dobin/**
    • Wszystko poniżej binkatalogu (i głębiej) jest nadal ignorowane (bez zmian)
  7. Edytuj bin/**dobin/**/
    • bin/Test.txt nie jest już ignorowany
  8. Edytuj bin/**/dobin/**/*
    • Wszystko poniżej binkatalogu (i głębiej) jest ponownie ignorowane
Joe Phillips
źródło
1
To dobry test, myślę, że zmiana nazwy text.txt na bin.txt lepiej pokazałaby kilka kluczowych różnic. Gdybyś to zrobił, zagłosowałbym za tą odpowiedzią.
Matt Johnson,
@MattJohnson True ... niestety jestem w tej chwili na nowszej wersji gita
Joe Phillips
@MattJohnson Wreszcie doszedłem do tego
Joe Phillips
8

Zauważ, że ' **', w połączeniu z podkatalogiem ( **/bar), musiało zmienić swoje domyślne zachowanie, ponieważ informacja o wydaniu dla git1.8.2 teraz wspomina:

Wzorce w .gitignorei .gitattributespliki mogą mieć **/, jako wzorzec, który odpowiada 0 lub więcej poziomów podkatalogu.

Np. „ foo/**/bar” Pasuje ” bar„ w samym „ foo” lub w podkatalogu „ foo”.


Reguła do zapamiętania (i która pomaga zrozumieć różnicę intencji kryjącą się za tą składnią) to:

Nie można ponownie dołączyć pliku, jeśli katalog nadrzędny tego pliku jest wykluczony.


Zwykle, jeśli chcesz wykluczyć pliki z podfolderu ignorowanego folderu f, wykonaj następujące czynności:

f/**
!f/**/
!f/a/sub/folder/someFile.txt

To jest:

  • Gdyby pierwsza reguła brzmiała f/, folder f/zostałby zignorowany, a poniższe reguły fnie miałyby znaczenia.
  • f/**osiągnąć to samo f/, ale zignorować wszystkie elementy podrzędne (pliki i podfoldery).
    Który daje możliwość whitelist (wyłączenia z gitignore) podfoldery: !f/**/.
  • Ponieważ wszystkie fpodfoldery nie są ignorowane, możesz dodać regułę wykluczającą plik ( !f/a/sub/folder/someFile.txt)
VonC
źródło
Jak to odpowiada na pytanie?
ThomasR
0

Istnieje inna różnica między bin/*i bin/.

bin/pasuje foo/bin/test.txt(zgodnie z oczekiwaniami), ale bin/*nie, co wydaje się dziwne, ale jest udokumentowane: https://git-scm.com/docs/gitignore

„Dokumentacja / *. Html” odpowiada wyrażeniu „Dokumentacja / git.html”, ale nie „Dokumentacja / ppc / ppc.html” ani „tools / perf / Documentation / perf.html”.

Wydaje się, że powodem tego są następujące zasady:

  • Jeśli wzór kończy się ukośnikiem, jest usuwany do celów poniższego opisu…

  • Jeśli wzorzec nie zawiera ukośnika /, Git traktuje go jako wzorzec globalnej powłoki i sprawdza dopasowanie względem ścieżki w odniesieniu do lokalizacji pliku .gitignore…

  • W przeciwnym razie Git traktuje wzorzec jako glob powłoki odpowiedni do użycia przez fnmatch (3) z flagą FNM_PATHNAME…

Więc jeśli wzorzec kończy się ukośnikiem, ukośnik jest usuwany i traktowany jako wzorzec globu powłoki, w którym to przypadku binpasuje foo/bin/test.txt. Jeśli kończy się na /*, ukośnik nie jest usuwany i jest przekazywany do fnmatch, co nie pasuje do podkatalogów.

Jednak to samo nie dotyczy foo/bin/i foo/bin/*, ponieważ nawet po usunięciu końcowego ukośnika z foo/bin/nadal zawiera ukośnik, więc jest traktowany jako wzorzec dopasowania fn, a nie glob. To znaczy nie będzie pasowaćbar/foo/bin/test.txt

user1431317
źródło