Hace poco moví de un repositorio a otro la parte de infraestructura de una aplicación WordPress desplegada en AWS. Durante la migración olvidé actualizar correctamente el archivo .gitignore y algunos archivos que no debían formar parte del repositorio acabaron entrando en varios commits. El problema apareció al intentar hacer git push a GitHub. En este post explico como arreglar el error GH001 en GitHub
Error al subir archivos de más de 100 MB a GitHub
Durante el push podemos empezar viendo una salida aparentemente normal:
Counting objects: 312, done.
Delta compression using up to 4 threads.
Compressing objects...Pero finalmente GitHub rechaza el push indicando que uno o varios archivos superan el tamaño máximo permitido.
El mensaje suele ser similar a:
remote: error: File path/to/file is 123.45 MB;
this exceeds GitHub's file size limit of 100.00 MB
remote: error: GH001: Large files detected.
GitHub permite trabajar normalmente con archivos relativamente grandes, pero bloquea los archivos superiores a 100 MiB en repositorios Git convencionales.
¿Por qué añadir el archivo a .gitignore no soluciona el problema?
Lo primero que hice fue corregir .gitignore.
Sin embargo, al volver a ejecutar:
git pushel error seguía apareciendo.
El motivo es importante: .gitignore únicamente evita que Git empiece a seguir archivos que todavía no están siendo rastreados.
Si el archivo ya fue añadido y forma parte de un commit, añadirlo posteriormente a .gitignore no lo elimina del historial de Git.
Por tanto, tenemos dos cosas que solucionar:
- Evitar que esos archivos vuelvan a añadirse al repositorio.
- Eliminar los archivos grandes de los commits donde ya fueron incluidos.
Caso 1: el archivo está únicamente en el último commit
Si acabamos de realizar el commit y todavía no hemos conseguido subirlo a GitHub, la solución es sencilla.
Primero añadimos el archivo o directorio correspondiente al .gitignore.
Por ejemplo:
path/to/generated-files/A continuación dejamos de seguirlo:
git rm -r --cached path/to/generated-files/El parámetro --cached es importante: elimina los archivos del índice de Git, pero no los borra de nuestro disco.
Añadimos el .gitignore:
git add .gitignoreY modificamos el último commit:
git commit --amend --no-editAhora podemos volver a intentar:
git pushSi el archivo solo estaba presente en ese último commit, esto debería ser suficiente.
Caso 2: el archivo ya aparece en commits anteriores
El problema se complica cuando el archivo grande ya forma parte de varios commits.
Aunque lo eliminemos en el último commit, Git sigue intentando enviar los objetos correspondientes a los commits anteriores.
En este caso hay que reescribir el historial del repositorio.
Una de las herramientas recomendadas actualmente para hacerlo es git-filter-repo.
En macOS podemos instalarla mediante Homebrew:
brew install git-filter-repoSi conocemos exactamente el archivo que queremos eliminar podemos ejecutar:
git filter-repo \
--invert-paths \
--path path/to/large-fileEsto elimina ese fichero del historial del repositorio.
Si el problema consiste simplemente en múltiples ficheros demasiado grandes, git-filter-repo también permite eliminar todos los blobs superiores a un determinado tamaño:
git filter-repo --strip-blobs-bigger-than 100MAntes de reescribir el historial conviene hacer una copia del repositorio y asegurarnos de que entendemos qué commits y ramas se verán afectados.
Un caso muy habitual: Terraform
Cuando trabajamos con infraestructura como código es especialmente fácil introducir archivos innecesariamente grandes.
Después de ejecutar:
terraform initTerraform descarga providers y módulos dentro del directorio:
.terraform/Este directorio no debe almacenarse en Git.
Un .gitignore básico para Terraform puede incluir:
# Terraform working directory
**/.terraform/*
# Terraform state
*.tfstate
*.tfstate.*
# Saved plans
*.tfplanSin embargo, hay un archivo que normalmente sí debemos versionar:
.terraform.lock.hclEl lock file registra las versiones y hashes de los providers y permite que los distintos entornos utilicen las mismas dependencias.
Comprobar qué archivos está siguiendo Git
Antes de realizar el commit podemos comprobar qué archivos van a entrar:
git statusTambién podemos comprobar si un archivo está siendo ignorado correctamente:
git check-ignore -v path/to/filePor ejemplo:
git check-ignore -v .terraform/providers/...Esto nos mostrará qué regla de .gitignore está haciendo match.
Conclusión
El principal aprendizaje de este problema es que añadir un archivo a .gitignore no elimina automáticamente un archivo que ya forma parte del historial de Git.
Si el fichero solamente está en el último commit podemos solucionarlo con:
git rm --cachedy modificando el commit.
Si, por el contrario, el archivo aparece en varios commits, tendremos que limpiar el historial utilizando herramientas como:
git filter-repoY, especialmente cuando trabajamos con Terraform, merece la pena revisar el .gitignore antes del primer commit para evitar incluir providers, estados, planes o cualquier otro archivo generado innecesariamente.
Links
- https://www.rubenortiz.es/?s=github
- https://docs.github.com/en/repositories/working-with-files/managing-large-files/resolving-git-large-file-storage-upload-failures
¿Te ha ayudado este artículo?
☕ Invítame a un café