Llegamos a la última lección del curso, y la pregunta que toca hacerse es la más honesta que puede plantearse al final de cualquier formación técnica: ¿cuánto de todo esto seguirá siendo cierto dentro de diez años?

Git tiene ya dos décadas. Nació en 2005 para un problema muy concreto —coordinar el desarrollo del kernel de Linux cuando su equipo perdió la herramienta que usaba— y se convirtió en el estándar universal del control de versiones. Pero no está congelado: sale una versión nueva cada pocos meses, y algunas de esas versiones traen cambios profundos que casi nadie usa todavía.

Esta lección separa con cuidado tres cosas que suelen mezclarse:

  1. Lo que ya existe y puedes usar hoy, aunque sea poco conocido o esté marcado como experimental.
  2. Lo que es tendencia: hacia dónde apunta el trabajo, sin fechas ni promesas.
  3. Lo que casi con seguridad no va a cambiar, y que es precisamente lo que aprendiste en el módulo 1.

Y luego cerramos el curso: cómo seguir aprendiendo por tu cuenta, y una recapitulación del camino completo.

Un aviso de método antes de empezar: en lo que sigue no daré fechas para nada que no esté ya disponible, y cuando mencione una versión de Git lo haré como referencia aproximada. El software evoluciona y las notas de versión son la única fuente fiable. Lo que sí puedo darte es el criterio para valorar cada cosa: qué problema resuelve, en qué estado está y si te conviene adoptarla.

Contenido

  1. Cómo evoluciona Git y cómo enterarse
  2. SHA-256: el sustituto de SHA-1
  3. reftable: un formato nuevo para las referencias
  4. Clones parciales e índice disperso
  5. commit-graph y git maintenance
  6. switch y restore frente a checkout
  7. Tabla de madurez: qué usar hoy
  8. Tendencias: hacia dónde apunta el trabajo
  9. El papel de los asistentes de IA
  10. Lo que no va a cambiar
  11. Cómo seguir aprendiendo
  12. El curso completo, recapitulado

  1. Cómo evoluciona Git y cómo enterarse

Git se desarrolla en abierto, por correo, con el modelo que estudiamos en la lección 10-01: parches en una lista pública, un mantenedor que integra, y publicaciones periódicas cada pocos meses. Cada versión trae un fichero de notas que describe lo nuevo, lo corregido y lo que cambia de comportamiento.

Saber en qué versión estás y qué trae es el primer hábito:

git --version
git version 2.51.0

Y las notas de cada versión están en el propio repositorio de Git, en Documentation/RelNotes/. Leer las de cada versión nueva cuesta cinco minutos y es la mejor forma de descubrir cosas que llevas años haciendo mal.

Un detalle que conviene conocer: Git es extraordinariamente conservador con la compatibilidad hacia atrás. Un repositorio creado en 2006 se abre hoy sin conversión, y los comandos que aprendiste siguen funcionando. Esa es una de las razones por las que las novedades tardan tanto en llegar al uso común: se introducen como opcionales, conviven años con lo anterior, y solo se convierten en el valor por defecto cuando el ecosistema entero está preparado.

Esto tiene una consecuencia práctica para ti: casi nada de lo que has aprendido va a dejar de funcionar. Lo que puede pasar es que aparezcan formas mejores de hacer lo mismo.

  1. SHA-256: el sustituto de SHA-1

Por qué hace falta

Recuerda del módulo 1 que todo en Git se identifica por el hash SHA-1 de su contenido: blobs, árboles, commits y etiquetas. Ese hash es el eje del sistema entero. Y SHA-1, como función criptográfica, está roto: en 2017 se demostró públicamente que es posible construir dos contenidos distintos con el mismo hash SHA-1 (una colisión), con un coste computacional elevado pero alcanzable.

Conviene ser preciso sobre lo que eso significa para Git, porque se exagera en ambas direcciones:

Preocupación Realidad
"Git está roto, cualquiera puede falsificar commits" No. Una colisión útil exige construir ambos contenidos a propósito; no permite fabricar un objeto que coincida con uno existente que no controlas
"SHA-1 en Git es solo un identificador, no seguridad" Parcialmente cierto, pero el hash es lo que garantiza la integridad y lo que firman las firmas de commit (08-05)
"Da igual, nunca pasará nada" Es una debilidad conocida en un sistema que sostiene la cadena de suministro de software del mundo

Git no se quedó parado. Desde hace años incorpora detección de colisiones: una variante del cálculo de SHA-1 que detecta los patrones característicos de un ataque de colisión y rechaza el objeto. Los ficheros del ataque público de 2017 son rechazados por Git.

Pero eso es un parche. La solución de fondo es cambiar de función de hash.

Qué existe hoy

Git puede crear repositorios con SHA-256:

git init --object-format=sha256 repositorio-nuevo
cd repositorio-nuevo

echo "hola" > fichero.txt
git add fichero.txt
git commit -m "Primer commit"
git log --format=%H
d0e7f5c1b8a4f39e2c6b0d5a7f1e3c9b4d8a2f6e0c5b1a9d3f7e2c8b4a6d0f5e

Sesenta y cuatro caracteres hexadecimales en lugar de cuarenta. Y comprobarlo:

git rev-parse --show-object-format
sha256

Todo lo que has aprendido funciona igual: add, commit, log, branch, merge, rebase, bisect. El modelo de datos es idéntico; solo cambia la función que calcula los identificadores. Es la mejor prueba de que lo que aprendiste en la lección 01-04 es el concepto, no el algoritmo concreto.

El problema real: la interoperabilidad

Aquí está el motivo de que SHA-256 siga siendo experimental y de que casi nadie lo use:

Un repositorio SHA-256 y uno SHA-1 no pueden intercambiar objetos.

git clone https://git.ejemplo.es/gestor-tareas.git   # repositorio SHA-1
cd gestor-tareas
git remote add otro /ruta/al/repositorio-sha256
git fetch otro
fatal: the remote uses a different object format than this repository

No hay conversión sobre la marcha ni traducción transparente. Y esto es un problema enorme, porque el mundo entero —todas las plataformas de alojamiento, todos los repositorios existentes, todas las herramientas— está en SHA-1.

El plan de fondo, largamente diseñado, contempla repositorios capaces de operar en ambos formatos a la vez, manteniendo una tabla de correspondencias entre el hash SHA-1 y el SHA-256 de cada objeto, de modo que un repositorio SHA-256 pueda hablar con un servidor SHA-1 traduciendo en la frontera. Esa interoperabilidad es la pieza que falta, y es un trabajo considerable.

Qué hacer tú

Nada, por ahora. SHA-256 es útil para experimentar y para entender el modelo, y puede tener sentido en un entorno cerrado que no intercambie con nadie. Para trabajo real, mientras el ecosistema esté en SHA-1, un repositorio SHA-256 es una isla.

Lo que sí conviene es entender la distinción, porque es un ejemplo perfecto de por qué el modelo de datos importa más que sus detalles: el día que Git cambie de función de hash, tú no tendrás que reaprender nada.

  1. reftable: un formato nuevo para las referencias

El problema del formato actual

Recuerda de la lección 03-01: una rama es un fichero de texto con un hash dentro.

cat .git/refs/heads/main
4f8a2e6c9d3b1a5e7f2c8b0d4a6e9f1c3b5d7a0e

Es de una simplicidad admirable y funciona perfectamente en gestor-tareas, con sus cinco ramas. Cuando hay muchas referencias, Git las compacta en un único fichero:

cat .git/packed-refs | head -5
# pack-refs with: peeled fully-peeled sorted
4f8a2e6c9d3b1a5e7f2c8b0d4a6e9f1c3b5d7a0e refs/heads/main
8a1f6c3d4e5b6a7c8d9e0f1a2b3c4d5e6f7a8b9c refs/remotes/origin/main
2e9f4c7b1a8d3e6f0c5b2a9d4e7f1a8c3b6d0e5f refs/tags/v1.3.0

Este sistema —ficheros sueltos más un fichero compactado— tiene límites que se notan a gran escala:

Problema Por qué ocurre
Muchos ficheros pequeños Un repositorio con 100.000 referencias tiene hasta 100.000 ficheros; los sistemas de ficheros sufren
Escrituras costosas Actualizar una referencia con packed-refs obliga a reescribir el fichero entero
Concurrencia limitada Dos procesos actualizando referencias a la vez compiten por bloqueos
Colisiones de nombres No pueden coexistir refs/heads/funcion y refs/heads/funcion/nueva: un directorio no puede ser también un fichero
Sin historial de referencias El reflog es un mecanismo aparte, con su propio formato
Mayúsculas y minúsculas En sistemas de ficheros que no las distinguen, Main y main chocan

Ese cuarto punto es el que probablemente ya has sufrido:

git branch funcion
git branch funcion/nueva
fatal: cannot lock ref 'refs/heads/funcion/nueva': 'refs/heads/funcion' exists;
cannot create 'refs/heads/funcion/nueva'

No es una regla arbitraria de Git: es que el sistema de ficheros no permite que funcion sea a la vez fichero y directorio.

Qué es reftable

reftable es un formato binario para almacenar referencias y su reflog en un conjunto pequeño de ficheros ordenados, diseñado originalmente para servidores con enormes cantidades de referencias y posteriormente incorporado a Git.

Sus propiedades:

  • Búsqueda binaria sobre datos ordenados y comprimidos por prefijo: encontrar una referencia entre cientos de miles es inmediato.
  • Escrituras por adición, con compactación periódica: actualizar no obliga a reescribir todo.
  • Transacciones atómicas sobre múltiples referencias.
  • Reflog integrado en el mismo formato.
  • Sin restricciones del sistema de ficheros: funcion y funcion/nueva pueden coexistir, y las mayúsculas se distinguen siempre.

Se usa así:

git init --ref-format=reftable repositorio-nuevo
cd repositorio-nuevo
git rev-parse --show-ref-format
reftable

Y entonces:

ls .git/reftable/
0x000000000001-0x000000000003-a1b2c3d4.ref
tables.list

No hay refs/heads/. No hay packed-refs. Las referencias viven dentro de esos ficheros binarios.

Todos los comandos que conoces funcionan exactamente igual. git branch, git tag, git switch, git log, git reflog. Lo único que cambia es que ya no puedes hacer cat .git/refs/heads/main, y tienes que usar la fontanería (lección 09-06):

git for-each-ref --format='%(refname) %(objectname)'
git show-ref
git rev-parse main

Que es, por cierto, lo que siempre deberías haber hecho: leer los ficheros internos a mano nunca fue una interfaz estable. Esta es una lección práctica que va más allá de reftable: usa los comandos, no los ficheros.

Estado y recomendación

reftable está disponible en versiones recientes de Git y se considera funcional, pero no es el formato por defecto y hay que contar con que herramientas externas que lean .git/refs/ directamente no funcionarán con él.

Qué hacer tú: conocerlo y no adoptarlo todavía salvo que tengas un repositorio con decenas de miles de referencias y estés sufriendo por ello. Es un ejemplo excelente de evolución bien hecha: cambia la implementación por completo sin cambiar ni un solo comando.

  1. Clones parciales e índice disperso

Estos ya los conoces de la lección 10-04, y aquí van con la etiqueta que les corresponde en esta lección: son presente, no futuro.

git clone --filter=blob:none https://git.ejemplo.es/proyecto.git
git sparse-checkout init --cone --sparse-index
git sparse-checkout set servicios/tareas

Ambas técnicas están disponibles, soportadas por las plataformas principales y en uso en producción en organizaciones grandes. Lo interesante desde la perspectiva de esta lección es qué representan: son la respuesta de Git a un problema que su diseño original no contemplaba.

Git nació con un supuesto implícito: cada clon tiene el repositorio completo. Eso es lo que lo hace distribuido y lo que le da su resistencia (lección 09-05: cualquier clon es una copia de seguridad). Pero también es lo que lo hacía inviable para repositorios de decenas de gigabytes.

Los clones parciales relajan ese supuesto sin romper el modelo: el repositorio sigue teniendo el grafo completo y sigue sabiendo qué objetos existen; simplemente algunos no están descargados todavía y se piden cuando hacen falta. Es una solución elegante porque conserva la semántica y solo difiere la transferencia.

La tendencia que representan es clara: Git evoluciona hacia poder trabajar con repositorios de cualquier tamaño sin renunciar a su modelo. Es previsible que este trabajo continúe: mejor gestión de qué se descarga, mejores heurísticas de prefetch y menos casos raros.

  1. commit-graph y git maintenance

También presente, también de la lección 10-04 y la 08-06.

git config --global core.commitGraph true
git config --global fetch.writeCommitGraph true
git maintenance start

Representan otra línea de evolución: estructuras auxiliares que aceleran sin cambiar el modelo. El commit-graph no es información nueva: es información que ya estaba en los objetos commit, precalculada en un formato que se lee rápido. Si lo borras, no pierdes nada; se regenera.

Ese principio de diseño —cachés derivadas, regenerables y opcionales— es muy sano y es previsible que se extienda. Es la forma de mejorar el rendimiento sin comprometer la simplicidad del formato de almacenamiento, que es el activo más valioso de Git.

git maintenance, por su parte, refleja un cambio de filosofía en el mantenimiento: en lugar de que gc te interrumpa cuando menos te conviene, hay tareas programadas que se ejecutan en segundo plano. Es previsible que acabe siendo el comportamiento por defecto.

  1. switch y restore frente a checkout

Un caso distinto: no es rendimiento ni criptografía, es diseño de la interfaz.

git checkout es el comando más sobrecargado de Git. Hace cosas conceptualmente distintas según sus argumentos:

git checkout main                    # cambiar de rama
git checkout -b nueva                # crear rama y cambiar
git checkout 4f8a2e6                 # ir a un commit (HEAD desprendido)
git checkout -- app.js               # DESCARTAR cambios en un fichero
git checkout main -- app.js          # traer un fichero de otra rama
git checkout --ours app.js           # resolver un conflicto

Seis operaciones distintas, y una de ellas —la cuarta— destruye trabajo sin posible recuperación. Que git checkout main (inofensivo) y git checkout -- app.js (irreversible) sean el mismo comando separados por dos guiones es un problema de diseño, y ha costado disgustos a mucha gente.

La solución fue partirlo en dos comandos con propósitos claros:

Antes Ahora Qué hace
git checkout main git switch main Cambiar de rama
git checkout -b nueva git switch -c nueva Crear rama y cambiar
git checkout 4f8a2e6 git switch --detach 4f8a2e6 HEAD desprendido, de forma explícita
git checkout - git switch - Volver a la rama anterior
git checkout -- app.js git restore app.js Descartar cambios del disco
git checkout HEAD -- app.js git restore --source=HEAD app.js Restaurar desde una revisión
git reset HEAD app.js git restore --staged app.js Quitar del índice
git checkout --ours app.js git restore --ours app.js Resolver conflicto

Fíjate en la fila del HEAD desprendido: con switch hay que pedirlo explícitamente con --detach. Se acabó el "he hecho checkout de una etiqueta y ahora estoy en un sitio raro" que estudiamos en la lección 03-02.

Y fíjate en la penúltima fila: git restore --staged sustituye a un uso de git reset que confundía a todo el mundo, porque reset es un comando que mueve ramas (lección 09-02) y usarlo para quitar un fichero del índice mezclaba dos conceptos.

Estado

switch y restore llevan años disponibles, estuvieron marcados como experimentales durante bastante tiempo y en versiones recientes se han consolidado. Se pueden usar con confianza.

Y checkout no va a desaparecer. Hay millones de guiones, tutoriales y hábitos que dependen de él, y Git es conservador con la compatibilidad. Convivirán indefinidamente.

Qué hacer tú: usar switch y restore en tu trabajo diario, y saber leer checkout porque lo vas a encontrar en toda la documentación existente, en las respuestas de internet y en los guiones de tu empresa. Es exactamente la recomendación que hicimos en la lección 03-02, y aquí se confirma.

  1. Tabla de madurez: qué usar hoy

Resumen operativo de todo lo anterior, más lo que ya conoces:

Novedad Estado ¿Adoptar hoy? Motivo
switch / restore Consolidado Sí, siempre Más claro y más seguro que checkout; sin riesgo
commit-graph Estable Sí, siempre Caché regenerable, sin inconvenientes, gran mejora en historiales grandes
git maintenance Estable en repositorios medianos y grandes Mantenimiento en segundo plano en vez de interrupciones
fsmonitor / untrackedCache Estable si git status tarda Mide antes; en repositorios pequeños no aporta
Clones parciales (--filter=blob:none) Estable, soporte amplio en repositorios grandes y en CI Historial completo con una fracción de la descarga
sparse-checkout --cone + índice disperso Estable Solo en monorepos con muchísimos ficheros Añade confusión si no hace falta
Git LFS Maduro (externo a Git) si hay binarios grandes que cambian Con las limitaciones de la 10-03
reftable Disponible, no por defecto No salvo decenas de miles de referencias Herramientas externas pueden no soportarlo
SHA-256 Experimental No para trabajo real Sin interoperabilidad con el ecosistema SHA-1

La columna que importa es la tercera, y su patrón: lo que no tiene inconvenientes, adóptalo ya; lo que tiene coste de comprensión, solo si mides que lo necesitas; lo que aún no interopera, conócelo y espera.

  1. Tendencias: hacia dónde apunta el trabajo

Aquí salimos de lo comprobable. Lo que sigue son direcciones observables en el ecosistema, no predicciones con fecha.

Mejor experiencia para monorepos

Es la línea de trabajo más visible de los últimos años, y todo lo que vimos en la lección 10-04 forma parte de ella: clones parciales, índice disperso, fsmonitor, commit-graph. La motivación es clara: las organizaciones grandes han apostado por repositorios enormes, y necesitan que Git funcione en ellos.

Es razonable esperar más en esa dirección: menos casos raros con sparse-checkout, mejores heurísticas sobre qué descargar, y quizá que algunas de estas opciones dejen de serlo y pasen a ser el comportamiento por defecto cuando Git detecte que le conviene.

Servidores Git especializados

Un dato que conviene tener presente: el git que ejecuta un servidor grande no siempre es el git que tienes tú. Las plataformas de gran escala han desarrollado implementaciones propias del lado del servidor —a veces basadas en Git, a veces reescrituras compatibles con el protocolo— optimizadas para servir a miles de clientes concurrentes.

Para ti, esto es transparente: hablas el protocolo de Git y no sabes qué hay al otro lado. Pero explica por qué algunas plataformas soportan funciones que otras no, y por qué las cuotas y los comportamientos difieren (algo que ya notamos con LFS en la lección 10-03).

La tendencia es que esa especialización aumente, y que el Git que ejecutas en tu portátil y el que corre en un servidor sean cada vez más distintos por dentro, aunque hablen lo mismo.

Herramientas construidas sobre Git, no sustitutos

Este es un patrón de veinte años que no da señales de cambiar. Han aparecido decenas de sistemas de control de versiones nuevos, y algunos son técnicamente interesantes. Pero lo que ha triunfado no son los sustitutos, sino las capas construidas encima: plataformas de alojamiento, sistemas de revisión, interfaces gráficas, herramientas de gestión de parches, sistemas de despliegue (lección 10-05), y clientes alternativos con modelos de trabajo distintos que almacenan en el formato de Git.

La razón es el efecto de red combinado con una propiedad técnica: el formato de almacenamiento de Git es simple, está documentado y es estable. Cualquiera puede escribir una herramienta que lo lea y lo escriba. Eso convierte a Git en algo parecido a un formato de fichero universal, y sustituir un formato universal es muchísimo más difícil que sustituir un programa.

La consecuencia práctica para ti es tranquilizadora: aunque la interfaz que uses cambie, el modelo de debajo probablemente no.

Mejor experiencia de línea de comandos

Hay una corriente sostenida de mejora en los mensajes de error y en las sugerencias. Git ha pasado de mensajes crípticos a mensajes que explican qué hacer:

git branch -d GT-231-filtro-ocultas
error: the branch 'GT-231-filtro-ocultas' is not fully merged.
hint: If you are sure you want to delete it, run 'git branch -D GT-231-filtro-ocultas'.

Esa línea hint: no existía hace años. Es un trabajo poco espectacular y muy valioso, y es previsible que continúe.

  1. El papel de los asistentes de IA

Merece un apartado propio porque es la novedad más visible del ecosistema y la que más confusión genera. Vamos a mirarlo con el mismo criterio que hemos aplicado a todo lo demás: qué aporta de verdad y dónde está el riesgo.

Dónde ayudan realmente

Redactar mensajes de commit. Un asistente puede leer un diff y proponer un mensaje. Funciona bien para la primera línea —resumir qué cambió— y es un buen punto de partida cuando estás en blanco.

Explicar código ajeno o un diff grande. Poner en palabras qué hace un cambio de 400 líneas ahorra tiempo real durante una revisión.

Recordar comandos y opciones. "¿Cómo era el comando para ver quién escribió cada línea ignorando los cambios de espaciado?" es exactamente el tipo de pregunta que un asistente responde bien.

Primera pasada de revisión. Detectar patrones sospechosos, olvidos evidentes, inconsistencias. Como comprobación informativa, en el sentido de la lección 10-02: que informe, no que bloquee.

Generar guiones auxiliares. Los guiones de las lecciones 10-05 y 10-04 son exactamente el tipo de cosa que un asistente escribe con soltura.

Dónde no ayudan, y el riesgo

El porqué del cambio no está en el diff. Y el porqué es lo importante de un mensaje de commit (lección 08-01). Un asistente puede escribir perfectamente "añade el filtro de tareas ocultas al listado" leyendo el código. Lo que no puede escribir es:

El contador debe seguir incluyéndolas porque la especificación de
GT-231 dice que "pendientes" cuenta todas las tareas no completadas,
estén visibles o no.

Ese párrafo —el mismo de la lección 09-06— contiene una decisión, un motivo y una referencia a una discusión. Está en tu cabeza, no en el código. Un mensaje generado automáticamente que describe el qué y omite el porqué es un mensaje que ha perdido su función.

Un mensaje generado y no revisado es peor que ninguno, porque parece documentación y no lo es. Y el historial es el único documento del proyecto que nadie puede editar después.

El riesgo de fondo: aceptar sin entender. Es el mismo riesgo del botón "Sincronizar" de la lección 10-02, amplificado. Si un asistente te propone git reset --hard HEAD~3 y lo ejecutas sin entenderlo, el problema no es el asistente: es que estás ejecutando operaciones destructivas a ciegas.

Y hay una asimetría muy concreta que conviene tener presente: las operaciones de Git no son igual de reversibles. Una sugerencia equivocada de git log no cuesta nada. Una sugerencia equivocada de git push --force, git reset --hard o git clean -fd puede costar trabajo que no se recupera. Cuanto más destructiva la operación, más exige entenderla antes.

El criterio

Un asistente ayuda a redactar, a recordar y a explorar. No exime de entender el cambio.

Y una regla práctica derivada de todo el curso:

Tarea Delegar en el asistente Por qué
Redactar el asunto de un commit , revisando El qué está en el diff
Escribir el cuerpo que explica el porqué No Solo tú lo sabes
Recordar la sintaxis de un comando Es documentación
Ejecutar una operación destructiva sugerida No sin entenderla reset --hard, push --force, clean -fd
Explicar un diff ajeno Ahorra tiempo real
Decidir si un cambio es buena idea No Requiere contexto del producto
Generar un guion auxiliar , leyéndolo Y probándolo en un repositorio de pruebas
Resolver un conflicto de fusión Con mucha cautela Requiere entender las dos intenciones (03-05)

Fíjate en que esta tabla es la misma de la lección 10-02 sobre interfaces gráficas, con otros nombres. La regla de fondo no ha cambiado en todo el módulo: automatiza lo repetitivo, entiende lo irreversible.

Y hay una consecuencia optimista: en un mundo donde escribir código cuesta menos, entender el historial, saber por qué se tomó una decisión y poder recuperarse de un error valen más, no menos. Justo lo que enseña este curso.

  1. Lo que no va a cambiar

Después de mirar lo que se mueve, toca mirar lo que no. Y esto es lo más importante de la lección.

El modelo de datos direccionable por contenido

Todo objeto se identifica por el hash de su contenido. El hash puede cambiar de SHA-1 a SHA-256; la idea, no. Y de ella se derivan, como consecuencias necesarias, propiedades que has usado durante todo el curso:

  • Los objetos son inmutables: cambiar el contenido produce un objeto distinto.
  • El contenido idéntico se guarda una sola vez, en cualquier rama y en cualquier momento.
  • El hash de un commit verifica todo su historial, porque incluye el de su árbol y el de sus padres.
  • Reescribir historia crea objetos nuevos; no modifica los viejos. Por eso el reflog puede recuperarlos (09-04).

Este último punto es la clave de medio curso. rebase, amend, cherry-pick, filter-repo: todos crean objetos nuevos y mueven referencias. Los originales siguen ahí hasta que la recolección de basura los borra. Eso no es una característica añadida: es una consecuencia matemática del direccionamiento por contenido.

El grafo dirigido acíclico de commits

Cada commit apunta a sus padres. De ahí salen las ramas, las fusiones, los antepasados comunes y todo lo demás:

  • Una rama es un puntero móvil a un nodo (03-01).
  • Una fusión es un commit con dos padres, y la base es el antepasado común (03-03).
  • bisect es búsqueda binaria sobre el grafo (06-02).
  • rebase es reproducir un camino sobre otro punto (05-01).
  • Una divergencia es que dos referencias apuntan a nodos sin relación de antepasado (09-03).
  • merge-base --is-ancestor es una pregunta sobre alcanzabilidad, y es lo que usábamos para verificar despliegues (10-05).

Ese grafo no va a cambiar. Es demasiado simple, demasiado potente y demasiado central.

La naturaleza distribuida

Cada clon es un repositorio completo, con todo el historial y toda la capacidad de operar sin conexión. De ahí salen:

  • Que puedas trabajar sin red y sincronizar después (04-04).
  • Que cualquier clon sea una copia de seguridad viable (09-05).
  • Que no exista un repositorio "oficial" salvo por acuerdo (04-01).
  • Que los forks funcionen (07-01) y que el modelo del kernel sea posible (10-01).

Los clones parciales relajan el "completo" para el contenido, pero no el modelo: sigues teniendo el grafo entero y sigues siendo autónomo para casi todo.

Las tres zonas

Copia de trabajo, índice y repositorio (lección 01-03). El índice es la pieza que más desconcierta al principio y la que más valor da después: es lo que permite add -p (02-04), lo que hace que git add sea también una red de seguridad, y lo que sostiene la resolución de conflictos con sus tres etapas (03-05).

Por eso este curso no caduca

Has aprendido comandos, pero sobre todo has aprendido un modelo. Los comandos cambiarán de nombre —checkout se ha partido en switch y restore— y aparecerán opciones nuevas. El modelo lleva veinte años idéntico y no hay ninguna señal de que vaya a moverse.

Cuando dentro de cinco años te encuentres un comando que no conoces, la pregunta que te permitirá entenderlo en treinta segundos será siempre la misma: ¿qué objetos crea y qué referencias mueve?

  1. Cómo seguir aprendiendo

Terminado el curso, la mejor forma de seguir es usando lo que Git ya trae.

La documentación integrada

# Manual completo de un comando
git help rebase
git rebase --help

# Resumen rápido de opciones, sin abrir el manual
git rebase -h

# La lista de TODAS las guías conceptuales
git help -g

Ese git help -g es un descubrimiento para mucha gente. Devuelve una lista de guías que no son manuales de comandos sino explicaciones de conceptos:

Guía De qué trata Cuándo leerla
gitcore-tutorial Git desde la fontanería: cómo se construyen los objetos a mano La mejor de todas. Léela ya
giteveryday Los ~20 comandos que cubren el 99 % del uso, por rol Como repaso ordenado
gitglossary Definiciones precisas de toda la terminología Cuando dudes de un término
gitworkflows Cómo trabaja el propio proyecto Git con sus ramas Complemento del módulo 7
gitrevisions La sintaxis completa de HEAD~3, main@{2}, A...B Después del módulo 2
gittutorial Introducción básica Ya la superaste
gitcvs-migration Migrar desde sistemas centralizados Si te toca
git help gitcore-tutorial
git help giteveryday
git help gitrevisions

Empieza por gitcore-tutorial. Construye un repositorio desde cero usando solo comandos de fontanería: hash-object, update-index, write-tree, commit-tree. Después de este curso, esa guía te resultará legible y consolidará el modelo mental como ninguna otra cosa.

Las notas de versión

Cada versión nueva de Git trae un fichero de notas. Leerlas cuando actualices es el hábito con mejor retorno para mantenerte al día. Es donde descubrirás la opción que llevas años echando de menos y que existe desde hace tres versiones.

Practicar la fontanería en un repositorio de pruebas

Esto es lo que más consolida. Crea un repositorio desechable y construye un commit sin usar git commit:

mkdir /tmp/laboratorio && cd /tmp/laboratorio && git init

# 1. Crear un blob a partir de contenido
echo "Hola desde la fontanería" | git hash-object -w --stdin
9f8c2e1a4b7d0f3c6e9b2a5d8f1c4e7a0b3d6f9c
# 2. Meterlo en el índice con un nombre
git update-index --add --cacheinfo 100644,9f8c2e1a4b7d0f3c6e9b2a5d8f1c4e7a0b3d6f9c,saludo.txt

# 3. Convertir el índice en un objeto árbol
git write-tree
2a4c8e0f1b3d6a9c2e5f8b1d4a7c0e3f6b9d2a5c
# 4. Crear un commit que apunte a ese árbol
echo "Mi primer commit hecho a mano" | git commit-tree 2a4c8e0f1b3d6a9c2e5f8b1d4a7c0e3f6b9d2a5c
7e1b4d9a2c5f8b0e3d6a9c2f5b8e1d4a7c0f3b6d
# 5. Apuntar la rama a ese commit
git update-ref refs/heads/main 7e1b4d9a2c5f8b0e3d6a9c2f5b8e1d4a7c0f3b6d

# 6. Comprobar que Git lo ve como un repositorio normal
git log --stat
git status

Acabas de hacer a mano lo que git commit hace por ti. Blob → índice → árbol → commit → referencia. Es el modelo de la lección 01-04 ejecutado paso a paso, y es el ejercicio que separa a quien usa Git de quien lo entiende.

Leer el historial de proyectos ajenos

Como vimos en la lección 10-01: clona un proyecto que admires con --filter=blob:none y mira cómo trabaja. Cómo escriben los mensajes, si el historial es lineal, quién integra, cómo estructuran las series de commits. Se aprende muchísimo.

Enseñarlo

El mejor filtro para saber si has entendido algo es explicárselo a alguien. Cuando un compañero se atasque con una divergencia o pierda un commit, ayúdale explicando el modelo, no solo dando el comando. Si eres capaz de dibujar el grafo en una pizarra y señalar dónde estaba el commit perdido, lo has entendido.

  1. El curso completo, recapitulado

Vamos a cerrar mirando el camino entero.

De dónde partiste

Empezaste sin saber qué es un repositorio. Sin saber qué diferencia hay entre guardar una copia de una carpeta y versionar un proyecto. Diez módulos después:

flowchart TD
    M1["1. Entender<br/>Modelo de datos, tres zonas,<br/>hash del contenido"]
    M2["2. Construir historia<br/>add, commit, diff, log"]
    M3["3. Ramificar<br/>ramas, merge, conflictos"]
    M4["4. Colaborar<br/>remotos, fetch, push, seguimiento"]
    M5["5. Manipular<br/>rebase, cherry-pick, tags, revert"]
    M6["6. Herramientas<br/>hooks, bisect, blame, submódulos"]
    M7["7. Proceso<br/>PRs, revisiones, flujos, CI"]
    M8["8. Oficio<br/>mensajes, historial limpio,<br/>seguridad, rendimiento"]
    M9["9. Rescatar<br/>reset, reflog, corrupción,<br/>depuración"]
    M10["10. Operar<br/>casos reales, LFS, escala,<br/>DevOps, futuro"]

    M1 --> M2 --> M3 --> M4 --> M5 --> M6 --> M7 --> M8 --> M9 --> M10

Los diez módulos, en una línea cada uno

Módulo Qué te llevas
1. Introducción Que Git es una base de datos de objetos direccionados por contenido, no una herramienta que guarda diferencias. Blob, árbol, commit, etiqueta; las tres zonas; el hash que lo verifica todo
2. Operaciones básicas El ciclo editar → preparar → confirmar, y que git add -p y git diff --staged convierten confirmar en un acto deliberado
3. Ramas y fusión Que una rama es un puntero móvil y que fusionar es encontrar el antepasado común. Que un conflicto no es un error: es una decisión que Git no puede tomar por ti
4. Remotos Que no hay servidor central salvo por acuerdo, y que fetch y pull son operaciones distintas. Autenticación, refspecs, ramas de seguimiento
5. Operaciones avanzadas Que la historia se puede reescribir antes de publicarla, y la regla de oro: nunca después. rebase, cherry-pick, stash, etiquetas anotadas con SemVer, revert
6. Herramientas Que Git trae respuestas para "¿cuándo se rompió?" (bisect) y "¿por qué es así?" (blame + el mensaje). Hooks, alias, submódulos, worktree
7. Colaboración Que el proceso importa tanto como los comandos: propuestas, revisión, flujos comparados e integración continua. Y que el flujo se elige según el equipo
8. Buenas prácticas El oficio: mensajes que explican el porqué, historial legible, .gitignore y .gitattributes, secretos fuera, y medir el rendimiento antes de tocar
9. Solución de problemas Que en Git casi nada se pierde de verdad. reset, divergencias, reflog, fsck --lost-found, bundle, y un método de depuración con GIT_TRACE y fontanería
10. Mundo real Que Git es una pieza de un ecosistema: casos reales, integración con herramientas, LFS, escalado, DevOps. Y criterio para elegir

Las cuatro ideas que lo sostienen todo

Si dentro de cinco años solo recuerdas cuatro cosas de este curso, que sean estas:

1. El hash del contenido. Todo objeto se identifica por el hash de lo que contiene. De ahí salen la inmutabilidad, la verificación de integridad, la deduplicación automática y el hecho de que reescribir historia siempre cree objetos nuevos en lugar de modificar los existentes.

2. El DAG de commits. Cada commit apunta a sus padres. Ramas, fusiones, antepasados comunes, bisect, rebase, divergencias: todo es este grafo mirado desde ángulos distintos. Cuando algo no cuadre, dibuja el grafo.

3. Las tres zonas. Copia de trabajo, índice y repositorio. Saber en cuál de las tres está cada cosa explica el 90 % de las confusiones. Y recuerda la asimetría que descubrimos en la lección 10-02: lo que nunca pasó por git add es lo único verdaderamente irrecuperable.

4. La regla de oro. Reescribe la historia que no has publicado; no reescribas la que otros ya tienen. Y cuando haya que hacerlo —una migración a LFS, una limpieza de secretos—, avisa, coordina y usa --force-with-lease.

Y una quinta, que es de actitud

Casi nada se pierde de verdad. Lo que se pierde es la calma. Ante un lío, la secuencia es siempre la misma: git status para saber dónde estás, git log --oneline --graph para ver la forma del problema, git reflog para saber por dónde has pasado. Y no ejecutar nada destructivo hasta entender la situación.

gestor-tareas fue solo la excusa

Ana Ferrer en Ubuntu, Bruno Salas en macOS, Carla Vidal en Windows 11 y Diego Rueda con su fork llevan diez módulos resolviendo problemas: el submódulo componentes-ui, los finales de línea que rompían las diferencias, la contraseña que llevaba meses en el historial, el reset --hard a las once de la noche, los tickets GT-NNN que atan cada commit a su porqué.

Nada de eso importa. gestor-tareas no existe. Lo que existe es el modelo mental que te has construido resolviendo sus problemas.

El día que llegues a un repositorio con veinte años de historia, cincuenta mil ficheros, un flujo que no habías visto y una convención de commits que te parecerá rara, no vas a reconocer nada. Pero vas a saber preguntar:

  • ¿Qué forma tiene este grafo? → git log --oneline --graph --all
  • ¿Cómo trabaja este equipo? → git log --format=%s -40, git rev-list --count --merges HEAD
  • ¿Quién integra de verdad? → git log --format='%cn' -300 | sort | uniq -c | sort -rn
  • ¿Por qué esta línea es así? → git blame -w -M y el mensaje del commit
  • ¿Cuándo se rompió? → git bisect
  • ¿Dónde estoy y cómo he llegado? → git status, git reflog
  • ¿Qué está desplegado? → git merge-base --is-ancestor, git describe

Esas preguntas funcionan en cualquier repositorio del mundo. Eso es lo que te llevas.

Errores Comunes y Consejos

Error 1: adoptar lo experimental en producción. SHA-256 sin interoperabilidad convierte tu repositorio en una isla. reftable puede romper herramientas externas. Distingue siempre entre "existe" y "es adoptable".

Error 2: creer que el modelo de datos va a cambiar. No va a cambiar. La función de hash puede cambiar; el direccionamiento por contenido, el DAG y las tres zonas, no. No dejes de aprenderlos "por si acaso quedan obsoletos".

Error 3: quedarse en checkout por costumbre. switch y restore son más claros y más seguros, y separan operaciones inofensivas de operaciones destructivas. Cámbiate; y aprende a leer checkout porque está en toda la documentación existente.

Error 4: aceptar mensajes de commit generados sin revisarlos. El asistente ve el diff, no tu cabeza. El porqué solo lo puedes escribir tú, y es lo único que aporta valor dentro de seis meses.

Error 5: ejecutar comandos sugeridos sin entenderlos. Especialmente reset --hard, push --force, clean -fd y filter-repo. Cuanto menos reversible es una operación, más exige entenderla antes.

Error 6: no leer nunca las notas de versión. Es donde está la opción que llevas dos años echando de menos.

Consejo 1: haz git help -g ahora mismo, y lee gitcore-tutorial. Después de este curso te resultará legible, y es lo que consolida el modelo mental definitivamente.

Consejo 2: ten un repositorio de laboratorio. /tmp/laboratorio, desechable. Prueba en él todo lo que no tengas claro antes de hacerlo en un repositorio real. Cuesta cinco segundos crearlo y ha salvado muchas tardes.

Consejo 3: activa hoy lo que no tiene inconvenientes.

git config --global core.commitGraph true
git config --global fetch.writeCommitGraph true
git config --global merge.conflictStyle zdiff3
git config --global rerere.enabled true
git config --global push.default simple
git config --global pull.ff only

Consejo 4: cuando encuentres un comando nuevo, pregunta lo mismo siempre. ¿Qué objetos crea? ¿Qué referencias mueve? ¿Es reversible? Con esas tres respuestas entiendes cualquier comando de Git.

Consejo 5: enseña lo que sabes. Es el mejor filtro para descubrir lo que creías entender y no entendías.

Ejercicios

Ejercicio 1: separar lo maduro de lo experimental

Un compañero vuelve de una conferencia y propone estos cambios para gestor-tareas. Para cada uno, di si lo adoptarías hoy, y por qué:

A. Migrar el repositorio a SHA-256 "porque SHA-1 está roto". B. Cambiar el formato de referencias a reftable "porque es más rápido". C. Activar el commit-graph en todos los repositorios del equipo. D. Clonar siempre con --filter=blob:none. E. Prohibir git checkout en el equipo y usar solo switch y restore. F. Activar sparse-checkout para que cada persona vea solo su área.

Ejercicio 2: construir un commit con fontanería

En un repositorio de pruebas vacío, crea sin usar git add ni git commit:

  1. Un fichero notas.txt con el contenido Aprendiendo la fontanería.
  2. Un segundo fichero README.md con el contenido # Laboratorio.
  3. Un commit que contenga ambos, con el mensaje Commit construido a mano.
  4. Una rama main que apunte a ese commit.

Después, verifica con comandos de porcelana que Git lo ve como un repositorio normal, y explica qué representa cada hash intermedio.

Ejercicio 3: llegar nuevo a un repositorio desconocido

Te incorporas a un proyecto que no conoces: 340.000 commits, 12 años de historia, 28.000 ficheros, 4,2 GB. Nadie tiene tiempo de explicarte nada y tu primera tarea es corregir un error en un fichero que no sabes cuál es.

Escribe la secuencia de comandos que ejecutarías en tu primer día, explicando qué pregunta responde cada uno y en qué orden lo harías.

Soluciones

Solución 1

A. SHA-256 — NO.

El razonamiento de partida es correcto (SHA-1 tiene colisiones demostradas) pero la conclusión no lo es hoy:

  • No hay interoperabilidad: un repositorio SHA-256 no puede intercambiar objetos con git.ejemplo.es, ni con las plataformas, ni con el fork de Diego. gestor-tareas quedaría aislado.
  • Git ya incorpora detección de colisiones, que mitiga el ataque conocido.
  • El riesgo real para un repositorio de aplicación es bajo: fabricar una colisión exige construir ambos contenidos a propósito.

Qué haría: conocerlo, crear un repositorio de pruebas con --object-format=sha256 para ver el modelo, y esperar a que exista interoperabilidad. Si el equipo quiere reforzar la integridad hoy, la respuesta útil es firmar los commits y las etiquetas (08-05), que es efectivo y compatible con todo.

B. reftable — NO.

gestor-tareas tiene unas pocas decenas de referencias. reftable resuelve problemas que aparecen con decenas de miles: coste de escritura, concurrencia, límites del sistema de ficheros. Aquí no aportaría nada medible.

Y tiene un coste: herramientas externas que lean .git/refs/ directamente pueden fallar, y no es el formato por defecto.

Qué haría: conocerlo, y saber que si algún día trabajo en un repositorio con muchísimas referencias, existe. La pregunta correcta es la de la lección 10-04: ¿he medido que esto duele?

C. commit-graph — SÍ, sin dudarlo.

Es la recomendación más fácil de todas:

  • Es una caché derivada y regenerable: si se borra, no se pierde nada.
  • No cambia el formato de almacenamiento ni el comportamiento de ningún comando.
  • Mejora log, blame y las consultas de alcanzabilidad, y en historiales grandes muchísimo.
  • No tiene ningún inconveniente conocido.
git config --global core.commitGraph true
git config --global fetch.writeCommitGraph true

En gestor-tareas, con 412 commits, la mejora será imperceptible. Pero es un buen hábito global y se nota en cuanto trabajes en algo grande.

D. --filter=blob:none siempre — DEPENDE, con matices.

En gestor-tareas (2 MB): innecesario. Y tiene un coste real: introduce dependencia de la red en operaciones que ahora son locales. git log -p de historia antigua sin conexión fallaría.

En repositorios grandes: sí, claramente.

En CI: sí, casi siempre, combinado con fetch-depth: 0. Es la configuración recomendada de la 10-04.

Qué haría: no como norma universal, sino como regla condicionada: "en repositorios de más de 1 GB o en CI, clon parcial". Y documentarlo.

E. switch y restore — SÍ, con un matiz.

Adoptarlos, sí:

  • git switch separa cambiar de rama de descartar ficheros, y esa separación evita pérdidas de trabajo reales.
  • Exige --detach explícito para el HEAD desprendido.
  • git restore --staged sustituye un uso confuso de reset.
  • Están consolidados.

El matiz está en "prohibir":

  • La documentación existente, las respuestas de internet y los guiones antiguos están llenos de checkout. Prohibirlo no hace que desaparezca del mundo.
  • Todo el mundo debe saber leerlo, y entender que git checkout -- fichero es destructivo.
  • Los guiones existentes que funcionan no hay que reescribirlos por gusto.

Qué haría: recomendarlos como opción por defecto en el CONTRIBUTING.md y usarlos en los ejemplos internos, sin prohibir nada.

F. sparse-checkout — NO, rotundamente.

Es el peor de los seis y por dos motivos independientes:

  1. gestor-tareas tiene nueve ficheros. No hay nada que dispersar.
  2. La motivación es equivocada. "Que cada uno vea solo su área" es un objetivo organizativo, no de rendimiento. sparse-checkout es una optimización de rendimiento, no un mecanismo de control de acceso: cualquiera puede ampliar su cono con un comando, y todo el historial está en su disco. Usarlo para eso da una falsa sensación de separación.
  3. Y tendría un coste real: ficheros que "no existen", búsquedas que no encuentran cosas, y confusión permanente.

Qué haría: explicar que si de verdad hace falta separar accesos, la respuesta es repositorios separados con permisos (10-04), no una optimización de rendimiento mal usada.

Patrón de las seis respuestas: las que adoptaría (C, E, y D condicionada) no tienen inconvenientes o resuelven un problema medido. Las que no (A, B, F) son experimentales, resuelven un problema que no tengo, o se están usando para algo que no son. Es exactamente la tabla de madurez del apartado 7.

Solución 2

mkdir /tmp/laboratorio && cd /tmp/laboratorio && git init

Paso 1: crear los blobs.

printf 'Aprendiendo la fontanería\n' | git hash-object -w --stdin
c4a8f2e91b6d3a70f5c2e8b14d9a6f0c3b7e2d51
printf '# Laboratorio\n' | git hash-object -w --stdin
7f2b9e4c1a8d5f0b3e6c9a2d5f8b1e4c7a0d3f6b

-w escribe el objeto en la base de datos; sin él solo calcularía el hash. Cada hash es el SHA-1 de ese contenido concreto (precedido de una cabecera con el tipo y el tamaño). Si dos personas crean el mismo contenido en cualquier repositorio del mundo, obtienen el mismo hash: eso es el direccionamiento por contenido.

Comprobar que están ahí:

git cat-file -t c4a8f2e9    # blob
git cat-file -s c4a8f2e9    # tamaño en bytes
git cat-file -p c4a8f2e9    # contenido

Paso 2: construir el índice.

git update-index --add --cacheinfo 100644,c4a8f2e91b6d3a70f5c2e8b14d9a6f0c3b7e2d51,notas.txt
git update-index --add --cacheinfo 100644,7f2b9e4c1a8d5f0b3e6c9a2d5f8b1e4c7a0d3f6b,README.md

git ls-files --stage
100644 c4a8f2e91b6d3a70f5c2e8b14d9a6f0c3b7e2d51 0	README.md
100644 7f2b9e4c1a8d5f0b3e6c9a2d5f8b1e4c7a0d3f6b 0	notas.txt

100644 es el modo: fichero normal no ejecutable (100755 sería ejecutable, 040000 un directorio, 120000 un enlace simbólico). El 0 es la etapa del índice: 0 significa "sin conflicto" (las etapas 1, 2 y 3 son la base, lo nuestro y lo suyo durante una fusión, lección 03-05).

Fíjate en un detalle que sorprende: los ficheros no están en el disco. El índice los declara, pero la copia de trabajo está vacía. Es la prueba más contundente de que el índice es una zona independiente, no un reflejo del disco.

Paso 3: convertir el índice en un árbol.

git write-tree
5e9c2f8b4a1d7e0c3f6b9a2d5e8c1f4b7a0d3e6c
git cat-file -p 5e9c2f8b
100644 blob 7f2b9e4c1a8d5f0b3e6c9a2d5f8b1e4c7a0d3f6b	README.md
100644 blob c4a8f2e91b6d3a70f5c2e8b14d9a6f0c3b7e2d51	notas.txt

El árbol es un directorio: asocia nombres a hashes de objetos. Su propio hash resume todo su contenido, y por eso comparar dos árboles por su hash basta para saber si son idénticos. Es exactamente la propiedad que hace posible el índice disperso de la lección 10-04.

Paso 4: crear el commit.

echo "Commit construido a mano" | git commit-tree 5e9c2f8b4a1d7e0c3f6b9a2d5e8c1f4b7a0d3e6c
b3f7a0c5e2d8b1f4a7c0e3d6b9f2a5c8e1d4b7f0
git cat-file -p b3f7a0c5
tree 5e9c2f8b4a1d7e0c3f6b9a2d5e8c1f4b7a0d3e6c
author Ana Ferrer <[email protected]> 1785926400 +0200
committer Ana Ferrer <[email protected]> 1785926400 +0200

Commit construido a mano

El commit apunta a un árbol (la foto completa del proyecto) y añade metadatos. No tiene línea parent porque es el commit raíz; si le hubiéramos pasado -p <hash>, la tendría. Esa línea parent es todo el DAG.

Paso 5: apuntar la rama.

git update-ref refs/heads/main b3f7a0c5e2d8b1f4a7c0e3d6b9f2a5c8e1d4b7f0
git symbolic-ref HEAD refs/heads/main

Paso 6: verificar con porcelana.

git log --stat
commit b3f7a0c5e2d8b1f4a7c0e3d6b9f2a5c8e1d4b7f0 (HEAD -> main)
Author: Ana Ferrer <[email protected]>
Date:   Sat Aug 1 12:00:00 2026 +0200

    Commit construido a mano

 README.md  | 1 +
 notas.txt  | 1 +
 2 files changed, 2 insertions(+)
git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	deleted:    README.md
	deleted:    notas.txt

Ese deleted es correcto y muy instructivo: los ficheros existen en el commit y en el índice, pero no en el disco, porque nunca los escribimos allí. Se materializan con:

git checkout .      # o: git restore .
ls
cat notas.txt

Qué representa cada hash:

Hash Objeto Qué es
c4a8f2e9 blob El contenido de notas.txt, sin nombre ni permisos
7f2b9e4c blob El contenido de README.md
5e9c2f8b tree El directorio raíz: nombres + modos + hashes de los blobs
b3f7a0c5 commit Un árbol + autor + fecha + mensaje + padres
refs/heads/main referencia Un fichero (o entrada reftable) con el hash del commit

Eso es Git entero. Cuatro tipos de objeto y una referencia. Todo lo demás —ramas, fusiones, rebase, stash, bisect, LFS, GitOps— se construye encima de esto.

Solución 3

El orden importa: primero entender la forma del repositorio, luego cómo trabaja el equipo, y solo al final buscar el fichero.

# ========== FASE 1: CLONAR SIN SUFRIR (10-04) ==========
git clone --filter=blob:none https://git.ejemplo.es/proyecto.git
cd proyecto

git config core.commitGraph true
git config core.fsmonitor true
git config core.untrackedCache true
git commit-graph write --reachable --changed-paths

Por qué: 4,2 GB con clon completo son muchos minutos. El clon parcial trae el grafo completo —que es lo que necesito para investigar— con una fracción de la descarga. Y el commit-graph con --changed-paths es imprescindible: sin él, git log sobre un fichero en 340.000 commits será insufrible.

# ========== FASE 2: LA FORMA DEL REPOSITORIO ==========
git rev-list --count --all           # ¿cuánto historial?
git ls-files | wc -l                 # ¿cuántos ficheros?
git count-objects -vH                # ¿cuánto pesa y por qué?
git log -1 --format=%ci              # ¿está vivo?
git log --reverse --format=%ci | head -1   # ¿desde cuándo?

# ¿Qué directorios de primer nivel hay?
git ls-tree --name-only HEAD

Por qué: estos seis comandos dan en un minuto el mapa que nadie tiene tiempo de explicarte: escala, estructura y si el proyecto está activo.

# ========== FASE 3: CÓMO TRABAJA EL EQUIPO ==========
# ¿Historial lineal o con merges? (lección 10-01)
git log --oneline --graph -40
git rev-list --count --merges HEAD
git rev-list --count HEAD

# ¿Qué convención de mensajes usan?
git log --format=%s -50

# ¿Quién escribe y quién integra?
git log --format='%an' -500 | sort | uniq -c | sort -rn | head
git log --format='%cn' -500 | sort | uniq -c | sort -rn | head

# ¿Hay convenciones documentadas?
ls CONTRIBUTING.md README.md .editorconfig .gitattributes 2>/dev/null
cat CONTRIBUTING.md 2>/dev/null

# ¿Qué ramas hay vivas?
git branch -r --sort=-committerdate --format='%(refname:short) %(committerdate:relative)' | head -20

# ¿Cómo versionan?
git tag --sort=-v:refname | head -10
git describe --tags

Por qué: antes de tocar nada hay que saber cómo se hacen las cosas aquí. Una propuesta que ignora la convención de mensajes o el flujo de ramas se rechaza, y aprenderlo del historial es más rápido y más fiable que preguntar.

La proporción de merges y la diferencia autor/committer revelan el modelo de trabajo (lección 10-01). El CONTRIBUTING.md, si existe, se lee entero.

# ========== FASE 4: PREPARAR EL ENTORNO ==========
# ¿Hay LFS? (10-03)
cat .gitattributes 2>/dev/null | grep lfs
git lfs install && git lfs ls-files | head

# ¿Hay submódulos? (06-05)
cat .gitmodules 2>/dev/null

# ¿Cómo se compila y se prueba?
cat README.md | head -60
ls Makefile package.json Containerfile .github/workflows/ 2>/dev/null

# Alias útiles para la investigación (06-04)
git config alias.hist "log --graph --oneline --decorate"
git config alias.quien "log --format='%h %an %ar %s'"
# ========== FASE 5: ENCONTRAR EL FICHERO DEL ERROR ==========
# Por el texto del mensaje de error que ve el usuario
git grep -n "El importe no puede ser negativo"

# Si el mensaje está en ficheros de traducción, buscar su clave
git grep -n "error.importe.negativo"

# Por nombre aproximado
git ls-files | grep -i importe

# ¿Se ha tocado esa zona últimamente?
git log --oneline -20 -- src/facturacion/

Por qué: git grep busca en el contenido versionado y es mucho más rápido que las herramientas del sistema porque solo mira lo que está en el índice, ignorando artefactos de compilación y ficheros ignorados.

# ========== FASE 6: ENTENDER ANTES DE CAMBIAR (06-03, 09-06) ==========
# ¿Quién escribió esa línea y por qué? Ignorando espaciado y movimientos
git blame -w -M -C -L 120,145 src/facturacion/validador.js

# El mensaje del commit: la intención
git log -1 --format=%B <hash-que-devuelve-blame>

# La historia completa de esa función
git log -L :validarImporte:src/facturacion/validador.js

# ¿Hay commits de reformateo que ensucian el blame?
ls .git-blame-ignore-revs 2>/dev/null

Por qué: este es el paso que separa una corrección de un bucle, y es la lección del ejercicio final del módulo 9. Si el comportamiento es intencionado y está justificado por escrito, cambiarlo sin más rompe algo que alguien pidió, y en dos semanas volverá el mismo ticket en sentido contrario.

# ========== FASE 7: SI EL ERROR ES UNA REGRESIÓN (06-02) ==========
git bisect start
git bisect bad HEAD
git bisect good v3.8.0
git bisect run ./pruebas/reproducir.sh
git bisect reset
# ========== FASE 8: TRABAJAR SEGÚN LAS CONVENCIONES ==========
git switch -c corregir-validacion-importe origin/main
# ... corregir, con una prueba de regresión ...
git add -p
git commit    # con el formato de mensaje que observé en la fase 3
git diff origin/main...HEAD   # revisarme a mí mismo antes de proponer
git push -u origin corregir-validacion-importe

Resumen del método: clonar sin sufrir, entender la forma, entender el proceso, preparar el entorno, encontrar el sitio, entender la intención antes de cambiar nada, y trabajar según las convenciones observadas.

Ninguno de esos comandos es específico de este proyecto. Funcionan en cualquier repositorio del mundo, y esa es exactamente la capacidad que da este curso.

Conclusión

Lo que se mueve, lo que no, y qué hacer

Ya existe y puedes usarlo hoy: switch y restore en lugar de checkout; el commit-graph y git maintenance; los clones parciales y el índice disperso; fsmonitor y untrackedCache; y Git LFS para los binarios. Todo eso está maduro, y lo que no tiene inconvenientes conviene activarlo ya.

Existe pero aún no es adoptable: SHA-256, que resuelve la debilidad conocida de SHA-1 pero no interopera con el ecosistema; y reftable, que sustituye refs/ y packed-refs por un formato binario mucho mejor a gran escala pero que aún no es el predeterminado. Conócelos; no los adoptes todavía.

Es tendencia, sin fechas: mejor experiencia para monorepos, servidores Git especializados que el cliente no ve, herramientas construidas encima de Git en lugar de sustituirlo —el patrón dominante de veinte años—, y mensajes de error cada vez más útiles.

Los asistentes de IA ayudan a redactar, a recordar y a explorar. No eximen de entender el cambio: el porqué no está en el diff, y las operaciones destructivas exigen comprensión antes que sugerencia. En un mundo donde escribir código cuesta menos, entender el historial y saber recuperarse de un error valen más, no menos.

Y lo que no va a cambiar es lo que aprendiste en el módulo 1: el modelo de datos direccionable por contenido, el DAG de commits, la naturaleza distribuida y las tres zonas. La función de hash puede cambiar; las ideas, no. Por eso este curso no caduca.

El final del curso

Empezaste sin saber qué es un repositorio.

Ahora entiendes que Git es una base de datos de objetos inmutables identificados por el hash de su contenido, organizados en un grafo dirigido acíclico, replicada completa en cada clon. Sabes construir historia con intención, ramificar y fusionar, colaborar con otros a través de un servidor, reescribir lo que aún no has publicado —y solo eso—, investigar cuándo y por qué se rompió algo, trabajar con un proceso de revisión e integración continua, escribir mensajes que sirvan dentro de dos años, mantener los secretos fuera y el rendimiento bajo control, salir de cualquier lío usando el reflog y la fontanería, y operar Git en producción con etiquetas que despliegan y artefactos identificados por el hash del commit.

Sobre todo, tienes un modelo mental con el que razonar sobre situaciones que este curso no ha cubierto. Cuando te encuentres un comando desconocido, sabrás preguntar qué objetos crea y qué referencias mueve. Cuando algo no cuadre, sabrás dibujar el grafo. Cuando alguien entre en pánico, sabrás que casi nada se pierde de verdad.

gestor-tareas fue solo la excusa. Ana, Bruno, Carla y Diego no existen, y sus tickets GT-NNN tampoco. Lo que existe es lo que te queda después de haber resuelto sus problemas: el submódulo que se desincronizó, los finales de línea entre tres sistemas operativos, la contraseña en el historial, el reset --hard de las once de la noche, la migración a LFS que reescribió dos años de commits.

Ese es el equipaje. Es transferible a cualquier repositorio, a cualquier empresa y a cualquier flujo de trabajo que te encuentres, hoy y dentro de diez años.

Ahora ve a un repositorio de verdad —el tuyo, el de tu equipo, o el de un proyecto abierto que admires— y úsalo. Ejecuta git log --oneline --graph --all y mira la forma que tiene. Lee git help gitcore-tutorial. Crea un repositorio de laboratorio en /tmp y rompe cosas a propósito para practicar el rescate.

Y cuando un compañero pierda un commit y se le ponga cara de pánico, siéntate a su lado, escribe git reflog, y explícale por qué nada se ha perdido.

Ahí es donde se comprueba que has aprendido Git.

Dominando Git: De Principiante a Avanzado

Módulo 1: Introducción a Git

Módulo 2: Operaciones Básicas de Git

Módulo 3: Ramas y Fusión

Módulo 4: Trabajando con Repositorios Remotos

Módulo 5: Operaciones Avanzadas de Git

Módulo 6: Herramientas y Técnicas de Git

Módulo 7: Estrategias de Colaboración y Flujo de Trabajo

Módulo 8: Mejores Prácticas y Consejos de Git

Módulo 9: Solución de Problemas y Depuración

Módulo 10: Git en el Mundo Real

© Copyright 2026. Todos los derechos reservados