Jak właściwie działają reguły wykluczania .gitignore?

146

Próbuję rozwiązać problem gitignore w dużej strukturze katalogów, ale aby uprościć moje pytanie, ograniczyłem go do następującego.

Mam następującą strukturę katalogów dwóch plików (foo, bar) w zupełnie nowym repozytorium git (na razie brak zatwierdzeń):

a/b/c/foo
a/b/c/bar

Oczywiście, 'git status -u' pokazuje:

# Untracked files:
...
#       a/b/c/bar
#       a/b/c/foo

To, co chcę zrobić, to utworzyć plik .gitignore, który ignoruje wszystko wewnątrz a / b / c, ale nie ignoruje pliku „foo”.

Jeśli utworzę .gitignore w ten sposób:

c/

Następnie 'git status -u' pokazuje, że zarówno foo, jak i bar są ignorowane:

# Untracked files:
...
#       .gitignore

I jest tak, jak się spodziewam.

Jeśli teraz dodam regułę wykluczenia dla foo, to:

c/
!foo

Według strony podręcznika gitignore, spodziewałbym się, że to zadziała. Ale tak nie jest - nadal ignoruje foo:

# Untracked files:
...
#       .gitignore

To też nie działa:

c/
!a/b/c/foo

Ani tego nie robi:

c/*
!foo

Daje:

# Untracked files:
...
#       .gitignore
#       a/b/c/bar
#       a/b/c/foo

W takim przypadku, chociaż foo nie jest już ignorowane, bar również nie jest ignorowany.

Kolejność reguł w .gitignore też nie wydaje się mieć znaczenia.

To również nie robi tego, czego bym się spodziewał:

a/b/c/
!a/b/c/foo

Ten ignoruje zarówno foo, jak i bar.

Jedna sytuacja, która działa, to utworzenie pliku a / b / c / .gitignore i umieszczenie go w nim:

*
!foo

Ale problem polega na tym, że w końcu będą inne podkatalogi pod a / b / c i nie chcę umieszczać oddzielnego .gitignore w każdym z nich - miałem nadzieję, że utworzę plik .gitignore oparty na projektach pliki, które mogą znajdować się w głównym katalogu każdego projektu i obejmują całą „standardową” strukturę podkatalogów.

To również wydaje się równoważne:

a/b/c/*
!a/b/c/foo

To może być najbliższa rzecz „pracy”, jaką mogę osiągnąć, ale należy podać pełne ścieżki względne i wyraźne wyjątki, co będzie uciążliwe, jeśli mam wiele plików o nazwie „foo” na różnych poziomach drzewa podkatalogów.

W każdym razie albo nie do końca rozumiem, jak działają reguły wykluczania, albo w ogóle nie działają, gdy katalogi (a nie symbole wieloznaczne) są ignorowane - przez regułę kończącą się na /

Czy ktoś może rzucić na to trochę światła?

Czy istnieje sposób, aby gitignore używał czegoś sensownego, takiego jak wyrażenia regularne, zamiast tej niezdarnej składni opartej na powłoce?

Używam i obserwuję to z git-1.6.6.1 na Cygwin / bash3 i git-1.7.1 na Ubuntu / bash3.

davidA
źródło
powiązane pytanie: stackoverflow.com/questions/2820255/…
Cascabel
4
Świetnie napisane pytanie! Liczba spraw, które wypróbowałeś, naprawdę pomogła mi zrozumieć, co robiłem źle w podobnej sprawie. Dzięki Bogu, długo szukałem dzisiaj.
Alex G

Odpowiedzi:

153
/ABC/*
!bla

Wydaje się, że działa u mnie (git 1.7.0.4 w systemie Linux). Jest *to ważne, ponieważ w przeciwnym razie ignorujesz sam katalog (więc git nie zajrzy do środka) zamiast plików w katalogu (co pozwala na wykluczenie).

Pomyśl o wykluczeniach jako stwierdzenie „ale nie tego” zamiast „ale uwzględnij ten” - „ignoruj ​​ten katalog ( /a/b/c/), ale nie ten ( foo)” nie ma większego sensu; „ignoruje wszystkie pliki w tym katalogu ( /a/b/c/*), ale nie ten ( foo)”. Cytując stronę podręcznika:

Opcjonalny prefiks! co neguje wzorzec; każdy pasujący plik wykluczony przez poprzedni wzorzec zostanie ponownie uwzględniony.

tzn. plik musiał zostać już wykluczony, aby można go było ponownie dołączyć. Mam nadzieję, że to rzuca trochę światła.

Chris
źródło
Tak, przykład, który podajesz, działa dobrze. Problem polega na tym, że / a / b / * nie działa, jeśli foo jest w c, co oznacza, że ​​mam wiele plików foo w różnych podkatalogach w c (np. D /, e /, f / g / h /, i / j / itp.), wówczas reguła negacji „! foo” ich nie złapie.
davidA
1
@meowsqueak Rzeczywiście, nie będzie. Jeśli zignorujesz / a / b / *, wtedy nie ma katalogu, w którym mógłby znajdować się foo! Jeśli próbujesz zignorować całe drzewo katalogów w / a / b, ale dołączasz dowolny plik o nazwie "foo", który może znajdować się w dowolnym miejscu w drzewie, nie sądzę, aby było to wykonalne z wzorcami .gitignore: \
Chris
Właściwie to może wyjaśniać, dlaczego to nie działa dla mnie - nie działają następujące reguły: „/ a / b / *”, „! C / foo”. Jeśli to zadziała, może nie być problemu.
davidA
5
„Nie sądzę, że jest to wykonalne z wzorami .gitignore” - niestety myślę, że masz rację.
davidA
@meowsqueak Możesz zignorować, /a/b/ca następnie napisać podpięcie do git add --forcedowolnego pasującego pliku przed zatwierdzeniem lub coś takiego
Chris
24

Mam podobną sytuację, moim rozwiązaniem było zastosowanie:

/a/**/*
!/a/**/foo

To powinno działać dla dowolnej liczby katalogów pośrednich, jeśli dobrze przeczytam **.

medge799
źródło
6

nie jest to na pewno jasne ze strony podręcznika .gitignore. To działa:

*
!/a
!/a/b
!/a/b/c
!/a/b/c/foo

# don't forget this one
!.gitignore

Jak wspomniał Chris, katalog nie jest nawet otwierany, jeśli jest wykluczony. Więc jeśli chcesz mieć możliwość ignorowania * ale niektórych plików, musisz zbudować ścieżkę do tych plików jak powyżej. Dla mnie jest to wygodne, ponieważ chcę zrobić recenzję kodu na 1 pliku biblioteki i jeśli chcę zrobić później kolejny, po prostu go dodaję, a wszystko inne jest ignorowane.


źródło
6

Oto inna opcja:

*
!/a*
!/a/*
!/a/*/*
!/a/*/*/*

To zignorowałoby każdy plik i katalog, z wyjątkiem plików / katalogów trzy poziomy w głębi pliku.

Peter Petrik
źródło
5

Na bardziej ogólnym notatki, git1.8.2 obejmie poprawki (również w jej v4 , poproszony przez jakiegoś przepełnienie stosu pytanie ) z Adam Wież o określenie, które gitignorerządzą faktycznie ignoruje plik.

Zobacz informacje o wydaniu git1.8.2 i pytanie SO „ która reguła gitignore ignoruje mój plik ”:
to będzie polecenie git check-ignore.

VonC
źródło
1
Dziękuję za przekazanie tej informacji. check-ignorepowie Ci również, która reguła zapobiega ignorowaniu Twojego pliku, jeśli tak jest.
Adam Spiers
@AdamSpiers brzmi świetnie. Z pewnością będę musiał się tym bawić.
VonC