.gitignore wyklucz folder, ale zawiera określony podfolder

964

Mam folder, application/który dodaję do .gitignore. Wewnątrz application/folderu znajduje się folder application/language/gr. Jak mogę dołączyć ten folder?

Próbowałem tego

application/
!application/language/gr/

bez powodzenia ...

chrystus
źródło
1
Mamy nadzieję, że .gitignoredokumentacja „formatu wzorca” właśnie stała się bardziej przejrzysta (grudzień 2013 r.). Zobacz moją odpowiedź poniżej
VonC
Moje ulubione pytanie i odpowiedź, dodane do ulubionych, a także do zakładek w przeglądarce.
codekiddy,

Odpowiedzi:

1611

Jeśli wykluczysz application/, to wszystko pod nim zawsze będzie wykluczone (nawet jeśli jakiś późniejszy wzór wykluczenia negatywnego („unignore”) może pasować do czegoś podapplication/ ).

Aby zrobić to, co chcesz, musisz „odignorować” każdy katalog nadrzędny wszystkiego, co chcesz „odignorować”. Zwykle kończy się pisanie reguł dla tej sytuacji parami: zignoruj ​​wszystko w katalogu, ale nie jakiś określony podkatalog.

# you can skip this first one if it is not already excluded by prior patterns
!application/

application/*
!application/language/

application/language/*
!application/language/gr/

Uwaga
Końcowe /*jest znaczące:

  • Wzorzec dir/wyklucza katalog o nazwie diri (domyślnie) wszystko pod nim.
    Dzięki dir/Git nigdy nie spojrzy na nic pod dir, a zatem nigdy nie zastosuje żadnego z wzorców „wykluczenia” do niczego pod dir.
  • Wzór dir/*nic nie mówi o dirsobie; po prostu wyklucza wszystko pod dir. Za pomocą dir/*Git przetworzy bezpośrednią zawartość dir, dając innym wzorcom szansę na „wykluczenie” części zawartości ( !dir/sub/).
Chris Johnsen
źródło
12
Czy końcowe gwiazdki są znaczące? Jeśli tak, jaka jest różnica w znaczeniu? Zgodnie z algorytmem opisanym w dokumentacji gitignore , końcówka ukośnika końcowego pasuje do katalogu i ścieżek pod tym katalogiem. Kończenie gwiazdką byłoby wówczas traktowane jako wzór globalny. Eksperymentowanie pokazuje, że wariant gwiazdki działa, ale nie kończy się na ukośniku. Chciałbym zrozumieć, dlaczego tak jest.
seh
123
@seh: Tak, końcowe /*jest znaczące. Jeśli katalog zostanie wykluczony, Git nigdy nie będzie przeglądał zawartości tego katalogu. Wzorzec dir/wyklucza katalog o nazwie diri (domyślnie) wszystko pod nim. Wzór dir/*nic nie mówi o dirsobie; po prostu wyklucza wszystko pod dir. Dzięki dir/Git nigdy nie spojrzy na nic pod dir, a zatem nigdy nie zastosuje żadnego z wzorców „wykluczenia” do niczego pod dir. Za pomocą dir/*Git przetworzy bezpośrednią zawartość dir, dając innym wzorcom szansę na „wykluczenie” części zawartości ( !dir/sub/).
Chris Johnsen
7
Ach, to wyjaśnia. Bez względu na to, ile razy czytałem dokumentację gitignore , nigdy nie zrozumiałem, kiedy odwrócone wzorce nie działają. Z twoim wyjaśnieniem jest teraz jasne. Dokumentacja gitignore wymaga sekcji „przepis”, aby wyjaśnić, jak to zrobić.
seh
8
Nie mogłem tego całkiem uruchomić (szalony plik .gitignore!), Więc zamiast tego po prostu dodałem pliki po cd'owaniu do katalogu, który chciałem. git add -f .
K0D4,
2
Zauważ, że nie możesz polegać na wynikach git status, które tylko powiedzą, że zostanie dodany katalog najwyższego poziomu. Zamiast tego wykonaj jeden git addz katalogu najwyższego poziomu, a następnie git status(miejmy nadzieję) wyświetli listę podzbiorów plików, które zostały dopasowane przez wzorzec.
Matthew Strawbridge,
136

Zobowiązanie 59856de od Karsten Blees (kblees) dla Git 1.9 / 2.0 (Q1 2014) wyjaśnia tę sprawę:

gitignore.txt: wyjaśnienie rekurencyjnego charakteru wykluczonych katalogów

Opcjonalny przedrostek „ !”, który neguje wzór; każdy pasujący plik wykluczony przez poprzedni wzór zostanie ponownie dołączony.

Ponowne dołączenie pliku nie jest możliwe, jeśli katalog nadrzędny tego pliku jest wykluczony. ( *)
( *: chyba że w git 2.8+ są spełnione pewne warunki, patrz poniżej)
Git nie wyświetla wykluczonych katalogów ze względu na wydajność, więc wszelkie wzorce w zawartych plikach nie mają wpływu, bez względu na to, gdzie są zdefiniowane.

Umieść ukośnik odwrotny („ \”) przed pierwszym „ !” dla wzorów rozpoczynających się od literału „ !”, na przykład „ \!important!.txt”.

Przykład wykluczenia wszystkiego oprócz określonego katalogu foo/bar(zwróć uwagę /*- bez ukośnika, symbol wieloznaczny również wyklucza wszystko w środku foo/bar):

 --------------------------------------------------------------
     $ cat .gitignore
     # exclude everything except directory foo/bar
     /*
     !/foo
     /foo/*
     !/foo/bar
 --------------------------------------------------------------

W Twoim przypadku:

application/*
!application/**/
application/language/*
!application/language/**/
!application/language/gr/**

Najpierw musisz umieścić na białej liście foldery , zanim będziesz mógł umieścić na białej liście pliki w danym folderze.


Aktualizacja luty / marzec 2016 r .:

Zauważ, że w przypadku git 2.9.x / 2.10 (połowa 2016 roku?) Może być możliwe ponowne dołączenie pliku, jeśli katalog nadrzędny tego pliku zostanie wykluczony, jeśli w ścieżce ponownie nie ma znaku zastępczego .

Nguyễn Thái Ngọc Duy ( pclouds) próbuje dodać tę funkcję:

W przypadku git 2.9+ mogło to faktycznie zadziałać, ale ostatecznie zostało cofnięte:

application/
!application/language/gr/
VonC
źródło
Próbowałem użyć zaktualizowanej składni ponownego dołączania opublikowanej na końcu twojej odpowiedzi na git dla Windows, v2.8.1.windows.1ale nie działa :(
David Hancock
1
@DavidHancock Przepraszamy, zedytowałem odpowiedź: nie jest jeszcze dostępna.
VCC
1
@DavidHancock ja też: to ponad 13 odpowiedzi przepełnienia stosu, które musiałem edytować wiele razy!
VCC
5
Git 2.9 został wydany wczoraj. Potwierdzenie wzorca application/+ !application/language/gr/wymienionego w odpowiedzi działa zgodnie z oczekiwaniami.
Ray Shan
1
@RayShan Dziwne: Widziałem anulowanie zatwierdzenia anulujące tę funkcję, ale nie widziałem (a informacja o wersji github.com/git/git/blob/master/Documentation/RelNotes/2.9.0.txt nie wspomina) wszelkie zmiany poprawiające reguły .gitignore.
VonC
52

Odpowiedź Christiana Johnsena jest świetna, ale w nowszych wersjach Gita (1.8.2 lub nowszej) istnieje podwójna gwiazdka, którą możesz wykorzystać, aby uzyskać nieco krótsze rozwiązanie:

# assuming the root folder you want to ignore is 'application'
application/**/*

# the subfolder(s) you want to track:
!application/language/gr/

W ten sposób nie musisz „anulować” nadrzędnego katalogu podfolderu, który chcesz śledzić.


Z Git 2.17.0 (Nie wiem, jak wcześnie przed tą wersją. Prawdopodobnie powrót do 1.8.2), działa **wzorzec w połączeniu z wykluczeniami dla każdego podkatalogu prowadzącego do twoich plików. Na przykład:

# assuming the root folder you want to ignore is 'application'
application/**

# Explicitly track certain content nested in the 'application' folder:
!application/language/
!application/language/gr/
!application/language/gr/** # Example adding all files & folder in the 'gr' folder
!application/language/gr/SomeFile.txt # Example adding specific file in the 'gr' folder
rpyzh
źródło
9
Niestety, to się nie powiedzie (Git 1.8.4.msysgit.0), ponieważ wzorzec **może pasować do zerowych podfolderów, a także *dopasuje go languagei wykluczy, uniemożliwiając włączenie gr. Cały łańcuch rodziców @Chris Johnson zaleca nadal wydaje się konieczny.
Sean Gugler
3
Brzmi idealnie, ale nie działa dla mnie na git 2.3.7 ... /www/**/* !/www/config.xml !/www/resconfig.xml, a katalog res jest nadal ignorowany.
Rob
@Rob musisz również dodać !/www/res/. Możesz użyć folder/**/*wzorca, ale nadal musisz dodać wykluczenia dla każdego podkatalogu, który chcesz dodać. Jest nadal krótszy i bardziej czytelny niż kombinacja ignorowania / wykluczania.
Ben Kane,
Wiem, że to stary komentarz, ale na wypadek, gdybyś był ciekawy. Do odpowiedzi dodałem edycję dokumentującą to podejście.
Ben Kane,
21

Istnieje wiele podobnych pytań na ten temat, więc opublikuję to, co napisałem wcześniej:

Jedynym sposobem, w jaki mogłem to zrobić na mojej maszynie, było zrobienie tego w ten sposób:

# Ignore all directories, and all sub-directories, and it's contents:
*/*

#Now ignore all files in the current directory 
#(This fails to ignore files without a ".", for example 
#'file.txt' works, but 
#'file' doesn't):
*.*

#Only Include these specific directories and subdirectories:
!wordpress/
!wordpress/*/
!wordpress/*/wp-content/
!wordpress/*/wp-content/themes/
!wordpress/*/wp-content/themes/*
!wordpress/*/wp-content/themes/*/*
!wordpress/*/wp-content/themes/*/*/*
!wordpress/*/wp-content/themes/*/*/*/*
!wordpress/*/wp-content/themes/*/*/*/*/*

Zwróć uwagę, w jaki sposób musisz wyraźnie zezwalać na zawartość dla każdego poziomu, który chcesz uwzględnić. Więc jeśli mam podkatalogi 5 głęboko pod tematami, nadal muszę to przeliterować.

To jest z komentarza @ Yarin tutaj: https://stackoverflow.com/a/5250314/1696153

Były to przydatne tematy:

Też próbowałem

*
*/*
**/**

i **/wp-content/themes/**

lub /wp-content/themes/**/*

To też nie działało dla mnie. Dużo prób i błędów!

Katie
źródło
1
Uwaga: pamiętaj, że kolejność ma również znaczenie w pliku .gitignore, więc upewnij się, że umieściłeś swoje !reguły na dole.
starbeamrainbowlabs,
15

Odkryłem, że tylko to faktycznie działa.

**/node_modules/*
!**/node_modules/keep-dir
Steve Kling
źródło
super, ocal mój dzień)
Jegor Zaremba
uratował również mój
tomrlh
8

Najprostszym i prawdopodobnie najlepszym sposobem jest ręczne dodanie plików (zazwyczaj ma to pierwszeństwo przed .gitignoreregułami stylu):

git add /path/to/module

Możesz nawet chcieć -N dodać flagę, aby zasugerować, że je dodasz, ale nie od razu. Często robię to dla nowych plików, których jeszcze nie jestem gotowy na scenę.


To jest kopia odpowiedzi opublikowana na temat czegoś, co może być z łatwością duplikatem kontroli jakości. Ponownie umieszczam go tutaj, aby zwiększyć widoczność - łatwiej mi nie mieć bałaganu z regułami gitignore.

D. Ben Knoble
źródło
2
Dziękuję za tę odpowiedź. Musiałem dodać -f, ponieważ jest w moim .gitignore, więc tak: git add -f ścieżka / do / pliku
jasonflaherty
6

Ponieważ wielu programistów używa węzła. przypadkiem użycia, który spełnia to pytanie, jest wykluczenie node_modulesoprócz jednego modułu, module-ana przykład:

!node_modules/

node_modules/*
!node_modules/module-a/
Abdennour TOUMI
źródło
2
Nie działa dla wersji git 2.10.2.windows.1.
Sebastian
1
W przypadku wersji git 2.10.2 postępuj zgodnie z tą odpowiedzią
nalexn
👌 świetne 👋 👋.
Abdennour TOUMI
6

Dodaj dodatkową odpowiedź:

!/.vs/              <== include this folder to source control, folder only, nothing else
/.vs/*              <== but ignore all files and sub-folder inside this folder
!/.vs/ProjectSettings.json <== but include this file to source control
!/.vs/config/       <== then include this folder to source control, folder only, nothing else
!/.vs/config/*      <== then include all files inside the folder

oto wynik:

wprowadź opis zdjęcia tutaj

Dongdong
źródło
2
To była dla mnie najbardziej użyteczna odpowiedź, ponieważ pomogły mi efekty wizualne. Dzięki!
Joel Murphy,
4

Zwłaszcza w przypadku starszych wersji Gita większość sugestii nie zadziała tak dobrze. W takim przypadku umieściłbym osobny plik .gitignore w katalogu, w którym chcę, aby zawartość była uwzględniana niezależnie od innych ustawień i pozwalała na to, co jest potrzebne.

Na przykład: /.gitignore

# ignore all .dll files
*.dll

/dependency_files/.gitignore

# include everything
!*

Więc wszystko w pliku / dependency_files (nawet pliki .dll) jest w porządku.

Breit
źródło
4

Znalazłem podobny przypadek tutaj, gdzie w laravel domyślnie .gitignoreignoruje wszystkie używając asterix, a następnie zastępuje katalog publiczny.

*
!public
!.gitignore

Nie wystarczy to, jeśli natrafisz na scenariusz PO.

Jeśli chcesz zatwierdzić określone podfoldery public, powiedzmy np. W swoim public/productskatalogu, że chcesz dołączyć pliki o głębokości jednego podfolderu, np. Aby dołączyć public/products/a/b.jpg, nie zostaną one poprawnie wykryte, nawet jeśli dodasz je specjalnie w ten sposób !/public/products,!public/products/* itp ..

Rozwiązaniem jest upewnienie się, że dodałeś pozycję dla każdego takiego poziomu ścieżki, aby zastąpić je wszystkie.

*
!.gitignore
!public/
!public/*/
!public/products/
!public/products/*
!public/products/*/
!public/products/*/
!public/products/*/*
blamb
źródło
3

Kolejny przykład chodzenia po strukturze katalogów, aby uzyskać dokładnie to, czego chcesz. Uwaga: nie wykluczam, Library/aleLibrary/**/*

# .gitignore file
Library/**/*
!Library/Application Support/
!Library/Application Support/Sublime Text 3/
!Library/Application Support/Sublime Text 3/Packages/
!Library/Application Support/Sublime Text 3/Packages/User/
!Library/Application Support/Sublime Text 3/Packages/User/*macro
!Library/Application Support/Sublime Text 3/Packages/User/*snippet
!Library/Application Support/Sublime Text 3/Packages/User/*settings
!Library/Application Support/Sublime Text 3/Packages/User/*keymap
!Library/Application Support/Sublime Text 3/Packages/User/*theme
!Library/Application Support/Sublime Text 3/Packages/User/**/
!Library/Application Support/Sublime Text 3/Packages/User/**/*macro
!Library/Application Support/Sublime Text 3/Packages/User/**/*snippet
!Library/Application Support/Sublime Text 3/Packages/User/**/*settings
!Library/Application Support/Sublime Text 3/Packages/User/**/*keymap
!Library/Application Support/Sublime Text 3/Packages/User/**/*theme

> git add Library

> git status

On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   Library/Application Support/Sublime Text 3/Packages/User/Default (OSX).sublime-keymap
    new file:   Library/Application Support/Sublime Text 3/Packages/User/ElixirSublime.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/Package Control.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/Preferences.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/RESTer.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/SublimeLinter/Monokai (SL).tmTheme
    new file:   Library/Application Support/Sublime Text 3/Packages/User/TextPastryHistory.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/ZenTabs.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/adrian-comment.sublime-macro
    new file:   Library/Application Support/Sublime Text 3/Packages/User/json-pretty-generate.sublime-snippet
    new file:   Library/Application Support/Sublime Text 3/Packages/User/raise-exception.sublime-snippet
    new file:   Library/Application Support/Sublime Text 3/Packages/User/trailing_spaces.sublime-settings
Adrian Dunston
źródło
Dokładnie to, co chciałem zrobić ;-)
Kitze
3

W WordPress pomogło mi to:

wp-admin/
wp-includes/
/wp-content/*
!wp-content/plugins/
/wp-content/plugins/*
!/wp-content/plugins/plugin-name/
!/wp-content/plugins/plugin-name/*.*
!/wp-content/plugins/plugin-name/**
T.Todua
źródło
3

gitignore - Określa celowo nieśledzone pliki do zignorowania.

Przykład wykluczenia wszystkiego oprócz określonego katalogu foo / bar (zwróć uwagę na / * - bez ukośnika, symbol wieloznaczny również wykluczy wszystko w obrębie foo / bar ):

$ cat .gitignore
# exclude everything except directory foo/bar
/*
!/foo
/foo/*
!/foo/bar

Kolejny przykład dla WordPress :

!/wp-content
wp-content/*
!/wp-content/plugins
wp-content/plugins/*
!wp-content/plugins/my-awesome-plugin

Więcej informacji tutaj: https://git-scm.com/docs/gitignore

Ali
źródło
2

Często używam tego obejścia w interfejsie CLI, gdzie zamiast konfigurować mój .gitignore, tworzę osobny .includeplik, w którym definiuję (pod) katalogi, które chcę uwzględnić, pomimo katalogów bezpośrednio lub rekurencyjnie ignorowanych przez.gitignore .

Dlatego korzystam dodatkowo

git add `cat .include`

podczas inscenizacji, przed popełnieniem.

Do OP sugeruję użycie takiego, .includektóry ma następujące linie:

<parent_folder_path>/application/language/gr/*

UWAGA: Użycie catnie pozwala na użycie aliasów (wewnątrz .include) do określenia $ HOME (ani żadnego innego określonego katalogu). Dzieje się tak, ponieważ homedir/app1/* po przejściu do git addużycia powyższego polecenia pojawia się jako git add 'homedir/app1/*', a umieszczenie znaków w pojedynczych cudzysłowach ('') zachowuje dosłowną wartość każdego znaku w cudzysłowie, uniemożliwiając w ten sposób działanie aliasów (takich jak homedir ) (patrz Bash Single Cytaty ).

Oto przykład .includepliku, którego używam w moim repozytorium tutaj .

/home/abhirup/token.txt
/home/abhirup/.include
/home/abhirup/.vim/*
/home/abhirup/.viminfo
/home/abhirup/.bashrc
/home/abhirup/.vimrc
/home/abhirup/.condarc
Abhirup Das
źródło
1

Chciałem śledzić pliki js produkcji jquery i zadziałało:

node_modules/*
!node_modules/jquery
node_modules/jquery/*
!node_modules/jquery/dist/*
Indrek Ruubel
źródło