Wie funktionieren die Ausschlussregeln für Gitignore tatsächlich?

146

Ich versuche, ein Gitignore-Problem in einer großen Verzeichnisstruktur zu lösen, aber um meine Frage zu vereinfachen, habe ich es auf Folgendes reduziert.

Ich habe die folgende Verzeichnisstruktur von zwei Dateien (foo, bar) in einem brandneuen Git-Repository (bisher keine Commits):

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

Offensichtlich zeigt ein 'Git-Status -u':

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

Was ich tun möchte, ist eine .gitignore-Datei zu erstellen, die alles in a / b / c ignoriert, aber die Datei 'foo' nicht ignoriert.

Wenn ich einen .gitignore so erstelle:

c/

Dann zeigt ein 'git status -u' sowohl foo als auch bar als ignoriert an:

# Untracked files:
...
#       .gitignore

Welches ist, wie ich erwarte.

Wenn ich nun eine Ausschlussregel für foo hinzufüge, also:

c/
!foo

Laut der gitignore-Manpage würde ich erwarten, dass dies funktioniert. Aber es tut es nicht - es ignoriert immer noch foo:

# Untracked files:
...
#       .gitignore

Das funktioniert auch nicht:

c/
!a/b/c/foo

Dies gilt auch nicht:

c/*
!foo

Gibt:

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

In diesem Fall wird der Balken, obwohl foo nicht mehr ignoriert wird, auch nicht ignoriert.

Die Reihenfolge der Regeln in .gitignore scheint ebenfalls keine Rolle zu spielen.

Dies macht auch nicht das, was ich erwarten würde:

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

Dieser ignoriert sowohl Foo als auch Bar.

Eine Situation, die funktioniert, ist, wenn ich die Datei a / b / c / .gitignore erstelle und dort eingebe:

*
!foo

Das Problem dabei ist jedoch, dass es irgendwann andere Unterverzeichnisse unter a / b / c geben wird und ich nicht in jeden einzelnen einen separaten .gitignore einfügen möchte - ich hatte gehofft, einen 'projektbasierten' .gitignore zu erstellen Dateien, die sich im obersten Verzeichnis jedes Projekts befinden können und die gesamte Standardverzeichnisstruktur abdecken.

Dies scheint auch gleichwertig zu sein:

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

Dies mag der "Arbeit" am nächsten kommen, die ich erreichen kann, aber es müssen die vollständigen relativen Pfade und expliziten Ausnahmen angegeben werden, was sehr schmerzhaft sein wird, wenn ich viele Dateien mit dem Namen "foo" auf verschiedenen Ebenen habe des Unterverzeichnisbaums.

Entweder verstehe ich nicht ganz, wie Ausschlussregeln funktionieren, oder sie funktionieren überhaupt nicht, wenn Verzeichnisse (anstelle von Platzhaltern) ignoriert werden - durch eine Regel, die mit einem / endet

Kann jemand bitte etwas Licht ins Dunkel bringen?

Gibt es eine Möglichkeit, Gitignore dazu zu bringen, etwas Sinnvolles wie reguläre Ausdrücke anstelle dieser ungeschickten Shell-basierten Syntax zu verwenden?

Ich benutze und beobachte dies mit git-1.6.6.1 unter Cygwin / bash3 und git-1.7.1 unter Ubuntu / bash3.

davidA
quelle
Verwandte Frage: stackoverflow.com/questions/2820255/…
Cascabel
4
Hervorragend geschriebene Frage! Die Anzahl der Fälle, die Sie ausprobiert haben, hat mir wirklich geholfen zu verstehen, was ich in meinem ähnlichen Fall falsch gemacht habe. Gott sei Dank, ich habe heute lange gesucht.
Alex G

Antworten:

151
/ABC/*
! foo

Scheint für mich zu funktionieren (Git 1.7.0.4 unter Linux). Das *ist wichtig , da sonst das Verzeichnis sind ignorieren selbst (so git nicht nach innen schauen) , anstatt die Dateien innerhalb des Verzeichnisses (die für den Ausschluss erlaubt).

Stellen Sie sich die Ausschlüsse so vor, dass sie sagen "aber nicht dieses" anstatt "aber schließen Sie dieses ein" - "dieses Verzeichnis ignorieren ( /a/b/c/), aber nicht dieses ( foo)" macht nicht viel Sinn; "Ignoriere alle Dateien in diesem Verzeichnis ( /a/b/c/*), aber nicht diese ( foo)". So zitieren Sie die Manpage:

Ein optionales Präfix! was das Muster negiert; Alle übereinstimmenden Dateien, die von einem vorherigen Muster ausgeschlossen wurden, werden wieder aufgenommen.

Das heißt, die Datei muss bereits ausgeschlossen worden sein, um wieder aufgenommen zu werden. Hoffe das bringt etwas Licht.

Chris
quelle
Ja, das Beispiel, das Sie geben, funktioniert auch für mich gut. Das Problem ist, dass / a / b / * nicht funktioniert, wenn foo in c ist, was bedeutet, wenn ich mehrere foo-Dateien in verschiedenen Unterverzeichnissen unter c habe (z. B. d /, e /, f / g / h /, i / j /, etc) dann fängt die Negationsregel '! foo' diese nicht ab.
DavidA
1
@meowsqueak In der Tat wird es nicht. Wenn Sie / a / b / * ignorieren, gibt es kein Verzeichnis für foo! Wenn Sie versuchen, den gesamten Verzeichnisbaum unter / a / b zu ignorieren, aber eine Datei mit dem Namen "foo" einfügen, die sich an einer beliebigen Stelle im Baum befinden kann, ist dies meiner Meinung nach mit .gitignore-Mustern nicht machbar: \
Chris
Eigentlich könnte dies erklären, warum dies bei mir nicht funktioniert - die folgenden Regeln funktionieren nicht: "/ a / b / *", "! C / foo". Wenn das funktioniert, gibt es möglicherweise kein Problem.
DavidA
5
"Ich denke nicht, dass es mit .gitignore-Mustern machbar ist" - ich denke, Sie haben leider Recht.
DavidA
@meowsqueak Sie könnten möglicherweise ignorieren /a/b/cund dann einen Hook zu git add --forcejeder passenden Datei Pre-Commit oder etwas schreiben
Chris
24

Ich habe eine ähnliche Situation, meine Lösung war zu verwenden:

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

Das sollte für eine beliebige Anzahl von Zwischenverzeichnissen funktionieren, wenn ich **richtig lese .

medge799
quelle
6

Dies ist definitiv nicht klar aus der .gitignore Manpage. Das funktioniert:

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

# don't forget this one
!.gitignore

Wie von Chris erwähnt, wird ein Verzeichnis nicht einmal geöffnet, wenn es ausgeschlossen wird. Wenn Sie also in der Lage sein möchten, * aber einige Dateien zu ignorieren, müssen Sie den Pfad zu diesen Dateien wie oben erstellen. Für mich ist dies praktisch, da ich eine Codeüberprüfung für eine Datei einer Bibliothek durchführen möchte und wenn ich später eine andere durchführen möchte, füge ich sie einfach hinzu und alles andere wird ignoriert.


quelle
6

Hier ist eine weitere Option:

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

Das würde jede Datei und jedes Verzeichnis ignorieren, mit Ausnahme von Dateien / Verzeichnissen, die drei Ebenen tief in a liegen.

Peter Petrik
quelle
5

Allgemeiner wird git1.8.2 den Patch (auch in Version 4 , der durch eine Frage zum Stapelüberlauf ausgelöst wurde ) von Adam Spiers enthalten, mit dem ermittelt wird, welche gitignoreRegel Ihre Datei tatsächlich ignoriert.

Siehe git1.8.2 Versionshinweise und die SO-Frage " Welche Gitignore-Regel ignoriert meine Datei ": Dies ist
der Befehl git check-ignore.

VonC
quelle
1
Vielen Dank für die Ausstrahlung dieser Informationen. check-ignoreAußerdem erfahren Sie, welche Regel verhindert, dass Ihre Datei ignoriert wird, wenn dies der Fall ist.
Adam Spires
@AdamSpiers klingt großartig. Ich werde sicher damit spielen müssen.
VonC