Git Reset y Reflog

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

¡Bienvenido, viajero del tiempo de Git! Hoy, vamos a explorar dos potentes características de Git que te darán un control sin precedentes sobre la historia de tu repositorio: git reset y git reflog. Estas herramientas son como los controles avanzados de tu máquina del tiempo de Git, que te permiten moverte entre diferentes estados de tu proyecto e incluso recuperar trabajo "perdido".

El comando git reset es una herramienta versátil que puede ayudarte a deshacer cambios, quitar archivos del área de preparación (staging area) e incluso reescribir la historia de tus commits. Sin embargo, con gran poder viene gran responsabilidad, y git reset puede resultar un poco intimidante para los recién llegados. Aquí es donde entra git reflog; es como una red de seguridad que registra todos los cambios que realizas en las referencias (refs) de tu repositorio (como las puntas de las ramas), lo que te permite recuperarte incluso de los resets más drásticos.

En este laboratorio (lab), cubriremos:

  1. Soft Reset: Mover el puntero HEAD sin cambiar el directorio de trabajo ni el área de preparación.
  2. Mixed Reset: Quitar cambios del área de preparación mientras se mantienen las modificaciones en el directorio de trabajo.
  3. Hard Reset: Descartar cambios por completo.
  4. Uso de Reflog para recuperarse de operaciones "destructivas".
  5. Resets basados en el tiempo: Mover tu repositorio a un estado de un momento específico.

Al final de este laboratorio, tendrás una sólida comprensión de cómo usar estas potentes características de Git de manera segura y efectiva. Podrás manipular la historia de tu repositorio con confianza, sabiendo que siempre puedes encontrar tu camino de regreso si es necesario.

¡Adentrémonos y empecemos a dominar git reset y reflog!


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL git(("Git")) -.-> git/BranchManagementGroup(["Branch Management"]) git(("Git")) -.-> git/SetupandConfigGroup(["Setup and Config"]) git(("Git")) -.-> git/BasicOperationsGroup(["Basic Operations"]) git(("Git")) -.-> git/DataManagementGroup(["Data Management"]) git/SetupandConfigGroup -.-> git/init("Initialize Repo") git/BasicOperationsGroup -.-> git/status("Check Status") git/BasicOperationsGroup -.-> git/commit("Create Commit") git/DataManagementGroup -.-> git/reset("Undo Changes") git/BranchManagementGroup -.-> git/log("Show Commits") git/BranchManagementGroup -.-> git/reflog("Log Ref Changes") subgraph Lab Skills git/init -.-> lab-387491{{"Git Reset y Reflog"}} git/status -.-> lab-387491{{"Git Reset y Reflog"}} git/commit -.-> lab-387491{{"Git Reset y Reflog"}} git/reset -.-> lab-387491{{"Git Reset y Reflog"}} git/log -.-> lab-387491{{"Git Reset y Reflog"}} git/reflog -.-> lab-387491{{"Git Reset y Reflog"}} end

Configuración de tu espacio de trabajo

Antes de comenzar a realizar resets y trabajar con el reflog, configuraremos un espacio de trabajo con algunos commits para experimentar. Crearemos un nuevo directorio, inicializaremos un repositorio de Git y agregaremos algunos archivos con múltiples commits.

Abre tu terminal y escribe estos comandos:

cd ~/project
mkdir git-reset-lab
cd git-reset-lab
git init

Ahora, crearemos algunos archivos y realizaremos una serie de commits. Copia y pega los siguientes comandos en tu terminal:

echo "## Git Reset and Reflog Lab" > README.md
git add README.md
git commit -m "Initial commit"

echo "function add(a, b) { return a + b; }" > math.js
git add math.js
git commit -m "Add addition function"

echo "function subtract(a, b) { return a - b; }" >> math.js
git add math.js
git commit -m "Add subtraction function"

echo "function multiply(a, b) { return a * b; }" >> math.js
git add math.js
git commit -m "Add multiplication function"

Analicemos lo que acabamos de hacer:

  1. Creamos un archivo README y realizamos nuestro primer commit.
  2. Creamos un archivo JavaScript con una función de suma y lo confirmamos.
  3. Agregamos una función de resta al mismo archivo y lo confirmamos.
  4. Finalmente, agregamos una función de multiplicación y lo confirmamos.

¡Ahora tenemos un repositorio con algo de historia para experimentar!

Soft Reset: Moviendo el puntero HEAD

El primer tipo de reset que exploraremos es el "soft" reset. Un soft reset mueve el puntero HEAD (y la rama actual) a un commit diferente, pero no cambia el área de preparación (staging area) ni el directorio de trabajo. Esto es útil cuando quieres "deshacer" algunos commits pero mantener todos los cambios para un nuevo commit.

Intentemos un soft reset:

git reset --soft HEAD~1

Este comando mueve el puntero HEAD un commit hacia atrás. El ~1 significa "un commit antes del actual". Puedes usar ~2, ~3, etc., para retroceder varios commits.

Ahora, si ejecutas git status, verás que los cambios del último commit están preparados (staged) y listos para ser confirmados nuevamente. Los archivos en tu directorio de trabajo no han cambiado.

Esto es útil en escenarios donde quieres "aplastar" (squash) tus últimos commits en uno solo. Puedes hacer un soft reset hacia atrás varios commits y luego hacer un nuevo commit con todos esos cambios.

Vamos a confirmar estos cambios nuevamente con un nuevo mensaje:

git commit -m "Add subtraction and multiplication functions"

Recuerda, aunque el soft reset generalmente es seguro (ya que no descarta ningún cambio), sí reescribe la historia. Si ya has subido (pushed) los commits originales, tendrás que hacer un push forzado para actualizar la rama remota, lo que puede causar problemas para los colaboradores. ¡Siempre comunícate con tu equipo antes de reescribir la historia compartida!

Mixed Reset: Quitando cambios del área de preparación

El siguiente tipo de reset que veremos es el "mixed" reset. En realidad, este es el modo predeterminado de git reset si no se especifica una bandera. Un mixed reset mueve el puntero HEAD y actualiza el área de preparación (staging area) para que coincida, pero no toca el directorio de trabajo.

Hagamos algunos cambios y los preparemos (stage):

echo "function divide(a, b) { return a / b; }" >> math.js
git add math.js

Ahora, supongamos que hemos cambiado de opinión y no queremos preparar (stage) este cambio todavía. Podemos usar un mixed reset:

git reset HEAD

Esto quita los cambios del área de preparación pero los mantiene en el directorio de trabajo. Si ejecutas git status ahora, verás que math.js está modificado pero no preparado (staged).

El mixed reset es útil cuando has preparado (stage) algunos cambios pero luego decides que aún no estás listo para confirmarlos. Quizás quieres revisar los cambios nuevamente o hacer más modificaciones antes de prepararlos.

Recuerda, a diferencia del soft reset, el mixed reset sí cambia el área de preparación. Sin embargo, sigue siendo seguro en el sentido de que no descarta ningún trabajo - todo sigue estando en tu directorio de trabajo.

Hard Reset: Descartando cambios

El tercer y más drástico tipo de reset es el "hard" reset. Un hard reset mueve el puntero HEAD, actualiza el área de preparación (staging area) y actualiza el directorio de trabajo para que coincidan. Esto significa que descarta todos los cambios desde el commit al que estás realizando el reset.

Intentemos un hard reset:

git add math.js
git commit -m "Add division function"
git status
git reset --hard HEAD~1

Esto prepara (stage) y confirma nuestra función de división, luego realiza un hard reset al commit anterior, deshaciendo efectivamente nuestro último commit y descartando los cambios.

Si miras math.js ahora, verás que la función de división ha desaparecido. Es como si nunca la hubiéramos escrito.

El hard reset es poderoso pero peligroso. Es útil cuando quieres descartar por completo algún trabajo y comenzar de nuevo desde un commit anterior. Sin embargo, ten mucho cuidado con él, ya que puede descartar cambios de forma permanente.

Siempre verifica dos veces que estás realizando el reset al commit correcto antes de hacer un hard reset. Si no estás seguro, es más seguro usar un soft o mixed reset, o crear una nueva rama antes de experimentar.

Uso del reflog para recuperar commits perdidos

Ahora, ¿qué pasa si te das cuenta de que no querías descartar esa función de división? Aquí es donde git reflog viene al rescate. El reflog es un registro de todos los lugares donde ha estado el puntero HEAD en tu repositorio local. Es como una super-historia que incluso registra comandos que reescriben la historia, como el reset.

Veamos el reflog:

git reflog

Deberías ver una lista de todas las acciones recientes que has realizado, incluyendo tus resets. Cada entrada tiene un identificador HEAD@{n}.

Para recuperar tu commit perdido, puedes hacer un reset al estado anterior al hard reset:

git reset --hard HEAD@{1}

Esto hace un reset al estado del puntero HEAD antes de tu última acción (que fue el hard reset).

Ahora, revisa math.js y deberías ver que tu función de división ha vuelto.

cat math.js
function add(a, b) { return a + b; }
function subtract(a, b) { return a - b; }
function multiply(a, b) { return a * b; }
function divide(a, b) { return a / b; }

El reflog es una poderosa red de seguridad que te permite recuperarte de casi cualquier accidente en Git. Sin embargo, recuerda que es local a tu máquina y temporal (las entradas generalmente se mantienen de 30 a 90 días). No es un sustituto para las copias de seguridad regulares o para subir tu trabajo a un repositorio remoto.

Resets basados en tiempo

Git también te permite restablecer tu repositorio a su estado en un momento específico del tiempo. Esto puede ser útil si recuerdas aproximadamente cuándo tu repositorio estaba en el estado al que deseas volver.

Intentemos un reset basado en tiempo:

git reset --hard master@{"1 hour ago"}

Esto restablece tu repositorio a su estado de hace 1 hora. Puedes usar varios descriptores de tiempo como "ayer", "hace 2 días", "hace 3 minutos", etc.

Ten cuidado con los resets basados en tiempo, ya que pueden ser menos precisos que restablecer a un commit específico. Siempre verifica el estado de tu repositorio después de un reset basado en tiempo para asegurarte de que has llegado al estado deseado.

Recuerda, siempre puedes usar el reflog para deshacer un reset basado en tiempo si no te da el resultado esperado.

Resumen

¡Felicidades, señor del tiempo de Git! Acabas de dominar algunos de los comandos más poderosos y potencialmente peligrosos de Git. Repasemos los conceptos clave que hemos cubierto:

  1. Soft Reset: Mueve el puntero HEAD sin cambiar el área de preparación (staging area) ni el directorio de trabajo. Útil para combinar (squash) commits.
  2. Mixed Reset: Mueve el puntero HEAD y actualiza el área de preparación, pero no toca el directorio de trabajo. Ideal para quitar cambios del área de preparación.
  3. Hard Reset: Mueve el puntero HEAD, actualiza el área de preparación y actualiza el directorio de trabajo. Poderoso pero potencialmente destructivo.
  4. Reflog: Una red de seguridad que registra todos los cambios en el puntero HEAD, lo que te permite recuperarte de casi cualquier accidente en Git.
  5. Resets basados en tiempo: Te permiten restablecer tu repositorio a su estado en un momento específico del tiempo.

Recuerda, con gran poder viene gran responsabilidad. Si bien estos comandos te dan un control increíble sobre la historia de tu repositorio, también pueden ser peligrosos si se usan sin cuidado. Siempre verifica dos veces antes de realizar un reset, especialmente un hard reset, y recuerda que el reflog es tu amigo si algo sale mal.

A medida que continúes tu viaje con Git, practica estos comandos en un entorno seguro hasta que te sientas cómodo con ellos. Son herramientas poderosas que pueden mejorar en gran medida tu flujo de trabajo con Git si se usan correctamente.

¡Que tengas un buen reset y que tu historia de Git siempre sea limpia y significativa!