Sytuacja, którą prędzej czy później spotyka każdy: w repozytorium wylądował plik .env albo katalog z logami. Dopisujesz go do .gitignore, robisz git status i – nic. Git dalej raportuje zmiany w tym pliku, jakby reguły w ogóle nie istniały.
Plik .gitignore działa poprawnie. Po prostu nie robi tego, czego się po nim spodziewasz. Pokażę, skąd bierze się to nieporozumienie, jak w dwie komendy potwierdzić diagnozę i jak wyjść z sytuacji bez kasowania czegokolwiek z dysku.
Dlaczego .gitignore nie działa na ten plik
Reguły z .gitignore dotyczą wyłącznie plików nieśledzonych (untracked). Decydują o tym, czego Git ma nie proponować do dodania. W momencie, w którym plik trafił choć raz do commita, przestaje być kandydatem – jest już częścią repozytorium i Git śledzi go dalej niezależnie od tego, co napiszesz w .gitignore.
To zachowanie jest opisane wprost w dokumentacji: reguły ignorowania nie mają wpływu na pliki znajdujące się już w indeksie. Innymi słowy – dopisanie .env do .gitignore nie jest instrukcją „przestań śledzić ten plik”, tylko „nie zaczynaj go śledzić”.
Diagnoza: sprawdź, czy plik jest śledzony
Zamiast zgadywać, zapytaj Gita wprost. Pierwsza komenda wypisze plik, jeśli jest w indeksie:
git ls-files .env
Jeśli w odpowiedzi zobaczysz ścieżkę pliku – masz sprawcę. Plik jest śledzony i żadna reguła w .gitignore tego nie zmieni.
Druga komenda bywa myląca, więc warto wiedzieć, jak jej używać. git check-ignore pokazuje, która reguła ignoruje dany plik:
git check-ignore -v .env
Dla pliku śledzonego ta komenda nie wypisze nic i zakończy się kodem wyjścia 1 – co łatwo odczytać jako „reguły są zepsute”. Tymczasem check-ignore domyślnie pomija pliki z indeksu, bo dla nich reguły ignorowania i tak nie mają zastosowania. Żeby zobaczyć samą regułę, dodaj --no-index:
git check-ignore -v --no-index .env
Teraz odpowiedź jest konkretna – plik źródłowy reguły, numer linii, sama reguła i sprawdzana ścieżka:
.gitignore:1:.env .env
Zestawienie obu wyników daje pełny obraz: reguła istnieje i jest poprawna, ale plik jest śledzony, więc reguła go nie dotyczy.
Rozwiązanie: usuń plik z indeksu
Potrzebujesz komendy, która usunie plik z indeksu Gita, ale zostawi go na dysku. Robi to git rm z flagą --cached:
git rm --cached .env git commit -m "Stop tracking .env"
Po pierwszej komendzie git status pokaże plik jako usunięty:
D .env
To wpis o usunięciu z repozytorium, nie z systemu plików – .env leży na dysku nietknięty. Po commicie zmiany w tym pliku przestają pojawiać się w git status, bo od tej chwili jest plikiem nieśledzonym, na który wreszcie działa reguła z .gitignore.
Wariant masowy, gdy plików jest więcej
Jeśli .gitignore powstał późno i śledzonych śmieci jest więcej niż jeden plik, prościej wyczyścić cały indeks i zbudować go od nowa – już z uwzględnieniem reguł:
git rm -r --cached . git add . git commit -m "Apply gitignore rules to tracked files"
Pierwsza komenda usuwa z indeksu wszystko, druga dodaje z powrotem tylko to, czego .gitignore nie wyklucza. Pliki na dysku pozostają nietknięte, a w commicie wylądują wyłącznie usunięcia tego, co i tak nie powinno było być śledzone.
Przed commitem warto zerknąć na git status i upewnić się, że lista usunięć zawiera to, czego się spodziewasz. Pomaga w tym prompt pokazujący stan repozytorium – opisywałem taką konfigurację we wpisie o promptcie PowerShella z obsługą Gita.
To nie usuwa pliku z historii
Najważniejsze zastrzeżenie i – sądząc po liczbie wyciekających repozytoriów – najczęściej pomijane. git rm --cached zdejmuje plik ze śledzenia od bieżącego commita w przód. Wszystkie wcześniejsze commity nadal go zawierają, razem z zawartością.
Łatwo to sprawdzić – wystarczy sięgnąć do dowolnego starszego commita:
git log --oneline --all -- .env git show <hash-starszego-commita>:.env
Druga komenda wypisze zawartość pliku sprzed usunięcia. Jeśli był tam klucz API albo hasło do bazy, nadal tam jest i nadal jest do odczytania przez każdego, kto ma dostęp do repozytorium.
Do przepisania historii służy git-filter-repo – narzędzie rekomendowane obecnie przez projekt Git zamiast przestarzałego git filter-branch. Trzeba się liczyć z tym, że operacja zmienia hashe commitów i wymaga wymuszonego push oraz ponownego sklonowania repozytorium przez resztę zespołu. GitHub opisuje całą procedurę w dokumentacji usuwania wrażliwych danych.
Osobna sprawa: jeśli repozytorium jest podpięte pod hosting albo automatyczny deploy, sprawdź też zmienne środowiskowe po stronie usługi. Sekrety powinny mieszkać tam, a nie w plikach repozytorium – pisałem o takiej konfiguracji przy okazji deployu z GitHuba na Netlify.
Dwie inne przyczyny, gdy plik jednak nie jest śledzony
Jeśli git ls-files nic nie zwrócił, problem leży gdzie indziej. Najczęściej w jednej z dwóch rzeczy.
Reguła pochodzi z globalnego .gitignore
Odwrotny objaw: plik jest ignorowany, choć w projektowym .gitignore nie ma o nim ani słowa. Git czyta bowiem także plik globalny, wskazany przez core.excludesfile:
git config --get core.excludesfile
Tu z pomocą przychodzi git check-ignore -v, bo w pierwszej kolumnie podaje pełną ścieżkę do pliku z regułą. Jeśli wskazuje na coś spoza repozytorium – masz odpowiedź. To częsty efekt uboczny konfiguracji przenoszonej między maszynami razem z resztą developerskiego zestawu narzędzi.
Negacja wewnątrz wykluczonego katalogu
Klasyczna pułapka. Chcesz wykluczyć cały build/, ale zachować jeden podkatalog, więc piszesz:
build/ !build/keep/
I nie działa – build/keep/ nadal jest ignorowany. Powód: skoro cały katalog build/ został wykluczony, Git w ogóle do niego nie zagląda, więc nie ma szans zastosować reguły negującej do jego zawartości. Rozwiązaniem jest wykluczenie zawartości katalogu zamiast jego samego:
build/* !build/keep/
Różnica sprowadza się do jednego znaku, a zmienia wynik – przy build/* Git wchodzi do katalogu i przetwarza jego elementy pojedynczo, dzięki czemu negacja ma na czym zadziałać.
Warto też pamiętać, że przy wielu pasujących regułach wygrywa ostatnia z nich. Jeśli reguły są rozrzucone po pliku, kolejność bywa istotniejsza niż sama treść – a git check-ignore -v i tak wskaże tę, która ostatecznie zadziałała.
Krótka ściąga
git ls-files <plik>– czy plik jest śledzonygit check-ignore -v --no-index <plik>– która reguła go ignorujegit rm --cached <plik>– przestań śledzić, zostaw na dyskugit rm -r --cached .+git add .– przebuduj cały indeks według regułgit config --get core.excludesfile– sprawdź globalny plik reguł
Wszystkie polecenia sprawdzone na Git 2.46. Są stabilne od wielu wersji, więc na starszych instalacjach też zadziałają – szczegóły składni znajdziesz w dokumentacji gitignore, git rm i git check-ignore.