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 |