Git submodule: cómo añadir, actualizar y eliminar submódulos
Un git submodule es otro repositorio anidado en el tuyo y fijado en un commit concreto. El submódulo se añade con git submodule add <url> <ruta>, se obtiene al clonar con git clone --recurse-submodules, se actualiza con git submodule update --remote y se elimina con git submodule deinit y git rm.
Un git submodule (submódulo) es un repositorio Git independiente anidado en una carpeta de tu proyecto. Su propiedad principal: el repositorio padre no guarda los archivos del submódulo, sino una referencia a un commit concreto suyo. Por eso todos los que clonen el proyecto tendrán el submódulo en la misma versión, hasta que tú la cambies explícitamente.
Los submódulos se usan cuando hay que incluir una librería ajena sin gestor de paquetes, compartir código común entre varios proyectos o llevar un tema visual o un firmware a un repositorio aparte con su propio historial.
Cómo funcionan los git submodules
Un submódulo tiene tres partes, y entender cómo se relacionan elimina la mayoría de los problemas:
- El archivo
.gitmodulesen la raíz del proyecto: se incluye en los commits y describe qué ruta se toma de qué URL. - La entrada gitlink en el árbol del commit: un elemento especial con modo
160000que, en lugar de contenido, guarda el hash del commit del submódulo. - El propio repositorio del submódulo: sus datos se guardan en
.git/modules/<nombre>del padre, y en la carpeta del submódulo hay un archivo.gitque apunta allí.
cat .gitmodules
[submodule "libs/ui-kit"]
path = libs/ui-kit
url = https://github.com/example/ui-kit.git
git ls-tree HEAD libs/
160000 commit 8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c libs/ui-kit
Fíjate: el padre fija un commit, no una rama. Es la diferencia fundamental entre un submodule de Git y una dependencia del tipo «la última versión de main».
Cómo añadir un submódulo
git submodule add https://github.com/example/ui-kit.git libs/ui-kit
Cloning into '/home/dev/shop/libs/ui-kit'...
done.
git status
new file: .gitmodules
new file: libs/ui-kit
git commit -m "Añadir ui-kit como submódulo"
El comando clona el repositorio, crea o amplía .gitmodules y añade el gitlink al índice. Solo queda hacer commit. Si no indicas la ruta, la carpeta llevará el nombre del repositorio.
Para que el submódulo siga una rama concreta al actualizarse, indícala desde el principio: git submodule add -b develop <url> <ruta>. La rama se guardará en .gitmodules como branch = develop.
URL relativa
Si el submódulo está en el mismo servidor, puedes indicar una ruta relativa: git submodule add ../ui-kit.git libs/ui-kit. Se resuelve respecto a la URL del remoto del padre, así que funciona tanto por SSH como por HTTPS, según cómo se clonó el proyecto.
Cómo clonar un proyecto con submódulos
La queja más frecuente: «lo he clonado y la carpeta del submódulo está vacía». Por defecto git clone no descarga los submódulos. Lo correcto es:
git clone --recurse-submodules https://github.com/example/shop.git
Si el proyecto ya está clonado sin submódulos:
git submodule update --init --recursive
--init registra los submódulos de .gitmodules en el .git/config local, update los descarga y los sitúa en el commit fijado, y --recursive hace lo mismo con los submódulos dentro de submódulos.
Cómo actualizar un submódulo
Aquí es importante distinguir dos tareas: «obtener la versión que han fijado los compañeros» y «pasar a una versión nueva de la librería».
Obtener la versión fijada en el proyecto
Tras un git pull en el padre, el gitlink puede haber cambiado, pero los archivos del submódulo no se actualizan solos: git status mostrará modified: libs/ui-kit (new commits). Sincroniza:
git pull
git submodule update --init --recursive
Para no hacerlo a mano, activa la automatización: git config submodule.recurse true. Así pull, checkout y switch actualizarán los submódulos por sí solos. La alternativa para una sola vez es git pull --recurse-submodules.
Pasar a una versión nueva de la librería
# Opción 1: traer el último commit de la rama seguida
git submodule update --remote libs/ui-kit
# Opción 2: elegir la versión a mano
cd libs/ui-kit
git fetch
git checkout v3.2.0
cd ../..
# En ambos casos, fijar la nueva versión en el padre
git add libs/ui-kit
git commit -m "Actualizar ui-kit a v3.2.0"
--remote toma la rama del parámetro branch de .gitmodules y, si no existe, la rama a la que apunta el HEAD del remoto del submódulo (normalmente main). Sin ese último commit en el padre, los compañeros no recibirán la versión nueva: actualizar un submódulo siempre es un cambio del gitlink que hay que confirmar con un commit.
Para ver qué ha cambiado en los submódulos:
git diff --submodule=log
git config diff.submodule log # usar esta salida por defecto
git submodule status
8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c libs/ui-kit (v3.1.0)
+1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b libs/charts (v1.4.0-3-g1a2b3c4)
El prefijo en git submodule status: -, el submódulo no está inicializado; +, su commit actual difiere del fijado en el padre; U, conflicto de fusión.
Cómo hacer cambios en un submódulo
Tras git submodule update, el submódulo queda en estado detached HEAD: en un commit concreto, no en una rama. Si empiezas a hacer commits así, es fácil perderlos en el siguiente update. El orden de trabajo:
cd libs/ui-kit
git switch main # situarse en una rama
# ... cambios ...
git commit -am "Corregir el margen del botón"
git push # ¡primero el push del submódulo!
cd ../..
git add libs/ui-kit
git commit -m "ui-kit: corregido el margen del botón"
git push
El orden de los push es crítico. Si subes el padre antes que el submódulo, en el servidor el proyecto apuntará a un commit que nadie salvo tú puede descargar, y a tus compañeros git submodule update les fallará con fatal: remote error: upload-pack: not our ref o did not contain. Te protege git push --recurse-submodules=check (rechaza el push si el submódulo no se ha subido) o =on-demand (sube primero los submódulos automáticamente).
Cómo eliminar un submódulo
Antes, eliminarlo exigía editar varios archivos a mano. En las versiones modernas de Git bastan tres pasos:
git submodule deinit -f libs/ui-kit # quitar de .git/config y vaciar la carpeta
git rm -f libs/ui-kit # quitar el gitlink y la entrada de .gitmodules
rm -rf .git/modules/libs/ui-kit # borrar los datos guardados del submódulo
git commit -m "Eliminar el submódulo ui-kit"
El último paso no es obligatorio para que todo sea correcto, pero sin él, al volver a añadir un submódulo en la misma ruta, Git puede quejarse de que la carpeta local ya existe (A git directory for 'libs/ui-kit' is found locally).
Si ha cambiado la URL del submódulo
El repositorio se ha mudado: corrige .gitmodules y sincroniza la configuración local:
git submodule set-url libs/ui-kit https://gitlab.com/example/ui-kit.git
git submodule sync --recursive
git commit -am "Nueva dirección de ui-kit"
Tus compañeros también tendrán que ejecutar git submodule sync tras el pull: la URL de .gitmodules solo se copia a su .git/config con init o sync.
Comandos útiles para varios submódulos
| Tarea | Comando |
|---|---|
| Lista y estado | git submodule status --recursive |
| Ejecutar un comando en cada uno | git submodule foreach 'git switch main && git pull' |
| Acelerar la descarga | git submodule update --init --jobs 4 |
| Descargar sin historial | git submodule update --init --depth 1 |
| Resumen breve en status | git config status.submoduleSummary true |
Problemas habituales con los submódulos
- Carpeta vacía tras clonar: falta el init. Solución:
git submodule update --init --recursive. - «modified: (new commits)» justo después del pull: el padre apunta a otro commit, pero los archivos son los antiguos. Solución:
git submodule update. - Retroceder el submódulo por accidente: ejecutaste
git add .con el submódulo en un commit antiguo y confirmaste la vuelta atrás de versión. Revisa siempregit diff --submoduleantes del commit. - Conflicto del gitlink al fusionar: en dos ramas el submódulo se actualizó a commits distintos. Entra en el submódulo, elige el commit correcto (a menudo el más nuevo, que incluye ambos cambios), luego
git add rutay termina el merge. - Submódulo privado en CI: el build no puede descargar el submódulo sin acceso. Hay que dar al CI una clave o token para el repositorio del submódulo; las URL relativas simplifican la configuración.
Submódulos en equipo: acuerdos
La mayoría de los problemas con submódulos no vienen de Git, sino de que cada miembro del equipo actúa a su manera. Algunas reglas que conviene dejar por escrito en el README del proyecto:
- Clonar siempre con
--recurse-submodulesy activarsubmodule.recurse trueen todos los equipos. - Actualizar el submódulo en un commit aparte con un mensaje claro: qué versión y por qué.
- No modificar el código del submódulo dentro del proyecto padre sin un commit y un push propios en el submódulo.
- En CI, ejecutar explícitamente
git submodule update --init --recursivey comprobar que el build no depende de cambios locales.
Para comprobar en CI que todos los commits fijados de los submódulos están realmente disponibles, basta un clon limpio: si alguien olvidó subir un submódulo, el build fallará en el paso de update y no en el equipo de un compañero una semana después.
git clone --recurse-submodules --jobs 4 "$REPO_URL" build
cd build
git submodule status --recursive
Submodule o alternativas
Los submódulos dan un control exacto de la versión y un historial separado, pero añaden pasos manuales para cada participante. Si el código es una librería normal, es más sencillo un gestor de paquetes (npm, pip, Composer, Go modules). Si lo importante es que el código ajeno simplemente esté dentro del proyecto y se clone sin comandos extra, considera git subtree: copia el historial al repositorio principal y los compañeros no tienen que saber nada. Para muchos servicios relacionados, los equipos suelen optar por un monorepo. El submódulo está justificado cuando el código anidado tiene vida propia, sus propias releases y su propio equipo.
Conclusión
Un git submodule es una referencia a un commit concreto de otro repositorio. Para añadirlo, git submodule add; para obtenerlo, --recurse-submodules o update --init; para pasar a una versión nueva, update --remote más un commit en el padre; para eliminarlo, deinit, git rm y limpiar .git/modules. Recuerda el detached HEAD dentro del submódulo y el orden de los push. Las herramientas avanzadas de Git se practican en el curso de Git.
Preguntas frecuentes
¿Por qué el submódulo siempre está en detached HEAD?
El padre guarda el hash de un commit, no una rama, y git submodule update sitúa el submódulo precisamente en ese commit. Para hacer cambios, cambia a una rama a mano o usa git submodule update --remote --merge.
¿Se puede fijar un submódulo en un tag?
No directamente, pero puedes ir al tag dentro del submódulo con git checkout y confirmar en el padre el commit resultante; en git submodule status el tag aparecerá entre paréntesis.
¿GitHub incluye los submódulos en el archivo ZIP?
No, los archivos de código fuente de GitHub no incluyen el contenido de los submódulos y las carpetas quedarán vacías. Usa git clone --recurse-submodules.
¿En qué se diferencia git submodule de git subtree?
Submodule guarda una referencia a un commit de un repositorio externo y requiere comandos aparte para obtener el código, mientras que subtree copia los archivos y el historial directamente en el repositorio principal.
¿Se puede mover un submódulo a otra carpeta?
Sí, con git mv ruta/antigua ruta/nueva: Git actualizará por sí solo .gitmodules y las referencias internas, y solo quedará hacer commit.
Este tema se explica a fondo y con práctica en el curso «Git desde cero hasta desarrollador seguro».
Ir al curso