.gitignore Sintaxis: bin vs bin / vs bin / * vs bin / **

89

¿Cuál es la diferencia entre añadir bin, bin/, bin/*y bin/**en mi archivo .gitignore? He estado usando bin/, pero mirando otros archivos .gitignore (en el archivo de eclipse, la estrella doble y la única se usan juntas de esta manera: tmp/**/*¿qué pasa con eso?) Veo que los dos primeros patrones también se usan ampliamente. ¿Alguien puede explicar las diferencias entre los tres?

Chandsie
fuente
4
@unutbu: La respuesta aceptada para esa pregunta aparentemente está en disputa. Uno de los comentarios principales afirma que la respuesta es de hecho un mito completo.
chandsie
El comportamiento está completamente especificado en la página de manual, y estoy seguro de que hay una pregunta / respuesta por aquí (o diez) que incluyen toda esa información.
Cascabel
3
Con respecto a **: stackoverflow.com/questions/1470572/…
Cascabel

Respuestas:

84

bincoincide con cualquier archivo o directorio llamado 'bin'.

bin/coincide con cualquier directorio llamado 'bin', lo que en efecto significa todo su contenido ya que Git no rastrea los directorios solo.

bin/*coincide con todos los archivos y directorios directamente en cualquier bin/. Esto evita que Git encuentre automáticamente archivos en sus subdirectorios, pero si, digamos, bin/foose crea un subdirectorio, esta regla no coincidirá con fooel contenido.

bin/**coincide con todos los archivos y directorios de cualquier bin/directorio y todos sus subdirectorios.

La palabra "cualquiera" es fundamental aquí, ya que las reglas no son relativas a la raíz del repositorio y se aplican en cualquier parte del árbol del sistema de archivos. Debe comenzar las reglas con a /(o !/dejar de ignorar) lo que significa la raíz del repositorio, no la raíz del sistema, para que coincida solo con lo que se pretendía.

ADVERTENCIA: Usted debe no utilizar reglas como dir/*, /dir/**, etc. solos a menos que también Dejar de ignorar algo que existe dentro de ese directorio . Omita el asterisco o podría perder permanentemente muchos datos de ciertas invocaciones de git gc, git stashy más.

Realmente no sé qué tmp/**/*se supone que debo hacer. Inicialmente pensé que podría usarse para hacer coincidir archivos en los subdirectorios, tmp/pero no archivos directamente presentes en tmp/sí mismo. Pero una prueba simple parece sugerir que esto ignora todos los archivos en formato tmp/.

Siddhartha Reddy
fuente
10
solo para aclarar, ¿cuál es la diferencia entre bin/y bin/**?
chandsie
1
Sospecho bin/que ignorará el directorio bin, mientras bin/**que incluirá el directorio bin pero no ninguno de sus contenidos
Robin Winslow
1
Eso parece inconsistente con la respuesta de Siddhartha. A partir de la respuesta, bin/ignorará el directorio en sí (incluidos todos los subdirectorios y archivos), mientras bin/**que ignorará todos los archivos en el directorio bin y sus subdirectorios, pero no el directorio bin en sí. Si eso es exacto o no, no estoy seguro.
Christopher Berman
3
tenga en cuenta que, si desea realizar un seguimiento de todos los archivos en el directorio bin / pero ignorar todos los archivos en sus subdirectorios, puede hacerlo (en las líneas siguientes) bin/** \n !bin/*(ya que no veo cómo forzar un salto de línea en mini-Markdown)
TomRoche
9
Esta respuesta es incorrecta en muchos sentidos. Primero, git no realiza un seguimiento de los directorios, por lo que una entrada .gitignore solo puede coincidir con el contenido del directorio, nunca con un directorio como tal. En segundo lugar, binlos partidos tanto un archivo con el nombre bin y el contenido de la bincarpeta. En tercer lugar, bin/* coincide con los archivos de sus subdirectorios. ¿Ustedes siquiera probaron esto?
ThomasR
46

biny bin/difieren solo en que este último solo coincidirá con un directorio.

bin/**/*es el mismo que bin/**(aparentemente desde 1.8.2, según la respuesta de @ VonC).

El asunto difícil, que me acaba de pasar una hora o así que rasga mi pelo otra vez, es que bin/y bin/**no son absolutamente lo mismo! Dado que el primero ignora el directorio como un todo, y el segundo ignora cada uno de los archivos dentro de él, y a git en casi todos los casos no le importan los directorios, normalmente no hay diferencia. Sin embargo, si intenta !anular el ignorar una subruta, encontrará que git (ejem) la ignora si ignora el directorio principal. (de nuevo, en lugar del contenido del directorio)

Esto es más claro con el ejemplo, por lo que para un repositorio recién iniciado configurado así:

$ 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

Existen los siguientes archivos sin seguimiento:

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

Pero puede ver que los siguientes archivos no se ignoran:

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

Y si intentas agregar, obtienes:

$ 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 este comportamiento un error. (Todo esto está encendido git version 1.8.4.msysgit.0)

Simon Buchan
fuente
1
+1, de hecho. Debería considerar presentar un informe de error real sobre esto porque el comportamiento parece inesperado.
chandsie
1
Exactamente mi caso de uso. ¡Gracias!
Sebastian Graf
3
El comportamiento diferente de dir/y dir/**re. no ignorar con !ocurre porque "No es posible volver a incluir un archivo si se excluye un directorio principal de ese archivo" [fuente ]. Confuso, pero hecho por motivos de rendimiento. Consulte una pregunta de SO relacionada .
tanius
23

Tenga en cuenta que, estrictamente hablando, git no rastrea directorios, solo archivos. Por lo tanto, no es posible agregar un directorio, solo su contenido .

En el contexto de .gitignoresin embargo, git pretende entender los directorios por la única razón de que

No es posible volver a incluir un archivo si se excluye un directorio principal de ese archivo.
https://git-scm.com/docs/gitignore#_pattern_format

¿Qué significa esto para los patrones de exclusión? Veámoslos en detalle:

bin

Esto ignora

  • archivos con nombre bin.
  • el contenido de las carpetas llamadas bin

Puede binincluir en la lista blanca archivos y carpetas ignorados agregando !entradas posteriores , pero no puede incluir en la lista blanca el contenido de las carpetas nombradasbin

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/

Igual que el anterior, excepto que no coincide con los archivos nombrados bin. Agregar un final /le dice a git que coincida solo con los directorios.

bin/*

Esto ignora

  • archivos contenidos en una carpeta llamadabin
  • contenido de subcarpetas directas de carpetas denominadas 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/**

Esto ignora

  • contenido de bin
  • contenido de subcarpetas (cualquier nivel de anidamiento) dentro 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
fuente
9

Acabo de hacer un nuevo repositorio y probé algunas cosas. Aquí están mis resultados:

NUEVOS RESULTADOS

git versión 2.10.1.windows.1

  1. Inicializar repositorio casi vacío. Solo el archivo README
  2. Llene el bindirectorio con varias capas de profundidad
    • 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. Agregar bina gitignore: Resultados
    • Todo lo que está debajo del bindirectorio (y más profundo) ahora se ignora
    • El nivel de raíz no se ignora (/bin.txt y /Test.txt todavía se muestran)
  4. Editar bina bin/la gitignore: Resultados
    • Ningún cambio
  5. Editar bin/parabin/*
    • Ningún cambio
  6. Editar bin/*parabin/**
    • Ningún cambio
  7. Editar bin/**parabin/**/
    • bin/bin.txty bin/Test.txtya no son ignorados
  8. Editar bin/**/parabin/**/*
    • bin/bin.txty bin/Test.txtvuelven a ser ignorados

ANTIGUOS RESULTADOS

versión de git: 2.7.0.windows.1

  1. Inicializar repositorio casi vacío. Solo el archivo README
  2. Llene el bindirectorio con varias capas de profundidad
    • bin/a/b/Test.txt
    • bin/a/bin/Test.txt
    • bin/a/Test.txt
    • bin/Test.txt
  3. Agregar bina gitignore: Resultados
    • Todo lo que está debajo del bindirectorio (y más profundo) ahora se ignora
  4. Editar bina bin/la gitignore: Resultados
    • Todo lo que está debajo del bindirectorio (y más profundo) aún se ignora (sin cambios)
  5. Editar bin/parabin/*
    • Todo lo que está debajo del bindirectorio (y más profundo) aún se ignora (sin cambios)
  6. Editar bin/*parabin/**
    • Todo lo que está debajo del bindirectorio (y más profundo) aún se ignora (sin cambios)
  7. Editar bin/**parabin/**/
    • bin/Test.txt ya no se ignora
  8. Editar bin/**/parabin/**/*
    • Todo lo que está debajo del bindirectorio (y más profundo) se ignora nuevamente
Joe Phillips
fuente
1
Esta es una buena prueba, creo que cambiar el nombre de text.txt a bin.txt mostraría mejor algunas diferencias clave. Si hicieras esto, votaría por esta respuesta.
Matt Johnson
@MattJohnson Es cierto ... desafortunadamente estoy en una versión de git más nueva en este momento
Joe Phillips
@MattJohnson Finalmente llegué a esto
Joe Phillips
8

Tenga en cuenta que ' **', cuando se combina con un subdirectorio ( **/bar), debe haber cambiado de su comportamiento predeterminado, ya que la nota de la versión de git1.8.2 ahora menciona:

Los patrones en los archivos .gitignorey .gitattributespueden tener **/, como un patrón que coincide con 0 o más niveles de subdirectorio.

Por ejemplo, " foo/**/bar" coincide con " bar" en " foo" sí mismo o en un subdirectorio de " foo".


La regla a recordar (y que ayuda a comprender la diferencia de intenciones detrás de esa sintaxis) es:

No es posible volver a incluir un archivo si se excluye un directorio principal de ese archivo.


Normalmente, si desea excluir archivos de una subcarpeta de una carpeta ignorada f, debería hacer:

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

Es decir:

  • Si la primera regla fuera f/, la carpeta f/se ignoraría y las reglas siguientes fno importarían.
  • f/**lograr lo mismo que f/, pero ignorar todos los subelementos (archivos y subcarpetas).
    Eso le da la oportunidad a la lista blanca (excluir de gitignore) las subcarpetas: !f/**/.
  • Dado que no se ignoran todas las fsubcarpetas , puede agregar una regla para excluir un archivo ( )!f/a/sub/folder/someFile.txt
VonC
fuente
¿Cómo responde esto a la pregunta?
ThomasR
0

Hay otra diferencia entre bin/*y bin/.

bin/coincide foo/bin/test.txt(como se esperaba), pero bin/*no, lo que parece extraño, pero está documentado: https://git-scm.com/docs/gitignore

"Documentation / *. Html" coincide con "Documentation / git.html" pero no con "Documentation / ppc / ppc.html" o "tools / perf / Documentation / perf.html".

La razón de esto parece ser estas reglas:

  • Si el patrón termina con una barra, se elimina con el propósito de la siguiente descripción ...

  • Si el patrón no contiene una barra inclinada /, Git lo trata como un patrón glob de shell y busca una coincidencia con el nombre de la ruta relativa a la ubicación del archivo .gitignore ...

  • De lo contrario, Git trata el patrón como un globo de shell adecuado para el consumo de fnmatch (3) con la bandera FNM_PATHNAME ...

Entonces, si el patrón termina con una barra, la barra se elimina y se trata como un patrón de shell glob, en cuyo caso bincoincide foo/bin/test.txt. Si termina con /*, la barra no se elimina y se pasa a fnmatch, que no coincide en los subdirectorios.

Sin embargo, no ocurre lo mismo con foo/bin/y foo/bin/*, porque incluso después de eliminar la barra inclinada foo/bin/, todavía contiene una barra, por lo que se trata como un patrón fnmatch, no como un glob. Es decir, no coincidirábar/foo/bin/test.txt

usuario1431317
fuente