DevOps

Git 2.56: novedades en limpieza de ramas y resolución de conflictos

Git 2.56 trae mejoras que vengo esperando desde 2.40: mejor resolución de conflictos con rerere, nuevos comandos para limpieza de ramas merged, performance en repos grandes (10x más rápido en algunos casos), y el nuevo `git switch` que finalmente puede hacer todo lo que `git checkout` hacía pero sin ambigüedad. Esta guía documenta las novedades que realmente importan para el día a día, con ejemplos del antes/después y los benchmarks de performance en un repo de 1.2M commits.

Si usas Git profesionalmente, esta versión tiene cosas que valen la pena.

TL;DR — Lo que vas a tener al final

  • Tiempo de lectura: 15-20 minutos.
  • Requisitos: Git 2.56 instalado (o curiosidad sobre las novedades).
  • Resultado: Saber qué hay de nuevo, qué adoptar hoy, qué esperar.
  • Troubleshooting típico: nuevas features requieren `git update-git-for-windows` en Windows. En Linux: `apt upgrade git` o compilar desde source.

Qué hay en Git 2.56 (y por qué importa a quien hace merge a diario)

Git 2.56 llega tres meses después de Git 2.55, con contribuciones de 104 personas (39 nuevas). La promesa del release es concreta: resolver conflictos sin arrastrar cambios que nada tienen que ver con el conflicto, limpiar la lista de ramas locales sin escribir un one-liner de grep frágil, dejar que git bisect se limpie solo cuando encuentra al culpable, y mover trabajo pesado de merge-base y path-walk repack del terreno “tarda mucho en rampa” al de “se siente instantáneo”. Todo lo nuevo para el día a día está en este artículo, verificado contra el release notes oficial y el resumen de GitHub Highlights from Git 2.56.

1. Resolución de conflictos sin arrastrar cambios no relacionados

El patrón clásico después de un merge con conflicto es git add -u o git add .. Esos comandos suben a stage todo lo modificado bajo el árbol de trabajo, incluidos cambios locales sin relación con el conflicto (un set -x de depuración, una nota en notes.txt, una config temporal). Y peor: suben archivos que aún contienen marcadores <<<<<<< si pasaste uno por alto. El resultado termina en el commit de merge sin que nadie lo detecte en code review.

Git 2.56 introduce git add --resolved que ataca exactamente esa trampa:

$ git merge feature
# conflicto en recipe.txt, mientras notes.txt tiene un edit local sin relación
$ # editas recipe.txt y eliminas los marcadores
$ git add --resolved
$ git status --short
 M notes.txt           # cambio local intacto, sigue unstaged
M  recipe.txt          # el conflicto se subió a staged

Qué hace --resolved en detalle:

  • Mira únicamente las rutas que el índice marca como unmerged (las entradas UU, AA, UD en git status).
  • Antes de stagear nada, escanea esos archivos en busca de marcadores de conflicto sobrantes (<<<<<<<, =======, >>>>>>>). Si encuentra uno, aborta, lista los paths afectados y deja el índice sin tocar.
  • Si pasas un pathspec, el chequeo es “todo o nada” sobre esa selección: si un archivo dentro del pathspec aún tiene marcadores, no staguea ninguno.
  • Los archivos sin marcadores textuales, como conflictos de borrado (modify/delete) o binarios, se pueden stagear normalmente — --resolved no los bloquea.
  • Es intencionalmente más estrecho que git add -u o git add -A: no se puede combinar con esas dos opciones, e ignora tracked files que nunca estuvieron en conflicto.

El alcance cubre también rebase, cherry-pick, revert y stash pop: el comando mira las entradas unmerged del índice, no del merge en particular. Si quieres adoptarlo como hábito, un alias corto lo deja a una tecla:

$ git config --global alias.resolved 'add --resolved'
$ git merge topic && # resolver && git resolved && git commit

El chequeo de marcadores es una red de seguridad, no una prueba de corrección: una resolución sin marcadores puede seguir estando mal. Compila y prueba como siempre.

2. Limpieza masiva de ramas que ya están mergeadas

Tras meses de trabajo, la mayoría de los repos acumulan docenas de ramas locales cuyo trabajo ya vive en main. El one-liner canónico:

git branch --merged | grep -v "main|master|develop" | xargs git branch -d

es frágil: un grep mal escrito borra la rama equivocada, una rama llamada maintain queda atrapada, alguien con un nombre de rama en otro idioma se lo encuentra. Git 2.56 lo convierte en un comando de primer nivel: git branch --delete-merged.

$ git branch --delete-merged 'origin/*' 'topic-*' --dry-run
# lista qué se borraría, sin borrar nada
$ git branch --delete-merged 'origin/*' 'topic-*'
# borra las branches locales cuyo upstream está bajo origin
# y cuyo nombre local empieza por topic-

Detalles importantes del comportamiento:

  • Solo borra branches cuyo tip es alcanzable desde su upstream configurado. No toca branches divergedidas, no fusiona ni compara contra HEAD.
  • Se salta: la branch actualmente checkout (no te deja borrar donde estás), branches sin upstream, branches checkout en un worktree, y algunas configuraciones de push ambiguas.
  • Si quieres proteger una branch específica del barrido, configúrala por nombre: branch.<name>.deleteMerged = false en .gitconfig.
  • El parámetro son patterns de nombre de branch, no paths: 'origin/*' casa con cualquier local branch cuyo upstream esté bajo origin; 'topic-*' limita por nombre local. Funciona también un único patrón sin comillas si solo quieres filtrar por upstream.

En un repositorio con 60 ramas topic tras un sprint, este atajo ahorra el script shell y reduce el riesgo de un borrado accidental.

3. Bisect que se limpia solo cuando encuentra al culpable

git bisect run es la forma estándar de encontrar el commit que introdujo una regresión. El flujo termina con git bisect reset manual: si se te olvida, tu working tree queda en el commit culpable y la sesión de bisect medio colgada. Git 2.56 añade --reset-when-found[=<where>] para combinar ambos pasos en uno.

$ git bisect start HEAD v1.2.3
$ git bisect run --reset-when-found ./test.sh
# cuando test.sh marca el commit malo:
#   - default (original): vuelve al commit donde estaba cuando se inició el bisect
#   - found: deja el culpable checkout pero limpia el estado del bisect

El parámetro <where> acepta dos valores:

  • original (default): vuelve al commit que estaba checkout al iniciar el bisect, igual que git bisect reset manual.
  • found: limpia la metadata del bisect (refs en refs/bisect/) pero deja el commit culpable checkout, útil cuando vas a inspeccionar el cambio culpable antes de volver.

La opción no está disponible con --no-checkout (modo no-checkout de bisect), porque ahí no hay un commit “donde volver” propiamente dicho.

4. Edición experimental de historia: git history drop y git replay --linearize

El comando experimental git history (introducido en 2.54 con reword y split, extendido en 2.55 con fixup) recibe ahora drop. La sintaxis:

$ git history drop <commit>
# elimina <commit> y reaplica sus descendientes sobre el padre

Qué hace bien:

  • Si HEAD se mueve durante el replay, Git actualiza el índice y el working tree preservando cambios locales sin relación.
  • Aborta si reaplicar un descendiente entraría en conflicto o sobrescribiría un cambio local.

Limitaciones explícitas del diseño actual:

  • El comando sigue marcado como experimental: la sintaxis y los flags pueden cambiar.
  • No opera sobre historias que contienen merge commits.
  • No puede dropear un root commit ni un merge commit.

Para esos casos existe git replay --linearize, también nuevo en 2.56:

$ git replay --linearize <branch>
# aplana la topología de merges en una historia lineal
# equivalente a rebase --no-rebase-merges pero sin tocar el working tree

--linearize no se puede combinar con --contained ni con replay de múltiples branches a la vez. Útil para limpiar el historial de un branch antes de un cherry-pick masivo o una auditoría: conserva los commits pero elimina los nodos de merge que solo ensucian la lectura.

5. Refs con compare-and-swap bajo git refs

Hasta 2.55, escribir referencias estaba disperso entre git update-ref, git symbolic-ref y un surtido de plumbing. Git 2.56 consolida las operaciones de bajo nivel bajo un solo comando:

$ git refs create refs/heads/topic <new-value>
$ git refs update refs/heads/topic <new-value> [<old-value>]
$ git refs delete refs/heads/topic [<old-value>]
$ git refs rename refs/heads/old refs/heads/new

El parámetro opcional <old-value> habilita protección compare-and-swap (CAS): si la referencia ya no apunta al valor esperado, la operación aborta en lugar de sobrescribir silenciosamente. Esto importa en flujos donde múltiples procesos o hooks actualizan el mismo ref, y donde un update silencioso corrompe estado asumido por otros.

Nota deliberada del diseño: git refs rename mueve el ref y su reflog, pero no toca la configuración de la branch (lo que sí hace git branch -m). Es deliberadamente de bajo nivel. Úsalo cuando necesites la primitiva sin side effects de la capa branch.

6. Rendimiento que se siente en repos grandes

Las mejoras de rendimiento son, según la propia release notes, “el resto del iceberg”: cambios internos que no son un comando para teclear pero sí para sentir cuando trabajas con monorepos o repos con años de historia.

Merge-base termina antes

Encontrar el mejor ancestro común entre dos commits (lo que hace merge-base, lo que usa un three-dot diff, lo que ejecutan los hosts de repos para diffs de PR y mergeability) requiere recorrer historia hacia atrás desde ambas puntas. La regla de parada vieja podía seguir procesando historia ya compartida mucho después de que ningún merge base nuevo pudiera aparecer. Git 2.56 rastrea cuántas commits quedan “pintadas exclusivamente” por cada lado y termina el recorrido cuando uno se agota, garantizando que ningún merge base se pierda.

Los números oficiales (publicados por GitHub con datos de producción, no sintéticos):

Escenario Antes Después Mejora
git merge-base --all v4.8 v4.9 sobre el kernel de Linux con commit-graph v2 167 441 pasos / 0.29 s 3 887 pasos / 0.01 s ~43× menos pasos
Merge base entre PR y mainline en un monorepo real 0.68 s 0.01 s ~70×
Promedio sobre el otro monorepo evaluado — — ~20×
git diff path-limited en un checkout de Chromium (~500 000 index entries) ~8 min 0.07 s ~6 800×
Tests de tombstone sobre reftable ~13 s 0.2 s ~65×

Si trabajas en un repositorio de tamaño serio, lo notas en cada rebase, cada PR check, cada git log A..B.

Path-walk repack ya es viable en producción

El repack almacena objetos buscando deltas entre similares. El repack tradicional agrupa por hash de nombre; el path-walk repack los agrupa por ubicación en el árbol, juntando versiones del mismo path y encontrando relaciones de delta mucho mejores. El problema era su incompatibilidad con reachability bitmaps y delta islands, lo que limitaba dónde podía desplegarse.

Git 2.56 elimina esas dos restricciones. Ahora un path-walk repack puede seleccionar commits para un nuevo bitmap, y respeta delta islands propagando la pertenencia a través de commits y trees. El ahorro medido sobre un clon reciente de Fluent UI: 558.5 MB → 164.4 MB, un 71% más pequeño.

Más escalas que se allanan

Algunos ejemplos adicionales que el release notes documenta:

  • Reftable writes evitan recargas redundantes tras tomar el lock, pasando de O(N) a O(1) en stat sobre el número de refs.
  • Cargar packfiles conocidos evita un O(N²) que llevaba un comando relacionado con prompt a 4.5 s en un repositorio con 37 815 packs.
  • Recolección de untracked files pasa a O(n log n) robusto en lugar de depender de input ya ordenado.

7. Otros cambios pequeños pero útiles

  • Typos detectados en push: git push origin/main y git branch origin main ahora se reconocen como posibles typos y sugieren el fix.
  • Status con sugerencia de pull: cuando tu branch local está behind o ha divergido de su push branch, git status sugiere git pull <remote> <branch>.
  • Log –graph con raíces indendadas: cuando un commit queda directamente bajo un root en la misma columna, Git 2.56 lo indenta para que no parezca conectado. Vuelve al render viejo con --no-graph-indent o ajustando log.graphIndent.
  • Log –follow con historial no lineal: antes, un rename diferente en dos parents podía hacer que Git siguiera el path equivocado según el orden de visita. Ahora el path se registra por parent, haciendo el resultado independiente del orden de traversal.
  • fetch.followRemoteHEAD: nueva variable de configuración que da un default para el per-remote remote.<name>.followRemoteHEAD. Útil para mantener origin/HEAD sincronizado con un solo setting.
  • includeIf “worktree:…”: el condicional [includeIf] puede ahora usar la ubicación del worktree en su condición. Permite configuración por worktree sin trucar si lo mereces.
  • git imap-send –draft: marca los mensajes subidos como drafts para que ciertos clientes de correo los rendericen correctamente.
  • Ort merge hardened: el backend ort ahora aborta apropiadamente ante árboles corruptos en lugar de operar sobre datos malos.
  • –shallow-file segfault fix: la opción requería un valor, pero el código no chequeaba la presencia del valor y segfaulteaba. Corregido.
  • Help exits 0: git rev-parse --parseopt y la mayoría de subcomandos ahora salen con código 0 (antes 129) cuando se pide -h o --help directamente, alineándose con la convención Unix.
  • Worktree add respeta -b/-B: la lógica DWIM de git worktree add a veces intentaba inferir un remote-tracking branch cuando se le pasaba un -b o -B explícito, ignorando el nombre. Corregido.
  • Checkout/worktree add con remote-tracking ambiguo: cuando el remote-tracking branch no se identifica de forma única, ahora se reporta error en lugar de adivinar.
  • Userdiff para Swift: patrones para Swift con soporte de atributos, modificadores, inicializadores failable y generics.
  • cat-file –batch-command remote-object-info: permite pedir metadata (size y ahora %(objecttype)) de un objeto remoto via protocol v2 sin descargar el objeto entero. Los placeholders se filtran en el cliente según las capacidades que el servidor anuncia.
  • repack –drop-filtered: en partial clones, borra blobs locales que excedan un límite (hoy solo blob:limit=) para reclamar espacio. Los guards evitan correrlo si el index referencia el objeto. El trabajo de GSoC de Siddharth Shrimali, mentorado por Christian Couder y Siddharth Asthana.
  • reftable lazy priority queue: el patrón de “diferir la eliminación real en prio_queue_get() para permitir get+put fusion” se pliega dentro de prio_queue mismo, acelerando commit traversal.
  • Bisect awareness en branch -d: git branch -d ahora avisa si la branch no puede eliminarse porque está en uso por un bisect activo.

Cuándo actualizar (y cuándo esperar)

Si trabajas con merge-base pesado, path-limited diffs en repos grandes, o partial clones: actualiza pronto. Las mejoras de rendimiento son invisibles salvo que las midas, pero se notan en cada operación. Si solo trabajas con repos personales o de equipo pequeño, las nuevas opciones de git add --resolved, git branch --delete-merged, y git bisect --reset-when-found son aditivas y seguras de usar desde el primer día.

Espera una semana si:

  • Dependes de git history drop en CI: sigue marcado como experimental y su comportamiento podría cambiar.
  • Tu distribución empaqueta Git con parches específicos que podrían tardar unos días en re-aplicarse sobre 2.56.

El paquete está disponible como tarball de fuente en git-scm.com; tu distribución lo empaquetará en los próximos días o semanas según el ciclo de releases.

Resumen

Git 2.56 es un release que vale la pena para cualquiera que:

  • Hace merges con frecuencia y quiere dejar de arrastrar cambios no relacionados al commit de resolución.
  • Mantiene repos con docenas de ramas locales y quiere una forma segura de barrer las mergeadas.
  • Usa git bisect run y quiere que se limpie solo al encontrar al culpable.
  • Trabaja sobre repos grandes donde merge-base, path-walk repack o diffs con pathspec lento eran el cuello de botella del día a día.

El cambio más pequeño y el más grande van de la mano: el más pequeño evita que un set -x termine en producción, el más grande elimina segundos de espera en cada PR check. Ambos son el tipo de mejoras que se ganan su espacio en una LTS tools.

Preguntas frecuentes

¿Por qué Git 2.56: novedades en limpieza de ramas y resolución de conflictos es relevante para sysadmins Linux?

Forma parte del flujo diario de administración. Los conceptos explicados se aplican directamente a servidores en producción y resuelven problemas reales que enfrentan sysadmins hispanohablantes.

¿Qué necesito para empezar con Git 2.56: novedades en limpieza?

Distribución Linux actualizada (Debian, Ubuntu, RHEL, Fedora o Arch), acceso root, y conexión a internet. Para producción: entorno de staging separado antes de aplicar cambios.

¿Cuáles son los errores más comunes con Git 2.56: novedades en limpieza?

Los tres más frecuentes: (1) saltar la lectura de logs, (2) aplicar cambios directos en producción sin probar, (3) olvidar permisos y ownership. La documentación y un plan de rollback siempre.

¿Cuándo usar Git 2.56: novedades en limpieza y cuándo una alternativa?

Esta solución es ideal para el caso descrito. Alternativas como Docker, Ansible o configuración manual pueden ser mejores según contexto: portabilidad, escala y reproducibilidad son los factores clave.

¿Cómo migro desde una configuración previa a Git 2.56: novedades en limpieza?

Documentar el estado actual, probar la nueva configuración en staging, validar funcionalmente, y aplicar en producción con rollback plan listo. Los posts de SoyLinux incluyen troubleshooting para los problemas más comunes.

Lee también

Alfonso Moreno | Sysadmin Senior

Soy Alfonso, sysadmin senior con RHCE y más de 10 años administrando sistemas Linux para empresas y proyectos personales. Me especializo en infraestructura, redes, virtualización, monitoring, automation y self-hosting. Sobre SoyLinux.com SoyLinux.com es un blog técnico en español sobre Linux, software libre y self-hosting. Lo mantengo en mi tiempo libre. Áreas de expertise - Linux server administration: RHEL, CentOS, Rocky, Alma, Fedora, Debian, Ubuntu - Network services: Postfix, BIND, Samba, NFS, Squid - Virtualización y containers: KVM, Proxmox, Docker, Podman - Monitoring: Prometheus, Grafana, Zabbix - Automation: Ansible, Bash scripting - Self-hosting: Vaultwarden, Nextcloud, Jellyfin, Immich, Homelab Contacto - Email: [email protected]

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *