¿Qué es un archivo .gitignore?
Un archivo .gitignore es un archivo de texto que le dice a Git qué archivos dejar fuera del control de versiones. Cada línea es un patrón — *.log, node_modules/, .env — y cualquier ruta sin seguimiento que coincida con un patrón desaparece de git status, es omitida por git add . y nunca se confirma.
La expresión sin seguimiento carga con casi todo el significado de esa frase, y por sí sola explica más casos de «gitignore no funciona» que todos los errores de patrón juntos. Git solo consulta .gitignore para archivos a los que aún no hace seguimiento. En cuanto un archivo se ha confirmado, Git sigue rastreándolo indefinidamente, por mucho que añadas después a .gitignore.
Qué hace el Gitignore Debugger
Pega una ruta de archivo y el contenido de tu .gitignore: este depurador de .gitignore emite el mismo veredicto que Git —ignorado o no ignorado— junto con la regla exacta y el número de línea responsables. Después enumera todas las reglas que coinciden con la ruta, en el orden en que Git las evalúa, y señala la que realmente decidió.
Esa última parte es la que un simple comprobador de gitignore deja fuera. Saber que un archivo está ignorado rara vez es lo difícil. Saber cuál de tus cuarenta reglas lo hizo —y por qué perdió la que esperabas— sí lo es. Funciona en ambos sentidos: por qué Git ignora tu archivo cuando quieres confirmarlo, y por qué no ignora el que quieres quitar de en medio.
El motor reimplementa las reglas de coincidencia de patrones de Git: patrones por nombre base frente a anclados, * que nunca cruza una /, las tres formas de **, los sufijos / solo para directorios, las clases de caracteres, los espacios finales escapados. Su comportamiento se verifica contra la salida real de git check-ignore, no se supone.
Cómo usarlo
- Introduce la ruta tal como aparece en tu repositorio, relativa a la raíz —
src/config/local.json, noC:\projects\app\src\config\local.json. Usa barras inclinadas; las barras invertidas de Windows se convierten por ti. - Pega todo tu
.gitignore. No lo reduzcas a la única regla que sospechas: la precedencia entre reglas suele ser el error real, y recortar el archivo tira esa información a la basura. - Lee el veredicto y luego la cadena de reglas que hay debajo. Ahí es donde una regla de gitignore que no funciona deja de ser un misterio.
Para probar reglas de gitignore sobre un directorio en lugar de un archivo, termina la ruta con una barra inclinada — build/. La distinción importa, porque una regla que termina en / solo coincide con directorios.
Por qué las reglas de gitignore a veces no funcionan
Los culpables, más o menos por orden de frecuencia:
- El archivo ya tiene seguimiento.
.gitignoreno es retroactivo. Si el archivo se confirmó antes de que existiera la regla, la regla se omite por completo. Ejecutagit rm --cached path/to/filey confirma: el archivo se queda en el disco y Git deja de rastrearlo. - Una regla posterior la anuló. Git no se detiene en el primer patrón que coincide.
- Un directorio padre está ignorado. La regla que escribiste para el archivo ni siquiera llega a alcanzarse.
- El patrón está anclado y creías que era libre. Un patrón con una barra inclinada en cualquier posición que no sea el final queda anclado al directorio que contiene el
.gitignore, así quesrc/*.logcoincide consrc/debug.logy con nada más profundo. Un patrón sin ninguna barra coincide por nombre base a cualquier profundidad:*.logatrapa tantodebug.logcomosrc/debug.logya/b/c/debug.log. - El patrón está mal formado. Una clase de caracteres sin cerrar como
[abcno se interpreta como un[literal. Git abandona la coincidencia y la regla no coincide en silencio con absolutamente nada: indistinguible de una regla que nunca se escribió. En el depurador, simplemente nunca aparece en la lista de reglas coincidentes.
Precedencia de reglas en gitignore: gana la última coincidencia
Esta es la que pilla a todo el mundo. Dentro de un mismo .gitignore, el último patrón que coincide con una ruta decide su destino, no el primero. Estos dos archivos contienen reglas idénticas y dan resultados opuestos para important.log:
*.log
!important.log → important.log se guarda en Git!important.log
*.log → important.log se ignoraAsí que pon primero las reglas amplias y después las excepciones. Entre archivos, un .gitignore situado en un subdirectorio gana a otro más arriba para las rutas que están por debajo. Git también lee .git/info/exclude, propio del repositorio y nunca confirmado, y tu core.excludesFile global. Una regla que no encuentras en ningún .gitignore a menudo se esconde en uno de esos dos sitios: git check-ignore -v muestra el archivo y la línea que decidieron.
Reglas de negación, y por qué ! falla tan a menudo
Poner ! delante de un patrón vuelve a incluir algo que una regla anterior había ignorado. Tiene dos límites, y el segundo explica casi todos los casos de «la negación de gitignore no funciona».
Primero, algo anterior tiene que haber ignorado la ruta. !important.log por sí solo no hace nada, porque nunca hubo nada que anular.
Segundo, y bastante más sorprendente: no puedes volver a incluir un archivo que está dentro de un directorio ignorado. Esto no funciona, y ningún reordenamiento lo arregla:
build/
!build/keep.txtCuando build/ está ignorado, Git nunca entra. No examina build/keep.txt, así que nunca llega a la línea 2. Ignora en su lugar el contenido del directorio, lo que deja el directorio en sí recorrible:
build/*
!build/keep.txtPara rescatar algo más profundo, cada directorio intermedio también tiene que seguir siendo accesible — build/*, luego !build/assets/, luego !build/assets/logo.svg.
Directorios padre ignorados
El recorrido de directorios de Git se detiene en el primer directorio ignorado que encuentra, y todo lo que hay debajo hereda el veredicto sin ser examinado. Por eso un padre ignorado gana a cualquier regla escrita para el archivo en sí, por específica que parezca.
En ese caso el depurador nombra al padre que bloqueó el recorrido en lugar de decir simplemente que el archivo está ignorado, y sigue mostrando las reglas de negación que escribiste para el archivo, marcadas como inalcanzables, porque Git nunca llegó lo bastante lejos para tenerlas en cuenta. Arregla primero el directorio y las reglas de debajo vuelven a funcionar por sí solas.
Errores comunes de .gitignore
- Añadir la regla después de confirmar el archivo. La regla es correcta y no hace nada. Mira
git rm --cachedmás arriba. - Comentarios al final de la línea.
#solo abre un comentario al principio de una línea, así que*.log # debug outputes un único patrón largo que no coincide con nada de lo que tienes. - Barras invertidas como separadores. En un
.gitignore,\es un carácter de escape, nunca un separador de rutas.src\configsignifica el nombre literalsrcconfig. Escribe siempresrc/config. - Esperar que
*cruce directorios.*se detiene en una barra inclinada, así quesrc/*/test.jssolo baja un nivel. Usasrc/**/test.jspara cualquier profundidad. - Olvidar la barra final.
logscoincide tanto con un archivo llamadologscomo con el directorio;logs/solo coincide con el directorio. - Creer que ignorar borra el historial. Una regla detiene los commits futuros. No elimina un secreto que ya se ha subido: rota la credencial.
Unos cuantos ejemplos reales
La ruta src/logs/debug.log frente a estas reglas no está ignorada. La negación es la última coincidencia y ningún directorio padre bloquea el recorrido, así que se alcanza y gana:
*.log
!src/logs/debug.logLa ruta build/keep.txt frente a estas reglas sí está ignorada, por build/ en la línea 1, que coincide con el directorio padre. La línea 2 nunca se activa:
build/
!build/keep.txtLa ruta docs/api/notes.md frente a esta regla no está ignorada, porque el patrón está anclado por su barra inclinada y * no cruzará la de api/notes.md. Lo que se quería era docs/**/*.md:
docs/*.mdPega cualquiera de estos ejemplos en el probador de gitignore de arriba para ver la cadena completa de reglas. De vuelta en la terminal, git check-ignore -v path/to/file es el equivalente integrado: nombra el archivo de origen, el número de línea y el patrón que decidieron, y merece la pena conocerlo.