Las ramas son baratas, y esa es su virtud. También es su problema: cuando crear una cuesta cero, se crean muchas, y al cabo de unos meses el repositorio de Ana tiene ramas de funcionalidades ya integradas, ramas de experimentos que nadie recuerda, ramas con nombres como prueba o arreglo2 y alguna que lleva seis semanas sin un solo commit.
Un repositorio con cuarenta ramas de las que solo tres están vivas no es un problema técnico —Git aguanta miles sin despeinarse—, pero sí un problema humano: nadie sabe cuál es cuál, nadie se atreve a borrar nada por miedo a perder trabajo y el autocompletado deja de ser útil.
Esta lección es la caja de herramientas para mantener eso bajo control: listar ramas con la información que necesitas, saber cuáles se pueden borrar sin perder nada, renombrar, borrar con seguridad y ponerles nombres que digan algo. Es la lección menos espectacular del módulo y probablemente la que más veces vas a aplicar.
Contenido
- Listar ramas:
git branchy sus variantes --mergedy--no-merged: qué se puede borrar sin perder nada- Listados a medida con
--sorty--format - Renombrar ramas con
-m - Borrar ramas:
-dfrente a-D - Cuando el borrado seguro falla
- Convenciones de nombres
- Qué nombres admite Git:
git check-ref-format - Higiene: detectar ramas obsoletas
- Cierre del módulo
- Listar ramas:
git branch y sus variantes
git branch y sus variantesEl comando base ya lo conoces:
correccion/mensaje-lista-vacia documentacion/actualizar-notas experimento/almacenamiento-indexeddb funcionalidad/contador-tareas funcionalidad/exportar-csv funcionalidad/filtro-pendientes funcionalidad/orden-alfabetico limpieza/borrar-notas * main prueba
Diez ramas. El asterisco marca la actual. Ordenadas alfabéticamente, sin más información. Es un punto de partida pobre: no sabes cuáles están integradas, ni cuáles llevan meses paradas.
-v: qué hay en la punta de cada una
correccion/mensaje-lista-vacia 6d3f8b2 Mostrar un mensaje cuando el listado está vacío documentacion/actualizar-notas 3f7a9c1 Actualizar las notas internas con el nuevo flujo experimento/almacenamiento-indexeddb 5e8b1d4 Guardar tareas en IndexedDB funcionalidad/contador-tareas 9d1e4b7 Marcar tareas como completadas al hacer clic funcionalidad/exportar-csv e9a2c5f Ahora sí funcionalidad/filtro-pendientes b2e6d3f Corregir el foco del campo tras añadir una tarea funcionalidad/orden-alfabetico a1e5c93 Mostrar las tareas ordenadas alfabéticamente limpieza/borrar-notas 7f3c9a2 Borrar las notas internas, ya obsoletas * main c2a8f1e Fusionar el mensaje de lista vacía prueba 8b6d3c2 Añadir estilos base del listado
Ya se puede empezar a razonar. Fíjate en prueba: apunta a 8b6d3c2, un commit antiquísimo del principio del proyecto. Es una rama muerta que se creó y nunca se usó.
Hay una variante doble:
Añade, entre corchetes, la rama de seguimiento de cada una: con qué rama del repositorio remoto está emparejada y cuántos commits lleva por delante o por detrás. Como en este módulo todo ocurre en un único repositorio local, no hay nada que mostrar todavía; -vv cobrará todo su sentido en el módulo 4, cuando el trabajo empiece a viajar entre máquinas.
Otras opciones de listado
# Solo ramas que contienen un commit concreto
git branch --contains a1e5c93
# Solo ramas que NO lo contienen
git branch --no-contains a1e5c93
# Filtrar por patrón (admite comodines)
git branch --list 'funcionalidad/*'
# Incluir también las ramas remotas (módulo 4)
git branch -a
# Solo las remotas
git branch -r--contains es especialmente útil para responder a "¿en qué ramas está este arreglo?":
El arreglo del mensaje de lista vacía está en su rama original y en main. En ninguna otra.
--merged y --no-merged: qué se puede borrar sin perder nada
--merged y --no-merged: qué se puede borrar sin perder nadaEstas dos opciones son el corazón de la higiene de ramas.
correccion/mensaje-lista-vacia experimento/almacenamiento-indexeddb funcionalidad/contador-tareas funcionalidad/filtro-pendientes funcionalidad/orden-alfabetico limpieza/borrar-notas * main prueba
El significado exacto, y conviene ser preciso porque genera confusión: --merged lista las ramas cuya punta es alcanzable desde la rama actual. Dicho de otro modo: todos sus commits están ya en el historial de main.
Traducido a la práctica: estas ramas se pueden borrar sin perder ni un solo commit.
Dos casos merecen comentario:
pruebaaparece aquí porque su punta es8b6d3c2, un commit del principio del proyecto que obviamente está enmain. No aportaba nada, y por eso consta como fusionada.experimento/almacenamiento-indexeddbtambién aparece, aunque su código nunca llegó al proyecto: la cerramos en la lección 03-04 congit merge -s ours, que crea el commit de fusión sin traer el contenido. Ese era precisamente el objetivo de aquella estrategia: que la rama dejara de figurar como pendiente.
Y ahora la otra cara:
Estas ramas tienen commits que main no tiene. Borrarlas dejaría esos commits sin ninguna referencia que los alcance. Por qué está cada una:
documentacion/actualizar-notas: resolvimos su conflictomodify/deletea favor del borrado del fichero, así que su commit nunca llegó amain. Correcto que aparezca aquí.funcionalidad/exportar-csv: la integramos con squash. Su contenido está enmain, pero sus seis commits originales no. Git dice la verdad: desde el punto de vista del grafo, no está fusionada.
Ese último caso es la trampa más habitual: una rama integrada con squash siempre aparece en --no-merged. Si tu equipo usa squash sistemáticamente, --merged deja de ser un criterio fiable para borrar y hay que llevar la cuenta de otra forma.
Comparar contra otra rama
Por defecto, las dos opciones comparan contra HEAD. Se puede indicar otra referencia:
# Ramas fusionadas en main, aunque yo no esté en main
git branch --merged main
# Ramas cuyo trabajo ya está en la versión 1.2
git branch --merged v1.2.0
# Ramas sin integrar en main
git branch --no-merged mainEs la forma correcta de usarlas en scripts, porque no depende de dónde estés situado.
El patrón de limpieza
Este es el idiom que verás en todas partes y que conviene entender pieza a pieza:
git branch --merged main: las candidatas.grep -v '^\*': quita la línea de la rama actual (la del asterisco).grep -v ' main$': quitamainde la lista, no vaya a ser que intente borrarse a sí misma.xargs -r git branch -d: borra cada una con el borrado seguro (-revita ejecutar nada si la lista viene vacía).
Antes de ejecutarlo, revisa siempre la lista quitando el último tramo:
Un consejo importante: usa -d y nunca -D en un comando automático como este. -d se niega si algo no cuadra; -D borra sin preguntar.
- Listados a medida con
--sort y --format
--sort y --formatEl orden alfabético no es el más útil. Lo que casi siempre quieres saber es qué ramas están vivas, y eso es una cuestión de fechas.
* main correccion/mensaje-lista-vacia funcionalidad/orden-alfabetico documentacion/actualizar-notas limpieza/borrar-notas funcionalidad/exportar-csv funcionalidad/filtro-pendientes funcionalidad/contador-tareas experimento/almacenamiento-indexeddb prueba
El guion delante de committerdate significa orden descendente: lo más reciente arriba. Las de abajo son las candidatas a desaparecer.
Campos de ordenación habituales:
| Campo | Ordena por |
|---|---|
committerdate |
Fecha del último commit de la rama |
authordate |
Fecha de autoría del último commit |
refname |
Nombre (el orden por defecto) |
-committerdate |
Fecha, de más reciente a más antigua |
Y con --format se construye un listado a medida, usando los mismos marcadores de git for-each-ref:
git branch --sort=-committerdate \
--format='%(HEAD) %(color:yellow)%(refname:short)%(color:reset) | %(committerdate:relative) | %(authorname) | %(contents:subject)'* main | hace 2 horas | Ana Ferrer | Fusionar el mensaje de lista vacía correccion/mensaje-lista-vacia | hace 3 horas | Bruno Salas | Mostrar un mensaje cuando el listado está vacío funcionalidad/orden-alfabetico | hace 5 horas | Ana Ferrer | Mostrar las tareas ordenadas alfabéticamente documentacion/actualizar-notas | hace 2 días | Bruno Salas | Actualizar las notas internas con el nuevo flujo limpieza/borrar-notas | hace 2 días | Ana Ferrer | Borrar las notas internas, ya obsoletas funcionalidad/exportar-csv | hace 6 días | Bruno Salas | Ahora sí funcionalidad/filtro-pendientes | hace 3 semanas | Bruno Salas | Corregir el foco del campo tras añadir una tarea funcionalidad/contador-tareas | hace 3 semanas | Ana Ferrer | Marcar tareas como completadas al hacer clic experimento/almacenamiento-indexeddb | hace 2 meses | Ana Ferrer | Guardar tareas en IndexedDB prueba | hace 4 meses | Ana Ferrer | Añadir estilos base del listado
Esta sí es una vista útil. De un vistazo se ve qué está vivo, quién lo lleva y qué es lo último que se hizo.
Los marcadores más prácticos:
| Marcador | Contenido |
|---|---|
%(HEAD) |
Un * si es la rama actual, un espacio si no |
%(refname:short) |
El nombre de la rama sin refs/heads/ |
%(committerdate:relative) |
"hace 3 semanas" |
%(committerdate:short) |
2026-07-10 |
%(authorname) |
Autor del último commit |
%(contents:subject) |
Primera línea del mensaje |
%(objectname:short) |
Hash abreviado |
%(color:...) / %(color:reset) |
Color |
Como es imposible recordar esa línea, se guarda como alias:
git config --global alias.ramas "branch --sort=-committerdate --format='%(HEAD) %(color:yellow)%(refname:short)%(color:reset) | %(committerdate:relative) | %(authorname) | %(contents:subject)'"Los alias tienen su propia lección en el módulo 6; este es un buen candidato para tu colección.
- Renombrar ramas con
-m
-mBruno abrió hace unos días una rama llamada arreglo2 y ahora nadie sabe qué contiene. Tras mirarla, Ana le pone un nombre que diga algo:
Si quieres renombrar la rama en la que estás, basta con un argumento:
Hay una variante mayúscula, -M, que fuerza el renombrado aunque ya exista una rama con el nombre de destino, sobrescribiéndola. La misma advertencia de siempre: -M puede dejar commits huérfanos sin avisar. Usa -m salvo que sepas exactamente lo que haces.
Qué hace realmente
Nada sorprendente, a estas alturas del módulo:
Renombrar una rama es renombrar el fichero de 41 bytes y, si era la rama actual, actualizar .git/HEAD para que apunte al nombre nuevo. Los commits no se tocan: son inmutables y ni se enteran.
Por eso renombrar es una operación totalmente segura en local. Cuando la rama ya se ha compartido con el equipo la cosa cambia, porque los demás siguen viendo el nombre antiguo; eso pertenece al módulo 4.
- Borrar ramas:
-d frente a -D
-d frente a -DEl seguro, sobre una rama ya integrada:
Fíjate en que Git te da el hash de donde estaba. No es cortesía: es el dato con el que puedes recrear la rama si te has equivocado (git branch <nombre> 9d1e4b7). Cópialo si tienes la más mínima duda.
Se pueden borrar varias de golpe:
Deleted branch funcionalidad/filtro-pendientes (was b2e6d3f). Deleted branch funcionalidad/orden-alfabetico (was a1e5c93). Deleted branch limpieza/borrar-notas (was 7f3c9a2).
Qué se borra exactamente
El fichero de 41 bytes. Nada más.
Los commits siguen íntegros en .git/objects/. Lo único que ha desaparecido es el nombre que apuntaba a ellos. Si esos commits siguen siendo alcanzables desde main (que es el caso cuando la rama estaba fusionada), no cambia absolutamente nada en el repositorio.
Este es el motivo por el que borrar una rama fusionada es una operación trivial y sin riesgo, y por el que no hay que tenerle ningún respeto.
Dos limitaciones
No puedes borrar la rama en la que estás:
Y, desde Git 2.5, tampoco puedes borrar una rama que esté activa en otra copia de trabajo (git worktree, lección 06-06). En los dos casos la solución es la misma: git switch a otra rama y repetir.
- Cuando el borrado seguro falla
error: The branch 'funcionalidad/exportacion-csv' is not fully merged. If you are sure you want to delete it, run 'git branch -D funcionalidad/exportacion-csv'.
Qué significa exactamente ese mensaje: la punta de esa rama no es alcanzable desde la rama actual (ni desde su rama de seguimiento, si la tuviera). Es decir, hay commits en ella que no están en ningún otro sitio del grafo.
Y qué NO significa: no significa que el trabajo se haya perdido, ni que el contenido no esté en main. Recuerda el caso del squash: el contenido de funcionalidad/exportacion-csv está íntegro en main en el commit 3b9e7d1, pero sus seis commits originales no. Git razona sobre el grafo, no sobre el contenido.
Antes de forzar, comprueba. Este es el reflejo que hay que adquirir:
e9a2c5f Ahora sí 7e2a9c4 Quitar el console.log 1d6b4f8 wip 2 5a8e2b9 Arreglar el separador 9f3a2c1 wip 4a1c7e3 Primer intento de exportación
Sin diferencias: el contenido es idéntico. Se integró con squash y no se pierde nada. Ahora sí, con conocimiento de causa:
-D es recuperable (durante un tiempo)
Un borrado con -D no es el fin del mundo, y conviene saberlo para no vivir con miedo. Los commits siguen en la base de datos de objetos, y Git mantiene un registro llamado reflog con todos los movimientos de referencias. Mientras el hash sea recuperable —de ahí que git branch -D lo imprima al borrar— basta con recrear la rama:
Los commits sin ninguna referencia acaban siendo eliminados por el recolector de basura (git gc), pero no de inmediato: el plazo por defecto es de 30 días para objetos inalcanzables y 90 días para las entradas del reflog. Hay margen de sobra.
Todo el procedimiento de rescate —encontrar el hash con git reflog y git fsck --lost-found cuando ya no lo tienes apuntado— se ve en la lección 09-04. Aquí quédate con la idea tranquilizadora: -D casi nunca es irreversible.
-d |
-D |
|
|---|---|---|
| Comprueba si está fusionada | Sí | No |
| Falla si hay commits sin integrar | Sí | No |
| Imprime el hash al borrar | Sí | Sí |
| Recuperable después | Sí (nada se pierde) | Sí, vía reflog, durante semanas |
| Cuándo usarlo | Siempre por defecto | Cuando has comprobado y sabes lo que haces |
- Convenciones de nombres
Git no impone ninguna estructura en los nombres de rama más allá de unos pocos caracteres prohibidos. Lo que hacen los equipos es adoptar prefijos con barra que agrupan las ramas por propósito, exactamente como hemos venido haciendo en el módulo.
| Prefijo | Para qué | Ejemplo |
|---|---|---|
funcionalidad/ (o feature/) |
Funcionalidad nueva | funcionalidad/exportar-csv |
correccion/ (o fix/, bugfix/) |
Corrección de un fallo | correccion/mensaje-lista-vacia |
hotfix/ |
Corrección urgente sobre la versión en producción | hotfix/error-al-guardar |
experimento/ (o spike/) |
Prueba que puede acabar descartada | experimento/almacenamiento-indexeddb |
documentacion/ (o docs/) |
Solo documentación | documentacion/guia-instalacion |
refactor/ |
Reorganización sin cambio funcional | refactor/separar-modulo-tareas |
limpieza/ (o chore/) |
Mantenimiento, dependencias, configuración | limpieza/borrar-notas |
release/ |
Preparación de una versión | release/1.3.0 |
Muchos equipos añaden el identificador de la tarea del gestor de incidencias, que permite ir de la rama a la especificación y viceversa:
Y algunos incluyen el nombre de quien la lleva, útil en equipos grandes:
La ventaja práctica de la barra
No es solo estética. La barra permite filtrar:
Y hace que el autocompletado sea utilizable: escribes git switch fun<TAB> y la shell te ofrece solo ese grupo.
También conviene saber por qué la barra tiene un límite: como los nombres se convierten en rutas dentro de .git/refs/heads/, no puede existir a la vez una rama llamada funcionalidad y otra llamada funcionalidad/orden. La primera sería un fichero y la segunda necesitaría que funcionalidad fuese un directorio. Git lo rechaza:
fatal: cannot lock ref 'refs/heads/funcionalidad': 'refs/heads/funcionalidad/orden-alfabetico' exists; cannot create 'refs/heads/funcionalidad'
Es un error críptico con una explicación muy simple, y ahora ya la conoces.
Reglas de estilo que ayudan
- Minúsculas y guiones:
funcionalidad/exportar-csv, noFuncionalidad/ExportarCSV. Evita sorpresas entre sistemas de ficheros sensibles y no sensibles a mayúsculas (recuerda que Ana está en Ubuntu, Bruno en macOS y Carla en Windows). - Sin tildes ni
ñ.correccion/, nocorrección/. Git los admite, pero acaban en URL, en nombres de fichero y en salidas de herramientas de terceros donde dan problemas. - Descriptivo pero corto:
correccion/foco-tras-anadir, nocorreccion/arreglar-el-problema-del-foco-que-reporto-el-cliente-el-martes. - Nada de
prueba,temporal,nueva,arreglo2. Dentro de dos semanas no sabrás qué eran, y por eso nadie se atreverá a borrarlas.
Los flujos de trabajo con nombre
Existen metodologías completas que definen qué ramas hay, cómo se llaman, de dónde salen y dónde se integran. Las más conocidas:
- Git Flow: ramas
develop,release/*,hotfix/*yfeature/*sobre unamainque solo contiene versiones publicadas. - GitHub Flow: una sola rama de larga duración (
main) y ramas de trabajo de vida corta que se integran mediante propuestas de cambio. - Trunk Based Development: todo el mundo integra en el tronco a diario, con ramas de horas y no de días.
Cada una tiene sus supuestos sobre el tamaño del equipo, la frecuencia de publicación y el nivel de automatización. Las estudiaremos en el módulo 7, donde ya tendremos los remotos y las propuestas de cambio para entenderlas de verdad. Por ahora, lo importante: la convención concreta importa menos que el hecho de que todo el equipo siga la misma.
- Qué nombres admite Git:
git check-ref-format
git check-ref-formatLas reglas están definidas formalmente y se pueden comprobar:
Si el nombre es válido, lo imprime y devuelve 0. Si no:
Las reglas principales, con la razón de cada una:
| Prohibido | Ejemplo inválido | Por qué |
|---|---|---|
| Espacios y caracteres de control | mi rama |
Rompen el uso en línea de comandos |
Dos puntos seguidos .. |
rama..vieja |
.. es el operador de rangos |
Los caracteres ~ ^ : ? * [ \ |
rama^2, rama:x |
Son sintaxis de referencias o comodines |
Empezar o acabar en /, o // |
/rama, rama//x |
No son rutas válidas |
Acabar en . o en .lock |
rama., rama.lock |
.lock es el mecanismo de bloqueo de Git |
El componente @{ |
rama@{1} |
Es la sintaxis del reflog |
Un solo @ |
@ |
Es un alias de HEAD |
Empezar un componente por . |
.oculta |
Sería un fichero oculto |
Un detalle curioso y coherente con todo lo que has aprendido: la mayoría de estas reglas existen porque el nombre de la rama es a la vez una ruta de fichero y una expresión que Git tiene que poder analizar. Prohibir ^ no es capricho: si existiera una rama llamada mi^rama, git log mi^rama sería ambiguo.
Comprobación en un script:
NOMBRE="funcionalidad/nueva-cosa"
if git check-ref-format --branch "$NOMBRE" > /dev/null 2>&1; then
git switch -c "$NOMBRE"
else
echo "Nombre de rama no válido: $NOMBRE"
fiEste tipo de validación es exactamente lo que se automatiza con un hook para impedir que entren nombres que no siguen la convención del equipo. Los hooks son el tema de la lección 06-01.
- Higiene: detectar ramas obsoletas
Reunamos todo en una rutina de mantenimiento. Basta con hacerla una vez al mes.
Paso 1: ver el panorama ordenado por antigüedad.
2026-03-12 prueba Ana Ferrer 2026-05-28 experimento/almacenamiento-indexeddb Ana Ferrer 2026-07-10 funcionalidad/contador-tareas Ana Ferrer 2026-07-12 funcionalidad/filtro-pendientes Bruno Salas ...
Sin el guion, el orden es ascendente: lo más viejo arriba, que es lo que quieres cuando buscas candidatos a borrar.
Paso 2: separar las integradas de las que no lo están.
echo "=== Fusionadas en main (se pueden borrar) ==="
git branch --merged main | grep -v ' main$'
echo "=== Sin fusionar (revisar antes) ==="
git branch --no-merged mainPaso 3: revisar una por una las que no están fusionadas. Para cada una:
RAMA=experimento/almacenamiento-indexeddb
# ¿Qué commits tiene que main no tenga?
git log --oneline main..$RAMA
# ¿Cuánto hace del último?
git log -1 --format='%ar por %an' $RAMA
# ¿Su contenido está en main de otra forma (squash)?
git diff main..$RAMA --statCon esos tres datos, la decisión es fácil: si el contenido ya está en main, -D sin miedo; si tiene trabajo real y reciente, se deja; si tiene trabajo real pero de hace meses, se habla con quien la abrió antes de tocar nada.
Paso 4: borrar las integradas.
Señales de una rama obsoleta
| Señal | Qué suele indicar |
|---|---|
Fusionada en main |
Cumplió su función; borrar |
| Sin commits en más de un mes | Trabajo abandonado; preguntar |
Nombre genérico (prueba, temp, nueva) |
Nadie recuerda para qué era |
Su contenido ya está en main sin estar fusionada |
Se integró con squash; borrar con -D |
Cientos de commits por detrás de main |
Fusionarla será un infierno; replantear |
Y una reflexión de fondo
La mejor gestión de ramas es no necesitarla: si las ramas viven dos o tres días y se borran al integrarlas, el repositorio nunca acumula basura. La limpieza mensual es un parche para un problema que se evita trabajando con ramas cortas.
Es la misma conclusión a la que llegamos con los conflictos en la lección anterior, y no es casualidad: la vida corta de las ramas resuelve a la vez la divergencia, los conflictos y el desorden. Es la idea que sostiene el desarrollo basado en tronco y buena parte del módulo 7.
- Cierre del módulo
Recapitulemos lo que hemos construido en estas seis lecciones.
Empezamos con una promesa del módulo 2 —"una rama es un fichero con un hash dentro"— y la cumplimos hasta el byte: 41, exactamente. Vimos que HEAD es otro fichero que apunta a una rama, no a un commit, y que esa indirección es lo que permite que confirmar mueva el puntero correcto. Entendimos por qué las ramas de Git son gratis frente a las de Subversion, y qué significa que dos ramas diverjan desde un ancestro común.
Después las creamos y nos movimos entre ellas con git branch, git switch y git switch -c, entendimos por qué Git separó checkout en dos comandos, qué pasa con los cambios sin confirmar al cambiar de rama y qué es el HEAD desacoplado.
Las volvimos a juntar con git merge, distinguiendo el fast-forward —un puntero que avanza— de la fusión a tres bandas que crea un commit con dos padres, y aprendimos a forzar o prohibir cada comportamiento con --no-ff y --ff-only. Estudiamos las estrategias (ort, resolve, octopus, ours, subtree), las opciones -X, la diferencia crítica entre -s ours y -X ours, y el squash merge.
Resolvimos un conflicto de verdad: los marcadores, el estilo zdiff3 que muestra el ancestro, las tres etapas del índice, git add como forma de decir "resuelto" y git merge --abort como tecla de escape.
Y hemos terminado poniendo orden: listar, filtrar, renombrar, borrar con criterio y nombrar bien.
El problema que queda abierto
Y sin embargo, todo lo que hemos hecho en este módulo ha ocurrido dentro de un único portátil. Todas las ramas viven en el .git/ de Ana. Cuando decíamos "Bruno abrió una rama", en realidad estábamos razonando como si su trabajo ya estuviera allí.
En el mundo real no lo está. Ana trabaja en Ubuntu, Bruno en macOS, y sus repositorios son dos bases de datos de objetos completamente independientes que no se conocen entre sí. Los commits de Bruno están en su MacBook y los de Ana en su Ubuntu, y ningún git merge del mundo puede fusionar algo que no está en tu propio .git/objects/.
Faltan las respuestas a preguntas muy concretas:
- ¿Cómo llegan los objetos de un repositorio a otro?
- ¿Dónde vive la copia "oficial" del proyecto?
- ¿Qué es exactamente ese
origin/mainque apareció fugazmente en la lección 02-06, y por qué lleva una barra en el nombre? - ¿Cómo se autentica Bruno para poder enviar su trabajo?
- ¿Qué pasa si dos personas envían cambios sobre la misma rama a la vez?
- Y sobre todo: Carla todavía no se ha incorporado. Está esperando en su Windows 11 a que le digan de dónde descargar el proyecto.
En el módulo 4: Trabajando con Repositorios Remotos cerramos ese círculo. Veremos qué es un remoto y por qué no es más que un nombre corto para una URL, cómo se registra con git remote add, cómo funciona la autenticación con SSH y con tokens, la diferencia crucial entre git fetch y git pull, cómo enviar el trabajo con git push, y qué son las ramas de seguimiento que hacen que git status te diga "tu rama está 3 commits por delante de origin/main".
Y la buena noticia es que todo lo que has aprendido en este módulo sigue siendo cierto sin ningún cambio. Fusionar el trabajo de otra persona es exactamente el mismo git merge que has usado aquí. Los conflictos se resuelven igual. Las estrategias son las mismas. Lo único que se añade es el transporte: cómo consiguen los objetos viajar de un .git/ a otro. Una vez han llegado, todo funciona como ya sabes.
Errores Comunes y Consejos
Error 1: interpretar --no-merged como "aquí hay trabajo perdido". Solo significa que la punta de esa rama no es alcanzable desde donde estás. Una rama integrada con squash siempre aparecerá ahí aunque su contenido esté íntegro en main. Comprueba con git diff main..<rama> antes de sacar conclusiones.
Error 2: usar -D por costumbre porque -d "da la lata". Esa lata es la red de seguridad. Cada vez que -d falla, te está diciendo algo que merece treinta segundos de comprobación. Reserva -D para después de haber mirado.
Error 3: no anotar el hash que imprime el borrado. Deleted branch X (was 9d1e4b7) es tu billete de vuelta. Si te das cuenta al minuto siguiente de que te has equivocado, git branch X 9d1e4b7 lo arregla. Sin el hash, hay que ir al reflog.
Error 4: crear una rama con el nombre de un directorio de ramas existente. Si tienes funcionalidad/orden-alfabetico, no puedes crear funcionalidad. El error que da Git es críptico, pero la causa es que los nombres son rutas de fichero.
Error 5: acumular ramas "por si acaso". El coste no es el espacio (son 41 bytes) sino la confusión: nadie sabe cuáles están vivas, el autocompletado se vuelve inútil y las listas dejan de leerse. Si la rama está fusionada, bórrala; los commits no se van a ninguna parte.
Consejo 1: guarda el listado bonito como alias. git ramas con fecha relativa, autor y último mensaje es la vista que de verdad usarás. La línea completa está en el apartado 3.
Consejo 2: haz la limpieza mensual, y hazla en dos pasos. Primero listar, revisar con los ojos, y después borrar. Nunca en un solo comando encadenado sin mirar.
Consejo 3: nombra las ramas pensando en quien las vea dentro de un mes. Incluye el prefijo de tipo y, si el equipo usa un gestor de incidencias, su identificador. Es la diferencia entre una lista de ramas que se entiende y una que da miedo tocar.
Consejo 4: borra la rama en cuanto la integres. Convertirlo en el último paso del ritual de fusión —fusionar, comprobar, borrar— hace que la limpieza mensual no llegue nunca a ser necesaria.
Ejercicios
Ejercicio 1: auditoría de un repositorio
En un repositorio con varias ramas (créalas si hace falta), responde con comandos:
- ¿Qué ramas se pueden borrar ahora mismo sin perder ningún commit?
- ¿Qué ramas tienen trabajo sin integrar y cuántos commits son en cada caso?
- ¿Cuál es la rama que lleva más tiempo sin actividad?
- ¿En qué ramas está presente un commit concreto?
Ejercicio 2: el caso del squash
Reproduce la situación que confunde a --merged: integra una rama con --squash y demuestra con comandos que:
- Su contenido está en
main. - Git la considera no fusionada.
-dse niega a borrarla y-Dno.- Después de borrarla con
-D, se puede recrear exactamente donde estaba.
Ejercicio 3: validar nombres de rama
Escribe un pequeño script que reciba un nombre de rama y decida si es válido según Git y según una convención de equipo que exija uno de estos prefijos: funcionalidad/, correccion/, hotfix/ o documentacion/. Pruébalo con al menos cuatro nombres, válidos e inválidos.
Soluciones
Solución 1:
Su punta es alcanzable desde main: borrarlas no deja ningún commit sin referencia.
# 2. Ramas con trabajo sin integrar, con el recuento
for r in $(git branch --no-merged main --format='%(refname:short)'); do
n=$(git rev-list --count main..$r)
echo "$r: $n commits sin integrar"
donedocumentacion/actualizar-notas: 1 commits sin integrar funcionalidad/exportacion-csv: 6 commits sin integrar
git rev-list --count main..<rama> cuenta los commits del rango, con la misma semántica que aprendiste en la lección 02-06.
# 3. La rama más inactiva: orden ascendente, primera línea
git branch --sort=committerdate --format='%(committerdate:short) %(refname:short)' | head -1Solución 2:
mkdir /tmp/practica-gestion && cd /tmp/practica-gestion
git init -b main
echo "base" > f.txt && git add . && git commit -m "Base"
git switch -c funcionalidad/algo
echo "paso 1" >> f.txt && git commit -am "Paso 1"
echo "paso 2" >> f.txt && git commit -am "Paso 2"
echo "paso 3" >> f.txt && git commit -am "Paso 3"
git switch main
git merge --squash funcionalidad/algo
git commit -m "Añadir la funcionalidad completa"Cero diferencias: los ficheros son idénticos.
Tres commits que main no tiene, aunque su efecto sí esté ahí, condensado en un único commit distinto.
error: The branch 'funcionalidad/algo' is not fully merged. If you are sure you want to delete it, run 'git branch -D funcionalidad/algo'.
# 4. Recreación exacta con el hash que Git nos dio
git branch funcionalidad/algo 5f8b2e1
git log --oneline -1 funcionalidad/algoIdéntica a como estaba. Los commits nunca se fueron: solo se borró el nombre que los señalaba.
Solución 3:
#!/bin/bash
# validar-rama.sh — comprueba nombre válido para Git y para el equipo
NOMBRE="$1"
if [ -z "$NOMBRE" ]; then
echo "Uso: $0 <nombre-de-rama>"
exit 2
fi
# 1. ¿Lo acepta Git?
if ! git check-ref-format --branch "$NOMBRE" > /dev/null 2>&1; then
echo "RECHAZADO: '$NOMBRE' no es un nombre de rama válido para Git."
exit 1
fi
# 2. ¿Cumple la convención del equipo?
case "$NOMBRE" in
funcionalidad/*|correccion/*|hotfix/*|documentacion/*)
;;
*)
echo "RECHAZADO: '$NOMBRE' debe empezar por funcionalidad/, correccion/, hotfix/ o documentacion/."
exit 1
;;
esac
# 3. Regla de estilo adicional: sin mayúsculas
if echo "$NOMBRE" | grep -q '[A-ZÑÁÉÍÓÚ]'; then
echo "RECHAZADO: '$NOMBRE' no debe llevar mayúsculas ni caracteres acentuados."
exit 1
fi
echo "ACEPTADO: $NOMBRE"
exit 0Pruebas:
chmod +x validar-rama.sh
./validar-rama.sh 'funcionalidad/exportar-csv'
./validar-rama.sh 'correccion/mensaje-lista-vacia'
./validar-rama.sh 'arreglo2'
./validar-rama.sh 'funcionalidad/Exportar CSV'
./validar-rama.sh 'correccion/rama..vieja'ACEPTADO: funcionalidad/exportar-csv ACEPTADO: correccion/mensaje-lista-vacia RECHAZADO: 'arreglo2' debe empezar por funcionalidad/, correccion/, hotfix/ o documentacion/. RECHAZADO: 'funcionalidad/Exportar CSV' no es un nombre de rama válido para Git. RECHAZADO: 'correccion/rama..vieja' no es un nombre de rama válido para Git.
Fíjate en el cuarto caso: el espacio hace que lo rechace ya git check-ref-format, sin que lleguemos a la comprobación de mayúsculas. Y el quinto lo rechaza por el .., que Git reserva para los rangos de commits.
Este script es exactamente lo que se convierte en un hook pre-commit o pre-push para que la convención se aplique sola, sin depender de la disciplina de cada persona. Lo veremos en la lección 06-01.
Conclusión
Con esta lección cerramos el módulo 3. Lo aprendido aquí:
git branch -vmuestra la punta y el último mensaje de cada rama;-vvañadirá la información de seguimiento cuando tengamos remotos (módulo 4).--mergedy--no-mergedresponden a la pregunta clave del mantenimiento: qué ramas se pueden borrar sin perder ningún commit. Ojo con las integradas mediante squash, que siempre aparecen como no fusionadas aunque su contenido esté enmain.--sort=-committerdatecon--formatconvierte un listado alfabético inútil en una vista real de qué está vivo, quién lo lleva y desde cuándo. Guárdalo como alias.git branch -mrenombra: es renombrar el fichero de 41 bytes y, si era la rama actual, actualizarHEAD. Totalmente seguro en local.-dfrente a-D: el primero comprueba que no se pierde nada y se niega si no es así; el segundo fuerza. Cuando-dfalla, significa que la punta no es alcanzable desde donde estás, no necesariamente que el trabajo se pierda. Comprueba congit log main..<rama>ygit diff main <rama>antes de forzar.-Des recuperable durante semanas gracias al reflog (módulo 9).- Borrar una rama borra 41 bytes. Los commits siguen intactos en la base de datos de objetos.
- Las convenciones de nombres (
funcionalidad/,correccion/,hotfix/…) agrupan, permiten filtrar y hacen útil el autocompletado. Las reglas formales de Git se comprueban congit check-ref-format --branch. - La higiene es una rutina de cuatro pasos: listar por antigüedad, separar fusionadas de no fusionadas, revisar las segundas y borrar las primeras. Y la mejor higiene es tener ramas de vida corta que no lleguen a acumularse.
Lo que viene
Ya dominas las ramas: qué son, cómo crearlas, cómo fusionarlas, cómo resolver los choques y cómo mantener el repositorio ordenado. Pero todo ello dentro de un único .git/.
En el módulo 4: Trabajando con Repositorios Remotos el proyecto sale por fin del portátil de Ana. Veremos qué es un remoto, cómo se registra, cómo se autentica el acceso, la diferencia entre git fetch y git pull, cómo enviar el trabajo con git push y qué son las ramas de seguimiento. Y Carla se incorporará por fin al equipo desde su Windows 11, clonando el proyecto tal como hizo Bruno en la lección 02-02, pero esta vez con un repositorio que ya tiene historia, ramas y una forma de trabajar.
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
