Comment les règles d'exclusion .gitignore fonctionnent-elles réellement?

146

J'essaie de résoudre un problème gitignore sur une grande structure de répertoires, mais pour simplifier ma question, je l'ai réduite à la suivante.

J'ai la structure de répertoires suivante de deux fichiers (foo, bar) dans un tout nouveau référentiel git (pas de validation jusqu'à présent):

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

De toute évidence, un 'git status -u' montre:

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

Ce que je veux faire, c'est créer un fichier .gitignore qui ignore tout ce qui se trouve dans a / b / c mais n'ignore pas le fichier «foo».

Si je crée un .gitignore ainsi:

c/

Ensuite, un 'git status -u' montre à la fois foo et bar comme ignorés:

# Untracked files:
...
#       .gitignore

C'est ce que j'attends.

Maintenant, si j'ajoute une règle d'exclusion pour foo, donc:

c/
!foo

D'après la page de manuel gitignore, je m'attendrais à ce que cela fonctionne. Mais ce n'est pas le cas - il ignore toujours foo:

# Untracked files:
...
#       .gitignore

Cela ne fonctionne pas non plus:

c/
!a/b/c/foo

Cela non plus:

c/*
!foo

Donne:

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

Dans ce cas, bien que foo ne soit plus ignoré, bar n'est pas non plus ignoré.

L'ordre des règles dans .gitignore ne semble pas non plus avoir d'importance.

Cela ne fait pas non plus ce à quoi je m'attendais:

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

Celui-ci ignore à la fois foo et bar.

Une situation qui fonctionne est si je crée le fichier a / b / c / .gitignore et y mets:

*
!foo

Mais le problème avec ceci est qu'il y aura finalement d'autres sous-répertoires sous a / b / c et je ne veux pas avoir à mettre un .gitignore séparé dans chacun d'eux - j'espérais créer .gitignore `` basé sur un projet '' fichiers qui peuvent se trouver dans le répertoire supérieur de chaque projet et couvrir toute la structure de sous-répertoire «standard».

Cela semble également être équivalent:

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

C'est peut-être la chose la plus proche du "travail" que je puisse réaliser, mais les chemins relatifs complets et les exceptions explicites doivent être indiqués, ce qui va être pénible si j'ai beaucoup de fichiers de nom "foo" à différents niveaux de l'arborescence des sous-répertoires.

Quoi qu'il en soit, soit je ne comprends pas très bien le fonctionnement des règles d'exclusion, soit elles ne fonctionnent pas du tout lorsque les répertoires (plutôt que les caractères génériques) sont ignorés - par une règle se terminant par /

Quelqu'un peut-il s'il vous plaît éclairer ce sujet?

Existe-t-il un moyen de faire en sorte que gitignore utilise quelque chose de sensé comme des expressions régulières au lieu de cette syntaxe maladroite basée sur un shell?

J'utilise et observe ceci avec git-1.6.6.1 sur Cygwin / bash3 et git-1.7.1 sur Ubuntu / bash3.

davidA
la source
question connexe: stackoverflow.com/questions/2820255/…
Cascabel
4
Excellente question écrite! Le nombre de cas que vous avez essayés m'a vraiment aidé à comprendre ce que je faisais de mal dans mon cas similaire. Dieu merci, je cherche depuis longtemps aujourd'hui.
Alex G

Réponses:

153
/abc/*
! toto

Semble fonctionner pour moi (git 1.7.0.4 sous Linux). Le *est important car sinon vous ignorez le répertoire lui-même (donc git ne regardera pas à l'intérieur) au lieu des fichiers dans le répertoire (ce qui permet l'exclusion).

Pensez aux exclusions comme disant "mais pas celle-ci" plutôt que "mais incluez ceci" - "ignorer ce répertoire ( /a/b/c/) mais pas celui-ci ( foo)" n'a pas beaucoup de sens; "ignore tous les fichiers de ce répertoire ( /a/b/c/*) mais pas celui-ci ( foo)" le fait. Pour citer la page de manuel:

Un préfixe facultatif! ce qui annule le modèle; tout fichier correspondant exclu par un modèle précédent sera de nouveau inclus.

c'est-à-dire que le fichier doit déjà avoir été exclu pour être à nouveau inclus. J'espère que cela jette un peu de lumière.

Chris
la source
Oui, l'exemple que vous donnez fonctionne bien pour moi aussi. Le problème est que / a / b / * ne fonctionne pas si foo est dans c, ce qui signifie que si j'ai plusieurs fichiers foo dans divers sous-répertoires sous c (par exemple d /, e /, f / g / h /, i / j /, etc) alors la règle de négation '! foo' ne les attrapera pas.
davidA
1
@meowsqueak En effet, ce ne sera pas le cas. Si vous ignorez / a / b / *, alors il n'y a pas de répertoire pour foo! Si vous essayez d'ignorer toute l'arborescence de répertoires sous / a / b mais d'inclure n'importe quel fichier nommé "foo" qui peut être n'importe où dans l'arborescence, je ne pense pas que ce soit faisable avec les modèles .gitignore: \
Chris
En fait, cela pourrait expliquer pourquoi cela ne fonctionne pas pour moi - les règles suivantes ne fonctionnent pas: "/ a / b / *", "! C / foo". Si cela fonctionnait, il se peut qu'il n'y ait pas de problème.
davidA
5
"Je ne pense pas que ce soit faisable avec les modèles .gitignore" - Je pense que vous avez raison, malheureusement.
davidA
@meowsqueak Vous pouvez peut-être ignorer /a/b/cpuis écrire un hook à git add --forcen'importe quel fichier correspondant avant la validation ou quelque chose
Chris
24

J'ai une situation similaire, ma solution était d'utiliser:

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

Cela devrait fonctionner pour un nombre arbitraire de répertoires intermédiaires si je lis **correctement.

medge799
la source
6

cela n'est certainement pas clair dans la page de manuel .gitignore. Cela marche:

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

# don't forget this one
!.gitignore

Comme mentionné par Chris, un répertoire n'est même pas ouvert s'il est exclu. Donc, si vous voulez pouvoir ignorer * mais certains fichiers, vous devez créer le chemin vers ces fichiers comme ci-dessus. Pour moi, c'est pratique, car je veux faire une révision de code sur 1 fichier d'une bibliothèque et si je veux en faire un autre plus tard, je l'ajoute simplement, et tout le reste est ignoré.


la source
6

Voici une autre option:

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

Cela ignorerait tous les fichiers et répertoires, sauf les fichiers / répertoires à trois niveaux profonds dans un fichier.

Peter Petrik
la source
5

Sur une note plus générale, git1.8.2 inclura le correctif (également dans sa v4 , invité par une question de Stack Overflow ) d' Adam Spiers pour déterminer quelle gitignorerègle ignore réellement votre fichier.

Voir les notes de version de git1.8.2 et la question SO " quelle règle gitignore ignore mon fichier ":
ce sera la commande git check-ignore.

VonC
la source
1
Merci d'avoir diffusé ces informations. check-ignorevous indiquera également quelle règle empêche votre fichier d'être ignoré, si tel est le cas.
Adam Spiers
@AdamSpiers sonne bien. Je devrai certainement jouer avec.
VonC