Skip to content

Latest commit

 

History

History
342 lines (236 loc) · 10.4 KB

File metadata and controls

342 lines (236 loc) · 10.4 KB

Curso: Dominando Git y la Conexión con la Nube

Introducción

Tarde o temprano pasa: haces un commit y te das cuenta de que algo está mal. El mensaje está mal escrito, te olvidaste de guardar un fichero, o directamente el cambio entero es un error.

Git tiene herramientas para cada situación. Lo que importa es saber en qué punto estás cuando te das cuenta del error, porque la solución es distinta según si ya subiste el commit a GitHub o no.

Vamos a ver los tres escenarios más habituales practicando cada uno desde cero.

El error que vamos a cometer

Seguimos en mi-proyecto. Para poder practicar necesitamos un commit malo. Abrimos index.js y añadimos una línea de debug que no debería estar en el código:

index.js

console.log("Iniciando aplicación...");
+ console.log("DEBUG: esto no debería estar aquí");

const saludo = (nombre) => {
  return `Hola, ${nombre}`;
};

const despedida = (nombre) => {
  return `Hasta luego, ${nombre}`;
};

Hacemos el commit:

git commit -am "Añade logs de depuración"
[main f3a1b2c] Añade logs de depuración
 1 file changed, 1 insertion(+)

Comprobamos el historial para ver dónde estamos:

git log --oneline
f3a1b2c (HEAD -> main) Añade logs de depuración
a1b2c3d Merge feature/despedida: resuelve conflicto en console.log
3f8a21c Añade hoja de estilos y la enlaza en el HTML
7d9e0f1 primer commit

El commit malo está ahí arriba. Aún no hemos hecho push. Ahora vamos a ver las distintas formas de deshacerlo.


Caso 1 — El commit está solo en local, no has hecho push

Opción A: deshacer el commit pero conservar los cambios

Esta es la opción más habitual. Deshace el commit pero deja el código intacto en el fichero, como si nunca hubieras llegado a hacer git commit.

El comando es git reset HEAD~1. Antes de ejecutarlo, vamos a entender qué significa:

  • HEAD es el commit en el que estás ahora mismo, el último que hiciste.
  • ~1 significa "retrocede uno".
  • Juntos: "deshaz el último commit".
git reset HEAD~1

Comprobamos el historial:

git log --oneline
a1b2c3d (HEAD -> main) Merge feature/despedida: resuelve conflicto en console.log
3f8a21c Añade hoja de estilos y la enlaza en el HTML
7d9e0f1 primer commit

El commit malo ha desaparecido. Ahora miramos el estado:

git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
        modified:   index.js

El código sigue en el fichero, pero el commit ya no existe. Ahora puedes corregir lo que necesites y hacer el commit correctamente.


Para practicar la siguiente opción vuelve a hacer el commit malo:

git commit -am "Añade logs de depuración"

Opción B: el código está bien, solo el mensaje está mal

A veces el problema no es el código sino el mensaje que pusiste. Para ese caso existe --amend, que reescribe el último commit sin tocar el código:

git commit --amend -m "Elimina console.log de debug"

Verificamos:

git log --oneline
e7c9d1a (HEAD -> main) Elimina console.log de debug
a1b2c3d Merge feature/despedida: resuelve conflicto en console.log
3f8a21c Añade hoja de estilos y la enlaza en el HTML
7d9e0f1 primer commit

El mensaje ha cambiado. Fíjate que el hash también ha cambiado (f3a1b2ce7c9d1a): --amend no modifica el commit, crea uno nuevo en su lugar.

--amend también sirve si te olvidaste de incluir un fichero en el commit. Editas el fichero, haces git add y luego git commit --amend sin -m para que se abra el editor con el mensaje anterior y puedas dejarlo igual.

Usa --amend solo antes de hacer push. Si ya subiste el commit, cambiar el hash causará problemas.


Para practicar la siguiente opción necesitas volver a tener el commit malo. Deshaz el amend y recréalo:

git reset HEAD~1
git commit -am "Añade logs de depuración"

Opción C: deshacer el commit y también borrar los cambios

Si el commit era tan malo que no quieres conservar ni el código, añadimos --hard:

git reset --hard HEAD~1
HEAD is now at a1b2c3d Merge feature/despedida: resuelve conflicto en console.log

Verificamos el historial:

git log --oneline
a1b2c3d (HEAD -> main) Merge feature/despedida: resuelve conflicto en console.log
3f8a21c Añade hoja de estilos y la enlaza en el HTML
7d9e0f1 primer commit

Y el estado:

git status
On branch main
nothing to commit, working tree clean

El commit ha desaparecido y los cambios también. Si abres index.js, la línea de debug ya no está. --hard no pregunta ni avisa: borra y punto. Úsalo solo cuando estés seguro de que no necesitas ese código para nada.


Caso 2 — Ya hiciste push, pero la rama es solo tuya

Para este caso vamos a trabajar en una rama. Primero nos aseguramos de que index.js tiene la línea de debug, la añadimos si no está, y creamos la rama:

git switch -c feature/experimento
git commit -am "Añade logs de depuración"

Subimos la rama a GitHub. Hasta ahora siempre hemos usado git push a secas, pero eso solo funciona cuando la rama ya existe en el remoto y Git sabe a dónde enviarla. Para una rama nueva que acabamos de crear en local, GitHub todavía no la conoce, así que tenemos que decirle explícitamente dónde subirla:

git push -u origin feature/experimento
  • origin es el nombre del remoto, que es como Git llama a GitHub en este proyecto.
  • feature/experimento es el nombre que tendrá la rama en GitHub.
  • -u le dice a Git que recuerde esta conexión, de forma que la próxima vez que estés en esta rama puedas usar git push a secas y ya sepa adónde ir.
To github.com:tu-usuario/mi-proyecto.git
 * [new branch]      feature/experimento -> feature/experimento

El commit malo ya está en GitHub. Comprobamos el historial para verlo:

git log --oneline
f3a1b2c (HEAD -> feature/experimento, origin/feature/experimento) Añade logs de depuración
a1b2c3d Merge feature/despedida: resuelve conflicto en console.log
3f8a21c Añade hoja de estilos y la enlaza en el HTML
7d9e0f1 primer commit

Como esta rama es solo nuestra, nadie más la está usando, así que podemos reescribir su historia sin afectar a nadie. Deshacemos el commit en local:

git reset HEAD~1
git log --oneline
a1b2c3d (HEAD -> feature/experimento) Merge feature/despedida: resuelve conflicto en console.log
3f8a21c Añade hoja de estilos y la enlaza en el HTML
7d9e0f1 primer commit

El commit malo ha desaparecido en local. Ahora le decimos a GitHub que actualice la rama con nuestra versión:

git push --force-with-lease
To github.com:tu-usuario/mi-proyecto.git
 + f3a1b2c...a1b2c3d feature/experimento -> feature/experimento (forced update)

El --force-with-lease es un push forzado con un seguro: antes de sobrescribir el remoto, comprueba que nadie haya subido algo a esa rama desde la última vez que hiciste pull. Si hubiera cambios remotos que no tienes, te avisa en lugar de machacarlos sin más. Es la forma segura de forzar, mucho mejor que usar --force a secas.

Nunca uses --force ni --force-with-lease en main o en ramas que compartan con otras personas.

Limpiamos la rama de prueba. Primero volvemos a main y borramos la rama en local, como ya vimos en la guía de ramas. Luego borramos también la rama en GitHub, porque al crearla con -u se quedó ahí:

git switch main
git branch -d feature/experimento
git push origin --delete feature/experimento

Caso 3 — Ya hiciste push a main

Este es el más delicado. En este punto index.js todavía tiene la línea de debug en el directorio de trabajo porque el git reset HEAD~1 del caso anterior la dejó ahí sin staging. Así que podemos hacer el commit directamente:

git commit -am "Añade logs de depuración"
git push
git log --oneline
f3a1b2c (HEAD -> main, origin/main) Añade logs de depuración
a1b2c3d Merge feature/despedida: resuelve conflicto en console.log
3f8a21c Añade hoja de estilos y la enlaza en el HTML
7d9e0f1 primer commit

El commit malo está en main y en GitHub. Si alguien del equipo hace git pull ahora, lo recibirá. En este caso usar reset y forzar el push rompería la historia de todos los que ya lo tienen, así que no podemos hacerlo.

La solución es git revert. En lugar de borrar el commit malo, crea uno nuevo que deshace exactamente lo que hizo el anterior. La historia no se reescribe, se añade encima.

Primero necesitamos el hash del commit que queremos deshacer. Lo vemos en el git log --oneline que acabamos de hacer: es f3a1b2c.

git revert --no-edit f3a1b2c

--no-edit le dice a Git que use el mensaje automático que genera él solo, sin abrir el editor. Sin ese flag, Git intentaría abrir el editor de texto para que confirmes el mensaje, y si no tienes configurado VS Code como editor por defecto podría abrirse vim, que tiene sus propios comandos para cerrar y puede resultar confuso.

[main 8e4d5f6] Revert "Añade logs de depuración"
 Date: ...
 1 file changed, 1 deletion(-)

Subimos:

git push

Miramos el historial:

git log --oneline
8e4d5f6 (HEAD -> main, origin/main) Revert "Añade logs de depuración"
f3a1b2c Añade logs de depuración
a1b2c3d Merge feature/despedida: resuelve conflicto en console.log

El commit malo sigue visible porque la historia no se borra, pero hay uno nuevo encima que deshace su efecto. Si abres index.js, la línea de debug ha desaparecido. El código está correcto y el historial del equipo sigue intacto.


Resumen: ¿qué uso en cada caso?

Situación Solución
Commit en local, quiero corregir el mensaje git commit --amend -m "..."
Commit en local, quiero deshacerlo y conservar el código git reset HEAD~1
Commit en local, quiero deshacerlo y borrar el código git reset --hard HEAD~1
Push en mi rama personal git reset HEAD~1 + git push --force-with-lease
Push en main o rama compartida git revert --no-edit <hash> + git push

La regla de oro: antes del push puedes reescribir lo que quieras. Después del push en una rama compartida, crea historia nueva en lugar de borrar la que ya existe.