Git intermedio I: trabajar con ramas
Primera entrega de la serie Git intermedio. Si vienes del nivel básico (init, clone y status, el ciclo de cambios, push y pull), ahora toca lo que de verdad convierte a git en una herramienta potente. Tiempo de lectura estimado: 5 minutos.
Las ramas son la razón por la que git se comió al resto de sistemas de control de versiones. En git son baratísimas, casi gratis: una rama es un puntero ligero a un commit, no una copia del código. Crear, cambiar y fusionar ramas es el 80% de lo que separa a alguien que "usa" git de alguien que "sufre" git. Tres comandos para esta entrega: git branch, git switch y git merge.
git branch: listar, crear y borrar ramas
git branch sin argumentos lista las ramas locales, marcando con un asterisco la actual:
$ git branch
* main
feature-login
fix-validacion
Con flags, hace más cosas:
git branch -r # solo ramas remotas
git branch -a # locales + remotas
git branch mi-rama # crea una rama nueva (no se cambia a ella)
git branch -d mi-rama # borra una rama ya fusionada
git branch -D mi-rama # borra sin comprobar si está fusionada
git branch -m nombre-viejo nuevo # renombra una rama
La diferencia entre -d y -D es importante: con -d, git te protege si la rama tiene commits que no están en ningún otro sitio; con -D, lo borras a pelo. Usa -d por defecto; si git se niega, piensa antes de pasar a -D.
El flag que más uso: --merged y --no-merged:
git branch --merged main # ramas ya integradas en main (se pueden borrar)
git branch --no-merged main # ramas con trabajo sin integrar
Combinado con xargs limpias ramas muertas en un comando. Lo vimos en el post de git aliases: git branch --merged main | grep -v '^\*\|main' | xargs git branch -d.
Para ver de qué commit parte una rama y cuál es su estado respecto al remoto:
git branch -vv
La doble v es "verbose + tracking": muestra el hash del último commit, el mensaje y qué rama remota trackea cada local.
git switch: cambiar de rama (sin pisar nada)
git switch llegó en git 2.23 como alternativa moderna a git checkout para la parte de cambiar de rama. checkout sigue funcionando, pero hace demasiadas cosas (cambiar rama, recuperar ficheros, reconstruir working tree) y es fácil confundirse. switch solo cambia de rama: más seguro, más claro.
git switch mi-rama # cambia a mi-rama
git switch - # vuelve a la rama anterior (como cd -)
git switch -c nueva-rama # crea y cambia a nueva-rama
git switch -c fix main # crea fix desde main y cambia
git switch --detach abc123 # checkout de un commit concreto
El -c es "create" y es el que uso más. Cuando empiezo a trabajar en algo nuevo:
git switch -c feature/login-oauth
Una rama nueva desde donde estaba, con el nombre dicho. Si la rama ya existía, error (no te la sobrescribe). Si necesitas partir desde otra base:
git switch -c feature/login-oauth main
Cosas que switch hace bien y checkout te dejaba meter la pata:
- Si tienes cambios locales sin committear que entrarían en conflicto con el cambio,
switchaborta limpiamente. Concheckouta veces aplicaba medio cambio. - No permite restaurar ficheros individuales (para eso está
git restore, que veremos en el siguiente post).
El truco que más agradezco: git switch - (guion). Te lleva a la rama anterior, como cd -. Súper útil cuando vas alternando entre tu rama y main.
git merge: fusionar una rama en otra
git merge trae los cambios de una rama a la actual. El patrón típico: estás en main, quieres integrar lo de feature/login:
git switch main
git merge feature/login
Git intentará una de dos cosas:
- Fast-forward: si
mainno ha avanzado desde que saliste a crear la rama, git simplemente mueve el puntero demainal último commit defeature/login. Historial limpio, sin merge commit. - Three-way merge: si ambas ramas tienen commits nuevos, git calcula el ancestro común y combina los dos caminos, creando un "merge commit" que tiene dos padres.
Para forzar siempre merge commit (útil en workflows tipo GitHub Flow):
git merge --no-ff feature/login
Para forzar fast-forward o fallar (útil si no quieres merge commits nunca):
git merge --ff-only feature/login
Lo que pasa durante un merge:
- Si los cambios no tocan las mismas líneas, git los fusiona solo y crea el commit.
- Si hay conflictos, git marca los ficheros, te deja los conflictos escritos con
<<<<<<<,=======,>>>>>>>y espera a que edites. Tras editar:git addlos ficheros,git commitpara completar.
Durante un merge con conflictos puedes abortarlo y quedarte como estabas:
git merge --abort
Este es uno de los comandos que más tranquilidad da: si te has metido en un merge feo y no sabes cómo salir, --abort te devuelve al estado justo antes.
Alternativa a merge: git rebase. Pero eso es material de la serie avanzada. Por ahora, merge es suficiente.
El flujo de ramas en la práctica
Un ciclo normal de feature:
git switch main
git pull --rebase # arranco con main al día
git switch -c feature/exportar-csv
# ... trabajo, commits ...
git push -u origin feature/exportar-csv
# ... más trabajo, más commits, más push ...
# cuando termino: abro PR, se revisa, se mergea en main
git switch main
git pull --rebase
git branch -d feature/exportar-csv # limpio la rama local
Ramas cortas, enfocadas, con un propósito claro. Eso es la mayor parte del trabajo.
Nombres de ramas: convención
Los nombres importan más de lo que parece porque git branch -vv los lista junto a cincuenta otros. Convenciones razonables:
- Prefijo por tipo:
feature/,fix/,chore/,docs/. - En inglés o español, pero consistente en todo el repo.
- Cortos pero descriptivos:
fix/login-timeoutmejor quefix/bugo quefix/login-timeout-when-redis-is-down-on-tuesdays. - Sin espacios ni mayúsculas. Git lo permite pero te va a molestar al autocompletar.
Algunos equipos meten número de ticket: feature/PROJ-123-exportar-csv. Útil si tu gestor de tareas no enlaza automáticamente las ramas; redundante si sí.
Errores típicos con ramas
Trabajar directamente en main. Cuesta mucho deshacer commits mezclados en main. Siempre rama por feature, incluso para cambios pequeños.
Ramas enormes que viven un mes. Cuanto más vive una rama, más difícil es mergear sin conflictos. Divide en ramas más pequeñas; mergea pronto y seguido.
Olvidarse de traer main antes de crear rama. Si creas feature/X desde un main viejo, al mergear chocarás con todo lo que el equipo ha metido desde entonces. Siempre git switch main && git pull antes de git switch -c.
Lo que viene
Con branch, switch y merge tienes el trabajo "normal" cubierto. En la siguiente entrega llega lo que hacemos cuando las cosas se tuercen: git restore, git reset y git revert. Tres formas muy distintas de deshacer, con niveles de peligro muy distintos también.