Syntaxe .gitignore: bin vs bin / vs bin / * vs bin / **

89

Quelle est la différence entre l' ajout bin, bin/, bin/*et bin/**dans mon dossier .gitignore? J'ai utilisé bin/, mais en regardant d' autres fichiers .gitignore (dans le fichier eclipse, les étoiles double et simple sont même utilisées ensemble comme ceci: tmp/**/*qu'est-ce qui se passe avec ça?) Je vois que les deux premiers modèles sont également largement utilisés. Quelqu'un peut-il expliquer les différences entre les trois?

chandsie
la source
4
@unutbu: La réponse acceptée à cette question est apparemment contestée. L'un des principaux commentaires affirme que la réponse est en fait un mythe complet.
chandsie
Le comportement est complètement spécifié dans la page de manuel, et je suis sûr qu'il y a une question / réponse ici (ou dix) qui incluent toutes ces informations.
Cascabel
3
En ce qui concerne **: stackoverflow.com/questions/1470572/…
Cascabel

Réponses:

84

bincorrespond à tous les fichiers ou répertoires nommés «bin».

bin/correspond à tous les répertoires nommés 'bin', ce qui signifie en fait tout son contenu puisque Git ne suit pas seul les répertoires.

bin/*correspond directement à tous les fichiers et répertoires dans any bin/. Cela empêche Git de trouver automatiquement des fichiers dans ses sous-répertoires, mais si, par exemple, un bin/foosous-répertoire est créé, cette règle ne correspondra pas aufoo contenu de.

bin/**correspond à tous les fichiers et répertoires dans n'importe quel bin/répertoire et tous ses sous-répertoires.

Le mot «any» est critique ici car les règles ne sont pas relatives à la racine du référentiel et s'appliquent n'importe où dans l'arborescence du système de fichiers. Vous devez commencer les règles par un /(ou !/pour un-ignorer) qui signifie la racine du référentiel, pas la racine du système, afin de ne correspondre qu'à ce qui était prévu.

AVERTISSEMENT: Vous devriez ne jamais utiliser des règles comme dir/*, /dir/**, etc. seul à moins que vous aussi non ignorer quelque chose qui existe à l' intérieur de ce répertoire . Omettez l'astérisque ou vous pourriez perdre définitivement beaucoup de données à cause de certaines invocations de git gc, git stashet plus encore.

Je ne sais pas vraiment ce que l' tmp/**/*on veut faire. J'ai d'abord pensé qu'il pouvait être utilisé pour faire correspondre des fichiers dans les sous-répertoires de tmp/mais pas des fichiers directement présents en tmp/lui-même. Mais un simple test semble suggérer que cela ignore tous les fichiers dans tmp/.

Siddhartha Reddy
la source
10
juste pour clarifier, quelle est la différence entre bin/et bin/**?
chandsie
1
Je soupçonne bin/que j'ignorerai le répertoire bin, alors bin/**qu'il inclura le répertoire bin mais aucun de son contenu
Robin Winslow
1
Cela semble incompatible avec la réponse de Siddhartha. À partir de la réponse, bin/ignorera le répertoire lui-même (y compris tous les sous-répertoires et fichiers), alors que bin/**tous les fichiers du répertoire bin et de ses sous-répertoires seront ignorés, mais pas le répertoire bin lui-même. Que ce soit exact ou non, je ne suis pas sûr.
Christopher Berman
3
notez que, si vous souhaitez suivre tous les fichiers du répertoire bin / mais ignorer tous les fichiers de ses sous-répertoires, vous pouvez le faire (sur les lignes suivantes) bin/** \n !bin/*(car je ne vois pas comment forcer un saut de ligne dans mini-Markdown)
TomRoche
9
Cette réponse est fausse à bien des égards. Premièrement, git ne garde pas trace des répertoires, donc une entrée .gitignore ne peut correspondre qu'au contenu du répertoire, jamais à un répertoire en tant que tel. Deuxièmement, bincorrespond à la fois à un fichier nommé bin et au contenu du bindossier. Troisièmement, bin/* correspond à tous les fichiers de ses sous-répertoires. Avez-vous même testé ça?
ThomasR
46

binet bin/ne diffèrent que par le fait que ce dernier ne correspondra qu'à un répertoire.

bin/**/*est le même que bin/**(apparemment depuis le 1.8.2, selon la réponse de @ VonC).

La délicate, que je viens de passer une heure ou déchirant mes cheveux plus, est que bin/et bin/**ne sont pas tout à fait la même chose! Puisque le premier ignore le répertoire dans son ensemble et que le dernier ignore chacun des fichiers qu'il contient, et que git dans presque tous les cas ne se soucie pas des répertoires, il n'y a normalement aucune différence. Cependant, si vous essayez d'utiliser !pour désignorer un sous-chemin, vous constaterez que git (ahem) l' ignore si vous avez ignoré le répertoire parent! (encore une fois, plutôt que le contenu du répertoire)

Ceci est plus clair par exemple, donc pour un référentiel nouvellement lancé configuré ainsi:

$ cat .gitignore
ignored-file
or-dir
dir-only/
!dir-only/cant-reinclude
dir-contents/**
!dir-contents/can-reinclude

$ mkdir or-dir dir-only dir-contents

$ touch file ignored-file or-dir/ignored-file dir-only/cant-reinclude dir-contents/can-reinclude

Les fichiers non suivis suivants existent:

$ git ls-files --other
.gitignore
dir-contents/can-reinclude
dir-only/cant-reinclude
file
ignored-file
or-dir/ignored-file

Mais vous pouvez voir que les fichiers suivants ne sont pas ignorés:

$ git ls-files --other --exclude-standard
.gitignore
dir-contents/can-reinclude
file

Et si vous essayez d'ajouter, vous obtenez:

$ git add dir-only/cant-reinclude
The following paths are ignored by one of your .gitignore files:
dir-only/cant-reinclude
Use -f if you really want to add them.
fatal: no files added

Je considère ce comportement comme un bug. (Tout est allumé git version 1.8.4.msysgit.0)

Simon Buchan
la source
1
+1, en effet. Vous devriez envisager de déposer un rapport de bogue à ce sujet car le comportement semble inattendu.
chandsie
1
Exactement mon cas d'utilisation. Merci!
Sebastian Graf
3
Le comportement différent de dir/et dir/**re. la annulation de l'ignorance avec !se produit car "Il n'est pas possible de ré-inclure un fichier si un répertoire parent de ce fichier est exclu" [source ]. Confus, mais fait pour des raisons de performances. Voir une question SO connexe .
tanius
23

Notez qu'à proprement parler, git ne suit pas les répertoires, seulement les fichiers. Il n'est donc pas possible d'ajouter un répertoire, seulement son contenu .

Dans le contexte de .gitignore cependant, git prétend comprendre les répertoires pour la seule raison que

Il n'est pas possible de ré-inclure un fichier si un répertoire parent de ce fichier est exclu.
https://git-scm.com/docs/gitignore#_pattern_format

Qu'est-ce que cela signifie pour les modèles d'exclusion? Passons en revue en détail:

bin

Cela ignore

  • fichiers nommés bin .
  • le contenu des dossiers nommés bin

Vous pouvez binajouter des fichiers et des dossiers ignorés à la liste blanche en ajoutant des !entrées ultérieures , mais vous ne pouvez pas mettre en liste blanche le contenu des dossiers nommésbin

bin

!bin/file_in_bin # has no effect, since bin/ is blacklisted!
!bin/* # has no effect, since bin/ is blacklisted!
!file_in_bin # has no effect, since bin/ is blacklisted!

!bin # this works

bin/

Idem que ci-dessus, sauf qu'il ne correspond pas aux fichiers nommés bin. L'ajout d'une fin /indique à git de ne rechercher que les répertoires.

bin/*

Cela ignore

  • fichiers contenus dans un dossier nommébin
  • contenu des sous-dossiers directs des dossiers nommés bin
bin/*  # blacklists bin/file_in_bin and bin/subfolder/

!bin/subfolder/file_in_sub # has no effect, since bin/subfolder is blacklisted!
!bin # whitelists files named bin/bin, since bin/ itself is not blacklisted
!bin/ # has no effect, since bin/ itself is not blacklisted


!bin/file_in_bin # works since bin/ itself is not blacklisted
!file_in_bin # works too
!bin/subfolder # works (so implicitly whitelists bin/subfolder/file_in_sub)
!bin/subfolder/ # works just as well
!bin/* # works for file_in_bin and subfolder/

bin/**

Cela ignore

  • contenu de bin
  • contenu des sous-dossiers (tout niveau d'imbrication) dans bin
bin/**  # blacklists bin/file_in_bin and
        # bin/subfolder/ and bin/subfolder/file_in_sub and
        # bin/subfolder/2/ and bin/subfolder/2/file_in_sub_2

!bin/subfolder/file_in_sub # has no effect, since bin/subfolder is blacklisted
!bin/subfolder/2/ # has no effect, since bin/subfolder is blacklisted
!bin/subfolder/2/file_in_sub_2 # has no effect, since bin/subfolder is blacklisted

!bin/subfolder # works only in combinations with other whitelist entries,
               # since all contents of subfolder are blacklisted (1)

!bin/file_in_bin # works since bin itself is not blacklisted
!bin/* # works for file_in_bin and subfolder; see (1)
ThomasR
la source
9

Je viens de faire un nouveau repo et j'ai essayé certaines choses. Voici mes résultats:

NOUVEAUX RÉSULTATS

git version 2.10.1.windows.1

  1. Initialisez le dépôt presque vide. Seul le fichier README
  2. Remplissez le binrépertoire de plusieurs couches de profondeur
    • bin.txt
    • Test.txt
    • bin/a/b/bin.txt
    • bin/a/b/Test.txt
    • bin/a/bin/bin.txt
    • bin/a/bin/Test.txt
    • bin/a/bin.txt
    • bin/a/Test.txt
    • bin/bin.txt
    • bin/Test.txt
  3. Ajouter binà gitignore: Résultats
    • Tout ce qui se trouve sous le binrépertoire (et plus profond) est maintenant ignoré
    • Le niveau racine n'est pas ignoré (/bin.txt et /Test.txt s'affichent toujours)
  4. Modifier bindans bin/dans le gitignore: Résultats
    • Pas de changement
  5. Modifier bin/versbin/*
    • Pas de changement
  6. Modifier bin/*versbin/**
    • Pas de changement
  7. Modifier bin/**versbin/**/
    • bin/bin.txtet bin/Test.txtne sont plus ignorés
  8. Modifier bin/**/versbin/**/*
    • bin/bin.txtet bin/Test.txtsont de nouveau ignorés

ANCIENS RÉSULTATS

version git: 2.7.0.windows.1

  1. Initialisez le dépôt presque vide. Seul le fichier README
  2. Remplissez le binrépertoire de plusieurs couches de profondeur
    • bin/a/b/Test.txt
    • bin/a/bin/Test.txt
    • bin/a/Test.txt
    • bin/Test.txt
  3. Ajouter binà gitignore: Résultats
    • Tout ce qui se trouve sous le binrépertoire (et plus profond) est maintenant ignoré
  4. Modifier bindans bin/dans le gitignore: Résultats
    • Tout ce qui se trouve sous le binrépertoire (et plus profond) est toujours ignoré (pas de changement)
  5. Modifier bin/versbin/*
    • Tout ce qui se trouve sous le binrépertoire (et plus profond) est toujours ignoré (pas de changement)
  6. Modifier bin/*versbin/**
    • Tout ce qui se trouve sous le binrépertoire (et plus profond) est toujours ignoré (pas de changement)
  7. Modifier bin/**versbin/**/
    • bin/Test.txt n'est plus ignoré
  8. Modifier bin/**/versbin/**/*
    • Tout ce qui se trouve sous le binrépertoire (et plus profond) est à nouveau ignoré
Joe Phillips
la source
1
C'est un bon test, je pense que renommer text.txt en bin.txt montrerait mieux certaines différences clés. Si vous faisiez cela, je voterais pour cette réponse.
Matt Johnson
@MattJohnson True ... malheureusement, je suis sur une version plus récente de git pour le moment
Joe Phillips
@MattJohnson J'ai enfin compris
Joe Phillips
8

Notez que le ' **', lorsqu'il est combiné avec un sous-répertoire ( **/bar), doit avoir changé de son comportement par défaut, puisque la note de publication de git1.8.2 mentionne maintenant:

Les modèles dans .gitignoreet les .gitattributesfichiers peuvent avoir**/ , comme modèle qui correspond à 0 ou plusieurs niveaux de sous-répertoire.

Par exemple, " foo/**/bar" correspond à " bar" dans " foo" lui-même ou dans un sous-répertoire de " foo".


La règle à retenir (et qui aide à comprendre la différence d'intention derrière ces syntaxes) est:

Il n'est pas possible de ré-inclure un fichier si un répertoire parent de ce fichier est exclu.


En règle générale, si vous souhaitez exclure des fichiers d'un sous-dossier d'un dossier ignoré f, procédez comme suit:

f/**
!f/**/
!f/a/sub/folder/someFile.txt

C'est:

  • Si la première règle l'était f/, le dossier f/serait ignoré et les règles ci-dessous concernantf n'auraient pas d'importance.
  • f/**obtenir la même chose que f/, mais ignorer tous les sous-éléments (fichiers et sous-dossiers).
    Cela vous donne la possibilité de mettre sur liste blanche (exclure de gitignore) les sous-dossiers:!f/**/ .
  • Puisque tous les fsous-dossiers ne sont pas ignorés, vous pouvez ajouter une règle pour exclure un fichier ( !f/a/sub/folder/someFile.txt)
VonC
la source
Comment cela répond-il à la question?
ThomasR
0

Il y a une autre différence entre bin/*et bin/.

bin/correspond foo/bin/test.txt(comme prévu), mais bin/*pas, ce qui semble étrange, mais c'est documenté: https://git-scm.com/docs/gitignore

"Documentation / *. Html" correspond à "Documentation / git.html" mais pas à "Documentation / ppc / ppc.html" ou "tools / perf / Documentation / perf.html".

La raison à cela semble être ces règles:

  • Si le motif se termine par une barre oblique, il est supprimé aux fins de la description suivante…

  • Si le motif ne contient pas de barre oblique /, Git le traite comme un motif de shell glob et vérifie une correspondance avec le chemin d'accès relatif à l'emplacement du fichier .gitignore ...

  • Sinon, Git traite le pattern comme un shell glob adapté à la consommation par fnmatch (3) avec le flag FNM_PATHNAME…

Donc, si le modèle se termine par une barre oblique, la barre oblique est supprimée et elle est traitée comme un modèle de shell glob, auquel cas bincorrespond foo/bin/test.txt. Si elle se termine par /*, la barre oblique n'est pas supprimée et elle est passée à fnmatch, qui ne correspond pas dans les sous-répertoires.

Cependant, il n'en va pas de même pour foo/bin/et foo/bin/*, car même après avoir supprimé la barre oblique de fin foo/bin/, elle contient toujours une barre oblique, elle est donc traitée comme un modèle fnmatch, pas comme un glob. Ie ça ne correspondra pasbar/foo/bin/test.txt

utilisateur1431317
la source