Ambos mecanismos resuelven el mismo problema —incluir un repositorio de Git dentro de otro— con compromisos distintos.
Un submódulo es una referencia a un repositorio externo y a un commit específico de ese repositorio, guardada dentro del repositorio principal (en un archivo .gitmodules). El contenido del submódulo no se copia al repositorio principal; solo se guarda el puntero.
git submodule add URL ruta/destino git submodule update --init --recursive
Quien clona el repositorio principal obtiene, por defecto, solo el puntero: hace falta el segundo comando para descargar el contenido real del submódulo. Esto mantiene el repositorio principal liviano, pero agrega un paso manual que se olvida con frecuencia.
git subtree copia el contenido completo de otro repositorio
directamente dentro del historial del repositorio principal, como si
fuera código propio. No requiere ningún paso adicional al clonar:
todo el contenido ya está ahí.
git subtree add --prefix=ruta/destino URL rama --squash
A cambio, actualizar más adelante el contenido incluido (traer
cambios nuevos del repositorio externo) es una operación más pesada,
y el historial del repositorio principal crece con todos los
commits importados (o se resume en uno solo con --squash).
Los submódulos convienen cuando el contenido externo cambia con frecuencia y se quiere mantener una separación clara de versionado. Los subtrees convienen cuando se prioriza la simplicidad para quien clona el repositorio, aceptando a cambio actualizaciones más costosas.