Cómo comprobar si un commit de Git es accesible desde el HEAD

GitGitBeginner
Practicar Ahora

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

En este laboratorio, aprenderás cómo determinar si un commit específico de Git es alcanzable desde el HEAD actual. Esta es una habilidad fundamental para entender la historia de tu repositorio e identificar los commits que forman parte de la línea de desarrollo activa.

Exploraremos dos métodos principales: utilizar el comando git log --ancestry-path para visualizar la línea de commits entre dos puntos, y emplear el comando git merge-base --is-ancestor para realizar una comprobación directa de la ascendencia. Practicarás estas técnicas configurando un repositorio de muestra con múltiples ramas y commits, incluyendo algunos que se hacen intencionalmente inalcanzables desde HEAD, lo que te permitirá probar y confirmar tu comprensión.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL git(("Git")) -.-> git/SetupandConfigGroup(["Setup and Config"]) git(("Git")) -.-> git/BasicOperationsGroup(["Basic Operations"]) git(("Git")) -.-> git/BranchManagementGroup(["Branch Management"]) git/SetupandConfigGroup -.-> git/init("Initialize Repo") git/BasicOperationsGroup -.-> git/add("Stage Files") git/BasicOperationsGroup -.-> git/commit("Create Commit") git/BranchManagementGroup -.-> git/branch("Handle Branches") git/BranchManagementGroup -.-> git/checkout("Switch Branches") git/BranchManagementGroup -.-> git/merge("Merge Histories") git/BranchManagementGroup -.-> git/log("Show Commits") git/BranchManagementGroup -.-> git/reflog("Log Ref Changes") git/BranchManagementGroup -.-> git/rebase("Reapply Commits") subgraph Lab Skills git/init -.-> lab-560063{{"Cómo comprobar si un commit de Git es accesible desde el HEAD"}} git/add -.-> lab-560063{{"Cómo comprobar si un commit de Git es accesible desde el HEAD"}} git/commit -.-> lab-560063{{"Cómo comprobar si un commit de Git es accesible desde el HEAD"}} git/branch -.-> lab-560063{{"Cómo comprobar si un commit de Git es accesible desde el HEAD"}} git/checkout -.-> lab-560063{{"Cómo comprobar si un commit de Git es accesible desde el HEAD"}} git/merge -.-> lab-560063{{"Cómo comprobar si un commit de Git es accesible desde el HEAD"}} git/log -.-> lab-560063{{"Cómo comprobar si un commit de Git es accesible desde el HEAD"}} git/reflog -.-> lab-560063{{"Cómo comprobar si un commit de Git es accesible desde el HEAD"}} git/rebase -.-> lab-560063{{"Cómo comprobar si un commit de Git es accesible desde el HEAD"}} end

Usar git log --ancestry-path

En este paso, exploraremos cómo usar el comando git log --ancestry-path. Este comando es útil para ver el historial de commits a lo largo de una ruta específica entre dos commits. Te ayuda a entender la línea de cambios.

Primero, creemos un repositorio de Git simple y hagamos algunos commits para configurar un escenario para usar --ancestry-path.

Navega a tu directorio de proyecto:

cd ~/project

Crea un nuevo directorio para este laboratorio e inicializa un repositorio de Git:

mkdir ancestry-lab
cd ancestry-lab
git init

Deberías ver una salida que indique que se ha inicializado un repositorio de Git vacío:

Initialized empty Git repository in /home/labex/project/ancestry-lab/.git/

Ahora, creemos un archivo y hagamos el primer commit:

echo "Initial content" > file1.txt
git add file1.txt
git commit -m "Initial commit"

Verás una salida que confirme el commit:

[master (root-commit) <commit-hash>] Initial commit
 1 file changed, 1 insertion(+)
 create mode 100644 file1.txt

A continuación, hagamos otro commit:

echo "Adding more content" >> file1.txt
git add file1.txt
git commit -m "Add more content"

Verás una salida para el segundo commit:

[master <commit-hash>] Add more content
 1 file changed, 1 insertion(+)

Ahora, creemos una nueva rama y hagamos un commit en esa rama:

git branch feature
git checkout feature
echo "Feature work" > file2.txt
git add file2.txt
git commit -m "Add feature file"

Verás una salida para la creación de la rama, el cambio a ella y el nuevo commit:

Switched to a new branch 'feature'
[feature <commit-hash>] Add feature file
 1 file changed, 1 insertion(+)
 create mode 100644 file2.txt

Volvamos a la rama master y hagamos otro commit:

git checkout master
echo "More master work" >> file1.txt
git add file1.txt
git commit -m "More master content"

Verás una salida para el cambio de ramas y el nuevo commit:

Switched to branch 'master'
[master <commit-hash>] More master content
 1 file changed, 1 insertion(+)

Ahora tenemos un historial de commits con una rama. Usemos git log para ver el historial completo:

git log --all --decorate --oneline

Verás un registro similar a este (los hashes de commit y el orden pueden variar):

<commit-hash> (HEAD -> master) More master content
<commit-hash> Add more content
<commit-hash> (feature) Add feature file
<commit-hash> Initial commit

Ahora, usemos git log --ancestry-path. Este comando requiere dos referencias de commit. Mostrará los commits que son ancestros del segundo commit y descendientes del primer commit.

Encontremos el hash de commit para "Initial commit" y "More master content". Puedes obtener estos del resultado de git log --all --decorate --oneline. Reemplaza <initial-commit-hash> y <master-commit-hash> con los hashes reales de tu salida.

git log --ancestry-path <initial-commit-hash> <master-commit-hash> --oneline

Este comando mostrará los commits en la ruta desde el commit inicial hasta el último commit en la rama master. Deberías ver los commits "Initial commit", "Add more content" y "More master content".

La opción --ancestry-path es útil para entender la línea directa de desarrollo entre dos puntos de tu historial, ignorando los commits de otras ramas que podrían haberse fusionado más tarde.

Ejecutar git merge-base --is-ancestor

En este paso, aprenderemos sobre git merge-base --is-ancestor. Este comando se utiliza para comprobar si un commit es ancestro de otro commit. Es una comprobación simple que devuelve un código de estado (0 para verdadero, 1 para falso) en lugar de mostrar información de los commits. Esto es especialmente útil en scripting o para comprobaciones rápidas.

Seguiremos utilizando el repositorio ancestry-lab que creamos en el paso anterior. Asegúrate de estar en el directorio correcto:

cd ~/project/ancestry-lab

Recuerda el historial de commits del paso anterior. Tenemos commits tanto en la rama master como en la rama feature.

Encontremos los hashes de commit para el "Initial commit" y el último commit en la rama master ("More master content"). Puedes usar git log --oneline para ver los commits recientes.

git log --oneline

La salida será similar a:

<master-commit-hash> (HEAD -> master) More master content
<commit-hash> Add more content
<initial-commit-hash> Initial commit

Ahora, usemos git merge-base --is-ancestor para comprobar si el "Initial commit" es ancestro del último commit en master. Reemplaza <initial-commit-hash> y <master-commit-hash> con los hashes reales.

git merge-base --is-ancestor <initial-commit-hash> <master-commit-hash>
echo $?

El comando echo $? imprime el estado de salida del comando anterior. Si el primer commit es ancestro del segundo, el estado de salida será 0. De lo contrario, será 1.

Dado que el "Initial commit" es de hecho ancestro del último commit en master, la salida de echo $? debería ser 0.

Ahora, comprobemos si el "Initial commit" es ancestro del último commit en la rama feature. Primero, encuentra el hash de commit para el commit "Add feature file".

git log --all --decorate --oneline

La salida será similar a:

<master-commit-hash> (HEAD -> master) More master content
<commit-hash> Add more content
<feature-commit-hash> (feature) Add feature file
<initial-commit-hash> Initial commit

Ahora, usa git merge-base --is-ancestor para comprobar si el "Initial commit" es ancestro del commit "Add feature file". Reemplaza <initial-commit-hash> y <feature-commit-hash> con los hashes reales.

git merge-base --is-ancestor <initial-commit-hash> <feature-commit-hash>
echo $?

Nuevamente, la salida de echo $? debería ser 0 porque el "Initial commit" es el punto de partida para ambas ramas.

Finalmente, comprobemos si el último commit en feature es ancestro del último commit en master. Reemplaza <feature-commit-hash> y <master-commit-hash> con los hashes reales.

git merge-base --is-ancestor <feature-commit-hash> <master-commit-hash>
echo $?

En este caso, el último commit en feature no es ancestro del último commit en master (están en ramas diferentes después de la división inicial). Entonces, la salida de echo $? debería ser 1.

Comprender la relación de ascendencia entre commits es fundamental para entender cómo Git registra el historial y cómo funcionan operaciones como la fusión (merge) y la rebase. La opción --is-ancestor proporciona una forma sencilla de comprobar esta relación.

Probar commits inaccesibles

En este paso, exploraremos el concepto de commits "inaccesibles" en Git. Un commit inaccesible es un commit que no se puede alcanzar desde ninguna rama, etiqueta (tag) u otra referencia. Estos commits no forman parte de la historia actual de tu proyecto tal como la muestra comandos estándar como git log.

Seguiremos utilizando el repositorio ancestry-lab. Asegúrate de estar en el directorio correcto:

cd ~/project/ancestry-lab

Actualmente, todos nuestros commits son accesibles desde la rama master o la rama feature. Creemos un escenario en el que un commit se vuelva inaccesible.

Primero, hagamos un nuevo commit en la rama master:

echo "Temporary commit" >> file1.txt
git add file1.txt
git commit -m "Temporary commit"

Verás la salida para este nuevo commit:

[master <commit-hash>] Temporary commit
 1 file changed, 1 insertion(+)

Ahora, restablezcamos la rama master al commit anterior. Esto hará que el "Temporary commit" sea inaccesible desde la rama master. Usaremos git reset --hard HEAD~1. HEAD~1 se refiere al commit justo antes del HEAD actual.

Ten cuidado con git reset --hard ya que descarta cambios. En este caso, estamos descartando intencionalmente el "Temporary commit" de la historia de la rama master.

git reset --hard HEAD~1

Verás una salida que indica que el HEAD ahora está en el commit anterior:

HEAD is now at <previous-commit-hash> More master content

Ahora, veamos el git log estándar:

git log --oneline

Verás que el "Temporary commit" ya no está en la salida del log de la rama master.

<previous-commit-hash> (HEAD -> master) More master content
<commit-hash> Add more content
<initial-commit-hash> Initial commit

El "Temporary commit" todavía existe en la base de datos de Git, pero no está referenciado por ninguna rama o etiqueta. Ahora es un commit "inaccesible".

¿Cómo podemos ver los commits inaccesibles? Git tiene una referencia especial llamada reflog que registra las actualizaciones de la punta de las ramas y otras referencias. Podemos usar git log con la opción --walk-reflogs o simplemente git reflog para ver estos commits.

Usemos git reflog:

git reflog

Verás un registro de las acciones realizadas, incluyendo el commit que acabamos de hacer y luego restablecer:

<master-commit-hash> (HEAD -> master) master@{0}: reset: moving to HEAD~1
<temporary-commit-hash> master@{1}: commit: Temporary commit
<previous-commit-hash> master@{2}: commit: More master content
<commit-hash> master@{3}: commit: Add more content
<initial-commit-hash> master@{4}: commit (initial): Initial commit

Observa la entrada para "Temporary commit". Es accesible a través de master@{1} en el reflog. Sin embargo, no es accesible desde el HEAD actual o la punta de ninguna rama.

Los commits inaccesibles eventualmente son limpiados por la recolección de basura de Git (git gc), pero permanecen accesibles a través del reflog durante un período predeterminado (generalmente 30 o 90 días). Esto puede ser una salvación si restableces o eliminas commits accidentalmente.

Comprender los commits inaccesibles te ayuda a entender la diferencia entre la base de datos interna de objetos de Git y las referencias (ramas, etiquetas, HEAD) que apuntan a los commits dentro de esa base de datos.

Resumen

En este laboratorio, aprendimos cómo comprobar si un commit de Git es accesible desde el HEAD utilizando dos métodos principales. Primero, exploramos el comando git log --ancestry-path, que nos permite visualizar el historial de commits a lo largo del camino entre dos commits especificados. Configuramos un repositorio sencillo con múltiples ramas y commits para demostrar cómo este comando ayuda a entender la línea de descendencia e identificar si un commit es ancestro de otro.

En segundo lugar, aprenderemos cómo usar el comando git merge-base --is-ancestor, que proporciona una forma más directa y programática de determinar si un commit es ancestro de otro. Finalmente, probaremos estos métodos con commits inaccesibles para consolidar nuestra comprensión de cómo verificar la accesibilidad dentro de un repositorio de Git.