Cómo arreglar el error GH001: Large files detected

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 push

el 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:

  1. Evitar que esos archivos vuelvan a añadirse al repositorio.
  2. 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 .gitignore

Y modificamos el último commit:

git commit --amend --no-edit

Ahora podemos volver a intentar:

git push

Si 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-repo

Si conocemos exactamente el archivo que queremos eliminar podemos ejecutar:

git filter-repo \
  --invert-paths \
  --path path/to/large-file

Esto 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 100M

Antes 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 init

Terraform 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
*.tfplan

Sin embargo, hay un archivo que normalmente sí debemos versionar:

.terraform.lock.hcl

El 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 status

También podemos comprobar si un archivo está siendo ignorado correctamente:

git check-ignore -v path/to/file

Por 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 --cached

y modificando el commit.

Si, por el contrario, el archivo aparece en varios commits, tendremos que limpiar el historial utilizando herramientas como:

git filter-repo

Y, 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

¿Te ha ayudado este artículo?

☕ Invítame a un café

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Rubenortiz.es
Información sobre privacidad

Este sitio web utiliza cookies para ofrecerte la mejor experiencia de usuario posible. La información sobre cookies se almacena en tu navegador y realiza funciones como reconocerte cuando vuelve a nuestro sitio web y ayudarme a comprender qué secciones de la web resultan más interesantes y útiles.