guide

gitignore-Vorrang und Negationsmuster (!)

Warum die letzte Regel gewinnt, wie der Vorrang zwischen .gitignore, info/exclude und globaler Konfiguration geregelt ist, und warum ! eine Datei in einem ausgeschlossenen Elternverzeichnis nicht zurückholen kann – samt Lösung.

Wenn gitignore-Regeln sich nicht wie erwartet verhalten, liegt die Ursache fast immer in der Reihenfolge oder im übergeordneten Verzeichnis. Dieser Artikel zeigt, welcher von mehreren Regeln git folgt.

Innerhalb einer Datei: Die zuletzt passende Regel gewinnt

Innerhalb einer .gitignore liest git von oben nach unten, und das zuletzt passende Muster bestimmt das Ergebnis. Beginnt dieses Muster mit !, wird nicht ignoriert, andernfalls schon.

*.log
!important.log

important.log passt auf beide Zeilen, doch die letzte ist eine Negation, daher wird die Datei verfolgt. Kehren Sie die Reihenfolge um, kehrt sich auch das Ergebnis um.

!important.log
*.log

Jetzt ist der letzte Treffer für important.log die Regel *.log, daher wird sie ignoriert. Deshalb gilt beim Zusammenführen mehrerer Vorlagen im Generator: Die Auswahlreihenfolge ist die Regelreihenfolge. Wer etwa *.jar aus der Java-Vorlage zusammen mit !gradle-wrapper.jar aus der Gradle-Vorlage nutzt, muss Gradle nach Java einordnen.

Vorrang zwischen mehreren Dateien

git liest Regeln aus den folgenden vier Quellen. Je weiter oben, desto höher der Vorrang.

  1. Auf der Befehlszeile übergebene Muster (bei einigen Befehlen wie git ls-files --exclude)
  2. .gitignore im selben oder einem übergeordneten Verzeichnis des Pfads – eine tiefer liegende Datei überschreibt die übergeordnete
  3. $GIT_DIR/info/exclude (meist .git/info/exclude)
  4. Die Datei, auf die die Einstellung core.excludesFile verweist (globale gitignore)

Steht zum Beispiel *.tmp in der Wurzel-.gitignore und !*.tmp in sub/.gitignore, wird sub/y.tmp gemäß der Negationsregel der tieferen Datei verfolgt. Umgekehrt gilt: Selbst wenn Sie !x in .git/info/exclude schreiben, hat x aus der .gitignore Vorrang, und x bleibt ignoriert.

$ git check-ignore -v x y.tmp sub/y.tmp
.gitignore:1:x	x
.gitignore:2:*.tmp	y.tmp
sub/.gitignore:1:!*.tmp	sub/y.tmp

Die wichtigste Ausnahme: Ein ausgeschlossenes Elternverzeichnis lässt sich nicht zurückholen

Die git-Dokumentation sagt es so: Ist das übergeordnete Verzeichnis einer Datei ausgeschlossen, kann die Datei nicht wieder aufgenommen werden. Aus Leistungsgründen schaut git gar nicht erst in ausgeschlossene Verzeichnisse hinein, sodass !-Regeln für Dateien darin nicht einmal gelesen werden.

logs/
!logs/keep.txt

Mit diesen Regeln bleibt logs/keep.txt ignoriert. git check-ignore -v zeigt logs/ als entscheidende Regel an.

Die Lösung: Schließen Sie nicht das Verzeichnis aus, sondern den Inhalt des Verzeichnisses.

logs/*
!logs/keep.txt

logs/* passt nicht auf das Verzeichnis logs selbst, also steigt git hinein und wendet die Negationsregel auf keep.txt an. /log/* + !/log/.keep in der Rails-Vorlage und .vscode/* + !.vscode/settings.json in der VS-Code-Vorlage beruhen beide auf diesem Prinzip.

Um einen tiefer liegenden Pfad zurückzuholen, müssen Sie auch jedes Zwischenverzeichnis einzeln zurückholen.

config/*
!config/app/
config/app/*
!config/app/defaults.yml

Das Allowlist-Muster

Dasselbe Prinzip gilt, wenn Sie statt der zu ignorierenden Dinge nur das aufzählen wollen, was verfolgt werden soll.

# Alles im Wurzelverzeichnis ignorieren
/*
# Nur das Nötige wieder aufnehmen
!/.gitignore
!/src/
!/README.md

/* passt nur auf die einzelnen Einträge im Wurzelverzeichnis und schließt die Wurzel selbst nicht aus, daher funktioniert !/src/. Bei diesem Ansatz wird auch die .gitignore selbst von /* erfasst – vergessen Sie !/.gitignore nicht. Tatsächlich erscheint .gitignore ohne diese Zeile bei git status --ignored in der Liste der ignorierten Dateien.

Doppelte Regeln und Reihenfolge

Kommt dieselbe Regel mehrmals vor, beeinflusst die frühere Kopie das Ergebnis nicht, denn die spätere passt immer später. Das Löschen einer späteren Kopie kann das Ergebnis dagegen ändern, wenn dazwischen eine Negationsregel steht.

*.log
!debug.log
*.log

Löschen Sie hier das letzte *.log, wird debug.log verfolgt. Der Generator dieser Website erkennt solche Fälle und entfernt spätere Duplikate nur dann, wenn keine Negationsregel dazwischen steht; die übrigen bleiben erhalten.

Quellen

← Zurück.gitignore-Mustersyntax im Überblick