El archivo `.gitignore` decide qué archivos Git ignora al hacer `git add` o `git status`, y es la diferencia entre un repositorio limpio y uno contaminado con `node_modules`, binarios compilados o credenciales que nunca deberían haberse subido. Su sintaxis parece trivial pero tiene reglas de precedencia poco intuitivas: el orden de los patrones importa, un `!` puede reabrir un archivo previamente ignorado, y un directorio ignorado impide que Git examine su contenido aunque haya excepciones dentro. Este generador de .gitignore compone plantillas por lenguaje y herramienta (Node, Python, Java, macOS, IDEs) y explica cómo depurar un archivo que ya está siendo versioneado pese a estar en la lista.
Sintaxis de patrones: comodines, anclaje y directorios
Un patrón sin barra como *.log ignora cualquier archivo con esa extensión en cualquier subdirectorio. Un patrón que empieza por /, como /dist, se ancla a la raíz del repositorio y no afecta a un dist dentro de packages/app/dist. Un patrón que termina en /, como build/, solo coincide con directorios, nunca con archivos llamados build. El comodín ** permite cruzar niveles: **/logs ignora cualquier carpeta logs en cualquier profundidad, mientras que logs/** ignora todo el contenido dentro de logs pero conserva la carpeta vacía si se llegara a versionar explícitamente.
Negación con ! y por qué a veces no funciona
La línea !important.log reincluye un archivo que un patrón anterior había excluido, pero Git tiene una limitación conocida: si el directorio que contiene ese archivo ya fue ignorado por completo (por ejemplo con logs/), Git nunca desciende a examinarlo y la excepción !logs/important.log no tiene efecto. La solución es ignorar el contenido en lugar del directorio, con logs/* seguido de !logs/important.log, de forma que Git sí visite el directorio y aplique la excepción sobre los archivos individuales.
Plantillas por ecosistema: qué cambia entre Node, Python y Java
Cada ecosistema genera artefactos distintos que no deben versionarse. En Node, la plantilla cubre node_modules/, dist/, .env, npm-debug.log* y .turbo/. En Python, __pycache__/, *.pyc, .venv/, *.egg-info/ y .pytest_cache/. En Java/Maven, target/, *.class y .mvn/wrapper/maven-wrapper.jar si no se versiona el wrapper. Mezclar plantillas de varios ecosistemas en un monorepo es habitual y seguro: los patrones no colisionan porque cada uno apunta a artefactos con nombre y extensión distintos.
Archivos de sistema operativo y de IDE: por qué van aparte
Ficheros como .DS_Store en macOS, Thumbs.db en Windows o .idea/ de IntelliJ no pertenecen al proyecto sino al entorno de quien lo edita, y por eso conviene mantenerlos en el .gitignore global del usuario (git config --global core.excludesfile ~/.gitignore_global) en lugar de en el del repositorio compartido. Así cada desarrollador gestiona sus propios artefactos de entorno sin obligar a todo el equipo a aceptar en un pull request una línea que solo le afecta a él.
Qué hacer cuando un archivo ya está trackeado antes de ignorarlo
Añadir un patrón a .gitignore no deja de rastrear un archivo que Git ya conocía de commits anteriores; el fichero sigue apareciendo en cada git status como modificado. Hay que eliminarlo explícitamente del índice con git rm --cached ruta/al/archivo (o git rm -r --cached carpeta/ para un directorio completo), conservando el archivo en el disco local pero retirándolo del control de versiones a partir del siguiente commit. Este paso es el que más gente olvida tras añadir .env al .gitignore cuando ya llevaba semanas versionado.
Depurar con git check-ignore -v
Cuando un archivo se ignora sin que quede claro por qué regla, git check-ignore -v ruta/archivo muestra exactamente qué línea de qué archivo .gitignore provocó la exclusión, incluyendo el número de línea. Esto es imprescindible en monorepos con .gitignore anidados por paquete, donde un patrón heredado del .gitignore raíz puede estar bloqueando un archivo que el desarrollador esperaba versionar desde un subdirectorio concreto.
.gitignore, .gitattributes y .git/info/exclude: tres capas distintas
.gitignore versionado en el repositorio afecta a todo el equipo. .git/info/exclude es local a tu copia del repositorio, no se sube nunca y sirve para exclusiones puramente personales, como una carpeta de notas de trabajo. .gitattributes no ignora archivos sino que controla cómo Git los trata (normalización de saltos de línea, atributos de merge, marcado como binario); confundirlo con .gitignore es un error común al depurar por qué un archivo binario aparece corrupto tras un git pull en otro sistema operativo.
Salidas reales de ejemplo
$ git check-ignore -v src/config/.env.local
.gitignore:14:.env* src/config/.env.localLa salida indica el archivo .gitignore exacto, la línea y el patrón responsables de ignorar el fichero.
$ echo ".env" >> .gitignore
$ git rm --cached .env
rm '.env'
$ git commit -m "chore: dejar de versionar .env"
[main 3f8a1c2] chore: dejar de versionar .env
2 files changed, 1 insertion(+), 1 deletion(-)El archivo permanece en disco pero se retira del índice; el siguiente commit lo elimina del historial futuro.
$ git status --short
?? node_modules/
?? dist/
?? .env
$ cat >> .gitignore <<'EOF'
node_modules/
dist/
.env
EOF
$ git status --short
(sin salida: árbol de trabajo limpio)node_modules desaparece del listado de archivos sin seguimiento en cuanto el patrón surte efecto.
# No funciona:
logs/
!logs/important.log
# Sí funciona:
logs/*
!logs/important.loglogs/ ignora el directorio entero antes de que Git evalúe la excepción; logs/* sí permite que la negación tenga efecto.
Alcance de patrones habituales en .gitignore
| Patrón | Coincide con | No coincide con |
|---|---|---|
| *.log | Cualquier .log en cualquier carpeta | Archivos sin extensión .log |
| /dist | dist en la raíz del repo | packages/app/dist |
| dist/ | Cualquier carpeta llamada dist | Un archivo llamado dist |
| **/temp | temp en cualquier profundidad | temporal (nombre distinto) |
| temp/** | Todo el contenido dentro de temp/ | La propia carpeta temp/ si está vacía |
El anclaje con / al inicio y el sufijo / al final cambian radicalmente qué rutas coinciden con el patrón.
Dónde declarar cada tipo de exclusión
| Archivo | Se versiona | Uso típico |
|---|---|---|
| .gitignore | Sí | node_modules, dist, .env, artefactos de build |
| .git/info/exclude | No | Notas personales, scratch files propios |
| ~/.gitignore_global | No (config del usuario) | .DS_Store, .idea/, archivos del SO/IDE |
Separar exclusiones de proyecto, de entorno personal y de repositorio local evita ruido en los pull requests.
Casos de uso comunes
- Inicializar un repositorio Node/Python/Java con las exclusiones correctas desde el primer commit.
- Retirar del control de versiones un .env o una carpeta build subida por error.
- Configurar un monorepo con plantillas combinadas para frontend y backend.
- Depurar por qué un archivo sigue apareciendo en git status pese a estar en .gitignore.
- Separar exclusiones de equipo (.gitignore) de exclusiones personales (.git/info/exclude).
Buenas prácticas
- Añade .gitignore antes del primer commit, no después de que node_modules ya esté versionado.
- Usa git rm --cached tras añadir un patrón para archivos que ya estaban trackeados.
- Mantén los artefactos de IDE y sistema operativo en un .gitignore global, no en el del repositorio.
- Verifica reglas dudosas con git check-ignore -v antes de asumir que un patrón es el problema.
- En monorepos, prefiere .gitignore por paquete solo cuando los artefactos difieran realmente entre paquetes.