.gitignore Sintassi: bin vs bin / vs. bin / * vs. bin / **


89

Qual è la differenza tra l'aggiunta bin, bin/, bin/*e bin/**nel mio file .gitignore? Ho usato bin/, ma guardando altri file .gitignore (nel file eclipse la doppia e la singola stella sono anche usate insieme in questo modo: tmp/**/*che succede?) Vedo che anche i primi due pattern sono ampiamente usati. Qualcuno può spiegare le differenze tra i tre?


4
@unutbu: la risposta accettata per questa domanda è apparentemente controversa. Uno dei commenti principali afferma che la risposta è in realtà un mito completo.
chandsie

Il comportamento è completamente specificato nella manpage, e sono sicuro che ci sia una domanda / risposta qui (o dieci) che include tutte queste informazioni.
Cascabel

Risposte:


84

bincorrisponde a qualsiasi file o directory denominato "bin".

bin/corrisponde a qualsiasi directory denominata "bin", che in effetti significa tutto il suo contenuto poiché Git non tiene traccia delle sole directory.

bin/*corrisponde a tutti i file e le directory direttamente in qualsiasi file bin/. Ciò impedisce a Git di trovare automaticamente i file nelle sue sottodirectory, ma se, diciamo che bin/fooviene creata una sottodirectory, questa regola non corrisponderà fooai contenuti di.

bin/**corrisponde a tutti i file e le directory in qualsiasi bin/directory e tutte le sue sottodirectory.

La parola "any" è critica qui poiché le regole non sono relative alla radice del repository e si applicano ovunque nell'albero del filesystem. È necessario iniziare le regole con /(o !/per non ignorare) che significa la radice del repository, non la radice del sistema, per far corrispondere solo ciò che era inteso.

ATTENZIONE: Si dovrebbe mai utilizzare le regole come dir/*, /dir/**e così via da solo a meno che anche non-ignorare qualcosa che esiste all'interno di tale directory . Omettere l'asterisco o si potrebbe perdere in modo permanente un sacco di dati da alcune invocazioni di git gc, git stashe altro ancora.

Non so davvero cosa tmp/**/*si intende fare. Inizialmente ho pensato che potesse essere utilizzato per abbinare i file nelle sottodirectory di tmp/ma non i file direttamente presenti in tmp/sé. Ma un semplice test sembra suggerire che questo ignori tutti i file in tmp/.


10
solo per chiarire, qual è la differenza tra bin/e bin/**?
chandsie

1
Sospetto bin/che ignorerà la directory bin, mentre bin/**includerà la directory bin ma non il suo contenuto
Robin Winslow

1
Ciò sembra incoerente con la risposta di Siddhartha. Attingendo alla risposta, bin/ignorerà la directory stessa (comprese tutte le sottodirectory e i file), mentre bin/**ignorerà tutti i file nella directory bin e nelle sue sottodirectory, ma non la directory bin stessa. Che sia accurato o meno, non sono sicuro.
Christopher Berman

3
nota che, se vuoi tenere traccia di tutti i file nella directory bin / ma ignori tutti i file nelle sue sottodirectory, puoi farlo (sulle righe successive) bin/** \n !bin/*(poiché non riesco a vedere come forzare un'interruzione di riga in mini-Markdown)
TomRoche

9
Questa risposta è sbagliata in tanti modi. Primo, git non tiene traccia delle directory, quindi una voce .gitignore può sempre e solo abbinare il contenuto della directory, mai una directory in quanto tale. In secondo luogo, bincorrisponde sia a un file denominato bin sia al contenuto della bincartella. Terzo, bin/* corrisponde a tutti i file nelle sue sottodirectory. Ragazzi, avete provato anche questo?
ThomasR

46

bine bin/differiscono solo per il fatto che quest'ultimo corrisponderà solo a una directory.

bin/**/*è lo stesso di bin/**(apparentemente dalla 1.8.2, secondo la risposta di @ VonC).

Quello complicato, su cui ho passato circa un'ora a strapparmi i capelli, è questo bin/e bin/**non sono proprio la stessa cosa! Poiché il primo ignora la directory nel suo insieme e il secondo ignora ciascuno dei file al suo interno, e git in quasi tutti i casi non si preoccupa delle directory, normalmente non c'è differenza. Tuttavia, se provi a utilizzare !per non ignorare un sottopercorso, scoprirai che git (ahem) lo ignora se hai ignorato la directory principale! (di nuovo, piuttosto che il contenuto della directory)

Questo è più chiaro dall'esempio, quindi per un repository appena avviato impostato in modo:

$ 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

Esistono i seguenti file non tracciati:

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

Ma puoi vedere che i seguenti file non vengono ignorati:

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

E se provi ad aggiungere, ottieni:

$ 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

Considero questo comportamento un bug. (Questo è tutto su git version 1.8.4.msysgit.0)


1
+1, davvero. Dovresti considerare di presentare una segnalazione di bug effettiva su questo perché il comportamento sembra inaspettato.
chandsie

1
Esattamente il mio caso d'uso. Grazie!
Sebastian Graf

3
Il diverso comportamento di dir/e dir/**re. l'annullamento con !accade perché "Non è possibile includere nuovamente un file se una directory padre di quel file è esclusa" [sorgente ]. Confuso, ma fatto per motivi di prestazioni. Vedi una domanda SO correlata .
tanius

23

Nota che, in senso stretto, git non tiene traccia delle directory, ma solo dei file. Non è quindi possibile aggiungere una directory, ma solo il suo contenuto .

Nel contesto di .gitignoretuttavia, git finge di comprendere le directory per l'unico motivo che

Non è possibile includere nuovamente un file se una directory padre di quel file è esclusa.
https://git-scm.com/docs/gitignore#_pattern_format

Cosa significa questo per i modelli di esclusione? Vediamoli in dettaglio:

bin

Questo ignora

  • file denominati bin.
  • il contenuto delle cartelle denominate bin

È possibile inserire nella whitelist binfile e cartelle ignorati aggiungendo !voci successive , ma non è possibile inserire nella whitelist i contenuti delle cartelle denominatebin

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/

Come sopra, tranne per il fatto che non corrisponde ai file denominati bin. L'aggiunta di un finale /dice a git di abbinare solo le directory.

bin/*

Questo ignora

  • file contenuti in una cartella denominatabin
  • contenuto delle sottocartelle dirette delle cartelle denominate 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/**

Questo ignora

  • contenuti di bin
  • contenuto delle sottocartelle (qualsiasi livello di nidificazione) all'interno di 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)

9

Ho appena creato un nuovo repository e ho provato alcune cose. Ecco i miei risultati:

NUOVI RISULTATI

git versione 2.10.1.windows.1

  1. Inizializza il repository quasi vuoto. Solo il file README
  2. Popolare la bindirectory in profondità di diversi livelli
    • 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. Aggiungi bina gitignore: Risultati
    • Tutto ciò che si trova sotto la bindirectory (e più in profondità) viene ora ignorato
    • Il livello di root non viene ignorato (/bin.txt e /Test.txt vengono ancora visualizzati)
  4. Modifica binin bin/in gitignore: Risultati
    • Nessun cambiamento
  5. Modifica bin/inbin/*
    • Nessun cambiamento
  6. Modifica bin/*inbin/**
    • Nessun cambiamento
  7. Modifica bin/**inbin/**/
    • bin/bin.txte bin/Test.txtnon vengono più ignorati
  8. Modifica bin/**/inbin/**/*
    • bin/bin.txte bin/Test.txttornano ad essere ignorati

VECCHI RISULTATI

versione git: 2.7.0.windows.1

  1. Inizializza il repository quasi vuoto. Solo il file README
  2. Popolare la bindirectory in profondità di diversi livelli
    • bin/a/b/Test.txt
    • bin/a/bin/Test.txt
    • bin/a/Test.txt
    • bin/Test.txt
  3. Aggiungi bina gitignore: Risultati
    • Tutto ciò che si trova sotto la bindirectory (e più in profondità) viene ora ignorato
  4. Modifica binin bin/in gitignore: Risultati
    • Tutto ciò che si trova sotto la bindirectory (e più in profondità) viene ancora ignorato (nessuna modifica)
  5. Modifica bin/inbin/*
    • Tutto ciò che si trova sotto la bindirectory (e più in profondità) viene ancora ignorato (nessuna modifica)
  6. Modifica bin/*inbin/**
    • Tutto ciò che si trova sotto la bindirectory (e più in profondità) viene ancora ignorato (nessuna modifica)
  7. Modifica bin/**inbin/**/
    • bin/Test.txt non viene più ignorato
  8. Modifica bin/**/inbin/**/*
    • Tutto ciò che si trova sotto la bindirectory (e più in profondità) viene nuovamente ignorato

1
Questo è un buon test, penso che rinominare text.txt in bin.txt mostrerebbe meglio alcune differenze chiave. Se lo facessi, voterei a favore di questa risposta.
Matt Johnson,

@ MattJohnson True ... purtroppo al momento sono su una versione più recente di git
Joe Phillips,

@ MattJohnson Finalmente sono arrivato a questo
Joe Phillips il

8

Nota che " **", se combinato con una sottodirectory ( **/bar), deve essere cambiato rispetto al suo comportamento predefinito, poiché la nota di rilascio per git1.8.2 ora menziona:

I modelli in .gitignoree .gitattributespossono avere **/un modello che corrisponde a 0 o più livelli di sottodirectory.

Ad esempio " foo/**/bar" corrisponde a " bar" in " foo" se stesso o in una sottodirectory di " foo".


La regola da ricordare (e che aiuta a capire la differenza di intenti dietro a quelle sintassi) è:

Non è possibile includere nuovamente un file se una directory principale di quel file è esclusa.


In genere, se vuoi escludere file da una sottocartella di una cartella ignora f, dovresti fare:

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

Questo è:

  • Se la prima regola fosse f/, la cartella f/verrebbe ignorata e le regole seguenti fnon avrebbero importanza.
  • f/**ottenere lo stesso di f/, ma ignorare tutti i sottoelementi (file e sottocartelle).
    Che ti dà l'opportunità di whitelist (escludi dalle gitignore) le sottocartelle: !f/**/.
  • Poiché tutte le fsottocartelle non vengono ignorate, puoi aggiungere una regola per escludere un file ( !f/a/sub/folder/someFile.txt)

Come risponde questo alla domanda?
ThomasR

0

C'è un'altra differenza tra bin/*e bin/.

bin/corrisponde foo/bin/test.txt(come previsto), ma bin/*non lo fa, il che sembra strano, ma è documentato: https://git-scm.com/docs/gitignore

"Documentation / *. Html" corrisponde a "Documentation / git.html" ma non a "Documentation / ppc / ppc.html" o "tools / perf / Documentation / perf.html".

La ragione di ciò sembra essere queste regole:

  • Se il motivo termina con una barra, viene rimosso ai fini della seguente descrizione ...

  • Se il pattern non contiene una barra /, Git lo tratta come un pattern glob della shell e verifica una corrispondenza con il percorso relativo alla posizione del file .gitignore ...

  • Altrimenti, Git tratta il pattern come un glob di shell adatto al consumo da fnmatch (3) con il flag FNM_PATHNAME ...

Quindi, se il pattern termina con una barra, la barra viene rimossa e viene trattata come un pattern glob di shell, nel qual caso bincorrisponde foo/bin/test.txt. Se termina con /*, la barra non viene rimossa e viene passata a fnmatch, che non corrisponde nelle sottodirectory.

Tuttavia, lo stesso non è vero per foo/bin/e foo/bin/*, poiché anche dopo aver rimosso la barra finale da foo/bin/, contiene ancora una barra, quindi viene trattata come un modello fnmatch, non come un glob. Cioè non corrisponderàbar/foo/bin/test.txt

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.