gen[IA]ll Tutor
DEVOPS-FUNDAMENTALS

Git Concepts

Actualizado en octubre de 2026 · 24 min de lectura

Conceptos de Git

Resumen

Git es un sistema de control de versiones distribuido que registra la evolución de un proyecto mediante instantáneas identificadas por un hash. Cada clon contiene el historial completo, por lo que crear ramas, hacer commits o consultar versiones funciona sin conexión; los remotos se usan para compartir y sincronizar.

Esta guía es material independiente para el topic git-concepts de LFCA; no tiene afiliación con Linux Foundation.

Objetivos de aprendizaje

  • Explicar el modelo distribuido y las tres áreas.
  • Relacionar commits, ramas y HEAD con la historia.
  • Distinguir merge de rebase y elegir cuándo usarlos.
  • Sincronizar el repositorio local con el remoto sin perder trabajo.
  • Aplicar ramas cortas y commits legibles.

Prerrequisitos y conexiones

Conviene haber leído devops-basics y saber usar una terminal. Git se relaciona con containers, porque las imágenes y los manifiestos suelen versionarse en repositorios, y con la automatización de pipelines, que se dispara con cambios en ramas o etiquetas.

Modelo distribuido y las tres áreas

En un sistema centralizado, el historial vive en un servidor y cada operación depende de la red. En Git, git clone copia objetos y referencias, de modo que cada clon es un repositorio completo. Esto permite trabajar sin conexión, comparar versiones localmente y recuperar la historia desde otra copia. El remoto coordina equipos, pero no es el único lugar donde existe la historia.

Git organiza el trabajo en tres áreas. El directorio de trabajo contiene los archivos editados. El área de preparación (staging area o index) contiene la instantánea del próximo commit. El repositorio local (.git) almacena commits y objetos. Un archivo puede estar sin seguimiento, modificado, preparado o confirmado; git status muestra esa transición.

El flujo básico es editar, preparar con git add y confirmar con git commit. Preparar permite elegir qué cambios entran. git diff muestra cambios no preparados y git diff --staged lo que se confirmará.

Anatomía de los objetos Git

Git almacena cuatro tipos de objetos: blobs con el contenido de un archivo sin su nombre, árboles que asocian rutas con blobs y subtrees, commits que apuntan a un árbol raíz y a uno o más padres, y etiquetas anotadas con metadatos. El nombre de cada objeto es una función de resumen de su contenido, de modo que dos archivos idénticos comparten blob y dos commits con el mismo árbol, padres y mensaje comparten identificador.

Ese diseño explica varios comportamientos prácticos. Modificar un archivo sin cambios reales no añade objetos, porque el blob resultante ya existía. Cambiar un archivo regenera los objetos de la ruta afectada, no el repositorio completo. Y el historial es un grafo acíclico: un commit puede tener dos padres cuando une ramas, y ninguna operación normal borra referencias que otro commit necesite para ser alcanzable.

Las referencias son nombres movibles sobre commits: ramas, etiquetas y HEAD. Cuando Git elimina objetos sin referencias durante la limpieza automática, descarta lo que considera inalcanzable; de ahí la utilidad del registro de movimientos de referencias (reflog) como red de seguridad local para recuperar posiciones anteriores de HEAD y de ramas.

Commits, ramas y HEAD

Un commit es una instantánea inmutable del árbol de archivos, con metadatos y referencias a sus padres. Su identificador se calcula a partir del contenido, por lo que cambiarlo produce otro hash. El historial es un grafo: la mayoría de commits tienen un padre y los de merge tienen dos.

Una rama es un puntero móvil a un commit. Crear una rama no copia archivos: crea otra referencia. HEAD indica la rama actual y dónde se añadirá el próximo commit. Al confirmar, la rama avanza. Si se hace checkout de un commit concreto, HEAD queda separado (detached HEAD); los commits nuevos no pertenecen a una rama hasta crearla.

Las ramas aíslan trabajo sin afectar a main. Los nombres son convenciones: feature/login o fix/timeout. Borrar una rama elimina el puntero, no necesariamente los commits, que pueden seguir alcanzables desde otra referencia.

Deshacer sin perder trabajo

Cuatro herramientas cubren necesidades distintas. git restore descarta o reemplaza cambios en el directorio de trabajo; git restore --staged devuelve al área de preparación un archivo ya añadido, sin tocar el contenido. git reset mueve la rama y puede ajustar el índice y el directorio: el modo --soft conserva índice y trabajo, el predeterminado actualiza el índice y deja el trabajo intacto, y --hard descarta ambos. git revert crea un commit nuevo que aplica el efecto inverso, por lo que es seguro en ramas compartidas.

git stash aparta cambios confirmados o no en una pila temporal para limpiar el directorio y reutilizarlos después con git stash pop. Es cómodo para cambiar de rama, pero olvidar un stash es habitual: conviene revisar la lista antes de descartar nada y confirmar que el trabajo reaparece tras el pop.

El registro de referencias guarda los últimos movimientos de HEAD, ramas y etiquetas. Tras un git reset --hard precipitado, la recuperación consiste en localizar el commit anterior con git reflog, crear una rama en ese punto y continuar desde ahí. La misma red de seguridad permite rescatar commits hechos en estado separado (detached HEAD) que quedaron sin rama.

Integración: merge y rebase

git merge integra el historial de otra rama en la actual. Si la rama actual no tiene commits nuevos, el merge es fast-forward: el puntero avanza sin commit de merge. Si ambas divergieron, Git busca un ancestro común y hace un merge de tres vías; sin conflictos, crea un commit con dos padres. Los conflictos aparecen cuando ambas partes modificaron las mismas líneas; se resuelven editando los marcadores, preparando el resultado y completando el merge.

git rebase reaplica los commits de una rama sobre otra base. Git toma los commits propios y los vuelve a crear sobre la nueva base. El resultado es lineal, pero los commits reescritos tienen hashes nuevos. Los conflictos se resuelven por commit con git add y git rebase --continue, o se cancelan con git rebase --abort.

Merge conserva la historia real y muestra dónde se unieron las ramas; rebase produce una historia más limpia pero altera commits existentes. No conviene reescribir commits que otros ya usan como base; si la rama es compartida, hay que coordinar.

Estrategias de integración comparadas

Merge, rebase y squash responden a preguntas distintas sobre el historial.

CriterioMergeRebaseSquash
Forma del historialConserva bifurcaciones y commits de uniónLinealiza reescribiendo commits propiosUn único commit en la rama destino
Integridad de identificadoresLos commits originales conservan su hashLos commits reaplicados reciben hashes nuevosEl detalle interno de la rama desaparece
Uso en ramas compartidasSeguro sin coordinación especialRequiere coordinar con quienes ya tienen los commitsSeguro: no reescribe lo publicado
ConflictosSe resuelven una vez al completar el mergePueden reaparecer en cada commit reaplicadoSe resuelven una sola vez al combinar
Reversión posteriorEl commit de merge documenta la uniónLa linealidad facilita revertir por rangosDifícil revertir partes aisladas de la rama

La elección depende del contrato del equipo. Un flujo de solicitudes de cambio que integra con squash mantiene la rama principal con cambios acotados y una historia legible, a costa de perder los pasos intermedios. Un merge explícito conserva el contexto de la rama, útil cuando la historia interna explica decisiones. El rebase local limpia una rama privada antes de publicarla; una vez compartida, la reescritura deja a los demás con bases divergentes.

Conflictos en la práctica

Durante un conflicto, el índice conserva las tres versiones del archivo: la de la rama actual, la de la rama integrada y la base común. git status lista los archivos sin fusionar y el contenido muestra los marcadores <<<<<<<, ======= y >>>>>>> que separan ambos lados. Resolver significa escribir el resultado correcto —no necesariamente elegir uno de los dos— y confirmarlo con git add.

Hay conflictos de contenido y conflictos de eliminación: una rama modifica un archivo que otra eliminó. La decisión —mantener o borrar— es del equipo y debe reflejarse en el resultado final antes de completar la integración. Tras resolver, git merge --continue o git rebase --continue avanzan; git merge --abort y git rebase --abort restauran el estado previo si la unión ya no conviene.

La validación posterior al conflicto es la parte más olvidada: el archivo puede combinar sintácticamente dos ramas y romper la lógica. Ejecutar las pruebas afectadas antes de confirmar evita que el problema reaparezca como fallo en producción. Registrar decisiones repetidas en la configuración del repositorio reduce conflictos similares en el futuro.

Repositorios remotos y colaboración

Un remoto es un nombre asociado a una URL, como origin. git remote -v lista los remotos. Al clonar, Git crea referencias de seguimiento remoto como origin/main, que reflejan la última información descargada; no son la rama local main.

git fetch descarga objetos y actualiza las referencias remotas, pero no modifica la rama local. git pull equivale a fetch más una integración, que puede ser merge o rebase según la configuración. Revisar lo descargado antes de integrarlo evita sorpresas. git push envía commits y actualiza la rama remota; si el remoto avanzó y el push no es fast-forward, Git lo rechaza para no sobrescribir trabajo ajeno.

Una rama local rastrea una remota con git push -u origin feature/login. git push --force reescribe la rama remota y puede borrar commits ajenos; --force-with-lease comprueba que el remoto no haya cambiado, pero sigue exigiendo coordinación.

Git en pipelines y automatización

Los ganchos locales (hooks) ejecutan scripts ante eventos como preparar un commit, validar su mensaje o publicar. Como viven en el repositorio local de cada persona, no garantizan nada en el servidor: complementan, no sustituyen, las comprobaciones de la pipeline. Un gancho que falla detiene la operación local y ahorra un ciclo completo de integración.

Las políticas de protección viven en el remoto: prohibir borrados, exigir revisión o bloquear el avance si las comprobaciones no pasan. Combinadas con ganchos locales forman dos capas: una evita errores al escribir, la otra evita integrar sin validar.

Las pipelines suelen trabajar con clonados reducidos para acelerar el arranque: un git clone --depth 1 trae solo el último commit, suficiente para compilar una versión concreta, pero insuficiente para consultas de historial o bisect. Los lanzamientos se disparan frecuentemente con etiquetas: crear la etiqueta en el commit correcto produce una versión reproducible y evita que el nombre de una rama desplazada cambie qué se publica.

Flujos de trabajo y buenas prácticas

Un flujo común crea una rama por cambio, confirma unidades lógicas y abre una pull request. El equipo integra en main tras las pruebas y elimina la rama. El trunk-based development reduce la vida de las ramas y exige pruebas automatizadas y, a veces, banderas de funcionalidad.

Los mensajes deben explicar el qué y el porqué. Un .gitignore evita artefactos, credenciales y archivos locales. Las etiquetas marcan versiones relevantes. Los secretos nunca deben confirmarse: si se hace por error, hay que rotarlos.

Calidad de los commits

Un commit legible se revisa en minutos: toca un tema, compila, incluye su prueba y explica el porqué en el mensaje. Los mensajes convencionales separan un resumen corto, redactado en imperativo, del cuerpo que describe el contexto y la decisión; el resumen permite escanear el historial y el cuerpo responde preguntas futuras sin leer el diff completo.

Separar cambios estructurales —renombrar archivos, mover código— de cambios de comportamiento facilita la revisión y permite revertir uno sin tocar el otro. Cuando una corrección y una refactorización llegan en el mismo commit, el revisor no puede distinguir riesgo de ruido.

Las ramas cortas reducen el conflicto con la base y acortan la espera de revisión. Publicar temprano, aunque el cambio esté incompleto y lleve marcas de progreso explícitas, suele mejorar la integración que acumular días de trabajo en una sola rama. El coste es coordinar qué partes son reversibles sin afectar a usuarios.

Etiquetas y versionado

Git distingue etiqueta ligera —solo un puntero a un commit— de etiqueta anotada, que almacena etiquetador, fecha, mensaje y, opcionalmente, una firma. Los lanzamientos suelen usar anotadas porque el mensaje documenta el porqué de la versión y la firma permite verificar el origen. Crear una etiqueta en el commit correcto es un acto explícito: git tag -a v1.5 -m "Versión 1.5" y después git push origin v1.5 para publicarla.

Las etiquetas son inmutables en la práctica: reasignar una publicada engaña a quien ya la consumió. Si una versión contiene un defecto, se publica una siguiente en lugar de mover la anterior. Los nombres convencionales de versión comunican compatibilidad, pero eso es un acuerdo del equipo, no una regla de Git.

Para consultas, git describe encuentra la etiqueta más cercana a un commit y la usa para nombrar artefactos de compilación, lo que permite rastrear qué código produce un binario concreto. El mismo identificador aparece en el artefacto, en la etiqueta y en el commit: la trazabilidad completa del flujo de entrega.

Ejemplos prácticos

Un equipo trabaja en el inicio de sesión. El desarrollador crea la rama, prepara el archivo y confirma una unidad coherente:

git switch -c feature/login
git add src/login.js
git commit -m "Valida credenciales vacías en el inicio de sesión"

Antes de publicar, actualiza su base y reaplica sus commits:

git fetch origin
git rebase origin/main
# resolver conflictos, git add y git rebase --continue
git push -u origin feature/login

Tras la revisión, la integración en main puede conservar el contexto de la rama:

git switch main
git pull --ff-only
git merge --no-ff feature/login
git push origin main

Ejemplo práctico: localizar una regresión

Una prueba falla y la semana anterior pasaba. El equipo reconstruye el rango con bisect:

git bisect start
git bisect bad HEAD
git bisect good v1.4.0

Git entrega un commit intermedio; se ejecuta la prueba y se marca git bisect bad o git bisect good según el resultado. El proceso acota el culpable a la mitad en cada paso, y con la prueba automatizada el recorrido es rápido. Al terminar, git bisect reset vuelve a la rama original. Si alguna revisión no se puede probar, se marca como omitida para mantener el rango válido.

En otro caso, un git reset --hard borró commits locales aún no publicados. Con el registro de referencias se localiza el estado anterior:

git reflog
git switch -c recuperar HEAD@{2}

La rama recuperada contiene el trabajo; desde ahí se reorganiza en commits limpios y se publica. Ambos casos ilustran el mismo principio: el historial local conserva suficiente información para corregir errores de proceso sin recomenzar.

Errores frecuentes

  • Rebasar una rama compartida sin avisar. Los demás obtienen historias divergentes y conflictos innecesarios.
  • Usar git push --force como solución habitual. Puede sobrescribir commits ajenos; si es imprescindible, coordinar y preferir --force-with-lease.
  • Confirmar secretos o archivos generados. Aumenta el riesgo y ensucia el historial; .gitignore y revisión previa lo evitan.
  • Mezclar cambios no relacionados en un commit. Dificulta la revisión y la reversión selectiva.
  • Resolver conflictos borrando marcadores sin probar. El archivo compila o no, pero la lógica puede quedar incorrecta.
  • Reconstruir el alcance de un clonado reducido. Un --depth insuficiente rompe consultas de historial y bisect en la pipeline.
  • Retrasar la publicación de una rama larga. Cuanto más vive, más diverge de la base y más conflictos acumula al integrarla.
  • Mover una etiqueta ya publicada. Engaña a quien ya la consumió; el defecto se corrige con una versión siguiente.

Puntos clave para el examen

  • Git es distribuido: cada clon tiene el historial completo y opera localmente.
  • Las tres áreas son directorio de trabajo, staging area y repositorio local.
  • Una rama es un puntero a un commit; HEAD indica la rama actual.
  • Merge conserva la historia y puede crear un commit de dos padres; rebase reescribe commits para lograr linealidad.
  • fetch descarga sin integrar; pull integra; push publica y rechaza actualizaciones no fast-forward.
  • Los objetos de Git (blobs, árboles, commits) se nombran por su contenido, de ahí la reutilización y el reflog como red de seguridad.
  • git restore trabaja el directorio de trabajo, git reset mueve rama e índice y git revert añade un commit inverso seguro en ramas compartidas.
  • Squash combina la rama en un commit único: legible en destino, sin detalle interno.
  • Los ganchos locales complementan las políticas del remoto; ninguna sustituye a la otra.

Recursos externos

Autoevaluación

  1. ¿Qué significa que Git sea distribuido? Respuesta: Que cada clon contiene el historial completo y puede operar localmente sin depender del servidor para cada acción. Justificación: El remoto sirve para sincronizar, no para almacenar la única copia de la historia.

  2. ¿Qué diferencia hay entre el directorio de trabajo y el staging area? Respuesta: El directorio contiene los archivos editados; el staging area contiene la instantánea seleccionada para el próximo commit. Justificación: git add mueve cambios al staging y git commit confirma esa instantánea.

  3. ¿Cuándo ocurre un merge fast-forward? Respuesta: Cuando la rama destino no tiene commits nuevos y puede avanzar directamente hasta la rama integrada. Justificación: No hay divergencia, por lo que no se necesita un commit de merge.

  4. ¿Cuál es la regla de oro del rebase? Respuesta: No rebasar commits que otras personas ya usan como base, salvo que el equipo lo coordine. Justificación: Rebase crea commits nuevos y deja divergente la historia compartida.

  5. ¿Qué hace git fetch que no hace git pull? Respuesta: fetch descarga y actualiza las referencias remotas sin integrar cambios en la rama local; pull además integra. Justificación: Separar descarga e integración permite revisar los cambios antes de aplicarlos.

  6. ¿Cuándo conviene git cherry-pick en lugar de integrar una rama completa? Respuesta: Cuando solo se necesita un commit concreto de otra rama, por ejemplo un arreglo urgente, sin arrastrar el resto de sus cambios. Justificación: cherry-pick crea un commit nuevo con el mismo cambio sobre la rama actual; el historial original no se mueve y el commit pasa a tener otro identificador.