Cómo Deshacer y Eliminar un Commit Específico de Git de la Rama Actual

GitBeginner
Practicar Ahora

Introducción

En este laboratorio, aprenderá a gestionar el historial de sus commits de Git. Practicará cómo deshacer cambios con git reset, cómo revertir de forma segura commits públicos con git revert y cómo eliminar commits específicos de su rama utilizando un rebase interactivo. También aprenderá a usar el reflog para recuperar commits que han sido eliminados. Estas son habilidades esenciales para mantener un historial de proyecto limpio y comprensible.

Este es un Guided Lab, que proporciona instrucciones paso a paso para ayudarte a aprender y practicar. Sigue las instrucciones cuidadosamente para completar cada paso y obtener experiencia práctica. Los datos históricos muestran que este es un laboratorio de nivel intermedio con una tasa de finalización del 69%. Ha recibido una tasa de reseñas positivas del 100% por parte de los estudiantes.

Inspeccionar el Historial de Commits

Antes de modificar el historial, primero necesitas saber cómo verlo. El comando git log es la herramienta principal para esto. Para este laboratorio, se ha inicializado un repositorio de Git con varios commits para simular el historial de un proyecto real.

Comencemos inspeccionando el historial de commits. Usaremos la bandera --oneline para mostrar cada commit en una sola línea y --graph para mostrar el historial de commits como un gráfico, lo cual es útil para visualizar ramas y fusiones (merges).

Ejecuta el siguiente comando en tu terminal:

git log --oneline --graph

Deberías ver una lista de los cinco commits que se crearon para ti, con el commit más reciente en la parte superior. Cada línea muestra un hash de commit único (una versión corta del mismo) y el mensaje del commit.

* 7d3d24a (HEAD -> master) chore: Add last-commit file
* 8a9f1b3 docs: Update documentation in file1
* c2e4d6f fix: Add a temporary file that should be removed
* 5e1a3b2 feat: Add file2.txt
* 1b4c0a9 feat: Add file1.txt

Nota: Tus hashes de commit serán diferentes de los del ejemplo anterior. Esto es esperado, ya que cada hash es único.

Tómate un momento para revisar el historial. Manipularás estos commits en los siguientes pasos.

Deshacer el Último Commit con git reset

A veces haces un commit y te das cuenta inmediatamente de que cometiste un error. El comando git reset es perfecto para este escenario. Puede mover el puntero HEAD de la rama actual a un commit anterior, "deshaciendo" efectivamente uno o más commits.

Usaremos la opción --soft, que deshace el commit pero deja los cambios de ese commit en tu área de preparación (el índice o staging area). Esto es útil si quieres volver a hacer el commit de los cambios, quizás con un mensaje diferente o combinado con otros cambios.

Vamos a deshacer el commit más reciente, "chore: Add last-commit file".

git reset --soft HEAD~1

Aquí, HEAD~1 se refiere al commit justo antes del HEAD actual.

Ahora, verifica el estado de tu repositorio:

git status

Verás que last-commit.txt ahora aparece en la sección "Changes to be committed" (Cambios a ser confirmados). Esto significa que el commit en sí fue deshecho, pero los cambios del archivo todavía están preparados (staged).

On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   last-commit.txt

Finalmente, vuelve a ver el log para confirmar que el commit ha desaparecido del historial:

git log --oneline --graph
* 8a9f1b3 (HEAD -> master) docs: Update documentation in file1
* c2e4d6f fix: Add a temporary file that should be removed
* 5e1a3b2 feat: Add file2.txt
* 1b4c0a9 feat: Add file1.txt

Como puedes ver, el último commit ha sido eliminado del log, pero tu trabajo está seguro y esperando en el área de preparación.

Revertir un Commit con git revert

Mientras que git reset es excelente para cambios locales, reescribe el historial, lo cual puede ser problemático si ya has compartido tus commits con otros. Una forma más segura de deshacer cambios en una rama pública es git revert. Este comando crea un nuevo commit que aplica la inversa de los cambios de un commit especificado.

Primero, vamos a limpiar nuestro directorio de trabajo volviendo a hacer el commit de los cambios del paso anterior.

git commit -m "chore: Add last-commit file again"

Ahora, supongamos que queremos deshacer el commit que añadió file2.txt. Este commit está más atrás en nuestro historial. Lo revertiremos. Podemos referenciarlo por su posición relativa a HEAD. Mirando el log del Paso 1, "feat: Add file2.txt" es el cuarto commit desde la parte superior, por lo que podemos referenciarlo como HEAD~3.

Ejecuta el comando de revertir:

git revert HEAD~3

Esto abrirá tu editor de texto por defecto (vim por defecto) con un mensaje de commit pre-rellenado como "Revert 'feat: Add file2.txt'". Simplemente puedes guardar y cerrar el archivo para aceptar el mensaje por defecto.

En vim, escribe :wq y presiona Enter para guardar y salir.

Ahora, revisa el log de nuevo:

git log --oneline --graph

Verás un nuevo commit en la parte superior, "Revert 'feat: Add file2.txt'".

* 1e2d3f4 (HEAD -> master) Revert "feat: Add file2.txt"
* 7d3d24a chore: Add last-commit file again
* 8a9f1b3 docs: Update documentation in file1
* c2e4d6f fix: Add a temporary file that should be removed
* 5e1a3b2 feat: Add file2.txt
* 1b4c0a9 feat: Add file1.txt

El commit original todavía está en el historial, pero sus cambios han sido deshechos por el nuevo commit de revertir. Puedes verificar esto listando los archivos en el directorio.

ls

Notarás que file2.txt ya no está presente.

Eliminar un Commit con Rebase Interactivo

Para una manipulación más compleja del historial, como eliminar un commit del medio de una rama, puedes usar el rebase interactivo (git rebase -i). Este es un comando muy potente que reescribe el historial de commits, por lo que debe usarse con precaución, especialmente en ramas que se comparten con otros desarrolladores.

Nuestro objetivo es eliminar el commit con el mensaje "fix: Add a temporary file that should be removed". Mirando el log actual, este commit es ahora HEAD~3.

Inicia el proceso de rebase interactivo:

git rebase -i HEAD~4

Este comando abrirá tu editor de texto (vim por defecto) con una lista de los últimos 4 commits.

pick fd5a181 fix: Add a temporary file that should be removed
pick 5f27a4d docs: Update documentation in file1
pick 284be6f chore: Add last-commit file again
pick 8a460c5 Revert "feat: Add file2.txt"

## Rebase 9403080..8a460c5 onto 9403080 (4 commands)
#
## Commands:
## p, pick <commit> = use commit
## d, drop <commit> = remove commit
## ...

Para eliminar el commit, cambia la palabra pick por drop (o d) en la línea que contiene "fix: Add a temporary file that should be removed".

Cambia esto:

pick fd5a181 fix: Add a temporary file that should be removed

Por esto:

drop fd5a181 fix: Add a temporary file that should be removed

En vim, presiona i para entrar en modo de inserción, haz tus cambios, luego presiona Esc para salir del modo de inserción. Guarda y sal escribiendo :wq y presionando Enter. Git reproducirá los commits restantes sobre el nuevo historial.

Verifica el log para ver el resultado:

git log --oneline --graph

El commit "malo" ahora ha desaparecido del historial.

* a5b4c3d (HEAD -> master) chore: Add last-commit file again
* f9e8d7c docs: Update documentation in file1
* 1e2d3f4 Revert "feat: Add file2.txt"
* 5e1a3b2 feat: Add file2.txt
* 1b4c0a9 feat: Add file1.txt

Además, verifica los archivos en el directorio. El archivo bad-commit-file.txt ha sido eliminado.

ls

Restaurar un Commit Eliminado con git reflog

¿Qué pasa si eliminas un commit por error? Git mantiene un registro de todos los cambios en el puntero HEAD en un log especial llamado reflog. Esta es tu red de seguridad. Puedes usarlo para encontrar y restaurar commits perdidos.

Veamos el reflog para encontrar el historial que acabamos de reescribir.

git reflog

Verás una lista de las acciones que has realizado. Busca la línea que dice rebase (finish): returning to refs/heads/master. La entrada justo debajo de ella, probablemente HEAD@{1}, representa el estado de tu rama antes del rebase.

a5b4c3d (HEAD -> master) HEAD@{0}: rebase (finish): returning to refs/heads/master
1e2d3f4 HEAD@{1}: rebase (start): checkout HEAD~3
...

Podemos restaurar nuestra rama a este estado anterior usando git reset --hard. Este comando es destructivo y descartará cualquier cambio no confirmado, así que úsalo con cuidado.

Vamos a resetear nuestra rama al estado anterior al rebase, que en este caso es HEAD@{1}.

git reset --hard HEAD@{1}

Verás un mensaje confirmando que HEAD está ahora en el commit anterior al inicio del rebase.

HEAD is now at 1e2d3f4 Revert "feat: Add file2.txt"

Ahora, verifica el log una última vez:

git log --oneline --graph

Deberías ver el historial como estaba antes del rebase:

* f461400 (HEAD -> master) Revert "feat: Add file2.txt"
* acea45c chore: Add last-commit file again
* c04b3f5 docs: Update documentation in file1
* 9403080 feat: Add file2.txt
* ee39412 feat: Add file1.txt

Nota que el commit "fix: Add a temporary file that should be removed" todavía no está presente - ¡esto es correcto! El comando git reset --hard HEAD@{1} nos restauró al estado antes de que comenzara el rebase interactivo, no antes de que elimináramos el commit. El rebase interactivo eliminó con éxito ese commit no deseado de nuestro historial. Puedes ejecutar ls para confirmar que bad-commit-file.txt todavía no está presente. El reflog es una herramienta invaluable para recuperarse de errores en Git.

Resumen

En este laboratorio, has aprendido técnicas prácticas para gestionar el historial de commits de tu Git. Comenzaste inspeccionando el historial con git log. Luego practicaste deshacer commits usando git reset --soft, revirtiendo cambios de forma segura con git revert, y eliminando commits específicos con el potente comando git rebase -i. Finalmente, aprendiste a usar git reflog como una red de seguridad para restaurar commits que fueron eliminados del historial. Estas habilidades son esenciales para mantener un historial de proyecto limpio y manejable.