Hasta ahora hemos mirado siempre hacia delante: crear el repositorio, preparar cambios, confirmarlos, revisar el diff antes de registrarlo. Esta lección cambia el sentido de la mirada. Vamos a consultar el pasado.
Y aquí está el verdadero valor de Git. Guardar versiones no sirve de gran cosa si después no puedes responder a preguntas como: ¿cuándo se introdujo esta función? ¿Quién tocó estilos.css la semana pasada y por qué? ¿En qué confirmación desapareció aquella línea que juraría haber escrito? ¿Qué se hizo en el proyecto entre el lunes y el jueves?
git log responde a todas ellas, pero su salida por defecto es solo la punta del iceberg. Es un comando enormemente configurable: formatos compactos, gráficos, resúmenes por fichero, plantillas propias y una batería de filtros que permiten localizar una confirmación entre miles. En esta lección lo recorreremos entero, junto con git show para inspeccionar una confirmación concreta y las distintas formas de referirse a un commit, que hemos ido usando de pasada (HEAD, HEAD~1) sin explicarlas del todo.
Contenido
- El repositorio de Bruno: nuestro banco de pruebas
git logpor defecto- Formatos compactos:
--oneliney--graph - Ver qué cambió:
--staty--patch - Formatos personalizados con
--pretty=format: - Filtrar el historial: cantidad, fechas, autor y mensaje
- Filtrar por ruta y la búsqueda "pickaxe" (
-Sy-G) git show: inspeccionar una confirmación- Cómo referirse a una confirmación
- Combinaciones útiles para el día a día
- El repositorio de Bruno: nuestro banco de pruebas
Han pasado unos días desde que Bruno clonó el proyecto. Ana ya tenía cuatro confirmaciones cuando él llegó, y desde entonces Bruno ha hecho tres más en su propia copia. Este es el historial que tiene en su MacBook:
b2e6d3f (HEAD -> main) Corregir el foco del campo tras añadir una tarea 7c1f4a9 Aplicar estilo a las tareas completadas 3d5b8e1 Añadir filtro de tareas pendientes c5d9b1e (origin/main, origin/HEAD) Documentar la instalación en el README 4e7f2a9 Añadir borrado de tareas al listado 8b6d3c2 Añadir estilos base del listado 1a4c8d6 Estructura inicial del gestor de tareas
Siete confirmaciones: las cuatro de Ana que llegaron con el clon y las tres que ha hecho Bruno en local. Fíjate en (origin/main): marca el punto en el que estaba el servidor cuando Bruno clonó. Sus tres confirmaciones están por delante de esa marca porque todavía no las ha enviado; eso es materia del módulo 4.
Este historial mixto nos vendrá muy bien: tiene dos autores, fechas distintas y cambios sobre ficheros distintos, que es justo lo que necesitamos para practicar los filtros.
git log por defecto
git log por defectocommit b2e6d3f8a1c5e9d2b4f7a3c6e8d1b5f9a2c4e7d3 (HEAD -> main) Author: Bruno Salas <[email protected]> Date: Fri Jul 31 09:14:22 2026 +0200 Corregir el foco del campo tras añadir una tarea Tras enviar el formulario el cursor se perdía y había que volver a pulsar en el campo. Ahora se devuelve el foco. commit 7c1f4a9e2b6d8f3a5c1e7b9d4f2a6c8e3b5d1f7a Author: Bruno Salas <[email protected]> Date: Thu Jul 30 17:42:08 2026 +0200 Aplicar estilo a las tareas completadas commit 3d5b8e1c4a7f2d9b6e3c8a1f5d2b7e4c9a6f3d8b Author: Bruno Salas <[email protected]> Date: Wed Jul 29 11:05:47 2026 +0200 Añadir filtro de tareas pendientes
(La salida continúa; se sale del paginador con q.)
Cada entrada tiene cuatro elementos:
| Elemento | Qué es |
|---|---|
commit <hash> |
El SHA completo de 40 caracteres del commit |
Author |
Quién escribió el cambio, con el user.name y user.email que configuró |
Date |
Fecha de autoría, con zona horaria |
| Mensaje | Indentado cuatro espacios; primera línea y cuerpo |
Un par de precisiones importantes:
- El orden es cronológico inverso: lo más reciente arriba. Es lo que quieres el 95 % de las veces. Se invierte con
--reverse. AuthoryCommitterson campos distintos. Recuerda del modelo de datos que un commit guarda ambos. Normalmente coinciden ygit logsolo muestra el primero; se separan cuando alguien aplica un parche ajeno o al hacer rebase. Para ver los dos:
commit b2e6d3f8a1c5e9d2b4f7a3c6e8d1b5f9a2c4e7d3 (HEAD -> main) Author: Bruno Salas <[email protected]> AuthorDate: Fri Jul 31 09:14:22 2026 +0200 Commit: Bruno Salas <[email protected]> CommitDate: Fri Jul 31 09:14:22 2026 +0200
git logsolo muestra el historial alcanzable desde donde estás. Por defecto parte deHEADy va siguiendo los enlaces a los padres. Confirmaciones que existan en otras ramas no aparecen salvo que las pidas explícitamente o uses--all.
- Formatos compactos:
--oneline y --graph
--oneline y --graph--oneline
b2e6d3f (HEAD -> main) Corregir el foco del campo tras añadir una tarea 7c1f4a9 Aplicar estilo a las tareas completadas 3d5b8e1 Añadir filtro de tareas pendientes c5d9b1e (origin/main, origin/HEAD) Documentar la instalación en el README 4e7f2a9 Añadir borrado de tareas al listado 8b6d3c2 Añadir estilos base del listado 1a4c8d6 Estructura inicial del gestor de tareas
Una línea por confirmación: hash abreviado, referencias que apuntan ahí y primera línea del mensaje. Es el formato que más se usa, y explica por qué esa primera línea debe funcionar por sí sola: en esta vista es lo único que se ve.
--oneline es en realidad un atajo de --pretty=oneline --abbrev-commit.
--graph
Dibuja el grafo de confirmaciones con caracteres de texto:
* b2e6d3f (HEAD -> main) Corregir el foco del campo tras añadir una tarea * 7c1f4a9 Aplicar estilo a las tareas completadas * 3d5b8e1 Añadir filtro de tareas pendientes * c5d9b1e (origin/main, origin/HEAD) Documentar la instalación en el README * 4e7f2a9 Añadir borrado de tareas al listado * 8b6d3c2 Añadir estilos base del listado * 1a4c8d6 Estructura inicial del gestor de tareas
Con una sola línea de desarrollo, el gráfico es una columna de asteriscos y no aporta gran cosa. Su valor aparece cuando hay ramas y fusiones, donde muestra las bifurcaciones y los puntos de unión:
* 9f3a2c1 (HEAD -> main) Fusionar la rama filtros |\ | * 5e8b1d4 Añadir el filtro por fecha | * 2c7f9a3 Añadir el selector de filtros * | 8d4e6b2 Corregir el contador |/ * c5d9b1e Documentar la instalación en el README
Ese es el aspecto que tendrá el historial del equipo a partir del módulo 3. Para ver el grafo de todas las ramas, no solo la actual:
--decorate muestra las referencias (ramas, etiquetas, HEAD) junto a cada confirmación. En Git moderno está activado por defecto en el terminal, pero conviene conocerlo.
- Ver qué cambió:
--stat y --patch
--stat y --patch--stat: resumen por ficheros
commit b2e6d3f8a1c5e9d2b4f7a3c6e8d1b5f9a2c4e7d3 (HEAD -> main) Author: Bruno Salas <[email protected]> Date: Fri Jul 31 09:14:22 2026 +0200 Corregir el foco del campo tras añadir una tarea app.js | 2 ++ 1 file changed, 2 insertions(+) commit 7c1f4a9e2b6d8f3a5c1e7b9d4f2a6c8e3b5d1f7a Author: Bruno Salas <[email protected]> Date: Thu Jul 30 17:42:08 2026 +0200 Aplicar estilo a las tareas completadas estilos.css | 7 +++++++ 1 file changed, 7 insertions(+) commit 3d5b8e1c4a7f2d9b6e3c8a1f5d2b7e4c9a6f3d8b Author: Bruno Salas <[email protected]> Date: Wed Jul 29 11:05:47 2026 +0200 Añadir filtro de tareas pendientes app.js | 18 ++++++++++++++++-- index.html | 6 ++++++ 2 files changed, 22 insertions(+), 2 deletions(-)
Es la vista más útil para hacerse una idea rápida del tamaño y el alcance de cada cambio sin leer el código. Variantes:
git log --shortstat -3 # solo la línea de totales
git log --name-only -3 # solo los nombres de fichero
git log --name-status -3 # nombres con M/A/D/R--patch (o -p): el diff completo
commit b2e6d3f8a1c5e9d2b4f7a3c6e8d1b5f9a2c4e7d3 (HEAD -> main) Author: Bruno Salas <[email protected]> Date: Fri Jul 31 09:14:22 2026 +0200 Corregir el foco del campo tras añadir una tarea Tras enviar el formulario el cursor se perdía y había que volver a pulsar en el campo. Ahora se devuelve el foco. diff --git a/app.js b/app.js index 3c9d4a2..5f8b2e1 100644 --- a/app.js +++ b/app.js @@ -58,5 +58,6 @@ document.querySelector('#form-tarea').addEventListener('submit', function (event if (campo.value.trim() !== '') { anadirTarea(campo.value.trim()); campo.value = ''; + campo.focus(); } });
Muestra el historial con el diff de cada confirmación, en el formato unified diff que aprendiste a leer en la lección anterior. Es la forma más completa de revisar trabajo, y también la más larga: úsala siempre con un límite (-1, -5) o combinada con un filtro.
Una combinación especialmente valiosa:
Muestra la evolución completa de un solo fichero, confirmación a confirmación, con el diff de cada una. Es como ver una película del fichero desde su nacimiento.
- Formatos personalizados con
--pretty=format:
--pretty=format:Cuando ninguno de los formatos predefinidos encaja, puedes diseñar el tuyo con una plantilla:
b2e6d3f · Bruno Salas · hace 5 horas · Corregir el foco del campo tras añadir una tarea 7c1f4a9 · Bruno Salas · hace 20 horas · Aplicar estilo a las tareas completadas 3d5b8e1 · Bruno Salas · hace 2 días · Añadir filtro de tareas pendientes c5d9b1e · Ana Ferrer · hace 5 días · Documentar la instalación en el README 4e7f2a9 · Ana Ferrer · hace 7 días · Añadir borrado de tareas al listado 8b6d3c2 · Ana Ferrer · hace 9 días · Añadir estilos base del listado 1a4c8d6 · Ana Ferrer · hace 11 días · Estructura inicial del gestor de tareas
Los marcadores empiezan por %. Estos son los que de verdad se usan:
| Marcador | Contenido | Ejemplo |
|---|---|---|
%H |
Hash completo | b2e6d3f8a1c5e9d2b4f7a3c6e8d1b5f9a2c4e7d3 |
%h |
Hash abreviado | b2e6d3f |
%T / %t |
Hash del árbol (completo / abreviado) | 9f4c2a8 |
%P / %p |
Hash de los padres | 7c1f4a9 |
%an |
Nombre del autor | Bruno Salas |
%ae |
Correo del autor | [email protected] |
%ad |
Fecha de autoría | Fri Jul 31 09:14:22 2026 +0200 |
%ar |
Fecha de autoría relativa | hace 5 horas |
%as |
Fecha de autoría corta | 2026-07-31 |
%cn / %ce / %cd / %cr |
Lo mismo para el committer | |
%s |
Asunto (primera línea del mensaje) | Corregir el foco… |
%b |
Cuerpo del mensaje | |
%d |
Referencias (ramas, etiquetas) | (HEAD -> main) |
%D |
Referencias sin paréntesis | HEAD -> main |
%n |
Salto de línea | |
%% |
Un signo de porcentaje literal |
Añadir color
Los marcadores %C... controlan el color y hacen la salida mucho más legible:
git log --pretty=format:"%C(yellow)%h%C(reset) %C(blue)%ad%C(reset) %C(green)%an%C(reset) %s" --date=shortb2e6d3f 2026-07-31 Bruno Salas Corregir el foco del campo tras añadir una tarea 7c1f4a9 2026-07-30 Bruno Salas Aplicar estilo a las tareas completadas 3d5b8e1 2026-07-29 Bruno Salas Añadir filtro de tareas pendientes c5d9b1e 2026-07-26 Ana Ferrer Documentar la instalación en el README
Colores disponibles: red, green, yellow, blue, magenta, cyan, white, y %C(auto) para que Git decida. %C(reset) vuelve al color normal, y %C(bold …) da negrita.
Controlar el formato de la fecha
git log --date=short --pretty=format:"%ad %s" # 2026-07-31
git log --date=relative --pretty=format:"%ad %s" # hace 5 horas
git log --date=iso --pretty=format:"%ad %s" # 2026-07-31 09:14:22 +0200
git log --date=format:"%d/%m/%Y" --pretty=format:"%ad %s" # 31/07/2026Un alias que vale la pena
Esta plantilla es un clásico y merece guardarse como alias:
git config --global alias.lg "log --graph --pretty=format:'%C(yellow)%h%C(reset)%C(auto)%d%C(reset) %s %C(dim)(%ar) <%an>%C(reset)' --abbrev-commit"* b2e6d3f (HEAD -> main) Corregir el foco del campo tras añadir una tarea (hace 5 horas) <Bruno Salas> * 7c1f4a9 Aplicar estilo a las tareas completadas (hace 20 horas) <Bruno Salas> * 3d5b8e1 Añadir filtro de tareas pendientes (hace 2 días) <Bruno Salas> * c5d9b1e (origin/main, origin/HEAD) Documentar la instalación en el README (hace 5 días) <Ana Ferrer> * 4e7f2a9 Añadir borrado de tareas al listado (hace 7 días) <Ana Ferrer>
Los alias se guardan en el ~/.gitconfig con el mecanismo que vimos en Configurando Git, y se tratan a fondo en Git Log y Alias.
- Filtrar el historial: cantidad, fechas, autor y mensaje
En un proyecto de dos años, git log devuelve miles de entradas. Los filtros son lo que convierte el comando en una herramienta de búsqueda.
Por cantidad
Por fecha
git log --since="2026-07-29"
git log --after="2026-07-29" # sinónimo de --since
git log --until="2026-07-30"
git log --before="2026-07-30" # sinónimo de --until
# Combinadas: una ventana temporal
git log --since="2026-07-25" --until="2026-07-30" --onelineGit acepta expresiones en lenguaje natural (en inglés), lo cual es muy cómodo:
git log --since="2 weeks ago"
git log --since="yesterday"
git log --since="last monday"
git log --since="3 days ago" --until="1 day ago"Por autor
c5d9b1e Documentar la instalación en el README 4e7f2a9 Añadir borrado de tareas al listado 8b6d3c2 Añadir estilos base del listado 1a4c8d6 Estructura inicial del gestor de tareas
El valor es una expresión regular que se busca tanto en el nombre como en el correo, así que basta con un fragmento:
git log --author="ejemplo.es" # todo el equipo
git log --author="Ana\|Bruno" # cualquiera de los dosY el filtro simétrico para el confirmador:
Por mensaje
También es una expresión regular, y por defecto distingue mayúsculas. Para ignorarlas:
Varios --grep se combinan con O lógico por defecto; --all-match los convierte en Y:
git log --grep="tarea" --grep="estilo" --oneline # cualquiera de los dos
git log --grep="tarea" --grep="estilo" --all-match --oneline # ambos a la vezY ojo con esto: cuando combinas filtros de distinto tipo, se aplican con Y lógico:
Confirmaciones de Bruno, del 30 de julio en adelante, cuyo mensaje contenga "estilo". Una sola.
- Filtrar por ruta y la búsqueda "pickaxe" (
-S y -G)
-S y -G)Por ruta
Solo las confirmaciones que modificaron ese fichero. Igual que en git diff, el -- separa opciones de rutas y es obligatorio cuando el nombre podría confundirse con una rama.
Acepta directorios y patrones:
Combinado con -p, es la mejor forma de entender cómo llegó un fichero a ser lo que es:
--follow sigue el fichero a través de renombrados. Sin él, el historial se cortaría en el momento en que el fichero cambió de nombre. Solo funciona con un fichero a la vez.
La búsqueda "pickaxe": -S
Aquí llega una de las capacidades más útiles y menos conocidas de Git. -S busca las confirmaciones en las que cambió el número de apariciones de una cadena. En la práctica: cuándo se introdujo o se eliminó ese texto.
Una sola confirmación: aquella en la que apareció la función. Si más adelante alguien la borrara, esa confirmación también saldría.
Compara con --grep, que es una cosa completamente distinta:
| Opción | Busca en | Responde a |
|---|---|---|
--grep="X" |
El mensaje del commit | ¿Quién escribió "X" en un mensaje? |
-S "X" |
El contenido de los ficheros | ¿Cuándo apareció o desapareció "X" en el código? |
-G "X" |
El contenido, por expresión regular | ¿Qué commits tocaron líneas que casan con "X"? |
El caso de uso estrella de -S es este: encuentras en el código una línea rara, quieres saber por qué está ahí, y git blame solo te dice quién la tocó por última vez. Con -S encuentras la confirmación original que la introdujo, con su mensaje explicativo.
Muestra la confirmación donde apareció esa declaración CSS, con su diff. Búsqueda quirúrgica.
Para verlo con más detalle:
-G: la variante por expresión regular
La diferencia con -S es sutil pero importante:
-S "texto"encuentra los commits donde cambió el número de veces que aparece ese texto. Si mueves una línea de sitio dentro del mismo fichero, el número no cambia y-Sno la encuentra.-G "regex"encuentra todos los commits cuyo diff contiene alguna línea añadida o eliminada que case con la expresión. Un movimiento sí aparece.
Regla práctica: -S para "¿cuándo se introdujo esto?"; -G para "¿qué commits tocaron algo parecido a esto?".
Ambos aceptan --pickaxe-regex y se combinan con todos los demás filtros:
git show: inspeccionar una confirmación
git show: inspeccionar una confirmaciónCuando ya has localizado la confirmación que te interesa, git show te la enseña entera:
commit 3d5b8e1c4a7f2d9b6e3c8a1f5d2b7e4c9a6f3d8b Author: Bruno Salas <[email protected]> Date: Wed Jul 29 11:05:47 2026 +0200 Añadir filtro de tareas pendientes diff --git a/app.js b/app.js index 5f8b2e1..8c3a7d9 100644 --- a/app.js +++ b/app.js @@ -30,6 +30,9 @@ function pintarLista() { const lista = document.querySelector('#lista-tareas'); lista.innerHTML = ''; - for (const tarea of tareas) { + const visibles = soloPendientes + ? tareas.filter(function (t) { return !t.hecha; }) + : tareas; + for (const tarea of visibles) { ...
Metadatos y diff en una sola vista. Es equivalente a git log -p -1 <commit>, pero más directo.
git show es más versátil de lo que parece, porque acepta cualquier objeto de Git:
# Una confirmación
git show 3d5b8e1
# La confirmación actual
git show
git show HEAD
# Solo el resumen por ficheros
git show --stat 3d5b8e1
# Solo el mensaje, sin diff
git show --no-patch 3d5b8e1
git show -s 3d5b8e1 # abreviado
# Solo el nombre de los ficheros afectados
git show --name-only 3d5b8e1
# Un fichero TAL COMO ESTABA en esa confirmación
git show 3d5b8e1:app.js
# El mismo fichero hace tres confirmaciones
git show HEAD~3:estilos.css
# Un árbol (el contenido de un directorio en ese momento)
git show 3d5b8e1^{tree}La sintaxis <commit>:<ruta> es especialmente útil. Ya la usamos en el módulo anterior para comprobar qué versión de un fichero había quedado registrada. Sirve, por ejemplo, para recuperar una versión antigua sin tocar nada más:
Y combinada con -s y un formato, git show sirve para extraer datos concretos:
- Cómo referirse a una confirmación
Llevamos usando HEAD, HEAD~1 y hashes sin haber sistematizado la notación. Vamos a cerrarla, porque aparece en prácticamente todos los comandos de Git.
El hash
git show b2e6d3f8a1c5e9d2b4f7a3c6e8d1b5f9a2c4e7d3 # completo
git show b2e6d3f # abreviado
git show b2e6 # aún más cortoBasta con escribir un prefijo inequívoco. Git necesita al menos 4 caracteres y usa 7 por defecto al mostrar. En repositorios muy grandes puede hacer falta alguno más; si el prefijo es ambiguo, Git te avisa:
Las referencias simbólicas
| Referencia | Significa |
|---|---|
HEAD |
La confirmación en la que estás ahora |
main |
La confirmación a la que apunta la rama main |
origin/main |
La última confirmación conocida de main en el remoto |
v1.0 |
La confirmación etiquetada como v1.0 |
@ |
Sinónimo abreviado de HEAD |
Los operadores ~ y ^
Estos dos son los que confunden, y la diferencia solo importa cuando hay fusiones (commits con más de un padre):
| Notación | Significa |
|---|---|
HEAD~1 o HEAD~ |
El primer padre de HEAD (la confirmación anterior) |
HEAD~2 |
El primer padre del primer padre: dos confirmaciones atrás |
HEAD~n |
n confirmaciones atrás, siguiendo siempre el primer padre |
HEAD^1 o HEAD^ |
El primer padre de HEAD — idéntico a HEAD~1 |
HEAD^2 |
El segundo padre de HEAD (solo existe en una fusión) |
HEAD^^ |
El padre del padre — idéntico a HEAD~2 |
En resumen:
~recorre generaciones hacia atrás por la línea principal.~3= tres pasos atrás.^elige entre los padres de un mismo commit.^2= el segundo padre.
En un historial lineal como el de Bruno, HEAD~2 y HEAD^^ son exactamente lo mismo:
git rev-parse HEAD~2
# → 3d5b8e1c4a7f2d9b6e3c8a1f5d2b7e4c9a6f3d8b
git rev-parse HEAD^^
# → 3d5b8e1c4a7f2d9b6e3c8a1f5d2b7e4c9a6f3d8bEl diagrama lo aclara:
graph RL
C7["b2e6d3f<br/>HEAD"] --> C6["7c1f4a9<br/>HEAD~1<br/>HEAD^"]
C6 --> C5["3d5b8e1<br/>HEAD~2<br/>HEAD^^"]
C5 --> C4["c5d9b1e<br/>HEAD~3"]
C4 --> C3["4e7f2a9<br/>HEAD~4"]
C3 --> C2["8b6d3c2<br/>HEAD~5"]
C2 --> C1["1a4c8d6<br/>HEAD~6<br/>(root-commit)"]
Las flechas apuntan hacia atrás porque así es como funciona Git: cada commit conoce a su padre, no a sus hijos. Por eso es fácil recorrer el historial hacia el pasado y no existe una notación sencilla para ir hacia el futuro.
Y cuando haya fusiones (módulo 3), ^ cobrará sentido:
graph RL
M["9f3a2c1<br/>fusión"] -->|"^1 (o ~1)"| A["8d4e6b2<br/>main"]
M -->|"^2"| B["5e8b1d4<br/>rama filtros"]
Rangos
Dos puntos definen un rango de confirmaciones:
Se lee como "las confirmaciones alcanzables desde c5d9b1e pero no desde 8b6d3c2". Es decir: lo que hay después de 8b6d3c2 hasta c5d9b1e incluido. Fíjate en que el extremo izquierdo queda fuera.
Formas muy usadas:
git log HEAD~3..HEAD --oneline # las 3 últimas confirmaciones
git log origin/main..HEAD --oneline # lo que tengo y el remoto no
git log HEAD..origin/main --oneline # lo que tiene el remoto y yo noEse penúltimo es exactamente lo que Bruno tiene pendiente de enviar:
b2e6d3f Corregir el foco del campo tras añadir una tarea 7c1f4a9 Aplicar estilo a las tareas completadas 3d5b8e1 Añadir filtro de tareas pendientes
Sus tres confirmaciones locales. Todo esto se desarrollará en el módulo 4.
Resolver cualquier referencia
git rev-parse traduce cualquier notación a un hash completo, y es la forma de salir de dudas:
git rev-parse HEAD~3 # → c5d9b1e...
git rev-parse main # → b2e6d3f...
git rev-parse --short HEAD # → b2e6d3f
- Combinaciones útiles para el día a día
Un recetario de consultas que resuelven preguntas reales:
# ¿Qué se ha hecho esta semana?
git log --since="1 week ago" --oneline
# ¿Qué he hecho yo esta semana? (para el informe del viernes)
git log --author="$(git config user.name)" --since="1 week ago" --oneline
# ¿Cuántas confirmaciones tiene el proyecto?
git rev-list --count HEAD
# ¿Quién ha contribuido y cuánto?
git shortlog -sn# ¿Cómo evolucionó este fichero?
git log -p --follow -- estilos.css
# ¿Cuándo se introdujo esta cadena?
git log -S "actualizarContador" --oneline
# ¿Qué ficheros toca más el proyecto?
git log --name-only --pretty=format: | sort | uniq -c | sort -rn | head
# La última confirmación que tocó cada fichero
git log --name-status -5
# Historial en una línea con fecha y autor
git log --pretty=format:"%h %as %an %s"
# ¿Qué confirmaciones NO están en el remoto?
git log origin/main..HEAD --onelineY una advertencia práctica: git log abre un paginador. Se recorre con las flechas o la barra espaciadora, se busca con /texto y se sale con q. Si prefieres la salida directa:
Errores Comunes y Consejos
- Confundir
--grepcon-S.--grepbusca en el mensaje de la confirmación;-Sbusca en el contenido de los ficheros. Son preguntas completamente distintas y es el error más frecuente al buscar en el historial. - Olvidar el
--antes de una ruta.git log estilos.cssfunciona si no hay ambigüedad, perogit log maininterpretarámaincomo una rama. Pon--siempre que puedas dudar. - Esperar que
git logmuestre todas las ramas. Por defecto solo recorre el historial alcanzable desdeHEAD. Para verlo todo,--all. - Interpretar mal los rangos
a..b. El extremo izquierdo no se incluye.git log HEAD~3..HEADdevuelve tres confirmaciones, no cuatro. - Usar
~cuando se quiere^. En un historial lineal son intercambiables, pero en una fusiónHEAD^2(el segundo padre) yHEAD~2(dos generaciones atrás) apuntan a sitios distintos. - Quedarse atrapado en el paginador. Se sale con
q. Es una de las primeras frustraciones de todo el mundo con Git. - Buscar en
git log -pa ojo. Si buscas dónde apareció un texto concreto,-Ste lo da en un comando en lugar de hacerte leer cien diffs. - No usar
--followal mirar el historial de un fichero renombrado. Verás un historial que "empieza" en el renombrado y concluirás erróneamente que el fichero es reciente. - Consejo: define el alias
lgdel apartado 5. Lo usarás a diario y hace el historial mucho más legible. - Consejo:
git shortlog -snes la forma más rápida de saber quién trabaja en un proyecto que acabas de clonar. Ygit log --oneline -20te da el pulso de lo que se está haciendo ahora mismo. - Consejo: si quieres exportar el historial para un informe,
--pretty=format:combinado con--date=shortproduce líneas fáciles de procesar con cualquier hoja de cálculo.
Ejercicios
Ejercicio 1: Consultas sobre el historial de gestor-tareas
Partiendo del historial de Bruno tal como aparece en el apartado 1:
b2e6d3f Bruno 2026-07-31 Corregir el foco del campo tras añadir una tarea (app.js) 7c1f4a9 Bruno 2026-07-30 Aplicar estilo a las tareas completadas (estilos.css) 3d5b8e1 Bruno 2026-07-29 Añadir filtro de tareas pendientes (app.js, index.html) c5d9b1e Ana 2026-07-26 Documentar la instalación en el README (README.md) 4e7f2a9 Ana 2026-07-24 Añadir borrado de tareas al listado (app.js) 8b6d3c2 Ana 2026-07-22 Añadir estilos base del listado (estilos.css, index.html) 1a4c8d6 Ana 2026-07-20 Estructura inicial del gestor de tareas (los 4 ficheros)
Escribe el comando exacto para cada consulta y predice su salida:
- Las tres últimas confirmaciones, en una línea cada una.
- Todas las confirmaciones de Ana.
- Todo lo hecho entre el 23 y el 27 de julio, ambos incluidos.
- Las confirmaciones que tocaron
estilos.css. - Las confirmaciones cuyo mensaje contenga "tarea", sin distinguir mayúsculas.
- Un listado con formato
hash | fecha corta | autor | asunto. - Cuántas confirmaciones hay en total y cuántas ha hecho cada persona.
- El contenido de
README.mdtal como estaba en4e7f2a9.
Ejercicio 2: La búsqueda pickaxe
Crea un repositorio con un historial en el que una línea aparece, se mueve y desaparece:
mkdir -p ~/practica/pickaxe && cd ~/practica/pickaxe
git init
printf 'function inicio() {\n return true;\n}\n' > app.js
git add app.js && git commit -m "Añadir la función de inicio"
printf 'function inicio() {\n console.log("DEPURACION: entrando");\n return true;\n}\n' > app.js
git commit -am "Añadir traza de depuración"
printf 'function inicio() {\n return true;\n}\n\nfunction fin() {\n console.log("DEPURACION: entrando");\n return false;\n}\n' > app.js
git commit -am "Mover la traza a la función fin y añadirla"
printf 'function inicio() {\n return true;\n}\n\nfunction fin() {\n return false;\n}\n' > app.js
git commit -am "Eliminar la traza de depuración"- ¿Qué devuelve
git log --oneline -S "DEPURACION"? Explica por qué aparece cada confirmación y por qué falta alguna. - ¿Qué devuelve
git log --oneline -G "DEPURACION"? Compara con el resultado anterior y explica la diferencia. - ¿Qué devuelve
git log --oneline --grep="DEPURACION"? ¿Y--grep="depuración"? - Escribe el comando que muestre el diff de la confirmación exacta en que se eliminó la traza.
- Redacta en una frase la regla que te permitirá elegir entre
-S,-Gy--grepen el futuro.
Ejercicio 3: Navegar por referencias
Sobre el repositorio del ejercicio 2 (cuatro confirmaciones, historial lineal):
- Escribe cuatro formas distintas de referirte a la segunda confirmación del historial (la que añadió la traza).
- Comprueba con
git rev-parsequeHEAD~2yHEAD^^apuntan al mismo objeto. - Muestra el contenido de
app.jstal como estaba en la primera confirmación, sin modificar tu directorio de trabajo. - Muestra únicamente los mensajes de las dos últimas confirmaciones, sin diff.
- Usa un rango para listar solo las dos últimas confirmaciones y explica por qué
HEAD~2..HEADdevuelve dos y no tres. - Averigua qué ficheros modificó cada una de las cuatro confirmaciones con un solo comando.
Soluciones
Solución al Ejercicio 1
1. Las tres últimas:
b2e6d3f (HEAD -> main) Corregir el foco del campo tras añadir una tarea 7c1f4a9 Aplicar estilo a las tareas completadas 3d5b8e1 Añadir filtro de tareas pendientes
2. Las de Ana:
c5d9b1e Documentar la instalación en el README 4e7f2a9 Añadir borrado de tareas al listado 8b6d3c2 Añadir estilos base del listado 1a4c8d6 Estructura inicial del gestor de tareas
3. Entre el 23 y el 27, ambos incluidos:
Atención al detalle: --until="2026-07-27" se interpreta como las 00:00 de ese día, así que excluiría las confirmaciones del propio 27. Como aquí queremos incluir el día 27 completo, ponemos --until="2026-07-28". Es un error clásico que hace "desaparecer" confirmaciones del último día del rango.
4. Las que tocaron estilos.css:
Nota: 1a4c8d6 creó el fichero, así que en un historial real también aparecería. En el enunciado se indica que la confirmación inicial contenía los cuatro ficheros, de modo que la salida completa sería de tres entradas.
5. Mensajes con "tarea", sin distinguir mayúsculas:
b2e6d3f Corregir el foco del campo tras añadir una tarea 7c1f4a9 Aplicar estilo a las tareas completadas 3d5b8e1 Añadir filtro de tareas pendientes 4e7f2a9 Añadir borrado de tareas al listado 1a4c8d6 Estructura inicial del gestor de tareas
Cinco de las siete. Quedan fuera c5d9b1e ("Documentar la instalación en el README") y 8b6d3c2 ("Añadir estilos base del listado"), cuyos mensajes no contienen la palabra.
6. Formato personalizado:
b2e6d3f | 2026-07-31 | Bruno Salas | Corregir el foco del campo tras añadir una tarea 7c1f4a9 | 2026-07-30 | Bruno Salas | Aplicar estilo a las tareas completadas 3d5b8e1 | 2026-07-29 | Bruno Salas | Añadir filtro de tareas pendientes c5d9b1e | 2026-07-26 | Ana Ferrer | Documentar la instalación en el README 4e7f2a9 | 2026-07-24 | Ana Ferrer | Añadir borrado de tareas al listado 8b6d3c2 | 2026-07-22 | Ana Ferrer | Añadir estilos base del listado 1a4c8d6 | 2026-07-20 | Ana Ferrer | Estructura inicial del gestor de tareas
7. Totales:
8. El README en 4e7f2a9:
Muestra el contenido del fichero tal como estaba en esa confirmación, sin tocar el directorio de trabajo. Si quisiéramos guardarlo aparte:
Solución al Ejercicio 2
1. Con -S:
Dos confirmaciones. -S busca cambios en el número de apariciones de la cadena:
9b3e5d7: pasó de 0 apariciones a 1. Cambió → aparece.1f4c8a2: pasó de 1 a 0. Cambió → aparece.
Falta 7a2d6f9 ("Mover la traza a la función fin y añadirla"). Y aquí está el matiz que hay que entender: en esa confirmación la traza se movió de inicio() a fin(), así que sigue apareciendo una sola vez. Como el recuento no varía (1 → 1), -S la considera irrelevante y no la muestra.
2. Con -G:
1f4c8a2 Eliminar la traza de depuración 7a2d6f9 Mover la traza a la función fin y añadirla 9b3e5d7 Añadir traza de depuración
Tres confirmaciones. -G no cuenta apariciones: mira si el diff de la confirmación contiene alguna línea añadida o eliminada que case con la expresión. En 7a2d6f9 hay una línea con DEPURACION eliminada y otra añadida, así que sí aparece.
Esta es exactamente la diferencia entre ambas opciones, y el ejercicio la aísla en su forma más pura.
3. Con --grep:
Ninguna, porque --grep busca en el mensaje, y ningún mensaje contiene la palabra en mayúsculas.
Dos: las que llevan esa palabra en el mensaje. Que coincidan con el resultado de -S es pura casualidad del ejemplo; se debe a que los mensajes describen bien lo que hacen.
4. El diff de la eliminación:
O, más directo y más legible, localizar el commit y mostrarlo:
commit 1f4c8a2...
Eliminar la traza de depuración
diff --git a/app.js b/app.js
@@ -4,4 +4,3 @@ function inicio() {
function fin() {
- console.log("DEPURACION: entrando");
return false;
}Una forma robusta de encadenarlo, sin conocer el hash de antemano:
que muestra la confirmación más reciente en la que cambió el recuento de esa cadena: precisamente la eliminación.
5. La regla:
--grepbusca en el mensaje ("¿quién dijo que hizo esto?");-Sbusca cuándo apareció o desapareció un texto en el código ("¿de dónde salió esta línea?");-Gbusca qué confirmaciones tocaron líneas que casan con un patrón, incluidos los movimientos ("¿quién ha andado por aquí?").
Solución al Ejercicio 3
1. Cuatro formas de referirse a la segunda confirmación (con HEAD en la cuarta):
git show 9b3e5d7 # 1. hash abreviado
git show HEAD~2 # 2. dos generaciones atrás
git show HEAD^^ # 3. el padre del padre
git show HEAD~1^ # 4. el padre de la anteriorY hay más: el hash completo, main~2, @~2… Todas resuelven al mismo objeto.
2. Comprobación:
git rev-parse HEAD~2
# → 9b3e5d7f4a2c8e1b5d3f7a9c2e6b4d8f1a3c5e7b
git rev-parse HEAD^^
# → 9b3e5d7f4a2c8e1b5d3f7a9c2e6b4d8f1a3c5e7bHashes idénticos. En un historial lineal ~n y n símbolos ^ son equivalentes, porque cada commit tiene un único padre y "elegir el primer padre" y "retroceder una generación" son la misma operación. La diferencia solo emerge en las fusiones.
3. El fichero en la primera confirmación:
git show <commit>:<ruta> lee del historial y escribe en la salida estándar. No modifica nada, a diferencia de git restore --source=..., que sí sobrescribiría el fichero del disco.
4. Solo los mensajes de las dos últimas:
O, si prefieres la vista completa sin el diff:
5. El rango:
Dos confirmaciones, no tres, porque el extremo izquierdo del rango queda excluido. a..b significa "todo lo alcanzable desde b que no lo sea desde a", y HEAD~2 es alcanzable desde sí mismo, así que se descarta. La forma de leerlo es: "lo que ha pasado después de HEAD~2".
Si quisieras incluir también HEAD~2, tendrías que escribir HEAD~3..HEAD.
6. Ficheros modificados por cada confirmación:
1f4c8a2 Eliminar la traza de depuración M app.js 7a2d6f9 Mover la traza a la función fin y añadirla M app.js 9b3e5d7 Añadir traza de depuración M app.js 4c1e8b5 Añadir la función de inicio A app.js
Las tres últimas confirmaciones modifican app.js (M) y la primera lo añade (A), lo cual es coherente: era el root-commit y el fichero no existía antes.
Conclusión
Con esta lección cerramos el ciclo básico de Git y el módulo 2. Recapitulando lo aprendido aquí:
git logrecorre el historial hacia atrás desdeHEAD, en orden cronológico inverso, siguiendo los enlaces a los padres.- Los formatos cambian por completo lo que ves:
--onelinepara la vista de trabajo,--graphpara la estructura (imprescindible en cuanto haya ramas),--statpara el alcance de cada cambio y-ppara el diff completo. --pretty=format:permite diseñar tu propia salida con marcadores como%h,%an,%ary%s, y merece la pena guardarla como alias.- Los filtros convierten
git logen un buscador:-npor cantidad,--since/--untilpor fecha,--authorpor persona,--greppor mensaje y-- <ruta>por fichero. Filtros de distinto tipo se combinan con Y lógico. - La búsqueda "pickaxe" es la joya escondida:
-Sencuentra cuándo apareció o desapareció un texto en el código, y-Gqué confirmaciones tocaron líneas que casan con un patrón. No confundir ninguna de las dos con--grep, que mira el mensaje. git showinspecciona una confirmación concreta —metadatos y diff— y con la sintaxis<commit>:<ruta>recupera cualquier fichero tal como estaba en cualquier momento del pasado.- Referirse a una confirmación se puede hacer por hash (completo o abreviado), por referencia simbólica (
HEAD,main,origin/main, una etiqueta) o por navegación relativa:~retrocede generaciones y^elige entre padres. Los rangosa..bexcluyen el extremo izquierdo.
Lo que llevas del módulo 2
Has recorrido el ciclo completo de trabajo con Git en solitario:
- Crear el repositorio (
git init) o unirte a uno existente (git clone). - Trabajar siguiendo el ciclo editar → preparar → confirmar, con
git statuscomo brújula. - Elegir con precisión qué entra en cada confirmación, incluso a nivel de fragmento con
git add -p. - Revisar los cambios con
git diffantes de registrarlos. - Consultar el pasado con
git logygit show.
Con esto ya puedes usar Git de forma productiva en cualquier proyecto propio. Pero hay un problema esperando.
Lo que viene: el equipo empieza a pisarse
Fíjate en la situación real en la que están Ana y Bruno ahora mismo. Los dos han estado trabajando sobre main, cada uno en su portátil, sin coordinarse. Ana ha añadido el contador de tareas y el marcado de completadas; Bruno ha añadido el filtro de pendientes y ha tocado los mismos ficheros. Ninguno de los dos ha visto el trabajo del otro.
Cuando intenten juntar todo eso, se encontrarán con dos versiones distintas de app.js que han evolucionado por separado desde el mismo punto de partida. Y esto es solo con dos personas y una semana de trabajo: cuando Carla se incorpore desde Windows y haya tres líneas de desarrollo simultáneas, trabajar todos sobre main será insostenible.
Además, hay un problema más sutil. Ahora mismo, si Ana quiere probar una idea arriesgada, no tiene dónde hacerlo: cualquier cosa que confirme va directa a la única línea del proyecto. Y si a mitad de una tarea urgente aparece un fallo crítico, tiene que abandonar lo que está haciendo o confirmarlo a medias.
La respuesta a todo esto son las ramas, y son la característica que convirtió a Git en el estándar de la industria. En el módulo 3: Ramas y Fusión veremos qué es realmente una rama (spoiler: ya lo sabes, es un fichero con un hash dentro, como comprobamos al crear el primer commit), cómo crearlas y moverse entre ellas, cómo fusionar el trabajo de varias personas, qué estrategias usa Git para hacerlo, cómo resolver los conflictos cuando dos personas tocan la misma línea, y cómo mantener ordenado un repositorio con muchas ramas vivas.
Todo lo que has aprendido en este módulo —las tres zonas, el área de preparación, el diff, el historial— sigue siendo válido exactamente igual. Solo que a partir de ahora habrá más de una línea de trabajo a la vez.
Dominando Git: De Principiante a Avanzado
Módulo 1: Introducción a Git
- ¿Qué es Git?
- Instalando Git
- Terminología Básica de Git
- El Modelo de Datos de Git
- Configurando Git
- Configuración Inicial
Módulo 2: Operaciones Básicas de Git
- Creando un Repositorio
- Clonando un Repositorio
- Flujo de Trabajo Básico de Git
- Preparando y Confirmando Cambios
- Inspeccionando Cambios con git diff
- Visualizando el Historial de Confirmaciones
Módulo 3: Ramas y Fusión
- Entendiendo las Ramas
- Creando y Cambiando Ramas
- Fusionando Ramas
- Estrategias de Fusión
- Resolviendo Conflictos de Fusión
- Gestión de Ramas
Módulo 4: Trabajando con Repositorios Remotos
- Entendiendo los Repositorios Remotos
- Agregando un Repositorio Remoto
- Autenticación con Repositorios Remotos
- Obteniendo y Extrayendo Cambios
- Enviando Cambios
- Rastreando Ramas
Módulo 5: Operaciones Avanzadas de Git
- Rebase
- Rebase Interactivo
- Cherry-Picking de Confirmaciones
- Guardando Cambios Temporales
- Etiquetando Confirmaciones
- Revirtiendo Confirmaciones
Módulo 6: Herramientas y Técnicas de Git
- Usando Git Hooks
- Git Bisect
- Git Blame
- Git Log y Alias
- Submódulos de Git
- Múltiples Copias de Trabajo con git worktree
Módulo 7: Estrategias de Colaboración y Flujo de Trabajo
- Forks y Pull Requests
- Revisiones de Código con Git
- Flujo de Trabajo Git Flow
- GitHub Flow
- Trunk Based Development
- Integración Continua con Git
Módulo 8: Mejores Prácticas y Consejos de Git
- Escribiendo Buenos Mensajes de Confirmación
- Manteniendo un Historial Limpio
- Ignorando Archivos con .gitignore
- Atributos de Fichero con .gitattributes
- Mejores Prácticas de Seguridad
- Consejos de Rendimiento
Módulo 9: Solución de Problemas y Depuración
- Problemas Comunes de Git
- Deshaciendo Cambios
- Resolviendo Divergencias con el Remoto
- Recuperando Confirmaciones Perdidas
- Tratando con Repositorios Corruptos
- Técnicas Avanzadas de Depuración
