# Git intermedio I: trabajar con ramas

*Primera entrega de la serie **[Git intermedio](/search?tag=git-intermedio)**. Si vienes del nivel básico (**[init, clone y status](/search?tag=git-basico)**, 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:

```bash
$ git branch
* main
  feature-login
  fix-validacion
```

Con flags, hace más cosas:

```bash
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`:

```bash
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:

```bash
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.

```bash
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:

```bash
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:

```bash
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, `switch` aborta limpiamente. Con `checkout` a 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`:

```bash
git switch main
git merge feature/login
```

Git intentará una de dos cosas:

1. **Fast-forward**: si `main` no ha avanzado desde que saliste a crear la rama, git simplemente mueve el puntero de `main` al último commit de `feature/login`. Historial limpio, sin merge commit.
2. **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):

```bash
git merge --no-ff feature/login
```

Para forzar fast-forward o fallar (útil si no quieres merge commits nunca):

```bash
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 add` los ficheros, `git commit` para completar.

Durante un merge con conflictos puedes abortarlo y quedarte como estabas:

```bash
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](/search?tag=git-avanzado). Por ahora, merge es suficiente.

## El flujo de ramas en la práctica

Un ciclo normal de feature:

```bash
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-timeout` mejor que `fix/bug` o que `fix/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](/search?tag=git-intermedio)** 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.
