La lección anterior dejó a Diego con su pull request abierta y a Ana a punto de revisarla. Ahora toca lo que ocurre dentro de ese canal: la revisión de código.

Casi todo el mundo revisa código de la misma manera: abre la pestaña de cambios en el navegador, hace scroll, deja tres comentarios sobre nombres de variables y aprueba. Eso no es revisar; es hojear. Una revisión de verdad requiere entender el problema, entender la solución, y muchas veces ejecutar el código. Y para eso hace falta Git, no un navegador.

Esta lección tiene dos mitades. La primera es técnica: qué comandos usar para revisar bien, incluyendo uno —git range-diff— que resuelve un problema que todo el mundo sufre y casi nadie sabe que tiene solución. La segunda es humana: qué mirar, en qué orden, cómo comentar sin desmoralizar y qué hacer cuando dos personas no se ponen de acuerdo. Las dos importan por igual.

Contenido

  1. Para qué sirve realmente una revisión (y para qué no)
  2. El diff correcto para revisar: por qué tres puntos
  3. Traer la rama y examinarla con Git
  4. Revisar commit a commit
  5. git range-diff: qué ha cambiado desde la revisión anterior
  6. Revisión por correo: request-pull, format-patch y am
  7. Qué mirar, por orden de prioridad
  8. Cómo comentar bien
  9. El tamaño importa: el efecto del tamaño de la PR
  10. Aprobar, pedir cambios y gestionar los desacuerdos

  1. Para qué sirve realmente una revisión (y para qué no)

Antes de los comandos, el propósito. Una revisión de código persigue tres objetivos, y no están en el mismo plano:

1. Detectar defectos. El obvio. Un segundo par de ojos encuentra el caso límite que no contemplaste, la condición invertida, la variable que se lee antes de asignarse. Es real, pero —y esto sorprende a mucha gente— no es el beneficio principal. Las pruebas automáticas y el CI (lección 07-06) atrapan más defectos que cualquier revisión humana, y lo hacen sin cansarse.

2. Difundir conocimiento. Este sí es el grande. Cuando Ana revisa el cambio de Carla en app.js, dos personas acaban entendiendo esa parte del código en lugar de una. Cuando Diego, que viene de fuera, lee los comentarios de Ana, aprende cómo se hacen las cosas en este proyecto. La revisión es el mecanismo por el que un equipo deja de tener islas de conocimiento donde solo una persona sabe cómo funciona algo. Ese "solo Bruno entiende el módulo de sincronización" es un riesgo operativo, y la revisión es la cura barata.

3. Mantener la coherencia. Un código donde cada fichero parece escrito por una persona distinta es más caro de mantener que uno uniforme, aunque las decisiones individuales sean peores. La revisión es donde se negocia esa uniformidad.

Y ahora, para qué no sirve:

No es tarea de la revisión Quién debería hacerlo
Comprobar la indentación y el formato Un formateador automático (Prettier, gofmt, Black)
Detectar variables sin usar, imports muertos El linter
Ejecutar la batería de pruebas El CI (lección 07-06)
Comprobar el formato del mensaje de commit El hook commit-msg (lección 06-01)
Verificar que compila El CI

La regla es demoledora en su simplicidad: si una máquina puede comprobarlo, que lo compruebe la máquina. Cada comentario humano sobre un espacio en blanco es un comentario que no se ha dedicado a la lógica, y además genera resentimiento. Si tu equipo discute sobre comillas simples o dobles en las revisiones, el problema no son las comillas: es que falta un formateador en el pre-commit.

Esta idea tiene una consecuencia directa en cómo se diseña el proceso: las herramientas automáticas van primero, en el hook local o en el CI, y la revisión humana empieza donde ellas acaban. Si el CI está en rojo, no revises: no tiene sentido gastar atención humana en algo que ni siquiera pasa las pruebas.

  1. El diff correcto para revisar: por qué tres puntos

Este apartado es el corazón técnico de la lección, y merece que lo leas despacio.

Ana quiere ver "lo que ha hecho Diego". Parece trivial. No lo es.

Recordemos la situación. Diego partió de main en el commit C3. Mientras trabajaba, el equipo integró dos commits más en main. El grafo (lección 03-01) es este:

gitGraph
   commit id: "C1"
   commit id: "C2"
   commit id: "C3"
   branch correccion/reordenar
   checkout correccion/reordenar
   commit id: "D1"
   commit id: "D2"
   checkout main
   commit id: "C4"
   commit id: "C5"

Ahora las dos opciones:

git diff main..correccion/reordenar (dos puntos)

Los dos puntos, en git diff, son decorativos: git diff A..B es exactamente lo mismo que git diff A B. Compara las dos puntas: el árbol de C5 contra el árbol de D2.

¿Qué muestra eso? La suma de dos cosas distintas:

  • Los cambios de Diego (D1, D2), en su sentido correcto.
  • Los cambios de C4 y C5, del revés — porque desde el punto de vista de C5, la rama de Diego "ha deshecho" lo que hicieron Ana y Carla.

El resultado es un diff contaminado. Ana ve líneas eliminadas que ella misma escribió ayer y que Diego jamás ha tocado. Es ruido puro.

git diff main...correccion/reordenar (tres puntos)

Los tres puntos en git diff significan algo muy concreto:

git diff A...B equivale a git diff $(git merge-base A B) B

Es decir: compara desde el ancestro común. En nuestro grafo, el ancestro común de main y la rama de Diego es C3. Por tanto git diff main...correccion/reordenar compara C3 con D2.

Y eso es exactamente lo que ha hecho Diego, y nada más. Los commits C4 y C5 no aparecen, porque C3 es anterior a ellos.

Compruébalo tú mismo:

# Estos dos comandos dan el mismo resultado
git diff main...correccion/reordenar
git diff $(git merge-base main correccion/reordenar) correccion/reordenar

La tabla resumen, que conviene memorizar:

Forma Qué compara Cuándo usarla
git diff A B Punta de A contra punta de B Comparar dos estados cualesquiera
git diff A..B Idéntico al anterior Nunca; solo confunde
git diff A...B Ancestro común de A y B contra punta de B Revisar una rama

Aviso importante: en git log los puntos significan otra cosa. Esta es una de las trampas más pesadas de Git, y ya la mencionamos en la lección 06-04. En git log, A..B es "los commits de B que no están en A" (lo que quieres para revisar) y A...B es la diferencia simétrica: los commits exclusivos de cada lado. Es decir, para revisar se usan tres puntos en diff y dos puntos en log. No es intuitivo. Es así.

La razón de fondo es que diff opera sobre árboles (dos fotos que comparar) y log opera sobre conjuntos de commits. Son operaciones distintas que reutilizan la misma notación con semánticas distintas. Una decisión histórica desafortunada, pero es la que hay.

Y aquí está la conexión importante: cuando la plataforma te enseña el diff de una pull request, te está enseñando el de tres puntos. Por eso a veces ves en la web un diff limpio y al hacer git diff main rama en local aparecen cambios ajenos. No es un error de la plataforma: es que estabas usando el comando equivocado.

  1. Traer la rama y examinarla con Git

Ana empieza por traerse el trabajo de Diego con la referencia de PR de la lección anterior:

git fetch origin pull/42/head:revision/pr-42

Con eso ya puede trabajar sin cambiar de rama, porque todos los comandos de inspección aceptan referencias:

# 1. ¿Cuántos commits trae y cuáles son?
git log --oneline main..revision/pr-42
a7c2e91 Repintar solo si el orden ha cambiado
b3f1a7d Reordenar la lista al marcar una tarea como completada

Dos puntos, porque es log. Lee: "los commits de la rama que no están en main".

# 2. ¿Qué ficheros toca y cuánto? La foto de conjunto, antes de leer nada.
git diff --stat main...revision/pr-42
 app.js      | 24 ++++++++++++++++--------
 estilos.css |  3 +++
 2 files changed, 19 insertions(+), 8 deletions(-)

Esta es siempre la primera orden que hay que ejecutar. En diez segundos sabes si tienes delante una revisión de cinco minutos o de una hora, y si el alcance coincide con lo que prometía la descripción. Si la PR dice "corrige la reordenación" y toca dieciocho ficheros, ya tienes tu primer comentario.

# 3. El diff completo, con contexto amplio
git diff -U10 main...revision/pr-42

La opción -U10 (lección 02-05) muestra diez líneas de contexto en lugar de tres. En una revisión es casi siempre lo que quieres: el defecto suele estar en la interacción entre lo que cambió y lo que no.

# 4. Diff ignorando cambios de espaciado
git diff -w main...revision/pr-42

Si alguien reindentó una función al modificarla, -w (equivalente a --ignore-all-space) revela el cambio real debajo del ruido. Combinado con --word-diff, muy útil para cambios de documentación:

git diff --word-diff main...revision/pr-42 -- README.md
# 5. Detectar código movido en lugar de reescrito
git diff --color-moved=dimmed-zebra main...revision/pr-42

--color-moved pinta de otro color los bloques que simplemente se han movido de sitio, distinguiéndolos de los que se han escrito de nuevo. En una PR de refactorización, la diferencia entre "ha movido 200 líneas" y "ha reescrito 200 líneas" cambia por completo lo que hay que revisar.

Y por supuesto, ejecutarlo. Aquí es donde git worktree (lección 06-06) se gana el sueldo:

git worktree add ../revision-42 revision/pr-42
cd ../revision-42
# abrir index.html, reproducir los pasos de la descripción de la PR

Ana comprueba el fallo original, aplica el cambio, comprueba que desaparece. Nada de esto se ve en un diff.

  1. Revisar commit a commit

Una PR bien construida (lección 07-01) tiene commits que cuentan una historia. Aprovéchalo: revisar tres commits de cincuenta líneas cada uno es muchísimo más fácil que revisar uno de ciento cincuenta.

# Recorrer los commits uno a uno, con su diff
git log --reverse -p main..revision/pr-42
  • --reverse los presenta del más antiguo al más reciente, que es el orden en que se pensaron.
  • -p añade el diff de cada uno.

Para saltar entre ellos sin salir de la terminal:

# Solo los mensajes, para hacerse el mapa
git log --reverse --format='%h %s%n%n%b' main..revision/pr-42

# El diff de un commit concreto
git show b3f1a7d

Otra vista muy útil: quién ha tocado antes estas líneas. Si el cambio de Diego modifica una función que Bruno escribió hace dos semanas por una razón concreta, conviene saberlo antes de aprobar:

git log -L :renderizarLista:app.js

Y git blame con las opciones del módulo 6:

git blame -L 40,80 -M -C main -- app.js

Fíjate en el main al final: blame sobre la versión anterior al cambio, para entender el contexto que Diego se encontró.

Consejo de método. Lee primero la descripción de la PR, luego el --stat, luego los mensajes de los commits, y solo entonces el código. Llegar al diff sabiendo qué esperas encontrar convierte la revisión en una verificación de hipótesis en lugar de una lectura a ciegas. Es varias veces más rápido y detecta más cosas.

  1. git range-diff: qué ha cambiado desde la revisión anterior

Este apartado es la joya de la lección.

Situación: Ana revisó la PR de Diego y pidió tres cambios. Diego los aplicó y, además, hizo git rebase upstream/main porque main había avanzado. Ahora envía con --force-with-lease y la PR se actualiza.

Ana vuelve. Y se encuentra con un problema real: todos los commits tienen hashes nuevos. El rebase los reescribió (lección 05-01). La plataforma le ofrece "ver los cambios desde tu última revisión", pero a menudo se rinde con un mensaje del tipo "el autor ha forzado el envío, no se puede mostrar la comparación". Ana no sabe si Diego solo aplicó sus tres sugerencias o si de paso cambió otras cosas. Su única opción aparente es revisarlo todo otra vez.

git range-diff resuelve exactamente esto:

git range-diff compara dos series de commits y te dice, para cada uno, si se ha mantenido igual, si ha desaparecido, si es nuevo o qué ha cambiado dentro de él. Es un diff de diffs.

La forma de usarlo requiere haber guardado la versión anterior. Ana, previsora, lo hizo antes de terminar su primera revisión:

# En la primera revisión, Ana guarda una referencia a lo que vio
git branch revision/pr-42-v1 revision/pr-42

Ahora, tras la actualización de Diego:

git fetch origin pull/42/head:revision/pr-42-v2

# Comparar las dos versiones de la serie
git range-diff main...revision/pr-42-v1 main...revision/pr-42-v2

La sintaxis con tres puntos es un atajo. La forma completa es de tres argumentos y a veces es más clara:

git range-diff <base-antigua>..<punta-antigua> <base-nueva>..<punta-nueva>
git range-diff C3..revision/pr-42-v1 C7..revision/pr-42-v2

La salida:

1:  b3f1a7d = 1:  9e4c2a8 Reordenar la lista al marcar una tarea como completada
2:  a7c2e91 ! 2:  4f8b1d3 Repintar solo si el orden ha cambiado
    @@ app.js: function marcarCompletada(id) {
       const ordenPrevio = listaOrdenada.map(t => t.id).join(',');
       actualizarEstado(id, true);
     - if (true) {
     + if (ordenPrevio !== listaOrdenada.map(t => t.id).join(',')) {
           renderizarLista();
       }
3:  -------- > 3:  7a1c5e9 Anadir prueba de reordenacion

Y ahora la clave: cómo se lee esa salida.

Marca Significado
= El commit es equivalente: mismo contenido, aunque el hash cambie por el rebase
! El commit ha cambiado; debajo se muestra el diff de sus diferencias
< El commit estaba en la serie antigua y ya no está
> El commit es nuevo en la serie

En el ejemplo, Ana lee en cinco segundos: el primer commit no ha cambiado (no hay que releerlo), el segundo ha cambiado exactamente en la condición que ella pidió corregir, y hay un tercero nuevo que añade una prueba. Revisión completada. Sin range-diff, habría releído ciento cincuenta líneas.

Detalles prácticos que hacen que funcione mejor:

# Diff de diffs coloreado y más legible
git range-diff --creation-factor=95 main...revision/pr-42-v1 main...revision/pr-42-v2

--creation-factor (por defecto 60) controla cuánto tienen que parecerse dos commits para considerarlos "el mismo commit modificado" en lugar de "uno borrado y otro nuevo". Si range-diff te muestra un montón de < y > cuando esperabas !, sube el valor.

# Usarlo también sobre tu propio trabajo, antes de enviar
git range-diff @{u}...HEAD

Este uso es igual de valioso: antes de forzar el envío tras un rebase, comprueba que no has estropeado nada sin querer. @{u} es el upstream de la rama (lección 04-06), es decir, lo que hay publicado; HEAD es lo que estás a punto de publicar. Si sale todo = salvo lo que querías cambiar, adelante con confianza. Si aparece un commit modificado que no esperabas, acabas de detectar un conflicto mal resuelto durante el rebase.

Y un tercer uso: comparar cómo se aplicó una serie de commits en dos ramas distintas, por ejemplo tras un cherry-pick masivo a una rama de mantenimiento (lección 05-03).

git range-diff v2.3.0..v2.3.1 main~5..main

git range-diff existe desde Git 2.19 (2018) y sigue siendo poco conocido. Es probablemente el comando con mejor relación entre lo que resuelve y lo que se usa. Si te llevas una sola cosa técnica de esta lección, que sea esta.

  1. Revisión por correo: request-pull, format-patch y am

Antes de que existieran las plataformas web, y todavía hoy en proyectos como el núcleo de Linux, Git, PostgreSQL o Buildroot, la revisión ocurre en una lista de correo. Merece la pena conocer el mecanismo por tres razones: explica de dónde viene la expresión "pull request", funciona sin depender de ninguna empresa, y aparece en proyectos importantes con los que puede que quieras colaborar.

git request-pull: la pull request original

git request-pull v2.3.0 https://git.ejemplo.es/drueda/gestor-tareas.git correccion/reordenar

Los tres argumentos son: el punto de partida (una etiqueta o commit que el destinatario ya tiene), la URL desde donde puede traerse el trabajo, y la rama.

La salida es un texto listo para pegar en un correo:

The following changes since commit 3f2a91c8d4b7e0a5c9f2d6b3a8e1c4f7d0b3a6e9:

  Publicar la version 2.3.0 (2026-07-12 09:14:22 +0200)

are available in the Git repository at:

  https://git.ejemplo.es/drueda/gestor-tareas.git correccion/reordenar

for you to fetch changes up to a7c2e91f4d8b1c5e9a2f7d0b3c6e9a1f4d8b1c5e:

  Repintar solo si el orden ha cambiado (2026-07-28 17:03:45 +0200)

----------------------------------------------------------------
Diego Rueda (2):
      Reordenar la lista al marcar una tarea como completada
      Repintar solo si el orden ha cambiado

 app.js      | 24 ++++++++++++++++--------
 estilos.css |  3 +++
 2 files changed, 19 insertions(+), 8 deletions(-)

Esto es literalmente una "pull request": un mensaje que dice "haz pull de aquí". Los botones de las plataformas automatizan este correo. Ahora el nombre tiene sentido.

format-patch y am: el modelo del kernel

Cuando el destinatario no puede o no quiere hacer fetch desde tu servidor, se envían los cambios como parches en el propio correo:

# Generar un fichero .patch por cada commit
git format-patch main..correccion/reordenar
0001-Reordenar-la-lista-al-marcar-una-tarea-como-completa.patch
0002-Repintar-solo-si-el-orden-ha-cambiado.patch

Cada fichero es un correo completo: cabeceras, autor, fecha, mensaje del commit y el diff. Opciones habituales:

# Con una carta de presentación (el "0000-cover-letter.patch")
git format-patch --cover-letter -o /tmp/parches main..correccion/reordenar

# Segunda ronda tras la revisión, marcada como v2
git format-patch -v2 --cover-letter -o /tmp/parches main..correccion/reordenar

La carta de presentación es el equivalente a la descripción de la PR, y el -v2 es cómo se indica que esta es la segunda versión de la serie tras los comentarios recibidos. En estos proyectos, git range-diff se incluye habitualmente en la carta de presentación de la v2, para que los revisores vean qué cambió respecto a la v1. Es precisamente el flujo para el que se diseñó el comando.

Del otro lado, quien recibe los parches los aplica con git am (apply mailbox):

git switch -c revision-parches main
git am /tmp/parches/*.patch

git am crea un commit por parche, conservando el autor original, su fecha y su mensaje. Esa es la diferencia crucial con git apply, que solo aplica los cambios al directorio de trabajo sin crear nada. Si un parche no aplica limpiamente:

git am --show-current-patch=diff    # ver qué falla
git am --3way                       # reintentar con fusión a tres bandas
git am --skip                       # saltarse este parche
git am --abort                      # cancelar toda la serie

Para enviarlos por correo existe git send-email, que habla directamente con un servidor SMTP. No lo desarrollaremos: si alguna vez colaboras con un proyecto de este tipo, su documentación explicará su configuración concreta.

Modelo Canal Herramienta Ventaja Inconveniente
Plataforma Web Pull request Accesible, integrada con CI Depende de un proveedor
request-pull Correo + fetch git request-pull Descentralizado, sin intermediarios Requiere servidor propio
Parches Correo format-patch / am No requiere ningún servidor Curva de aprendizaje alta

  1. Qué mirar, por orden de prioridad

Aquí empieza la mitad humana. El error más común de un revisor novato es empezar por lo pequeño: comenta el nombre de una variable en la línea 3, se enreda, y nunca llega a darse cuenta de que la solución entera está mal planteada.

Revisa en este orden, y no bajes de nivel hasta haber cerrado el anterior:

Nivel 1 — Corrección: ¿hace lo que dice?

  • ¿Resuelve realmente el problema descrito? ¿Lo has reproducido?
  • ¿Casos límite: lista vacía, valores nulos, textos larguísimos, caracteres raros?
  • ¿Condiciones de carrera, estado compartido, orden de eventos?
  • ¿Manejo de errores, o el catch está vacío?
  • ¿Hay pruebas? ¿Prueban el comportamiento o la implementación?
  • ¿Seguridad: entradas sin validar, datos de usuario insertados sin escapar, secretos en el código? (módulo 8)

Si algo falla aquí, para y coméntalo. No sigas revisando nombres de variables de código que va a desaparecer.

Nivel 2 — Diseño: ¿es la forma correcta de resolverlo?

  • ¿Encaja con la arquitectura existente o introduce un patrón nuevo sin justificación?
  • ¿Está en el sitio correcto? ¿Debería vivir en componentes-ui en lugar de en app.js?
  • ¿Duplica algo que ya existe?
  • ¿Es más complicado de lo necesario? ¿Y demasiado genérico "por si acaso"?
  • ¿Rompe alguna interfaz de la que dependa otro código?

Este nivel es el más valioso y el que más se descuida, porque exige entender el sistema. También es el que cuesta más caro corregir después: un problema de diseño detectado en revisión son dos horas; el mismo problema detectado seis meses más tarde es una refactorización.

Nivel 3 — Legibilidad: ¿lo entenderá alguien dentro de un año?

  • ¿Los nombres dicen lo que las cosas son?
  • ¿Hay comentarios donde hacen falta —el porqué, no el qué— y ausencia de comentarios obvios?
  • ¿Las funciones hacen una sola cosa?
  • ¿La complejidad está justificada o hay un if anidado a cuatro niveles?
  • ¿Los mensajes de los commits explican el porqué? (lección 08-01)

Nivel 4 — Estilo: convenciones

Y aquí, recuerda el apartado 1: si el linter puede comprobarlo, no lo comentes. Este nivel debería estar prácticamente vacío en un proyecto con buenas herramientas. Si no lo está, la conclusión correcta no es "hay que comentar más", sino "hay que configurar el formateador".

  1. Cómo comentar bien

Una revisión mal escrita hace más daño que ninguna revisión. Cuatro reglas.

Regla 1: sobre el código, no sobre la persona

En vez de Escribe
"No has controlado el caso de lista vacía" "Si tareas llega vacío, tareas[0] da undefined aquí"
"Esto está mal" "Esto falla cuando el título tiene comillas: se rompe el innerHTML"
"¿Por qué has hecho esto así?" "¿Qué te llevó a este enfoque? Pregunto porque en renderizarLista usamos el otro y me pregunto si hay una razón"

No es cuestión de suavizar por educación: es que la formulación centrada en el código incluye la información necesaria para arreglarlo, y la centrada en la persona no. "Está mal" no se puede accionar; "falla con comillas en el título" sí.

Regla 2: distingue lo bloqueante de lo opcional

El revisor sabe cuáles de sus comentarios son imprescindibles y cuáles son preferencias. El autor no lo sabe si no se lo dices. Marcarlo explícitamente ahorra muchísima fricción, y hay una convención bastante extendida:

Prefijo Significado ¿Bloquea?
bloqueante: Hay que arreglarlo antes de integrar
pregunta: No entiendo algo; puede que esté bien Depende de la respuesta
sugerencia: Creo que sería mejor así, pero es tu decisión No
nit: (de nitpick) Detalle menor, cógelo o déjalo No
elogio: Esto está bien resuelto No

Ejemplo sobre la PR de Diego:

bloqueante: si `tareas` está vacío, `tareas[0].id` lanza una excepción
            en la línea 47. Hace falta la comprobación antes del acceso.

sugerencia: `renderizarLista()` recorre el array dos veces (líneas 52 y 58).
            Se podría hacer en una sola pasada, aunque con listas de menos
            de mil elementos no creo que se note. Tú decides.

nit: `l` como nombre de variable en la línea 61. `listaOrdenada`?

elogio: buena idea comparar el orden antes de repintar. No se me había
        ocurrido y evita el parpadeo.

El último punto no es relleno. Comentar lo que está bien no es cortesía vacía: le dice al autor qué debe seguir haciendo, y en un equipo donde solo se comenta lo malo la revisión se vive como un castigo.

Regla 3: sugiere código cuando sea más rápido que explicarlo

Las plataformas permiten proponer un cambio concreto que el autor acepta con un clic. Si tu comentario va a ocupar tres párrafos explicando cómo reescribir cuatro líneas, escribe las cuatro líneas.

Regla 4: revisa pronto

Una PR que espera dos días bloquea a su autor, acumula conflictos con main y pierde contexto (el autor ya ha olvidado por qué hizo lo que hizo). Muchos equipos acuerdan un compromiso explícito del tipo "toda PR recibe una primera respuesta antes de 24 horas". El compromiso es responder, no necesariamente aprobar: un "lo miro mañana por la mañana" ya desbloquea la planificación de la otra persona.

  1. El tamaño importa: el efecto del tamaño de la PR

Si de esta lección solo se aplicara una cosa en el equipo, debería ser esta.

La capacidad de un revisor humano no escala con el tamaño del cambio: se derrumba. Los estudios sobre revisión de código llevan décadas apuntando en la misma dirección, y la experiencia de cualquiera que haya revisado mucho lo confirma:

Líneas cambiadas Qué pasa en la práctica Defectos detectados
< 50 Se revisa entera y con atención, en minutos Muy alta
50 – 200 Revisión sólida y realista. El punto óptimo Alta
200 – 400 Se empieza bien y se termina en diagonal Media
400 – 1000 Se revisa la primera parte; el resto se hojea Baja
> 1000 "Me parece bien" en cuatro minutos Casi nula

Hay una paradoja cruel escondida ahí: cuanto más grande es un cambio, más riesgo tiene y menos se revisa. Una PR de mil quinientas líneas, que es justo la que más necesitaría atención, es la que recibe una aprobación en cuatro minutos porque nadie tiene el tiempo ni la energía para hacerlo bien.

Y hay un segundo efecto, menos evidente: el número de comentarios no crece con el tamaño, decrece. Un revisor deja diez comentarios en una PR de cien líneas y tres en una de mil, porque en la segunda se rinde. El autor interpreta el silencio como aprobación.

Qué hacer con un cambio grande de verdad

Algunos cambios son grandes por naturaleza. Estrategias, por orden de preferencia:

  1. Trocear en varias PRs encadenadas. Cada una completa, coherente y revisable por separado; cada una parte de la anterior. Es más trabajo para el autor y muchísimo mejor para el equipo.
  2. Separar lo mecánico de lo sustancial. Una PR con el renombrado masivo o el reformateo (revisable en dos minutos con git diff -w) y otra con el cambio de comportamiento. Y anota el commit del reformateo en .git-blame-ignore-revs (lección 06-03).
  3. Revisar commit a commit, si el autor los construyó bien (apartado 4).
  4. Revisión en pareja, en directo. Para cambios arquitectónicos grandes, media hora de conversación rinde más que doscientos comentarios asíncronos.

Y una recomendación al autor: si tu PR va a ser inevitablemente grande, avísalo en la descripción y sugiere un orden de lectura. "Empieza por app.js líneas 40-90, que es el cambio real; el resto es propagación mecánica" ahorra media hora al revisor.

  1. Aprobar, pedir cambios y gestionar los desacuerdos

Los tres veredictos

Veredicto Cuándo Efecto
Aprobar No hay nada bloqueante Habilita la integración
Pedir cambios Hay al menos un bloqueante: Suele impedir la integración hasta resolverlo
Comentar Has opinado pero no quieres decidir Neutro

Dos matices sobre "aprobar" que evitan mucha fricción:

  • Aprobar no significa "es perfecto", significa "esto mejora el estado actual del proyecto y no introduce problemas". Perseguir la perfección en cada PR paraliza al equipo y quema a la gente.
  • Se puede aprobar con comentarios menores. Es la salida sana para los nit: y las sugerencia:: apruebas, el autor decide si aplicarlos, y nadie espera otra ronda de revisión por un nombre de variable. Muchos equipos lo llaman "aprobar confiando" (LGTM with nits).

Qué hacer con los desacuerdos

Los desacuerdos técnicos son normales y sanos. Lo que hay que evitar es que se enquisten en el hilo de la PR durante días.

Protocolo recomendado, en orden:

  1. Aporta datos, no opiniones. "Esto es lento" no avanza; "he medido con mil tareas: 340 ms frente a 12 ms" avanza.
  2. Distingue lo importante de lo estético. Si no puedes explicar qué problema real causa lo que estás criticando, probablemente sea una preferencia. Márcala como nit: y sigue.
  3. Si tras dos rondas seguís sin acuerdo, sal del texto. Una llamada de diez minutos resuelve lo que veinte comentarios no. Después, escribe la conclusión en la PR para que quede registro.
  4. Si sigue sin resolverse, escala con un criterio acordado de antemano: la persona responsable de esa parte del código decide, o un tercero desempata. Lo importante es que la regla exista antes del conflicto, no que se improvise durante él.
  5. Documenta la decisión para no repetir la discusión dentro de tres meses. Si es una convención general, va al CONTRIBUTING.md; si es una decisión de arquitectura, a un documento de decisión.

Y una asimetría útil que muchos equipos adoptan: quien propone un cambio al statu quo tiene la carga de la prueba. Si el proyecto ya hace las cosas de una manera y el revisor prefiere otra, el revisor debería justificar el cambio, no el autor defender lo existente. Evita que cada PR se convierta en un referéndum sobre el estilo del proyecto.

Sobre los mensajes de commit

Un tipo de comentario aparece en casi todas las revisiones: el mensaje del commit no explica el porqué, no sigue el formato acordado, o dice "arreglos varios". Es un comentario legítimo y vale la pena hacerlo —el historial es documentación permanente— pero las reglas concretas de redacción son el contenido de la lección 08-01. Ahí veremos qué formato usar, cómo estructurar el cuerpo y cómo enlazar los tickets GT-NNN que los hooks del módulo 6 ya validan.

Errores Comunes y Consejos

Error 1: usar git diff main rama (o main..rama) para revisar. Mezcla el trabajo del autor con lo que ha avanzado main desde que se separó. Tres puntos: git diff main...rama.

Error 2: confundir la semántica de los puntos entre diff y log. En diff quieres ...; en log quieres ... Es contraintuitivo y es así.

Error 3: revisar solo en el navegador. Sin ejecutar el código, la revisión detecta erratas y poco más. Trae la rama con la referencia de PR y pruébala.

Error 4: empezar por el estilo. Se gasta toda la atención en lo trivial y se aprueba un diseño equivocado. Corrección, diseño, legibilidad, estilo. En ese orden.

Error 5: comentar lo que ya comprueba el linter. Es ruido, genera resentimiento y revela un hueco en las herramientas del proyecto, no en el código del autor.

Error 6: no distinguir lo bloqueante de lo opcional. El autor se queda sin saber qué debe cambiar para poder integrar. Usa prefijos explícitos.

Error 7: releer la PR entera tras un rebase del autor. Para eso existe git range-diff. Guarda una rama con lo que revisaste y compara.

Error 8: aprobar una PR de mil líneas en cinco minutos. Es peor que no revisarla, porque genera una falsa sensación de control. Pide que se trocee.

Error 9: convertir la revisión en una demostración de superioridad técnica. Destruye la disposición del equipo a proponer cambios, que es el activo que la revisión pretendía proteger.

Consejo 1: git diff --stat main...rama siempre primero. Diez segundos que orientan toda la revisión.

Consejo 2: guarda una rama marcadora tras revisar. git branch revision/pr-42-v1 revision/pr-42. Tu yo futuro te lo agradecerá cuando llegue la v2.

Consejo 3: git range-diff @{u}...HEAD antes de cada push --force-with-lease. Verifica que el rebase no ha roto nada. Treinta segundos bien invertidos.

Consejo 4: -U10 y --color-moved por defecto en revisiones. Más contexto y distinción entre código movido y reescrito.

Consejo 5: un worktree dedicado a revisar. git worktree add ../revision <rama>. Revisar deja de costar cambio de contexto.

Consejo 6: alias para lo que repites. Por ejemplo git config --global alias.revisar '!f() { git fetch origin pull/$1/head:revision/pr-$1 && git diff --stat main...revision/pr-$1; }; f' (lección 06-04).

Ejercicios

Ejercicio 1: dos puntos y tres puntos

  1. Crea un repositorio con app.js y estilos.css y tres commits en main.
  2. Crea funcionalidad/filtros desde ahí y haz dos commits que solo toquen app.js.
  3. Vuelve a main y haz dos commits más que toquen solo estilos.css.
  4. Ejecuta git diff main funcionalidad/filtros, git diff main..funcionalidad/filtros y git diff main...funcionalidad/filtros. Explica qué muestra cada uno y por qué el tercero es el correcto para revisar.
  5. Demuestra la equivalencia de la forma de tres puntos usando git merge-base.
  6. Ejecuta git log --oneline main..funcionalidad/filtros y git log --oneline main...funcionalidad/filtros --left-right. Explica la diferencia con el caso de diff.

Ejercicio 2: revisar como Ana

Sobre el repositorio anterior:

  1. Obtén el resumen de ficheros y líneas del cambio.
  2. Lista los commits de la rama con sus mensajes completos.
  3. Recórrelos uno a uno del más antiguo al más nuevo con su diff.
  4. Muestra el diff completo con diez líneas de contexto e ignorando espaciado.
  5. Crea un worktree en /tmp/revision apuntando a la rama, sin abandonar main.
  6. Averigua quién escribió por última vez las líneas que la rama modifica, antes del cambio.

Ejercicio 3: range-diff en acción

  1. Guarda una referencia de la rama tal como está: git branch revision/v1 funcionalidad/filtros.
  2. Simula la respuesta del autor a una revisión: rebasa funcionalidad/filtros sobre main, modifica una línea de uno de los commits con un rebase interactivo (edit) y añade un commit nuevo al final.
  3. Ejecuta git range-diff main...revision/v1 main...funcionalidad/filtros.
  4. Identifica en la salida qué commit es equivalente, cuál ha cambiado y cuál es nuevo.
  5. Prueba a subir --creation-factor y observa cómo cambia el emparejamiento.
  6. Genera los parches de la rama con git format-patch -v2 --cover-letter y aplícalos en una rama nueva con git am. Comprueba que el autor original se conserva.

Soluciones

Solución 1:

mkdir /tmp/practica-revision && cd /tmp/practica-revision
git init -qb main
printf 'const tareas = [];\n' > app.js
printf 'body { margin: 0; }\n' > estilos.css
git add . && git commit -q -m "Estructura inicial"
echo "function anadir(t) { tareas.push(t); }" >> app.js
git commit -qam "Anadir funcion de alta de tareas"
echo "function borrar(i) { tareas.splice(i, 1); }" >> app.js
git commit -qam "Anadir funcion de borrado"
git switch -qc funcionalidad/filtros
echo "function filtrar(f) { return tareas.filter(f); }" >> app.js
git commit -qam "Anadir filtrado de tareas"
echo "function contarPendientes() { return filtrar(t => !t.hecha).length; }" >> app.js
git commit -qam "Anadir contador de pendientes"
git switch -q main
echo ".tarea { padding: 8px; }" >> estilos.css
git commit -qam "Estilar el elemento de tarea"
echo ".tarea.hecha { opacity: 0.5; }" >> estilos.css
git commit -qam "Atenuar las tareas completadas"
# 4. Las tres comparaciones
git diff --stat main funcionalidad/filtros
git diff --stat main..funcionalidad/filtros
git diff --stat main...funcionalidad/filtros
 app.js      | 2 ++
 estilos.css | 2 --
 2 files changed, 2 insertions(+), 2 deletions(-)
 app.js      | 2 ++
 estilos.css | 2 --
 2 files changed, 2 insertions(+), 2 deletions(-)
 app.js | 2 ++
 1 file changed, 2 insertions(+)

Las dos primeras formas son idénticas y muestran estilos.css con líneas eliminadas que la rama nunca tocó: son los commits de main vistos del revés. La tercera muestra solo app.js, que es lo que realmente hizo la rama.

# 5. Equivalencia
BASE=$(git merge-base main funcionalidad/filtros)
git diff --stat "$BASE" funcionalidad/filtros     # idéntico al de tres puntos
# 6. En log, la semántica se invierte
git log --oneline main..funcionalidad/filtros
c9f1a2d Anadir contador de pendientes
7b3e0c4 Anadir filtrado de tareas
git log --oneline --left-right main...funcionalidad/filtros
< 4e8a1f7 Atenuar las tareas completadas
< 2d6c9b3 Estilar el elemento de tarea
> c9f1a2d Anadir contador de pendientes
> 7b3e0c4 Anadir filtrado de tareas

En log, .. da los commits exclusivos de la rama (lo que quieres) y ... da los de ambos lados. En diff es al revés. La causa es que diff compara árboles y log selecciona conjuntos de commits.

Solución 2:

# 1, 2, 3
git diff --stat main...funcionalidad/filtros
git log --format='%h %s%n%n%b' main..funcionalidad/filtros
git log --reverse -p main..funcionalidad/filtros
# 4
git diff -U10 -w main...funcionalidad/filtros
# 5
git worktree add /tmp/revision funcionalidad/filtros
git worktree list
# 6: blame sobre la versión anterior al cambio
git blame -M -C main -- app.js

Solución 3:

# 1
git branch revision/v1 funcionalidad/filtros

# 2. El autor rebasa y corrige
git switch -q funcionalidad/filtros
git rebase -q main
GIT_SEQUENCE_EDITOR="sed -i '1s/^pick/edit/'" git rebase -i main
sed -i 's/return tareas.filter(f);/return (tareas || []).filter(f);/' app.js
git commit -qam "Anadir filtrado de tareas" --amend
git rebase --continue
echo "function limpiar() { tareas.length = 0; }" >> app.js
git commit -qam "Anadir limpieza de la lista"
# 3
git range-diff main...revision/v1 main...funcionalidad/filtros
1:  7b3e0c4 ! 1:  a1f4d82 Anadir filtrado de tareas
    @@ app.js
      function borrar(i) { tareas.splice(i, 1); }
     -function filtrar(f) { return tareas.filter(f); }
     +function filtrar(f) { return (tareas || []).filter(f); }
2:  c9f1a2d = 2:  5e2b7c9 Anadir contador de pendientes
3:  -------- > 3:  8d3a6f1 Anadir limpieza de la lista
# 4. Lectura:
#   commit 1 -> '!' : cambió, y se ve exactamente qué (la guarda `|| []`)
#   commit 2 -> '=' : equivalente, no hay que releerlo
#   commit 3 -> '>' : nuevo, hay que revisarlo entero
# 5
git range-diff --creation-factor=95 main...revision/v1 main...funcionalidad/filtros
# 6. Parches y aplicación
git format-patch -v2 --cover-letter -o /tmp/parches main..funcionalidad/filtros
ls /tmp/parches
git switch -qc revision-parches main
git am /tmp/parches/v2-000[123]*.patch
git log --format='%h %an <%ae> %s' -3

El autor y la fecha originales se conservan: eso es lo que distingue git am de git apply.

Conclusión

Revisar código bien es una habilidad, y la mitad de esa habilidad es técnica. Lo esencial:

  • Una revisión sirve sobre todo para difundir conocimiento y mantener la coherencia; detectar defectos es importante pero es donde menos aporta frente a las pruebas automáticas. Lo que puede comprobar una máquina, que lo compruebe la máquina.
  • El diff correcto para revisar es git diff main...rama, con tres puntos: compara desde el ancestro común, no desde la punta de main, y por tanto muestra solo lo que hizo el autor. Es el mismo que enseñan las plataformas.
  • Cuidado con la asimetría: tres puntos en diff, dos puntos en log. git log --oneline main..rama para ver los commits propuestos.
  • El repertorio de inspección: --stat primero siempre, -U10 para más contexto, -w para ignorar espaciado, --color-moved para distinguir código movido de reescrito, --reverse -p para recorrer commit a commit, y un worktree para ejecutarlo de verdad.
  • git range-diff compara dos versiones de la misma serie de commits y marca cada uno como igual (=), modificado (!), eliminado (<) o nuevo (>). Convierte una segunda revisión tras un rebase en cinco segundos de trabajo. Úsalo también sobre ti mismo antes de cada push --force-with-lease.
  • git request-pull genera el correo que dio nombre a la "pull request", y format-patch/am son el modelo de revisión por correo del kernel de Linux, donde range-diff en la carta de presentación de la v2 es práctica habitual.
  • Revisa por orden: corrección → diseño → legibilidad → estilo, y no bajes de nivel sin cerrar el anterior.
  • Comenta sobre el código, no sobre la persona; marca explícitamente lo bloqueante: frente a sugerencia: y nit:; y comenta también lo que está bien.
  • El tamaño de la PR es el factor que más determina la calidad de la revisión. Entre 50 y 200 líneas es el punto óptimo; por encima de mil, la revisión es prácticamente ficticia. Trocea, separa lo mecánico de lo sustancial, o revisa en pareja.
  • Aprobar significa "esto mejora el proyecto", no "es perfecto". Y ten acordado de antemano cómo se desempatan los desacuerdos.

El equipo ya sabe proponer cambios y revisarlos. Pero sigue faltando la pieza de arriba: ¿qué ramas existen, para qué sirve cada una y hacia dónde van las propuestas? Diego abre su PR contra main… ¿y si el proyecto tuviera una rama develop? ¿Y si hubiera que arreglar un fallo urgente en una versión antigua que ya está en producción? Eso son los flujos de trabajo, y empezamos por el más estructurado y clásico de todos en la lección 07-03: Flujo de Trabajo Git Flow.

Dominando Git: De Principiante a Avanzado

Módulo 1: Introducción a Git

Módulo 2: Operaciones Básicas de Git

Módulo 3: Ramas y Fusión

Módulo 4: Trabajando con Repositorios Remotos

Módulo 5: Operaciones Avanzadas de Git

Módulo 6: Herramientas y Técnicas de Git

Módulo 7: Estrategias de Colaboración y Flujo de Trabajo

Módulo 8: Mejores Prácticas y Consejos de Git

Módulo 9: Solución de Problemas y Depuración

Módulo 10: Git en el Mundo Real

© Copyright 2026. Todos los derechos reservados