¿Cómo funcionan realmente las reglas de exclusión .gitignore?

146

Estoy tratando de resolver un problema de gitignore en una estructura de directorio grande, pero para simplificar mi pregunta, la he reducido a lo siguiente.

Tengo la siguiente estructura de directorios de dos archivos (foo, bar) en un nuevo repositorio de git (hasta ahora no hay confirmaciones):

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

Obviamente, un 'git status -u' muestra:

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

Lo que quiero hacer es crear un archivo .gitignore que ignore todo dentro de a / b / c pero no ignore el archivo 'foo'.

Si creo un .gitignore así:

c/

Luego, un 'git status -u' muestra tanto foo como bar como ignorados:

# Untracked files:
...
#       .gitignore

Que es lo que espero.

Ahora si agrego una regla de exclusión para foo, entonces:

c/
!foo

Según la página de manual de gitignore, esperaría que esto funcione. Pero no lo hace, todavía ignora a foo:

# Untracked files:
...
#       .gitignore

Esto tampoco funciona:

c/
!a/b/c/foo

Tampoco esto:

c/*
!foo

Da:

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

En ese caso, aunque foo ya no se ignora, la barra tampoco se ignora.

El orden de las reglas en .gitignore tampoco parece importar.

Esto tampoco hace lo que esperaría:

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

Ese ignora tanto el foo como el bar.

Una situación que funciona es si creo el archivo a / b / c / .gitignore y lo pongo allí:

*
!foo

Pero el problema con esto es que eventualmente habrá otros subdirectorios bajo a / b / c y no quiero tener que poner un .gitignore separado en cada uno; esperaba crear .gitignore 'basado en proyectos' archivos que pueden ubicarse en el directorio superior de cada proyecto y cubrir toda la estructura del subdirectorio 'estándar'.

Esto también parece ser equivalente:

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

Esto podría ser lo más cercano al "trabajo" que puedo lograr, pero es necesario establecer las rutas relativas completas y las excepciones explícitas, lo que será difícil si tengo muchos archivos de nombre 'foo' en diferentes niveles del árbol subdirectorio.

De todos modos, o no entiendo cómo funcionan las reglas de exclusión, o no funcionan cuando los directorios (en lugar de los comodines) son ignorados, por una regla que termina en un /

¿Alguien puede arrojar algo de luz sobre esto?

¿Hay alguna manera de hacer que gitignore use algo sensible como expresiones regulares en lugar de esta sintaxis torpe basada en shell?

Estoy usando y observo esto con git-1.6.6.1 en Cygwin / bash3 y git-1.7.1 en Ubuntu / bash3.

davidA
fuente
pregunta relacionada: stackoverflow.com/questions/2820255/…
Cascabel
44
¡Pregunta excelentemente escrita! La cantidad de casos que trató realmente me ayudó a comprender lo que estaba haciendo mal en mi caso similar. Gracias a Dios, he estado buscando por mucho tiempo hoy.
Alex G

Respuestas:

153
/a B C/*
! foo

Parece funcionar para mí (git 1.7.0.4 en Linux). El *es importante ya que de lo contrario estás ignorando el propio directorio (por lo git no se verá dentro) en lugar de los archivos dentro del directorio (que permite la exclusión).

Piense en las exclusiones como decir "pero no este" en lugar de "pero incluya esto" - "ignorar este directorio ( /a/b/c/) pero no este ( foo)" no tiene mucho sentido; "ignorar todos los archivos en este directorio ( /a/b/c/*) pero no este ( foo)" lo hace. Para citar la página del manual:

Un prefijo opcional! que niega el patrón; cualquier archivo coincidente excluido por un patrón anterior se volverá a incluir.

es decir, el archivo tiene que haber sido excluido para ser incluido nuevamente. Espero que arroje algo de luz.

Chris
fuente
Sí, el ejemplo que das también funciona bien para mí. El problema es que / a / b / * no funciona si foo está en c, lo que significa que si tengo múltiples archivos foo en varios subdirectorios bajo c (por ejemplo, d /, e /, f / g / h /, i / j /, etc.) entonces la regla negativa '! foo' no los atrapará.
davidA
1
@meowsqueak De hecho, no lo hará. Si ignora / a / b / *, ¡entonces no hay un directorio en el que pueda estar foo! Si está tratando de ignorar todo el árbol de directorios en / a / b pero incluye cualquier archivo llamado "foo" que puede estar en cualquier parte del árbol, no creo que sea factible con patrones .gitignore: \
Chris
En realidad, esto podría explicar por qué esto no funciona para mí: las siguientes reglas no funcionan: "/ a / b / *", "! C / foo". Si eso funcionó, puede que no haya un problema.
davidA
55
"No creo que sea factible con patrones .gitignore". Creo que tienes razón, desafortunadamente.
davidA
@meowsqueak Posiblemente podría ignorar /a/b/cy luego escribir un enlace a git add --forcecualquier archivo previo que se haya comprometido o algo así
Chris
24

Tengo una situación similar, mi solución fue usar:

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

Eso debería funcionar para un número arbitrario de directorios intermedios si leo **correctamente.

medge799
fuente
6

esto definitivamente no está claro en la página del manual .gitignore. Esto funciona:

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

# don't forget this one
!.gitignore

Como mencionó Chris, un directorio ni siquiera se abre si se excluye. Entonces, si desea poder ignorar * pero algunos archivos, debe construir la ruta a esos archivos como se indicó anteriormente. Para mí, esto es conveniente, porque quiero hacer una revisión de código en 1 archivo de una biblioteca y si quiero hacer otra más tarde simplemente la agrego, y todo lo demás se ignora.


fuente
6

Aquí hay otra opción:

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

Eso ignoraría todos los archivos y directorios, excepto los archivos / directorios de tres niveles dentro de a.

Peter Petrik
fuente
5

En una nota más general, git1.8.2 incluirá el parche (también en su v4 , provocado por alguna pregunta de Stack Overflow ) de Adam Spires sobre la determinación de qué gitignoreregla realmente ignora su archivo.

Consulte las notas de la versión de git1.8.2 y la pregunta SO " qué regla de gitignore ignora mi archivo ":
ese será el comando git check-ignore.

VonC
fuente
1
Gracias por transmitir esta información. check-ignoretambién le dirá qué regla impide que su archivo sea ignorado, si ese es el caso.
Adam Spires
@AdamSpiers suena genial. Seguro tendré que jugar con eso.
VonC