Saltar al contenido

Git Subtree

Como se mencionó en nuestra página anterior, Git Submodule es útil para casos específicos. Para el seguimiento de dependencias de software, muchos desarrolladores prefieren Git Subtree.

Qué es Git Subtree

Git Subtree es una alternativa a Git Submodule. Permite anidar un repositorio dentro de otro como subdirectorio. Es una de las formas de seguir el historial de las dependencias de software. Pero los subtrees no deben confundirse con los submodules. A diferencia de los submodules, los subtrees no necesitan archivos .gitmodules ni gitlinks en el repositorio. Un subtree es simplemente un subdirectorio al que puedes hacer commit, crear ramas y fusionar junto con tu proyecto.

Por qué usar Git Subtree

Ventajas

  • Compatible con Git 1.7.10 y versiones posteriores.
  • Gestión de flujo de trabajo sencilla.
  • Código del subproyecto disponible después de que el superproyecto esté terminado.
  • No se requiere nuevo conocimiento de Git para usar git subtree.
  • No añade nuevos archivos de metadatos (por ejemplo, .gitmodules).
  • Permite modificar el contenido sin una copia separada del repositorio de la dependencia.

Desventajas

  • Requiere aprender una nueva estrategia de merge.
  • Proceso complicado para devolver código upstream a los subproyectos.
  • El código del superproyecto y del subproyecto se mezcla en el mismo repositorio.

Cómo usar Git Subtrees

Supongamos que hay un proyecto externo y quieres añadirlo a tu repositorio.

Por ejemplo, para añadir una extensión de vim en un repositorio que almacena tu configuración de vim, haz lo siguiente:

bash
git subtree add --prefix .vim/bundle/example https://github.com/Example/vim-example.git master --squash

Esto compactará todo el historial del proyecto vim-example dentro de tu carpeta .vim/bundle/example, registrando el SHA-1 de master en ese momento para futuras referencias. La salida es la siguiente, con dos commits:

bash
commit 6d7054b3acea64e2e31f4d6fb2e3be12e5865e87
Merge: 87fa91e ef86deb
Author: Ann Smith<[email protected]m>
Date:   Tue Jun 10 13:37:03 2016 +0200
    Merge commit 'fe67ddf158faccff4082d78a25c45d8cd93e8ba8' as '.vim/bundle/example'
commit fe67ddf158faccff4082d78a25c45d8cd93e8ba8
Author: Ann Smith<[email protected]m>
Date:   Tue May 12 13:37:03 2015 +0200
    Squashed '.vim/bundle/example/' content from commit b999b09
    git-subtree-dir: .vim/bundle/example
    git-subtree-split: b999b09cd9d69f359fa5668e81b09dcfde455cca

Para actualizar la subcarpeta a la última versión del repositorio hijo, ejecuta lo siguiente:

bash
git subtree pull --prefix .vim/bundle/example https://github.com/Example/vim-example.git master --squash

Pero git subtree almacena los IDs de commit del subproyecto y no referencias en los metadatos. Encuentra el nombre simbólico asociado con un commit:

bash
git ls-remote https://github.com/Example/vim-example.git | grep <commit-sha>

Reemplaza <commit-sha> con el hash real del commit.

Rebase después de Git Subtree

Para hacer rebase de un repositorio que contiene subtrees, usa el modo --interactive de git rebase. Puedes eliminar o compactar los commits de merge del subtree y luego ejecutar git rebase --continue. Ten en cuenta que reescribir el historial requiere volver a ejecutar después git subtree add o git subtree pull. Ten presente que reescribir el historial puede causar conflictos de merge al hacer rebase de subtrees, ya que la estructura de commits cambia.

OPCIONES

-q, --quietSuprime mensajes de resultado innecesarios en stderr.
-d, --debugProduce mensajes de depuración adicionales en stderr.
-P <prefix>, --prefix=<prefix>Define la ruta en el repositorio hacia el subtree que quieres manipular. Es obligatoria para todos los comandos.
-m <message>, --message=<message>Especifica <message> como mensaje de commit para el commit de merge. Solo es válida para add, merge y pull.

Usar Git Subtree sin seguimiento remoto

Añade el git subtree en una carpeta de prefijo especificada. Usa la opción --squash para conservar todo el historial del subproyecto en tu repositorio principal:

bash
git subtree add --prefix .vim/bundle/vim-double-upon https://hostname.org/example/vim-plugins.git master --squash

El comando realiza un fetch y compacta el historial. La salida normalmente muestra el progreso del fetch seguido de la confirmación de la adición:

bash
git fetch https://hostname.org/example/vim-plugins.git  master
warning: no common commits
remote: Counting objects: 325, done.
remote: Compressing objects: 100% (145/145), done.
remote: Total 325 (delta 101), reused 313 (delta 89)
Receiving objects: 100% (325/325), 61.47 KiB, done.
Resolving deltas: 100% (110/110), done.
From https://hostname.org/vim-plugins.git
* branch master -> FETCH_HEAD
Added dir '.vim/bundle/vim-double-upon'

Esto crea un commit de merge compactando todo el historial del subproyecto en uno solo:

bash
3bca0ad [4 minutes ago] (HEAD, stree) Merge commit 'fa2f5dc4f1b94356bca8a440c786a94f75dc0a45' as '.vim/bundle/vim-double-upon' [John Brown]
fa2f5dc [4 minutes ago] Squashed '.vim/bundle/vim-double-upon/' content from commit 13189ec [John Brown]

Para actualizar el código del plugin desde el repositorio upstream, haz un git subtree pull:

bash
git subtree pull --prefix .vim/bundle/vim-double-upon https://hostname.org/example/vim-plugins.git master --squash

Para contribuir con cambios de vuelta al repositorio upstream, extrae el historial del subtree usando git subtree split:

bash
git subtree split --prefix .vim/bundle/vim-double-upon -b split-branch
git push split-branch

Esto crea una nueva rama que contiene solo el historial del subtree, que luego puede enviarse al repositorio upstream.

Para hacer los comandos más cortos, añade el subproyecto como remoto.

Añadir el subproyecto como remoto

Añadirlo como remoto acorta el proceso:

bash
git remote add -f vim-double-upon https://hostname.org/example/vim-plugins.git

Añade el subtree:

bash
git subtree add --prefix .vim/bundle/vim-double-upon vim-double-upon master --squash

Actualiza el subproyecto así:

bash
git fetch vim-double-upon master
git subtree pull --prefix .vim/bundle/vim-double-upon vim-double-upon master --squash

Git Subtree es una alternativa a los submodules. Mientras que los submodules colocan otro proyecto en un directorio y mantienen sincronizado el repositorio remoto, Git Subtree mantiene el subproyecto como un directorio normal. Contribuir cambios de vuelta al repositorio upstream requiere usar git subtree split para extraer el historial del subproyecto, ya que la sincronización bidireccional directa no está soportada de forma nativa.

Practice

What are the features and usage of Git subtree?

¿Te resulta útil?

Vista previa dual-run — compárala con las rutas Symfony en producción.