Saltar al contenido
sudoquest@root
Git

Ver y deshacer

Ver qué cambió y volver atrás: tirar un cambio, sacarlo del índice, rehacer o deshacer un commit y recuperar lo borrado.

30 min de lectura

Guardar la historia sirve de poco si no sabes mirarla ni volver atrás. Este tema va de eso: ver qué ha cambiado, línea a línea, y deshacer: un cambio que no tocaba, algo preparado por error, un commit con una errata, un commit que rompió algo… o un archivo que se borró hace días.

Ahora trabajas con La Cuchara, un restaurante que guarda en Git su carta (carta.md), sus precios (precios.csv) y sus recetas (recetas/). Como en el tema 1, cada sección tiene un ejercicio con una terminal.

Ver lo cambiado: git diff

git status dice qué archivos han cambiado; git diff dice qué líneas:

tecnico@srv-pruebas:~/restaurante$ git diff
diff --git a/carta.md b/carta.md
index 4418c06..e9ec4d6 100644
--- a/carta.md
+++ b/carta.md
@@ -1,7 +1,7 @@
# La Cuchara

## Entrantes
-- Croquetas: 8 €
+- Croquetas: 9 €
- Gazpacho: 6 €

## Principales

Cómo se lee: --- es el archivo como estaba y +++ como está; las líneas con - (en rojo) se quitan y las de + (en verde) se añaden. Un cambio de una línea es eso: quitar la vieja y poner la nueva. Alrededor van tres líneas de contexto para ubicarse, y @@ -1,7 +1,7 @@ dice dónde: desde la línea 1, siete líneas antes y siete después.

Ojo con qué compara cada forma:

Orden Compara
git diff la carpeta con el índice: lo cambiado sin preparar
git diff --staged (o --cached) el índice con el último commit: lo que entraría en el próximo commit
git diff HEAD la carpeta con el último commit: todo, preparado o no
git diff --stat lo mismo, en resumen: archivos y líneas
git diff carta.md solo ese archivo

Por eso, tras git add carta.md, git diff ya no enseña nada: el cambio está preparado, y lo ves con git diff --staged.

Nombrar un commit y verlo: git show

Para hablar de un commit no hace falta su hash entero: bastan las primeras cifras (050168c). También tiene nombres relativos:

Nombre Qué commit es
HEAD el último (donde estás)
HEAD~1 (o HEAD^) el de antes
HEAD~2 el de dos antes, y así

git show enseña un commit: su cabecera, su mensaje y su diff. Sin nada, el último:

tecnico@srv-pruebas:~/restaurante$ git show HEAD
commit 050168c3f83acaf23d19efe1e31eba2cc9ff9c06 (HEAD -> main)
Author: Ana Técnica <[email protected]>
Date:   Thu Nov 5 09:00:01 2026 +0000

  La paella sube

diff --git a/precios.csv b/precios.csv
index 0fe3ef8..6a7d438 100644
--- a/precios.csv
+++ b/precios.csv
@@ -1,5 +1,5 @@
plato,precio
croquetas,8
gazpacho,6
-paella,14
+paella,15
flan,4

Con --stat, solo el resumen. Y con commit:ruta, un archivo tal como estaba en ese commit (útil para mirar sin tocar nada):

tecnico@srv-pruebas:~/restaurante$ git show HEAD~2:recetas/gazpacho.md
Gazpacho

1. Tomate.
2. Pepino.
3. Aceite.

git log -p es como git log, con el diff de cada commit debajo.

Tirar un cambio: git restore

git restore archivo deja el archivo de la carpeta como está en el índice (si no lo preparaste, como en el último commit): el cambio sin preparar desaparece. No escribe nada si sale bien:

tecnico@srv-pruebas:~/restaurante$ git restore carta.md

Antes de restore existía (y sigue funcionando) git checkout -- archivo, que hace lo mismo; lo verás en muchas guías. git restore . lo restaura todo, así que, si solo uno sobra, nómbralo.

Ejercicio

Tirar un cambio

En ~/restaurante hay dos archivos cambiados sin guardar: el de precios.csv es bueno (la paella sube), pero en carta.md alguien se apoyó en el teclado. Tira lo de la carta y deja lo de los precios.

Preparando la máquina…

Sacar del índice: git restore --staged

Si preparaste algo que no tocaba, git restore --staged archivo lo saca del índice. El cambio no se pierde: vuelve a estar en la carpeta, sin preparar. El propio git status lo recuerda:

tecnico@srv-pruebas:~/restaurante$ git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified:   carta.md
tecnico@srv-pruebas:~/restaurante$ git restore --staged carta.md
tecnico@srv-pruebas:~/restaurante$ git status -s
 M carta.md

La M ha pasado de la primera columna (preparado) a la segunda (sin preparar). Lo mismo hace git reset archivo (o git reset HEAD archivo), la forma antigua.

Ejercicio

Se preparó lo que no era

Alguien preparó con git add los dos cambios: la carta (las natillas, que sí van) y notas.txt, que son apuntes suyos y no deben guardarse. Saca notas.txt de lo preparado, sin perder lo escrito, y guarda solo la carta.

Preparando la máquina…

Rehacer el último commit: git commit --amend

¿Una errata en el mensaje? ¿Un archivo olvidado? git commit --amend rehace el último commit con lo que haya preparado ahora: prepara lo olvidado con git add y, después:

tecnico@srv-pruebas:~/restaurante$ git commit --amend -m 'Natillas en los postres'
[main 418f130] Natillas en los postres
Date: Mon Nov 9 08:15:01 2026 +0000
1 file changed, 1 insertion(+)

Con -m, el mensaje nuevo; con --no-edit, el de antes; sin nada, abre el editor con el mensaje de antes para corregirlo. No hay un commit más: el último se sustituye por otro (con otro hash), y se queda con su autor y su fecha (la línea Date: lo recuerda).

Ejercicio

Al último commit le falta algo

Hace un momento guardaste las natillas en la carta, pero el mensaje tiene una errata («Natllas») y se te olvidó su precio en precios.csv. Arregla ese commit (aún no lo tiene nadie) en vez de hacer otro.

Preparando la máquina…

Deshacer un commit compartido: git revert

git revert commit crea un commit nuevo que hace lo contrario de aquel: lo que añadió, lo quita, y lo que quitó, lo vuelve a poner. La historia no se toca: el commit malo sigue ahí, y detrás, el que lo deshace.

tecnico@srv-pruebas:~/restaurante$ git revert --no-edit HEAD~1
[main c6252ef] Revert "La paella sube"
Date: Mon Nov 9 08:18:01 2026 +0000
1 file changed, 1 insertion(+), 1 deletion(-)
tecnico@srv-pruebas:~/restaurante$ git log --oneline
c6252ef (HEAD -> main) Revert "La paella sube"
418f130 Natillas en los postres
050168c La paella sube
3f179d0 Fuera el gazpacho de invierno
f1d9a55 La carta, los precios y las recetas

Sin --no-edit, abre el editor con el mensaje ya escrito (Revert "…" y This reverts commit …) por si quieres explicar por qué. Si lo que vino después tocó las mismas líneas, Git no puede deshacerlo solo: es un conflicto, y eso llega en el tema 4.

Ejercicio

El commit de ayer lo rompió

Ayer Jorge subió «Precios de otoño» y el flan salió a 40 €. Esos commits ya están en el servidor y los tiene todo el equipo, así que no se puede reescribir la historia: deshaz aquel commit con uno nuevo, sin perder lo que vino después.

Preparando la máquina…

Mover la rama atrás: git reset

git reset commit lleva la rama a ese commit: los de después dejan de estar en la historia. Qué pasa con lo que traían depende de cómo se lance:

Orden La rama El índice La carpeta
git reset --soft HEAD~1 atrás se queda (lo de aquel commit, preparado) se queda
git reset HEAD~1 (--mixed) atrás como en ese commit se queda (lo de aquel commit, sin preparar)
git reset --hard HEAD~1 atrás como en ese commit como en ese commit
tecnico@srv-pruebas:~/restaurante$ git reset HEAD~1
Unstaged changes after reset:
M	precios.csv

Con el de siempre (--mixed), el trabajo sigue en la carpeta, listo para rehacer los commits a tu manera. Igual que --amend, reescribe la historia: solo para commits que aún no has compartido.

Ejercicio

Los dos últimos commits no tocaban

Tus dos últimos commits (el menú del día) eran pruebas: aún no los has compartido y quieres rehacerlos con calma. Quítalos de la historia, pero deja el trabajo en la carpeta.

Preparando la máquina…

La red de seguridad: git reflog

Un commit que sacaste de la rama con reset o --amend no se borra al momento: Git apunta en el reflog por dónde ha pasado HEAD, y desde ahí se puede volver.

tecnico@srv-pruebas:~/restaurante$ git reflog
418f130 (HEAD -> main) HEAD@{0}: reset: moving to HEAD~1
c6252ef HEAD@{1}: revert: Revert "La paella sube"
418f130 (HEAD -> main) HEAD@{2}: commit (amend): Natillas en los postres
be34562 HEAD@{3}: commit: Natllas
050168c HEAD@{4}: commit: La paella sube
…

HEAD@{1} es “donde estaba antes del último movimiento”: git reset --hard HEAD@{1} deshace un reset equivocado. Lo que no guarda el reflog es lo que nunca llegó a un commit.

Recuperar un archivo borrado: git restore --source

Lo que estuvo en un commit sigue en la historia aunque luego se borrara. Primero, encuentra cuándo: git log -- ruta enseña los commits que tocaron ese archivo (el último, el que lo borró).

tecnico@srv-pruebas:~/restaurante$ git log --oneline -- recetas/gazpacho.md
3f179d0 Fuera el gazpacho de invierno
f1d9a55 La carta, los precios y las recetas

En el commit que lo borró ya no está; en el de antes, sí. git restore --source=commit archivo lo trae a la carpeta tal como estaba en ese commit (sin prepararlo):

tecnico@srv-pruebas:~/restaurante$ git restore --source=3f179d0~1 recetas/gazpacho.md

Después, como siempre: git add y git commit. (git checkout commit -- archivo hace lo mismo, y además lo deja preparado.)

Ejercicio

Volved a traer lo que se borró

Hace unos días se borró la receta del gazpacho (era invierno). Vuelve el calor: recupera recetas/gazpacho.md tal como estaba y guárdala, sin perder lo que se hizo después.

Preparando la máquina…

Resumen

Orden Qué hace
git diff / --staged / HEAD qué líneas han cambiado: sin preparar / preparadas / todas
git show commit / commit:archivo un commit con su diff / un archivo en ese commit
git restore archivo tira lo cambiado en la carpeta (no vuelve)
git restore --staged archivo lo saca del índice (el cambio se queda)
git commit --amend rehace el último commit (solo si no está compartido)
git revert commit un commit nuevo que deshace aquel (para lo compartido)
git reset commit (--soft, --hard) lleva la rama atrás (solo lo no compartido)
git reflog por dónde ha pasado HEAD, para volver
git restore --source=commit archivo trae un archivo de cualquier commit