En la lección anterior desmontamos el misterio: una rama es un fichero de 41 bytes con un hash dentro, y HEAD es otro fichero que dice en qué rama estás. Ahora vamos a usar esa maquinaria. Ana abrirá por fin funcionalidad/contador-tareas, Bruno hará lo propio con funcionalidad/filtro-pendientes, y el repositorio dejará de tener una sola línea de trabajo.

Crear ramas en Git es trivial —lo verás en un segundo—, pero moverse entre ellas tiene matices que conviene dominar desde el principio: qué ocurre con el trabajo que tienes a medias, por qué a veces Git te deja cambiar de rama y otras se niega, y qué es ese estado de HEAD desacoplado que aparece cuando menos lo esperas y que asusta a todo el mundo la primera vez.

También aclararemos una duda que arrastra cualquiera que aprendiera Git antes de 2019: la diferencia entre git checkout y git switch, y por qué Git decidió partir un comando en dos.

Contenido

  1. git branch: crear sin moverse
  2. git switch: moverse
  3. git switch -c: crear y moverse en un paso
  4. git switch frente a git checkout
  5. Crear una rama desde un punto concreto del historial
  6. git switch -: alternar entre dos ramas
  7. Cambios sin confirmar al cambiar de rama
  8. HEAD desacoplado (detached HEAD)
  9. El repositorio del equipo, ya con ramas

  1. git branch: crear sin moverse

Ana está en main, con el repositorio tal como lo dejamos:

git log --oneline
c5d9b1e (HEAD -> main) Documentar la instalación en el README
4e7f2a9 Añadir borrado de tareas al listado
8b6d3c2 Añadir estilos base del listado
1a4c8d6 Estructura inicial del gestor de tareas

Quiere una rama para el contador de tareas:

git branch funcionalidad/contador-tareas
(sin salida)

Silencio absoluto. En Git, el silencio suele significar éxito. Veamos qué ha pasado exactamente:

ls .git/refs/heads/
funcionalidad  main
cat .git/refs/heads/funcionalidad/contador-tareas
c5d9b1e7a3f2d8b4e6c1a9f5d3b7e2c8a4f6d1b9

Un fichero nuevo, con el hash del commit en el que estaba Ana. (Nota lateral interesante: como el nombre de la rama lleva una barra, Git ha creado un directorio funcionalidad/ dentro de refs/heads/. Los nombres de rama con barras son simplemente rutas de fichero; volveremos sobre las convenciones de nombres en la lección 03-06.)

Ahora la parte importante:

cat .git/HEAD
ref: refs/heads/main

HEAD no se ha movido. Ana sigue en main. Ha creado un puntero nuevo, pero no se ha situado en él:

git status
On branch main
nothing to commit, working tree clean
git branch
  funcionalidad/contador-tareas
* main

El asterisco marca la rama actual. Hay dos ramas y las dos apuntan al mismo commit.

gitGraph
   commit id: "1a4c8d6"
   commit id: "8b6d3c2"
   commit id: "4e7f2a9"
   commit id: "c5d9b1e"
   branch contador-tareas

Regla: git branch <nombre> crea el puntero y no toca nada más. Ni HEAD, ni el directorio de trabajo, ni el área de preparación.

Si Ana confirmase ahora, el commit iría a main, porque es donde sigue apuntando HEAD. Es un error clásico: crear la rama y olvidarse de cambiarse a ella. Por eso, en la práctica, casi nunca se usa git branch a secas para empezar a trabajar; se usa la variante del apartado 3.

  1. git switch: moverse

Para situarse en la rama recién creada:

git switch funcionalidad/contador-tareas
Switched to branch 'funcionalidad/contador-tareas'

Y ahora sí:

cat .git/HEAD
ref: refs/heads/funcionalidad/contador-tareas

Eso es todo lo que ha hecho Git en este caso: reescribir el fichero .git/HEAD. Como las dos ramas apuntaban al mismo commit, el directorio de trabajo no ha cambiado ni un byte.

git status
On branch funcionalidad/contador-tareas
nothing to commit, working tree clean

Cuando las ramas apuntan a commits distintos, git switch hace algo más: además de reescribir HEAD, actualiza el directorio de trabajo y el área de preparación para que reflejen el árbol del commit de destino. Es decir, cambia los ficheros de tu disco. Eso es lo que ocurre en las tres operaciones internas de un cambio de rama:

Paso Qué hace Git
1 Comprueba que el cambio es seguro (ver apartado 7)
2 Actualiza el directorio de trabajo y el índice al árbol del commit de destino
3 Reescribe .git/HEAD con la nueva rama

Ana escribe el contador de tareas en app.js y confirma:

git add app.js
git commit -m "Añadir el contador de tareas pendientes"
[funcionalidad/contador-tareas 6f2b9d4] Añadir el contador de tareas pendientes
 1 file changed, 12 insertions(+)

Fíjate en el nombre entre corchetes: [funcionalidad/contador-tareas 6f2b9d4]. Git te está diciendo en qué rama ha caído el commit. Es una comprobación gratuita que conviene leer siempre.

Ana añade después el marcado de completadas:

git commit -am "Marcar tareas como completadas al hacer clic"
[funcionalidad/contador-tareas 9d1e4b7] Marcar tareas como completadas al hacer clic
 2 files changed, 18 insertions(+), 2 deletions(-)

Estado actual:

git log --oneline --graph --all
* 9d1e4b7 (HEAD -> funcionalidad/contador-tareas) Marcar tareas como completadas al hacer clic
* 6f2b9d4 Añadir el contador de tareas pendientes
* c5d9b1e (main) Documentar la instalación en el README
* 4e7f2a9 Añadir borrado de tareas al listado
* 8b6d3c2 Añadir estilos base del listado
* 1a4c8d6 Estructura inicial del gestor de tareas

main se ha quedado quieta en c5d9b1e, exactamente como estaba. El trabajo de Ana está aislado: puede confirmar cuanto quiera sin tocar la línea principal del proyecto.

  1. git switch -c: crear y moverse en un paso

Crear una rama y no cambiarse a ella es raro. Por eso existe el atajo:

git switch -c funcionalidad/filtro-pendientes
Switched to a new branch 'funcionalidad/filtro-pendientes'

La -c viene de create. Es exactamente equivalente a:

git branch funcionalidad/filtro-pendientes
git switch funcionalidad/filtro-pendientes

Esta es la forma que usarás el 95 % de las veces. git branch <nombre> a secas queda reservado para los casos en los que quieres marcar un punto del historial sin abandonar lo que estás haciendo (por ejemplo, dejar un marcador antes de una operación arriesgada).

Un detalle importante que se pasa por alto: la rama nueva se crea desde donde estás ahora. Si Ana ejecutase ese comando estando en funcionalidad/contador-tareas, la rama nueva partiría de 9d1e4b7 y arrastraría el contador de tareas, que no es lo que quiere. Para el trabajo de Bruno, el punto de partida correcto es main:

git switch main
git switch -c funcionalidad/filtro-pendientes

O, mejor aún, indicándolo explícitamente en un solo comando —la forma que veremos en el apartado 5.

Hay una variante mayúscula, -C, que crea la rama o la reinicia si ya existe:

git switch -C funcionalidad/filtro-pendientes

Úsala con cuidado: si la rama ya existía apuntando a otro sitio, -C mueve el puntero sin preguntar y puedes dejar commits huérfanos. -c en cambio falla con fatal: a branch named '…' already exists, que casi siempre es lo que quieres.

  1. git switch frente a git checkout

Si has visto Git en cualquier tutorial anterior a 2019, o en la mayoría de los que hay por internet hoy, habrás visto esto:

git checkout funcionalidad/contador-tareas      # equivale a git switch
git checkout -b funcionalidad/filtro-pendientes # equivale a git switch -c

Funciona. git checkout no está obsoleto ni va a desaparecer. Pero desde Git 2.23 (agosto de 2019) existen git switch y git restore, y para trabajo nuevo son la opción recomendada. La razón es sencilla: git checkout hacía demasiadas cosas distintas.

Necesidad Comando antiguo Comando moderno
Cambiar de rama git checkout <rama> git switch <rama>
Crear rama y cambiarse git checkout -b <rama> git switch -c <rama>
Crear rama desde un commit git checkout -b <rama> <commit> git switch -c <rama> <commit>
Ir a un commit suelto git checkout <commit> git switch --detach <commit>
Volver a la rama anterior git checkout - git switch -
Descartar cambios de un fichero git checkout -- <fichero> git restore <fichero>
Quitar del área de preparación git reset HEAD <fichero> git restore --staged <fichero>
Recuperar un fichero de otro commit git checkout <commit> -- <fichero> git restore --source=<commit> <fichero>

Mira las tres últimas filas, que son las importantes. git checkout servía tanto para navegar entre ramas (una operación inofensiva y reversible) como para destruir cambios del directorio de trabajo (una operación irreversible). El mismo verbo para dos cosas de riesgo opuesto.

Y el desastre potencial estaba en la ambigüedad. Imagina que existe un fichero llamado informe y también una rama llamada informe:

git checkout informe

¿Cambia de rama o descarta los cambios del fichero? Git resolvía a favor de la rama y había que escribir git checkout -- informe (con el doble guion separador) para forzar la interpretación de fichero. Si te olvidabas del --, podías perder trabajo sin aviso.

La separación en Git 2.23 elimina la ambigüedad de raíz:

  • git switch solo trabaja con ramas. No puede tocar ficheros.
  • git restore solo trabaja con ficheros. No puede cambiar de rama.

Ya usaste git restore en la lección 02-04, así que ya conoces la mitad de la reforma. Esta lección te da la otra mitad.

Qué hacer en la práctica:

  • Escribe git switch y git restore en tu trabajo diario.
  • Aprende a leer git checkout, porque lo verás en todas partes: documentación antigua, respuestas de Stack Overflow, scripts de integración continua y compañeros con costumbres arraigadas.
  • No conviertas los scripts existentes que funcionan solo por moda; checkout sigue siendo perfectamente válido.

  1. Crear una rama desde un punto concreto del historial

Por defecto, una rama nueva parte de HEAD. Pero puedes indicarle otro punto de partida como segundo argumento, y eso vale para las dos formas del comando:

git branch <nombre> <punto-de-partida>
git switch -c <nombre> <punto-de-partida>

El punto de partida puede ser cualquier cosa que Git sepa resolver a un commit, con toda la sintaxis que aprendiste en la lección 02-06:

# Desde otra rama, sin tener que cambiarte primero
git switch -c funcionalidad/filtro-pendientes main

# Desde un commit concreto por su hash
git switch -c experimento/rediseno 8b6d3c2

# Desde una posición relativa
git switch -c correccion/regresion HEAD~2

# Desde el padre de un commit concreto
git switch -c prueba 4e7f2a9^

El primero es el caso de Bruno y es la forma correcta de hacerlo: crea la rama a partir de main sin necesidad de cambiarse a main antes. Un comando en lugar de dos, y sin riesgo de olvidarse.

Un caso real donde esto salva la tarde: has estado programando durante media hora en main por despiste, has hecho tres commits, y te das cuenta de que deberían haber ido en una rama. Solución:

# 1. Crear la rama aquí mismo, con los tres commits dentro
git branch funcionalidad/lo-que-sea

# 2. Devolver main a donde debía estar
git switch main
git reset --hard HEAD~3

# 3. Irse a la rama, donde está todo el trabajo intacto
git switch funcionalidad/lo-que-sea

Fíjate en que el paso 1 usa git branch sin cambiarse: crea el marcador antes de mover nada. (git reset --hard es potente y peligroso; lo estudiaremos a fondo en la lección 09-02. Aquí es seguro porque los commits siguen alcanzables desde la rama nueva.)

  1. git switch -: alternar entre dos ramas

Cuando estás revisando el trabajo de un compañero y saltas continuamente entre dos ramas, escribir el nombre completo cansa. El guion solo significa "la rama anterior":

git switch funcionalidad/contador-tareas
Switched to branch 'funcionalidad/contador-tareas'
git switch -
Switched to branch 'funcionalidad/filtro-pendientes'
git switch -
Switched to branch 'funcionalidad/contador-tareas'

Es el mismo concepto que cd - en la shell. Funciona con nombres de rama tan largos como funcionalidad/exportar-listado-a-csv y ahorra muchísimas pulsaciones.

Por debajo, el guion es un alias de @{-1}, que significa "la rama en la que estuve hace un cambio". Y hay más:

git switch @{-2}    # la rama en la que estuve hace dos cambios

Esta información sale del reflog, el registro que Git mantiene de todos los movimientos de HEAD. Es la misma herramienta que permite recuperar trabajo aparentemente perdido, y la estudiaremos en la lección 09-04.

  1. Cambios sin confirmar al cambiar de rama

Aquí está el matiz que más confusión genera. ¿Qué pasa con lo que tienes a medias cuando cambias de rama?

La respuesta corta: Git intenta llevárselo contigo, y si no puede hacerlo sin perder nada, se niega.

La respuesta larga depende de si los cambios afectan a ficheros que difieren entre las dos ramas.

Caso A: Git te deja pasar y se lleva los cambios

Ana está en funcionalidad/contador-tareas y ha empezado a retocar README.md, un fichero que es idéntico en las dos ramas:

git status --short
 M README.md
git switch main
M	README.md
Switched to branch 'main'

Ha funcionado, y esa línea M README.md es Git avisándote: "ese fichero venía modificado y sigue modificado". El cambio ha viajado con Ana a la otra rama. Sigue sin confirmar, en su directorio de trabajo:

git status --short
 M README.md

Esto es útil a propósito: te permite empezar a escribir algo, darte cuenta de que estás en la rama equivocada y corregirlo sin perder nada.

Caso B: Git se niega

Ahora Ana está en main y modifica app.js, que sí difiere entre main y la rama del contador (el contador lo cambió allí):

git status --short
 M app.js
git switch funcionalidad/contador-tareas
error: Your local changes to the following files would be overwritten by checkout:
	app.js
Please commit your changes or stash them before you switch branches.
Aborting

Git se ha negado, y ha hecho bien. Para situarse en la otra rama tendría que sobrescribir app.js con la versión de allí, y eso destruiría los cambios de Ana sin posibilidad de recuperarlos, porque nunca han estado en la base de datos de objetos.

Lo importante es que la operación se ha abortado por completo. Sigues donde estabas y tus cambios están intactos.

La regla, resumida

Situación Comportamiento
El fichero modificado es igual en las dos ramas Cambia de rama y se lleva las modificaciones
El fichero modificado difiere entre las dos ramas Rechaza el cambio de rama y aborta
Hay cambios en el área de preparación sobre ficheros que no difieren Cambia de rama y conserva el área de preparación
Hay ficheros nuevos sin seguimiento que no existen en el destino Cambia de rama y los deja donde están
Hay ficheros sin seguimiento que existen en el destino Rechaza, para no sobrescribirlos

Las tres salidas cuando Git se niega

  1. Confirmar. Si el trabajo tiene sentido en la rama actual, confírmalo. Si está a medias, puedes hacer un commit provisional y arreglarlo después con git commit --amend (lección 02-04) o con el rebase interactivo (lección 05-02).

  2. Descartar. Si el cambio no vale nada, git restore app.js lo elimina y el cambio de rama pasa a estar permitido.

  3. Guardarlo temporalmente. Para el caso real —trabajo a medias que quieres conservar pero que aún no merece un commit— Git tiene una herramienta específica: git stash, que aparta los cambios a un almacén temporal, deja el directorio limpio y permite recuperarlos después con git stash pop:

git stash                                  # aparta los cambios
git switch funcionalidad/contador-tareas   # ahora sí puede
# … lo que fuera a hacer …
git switch main
git stash pop                              # recupera los cambios

Es un comando con bastante más profundidad de la que sugiere este ejemplo (varias entradas apiladas, incluir ficheros sin seguimiento, aplicar en otra rama…). Lo estudiaremos entero en la lección 05-04; por ahora quédate con que existe y con que es la respuesta estándar a este problema.

Forzar el cambio de rama (con cuidado)

Existe la opción de descartar los cambios y cambiar de rama de todas formas:

git switch --discard-changes funcionalidad/contador-tareas

(El equivalente antiguo era git checkout -f.) Es destructivo e irreversible: los cambios no confirmados no están en ningún objeto de Git, así que no hay ningún reflog ni ninguna recuperación posible. Úsalo solo cuando estés absolutamente seguro de que quieres tirar ese trabajo.

  1. HEAD desacoplado (detached HEAD)

Recuerda la lección anterior: HEAD normalmente contiene ref: refs/heads/<rama>. Pero puede contener directamente un hash. A ese estado se le llama HEAD desacoplado.

Cómo se llega

La forma deliberada es pedirlo:

git switch --detach 4e7f2a9
Note: switching to '4e7f2a9'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

HEAD is now at 4e7f2a9 Añadir borrado de tareas al listado

Git te suelta un discurso porque el estado es inusual, no porque sea peligroso. De hecho ese texto explica ya casi todo lo que necesitas saber.

Las formas accidentales de llegar, que son las que asustan:

  • git checkout <hash> (sin -b), la más frecuente con el comando antiguo.
  • git checkout v1.2.0, al situarse en una etiqueta —las etiquetas apuntan a commits, no son ramas.
  • Durante un git rebase o un git bisect, que trabajan internamente en este estado (módulos 5 y 6).
  • Al situarse en origin/main en lugar de en main, un error muy común que veremos en el módulo 4.

Qué se ve

cat .git/HEAD
4e7f2a9c8b1d5e3f7a2c9d4b6e8f1a3c5d7b9e2f

Un hash directamente, sin ref:. HEAD apunta al commit, no a una rama.

git status
HEAD detached at 4e7f2a9
nothing to commit, working tree clean
git branch
* (HEAD detached at 4e7f2a9)
  funcionalidad/contador-tareas
  funcionalidad/filtro-pendientes
  main

El directorio de trabajo contiene los ficheros tal como estaban en ese commit. Es un estado perfectamente legítimo: puedes compilar, ejecutar las pruebas, mirar el código antiguo o incluso confirmar.

Para qué sirve de verdad

No es un accidente que haya que evitar; es una herramienta:

  • Revisar cómo estaba el proyecto en un momento dado. Ana quiere ver la aplicación tal como estaba el día del primer commit: git switch --detach 1a4c8d6, abre index.html en el navegador y ya está.
  • Probar si un fallo existía en una versión antigua, situándose en distintos puntos del historial.
  • Experimentar sin comprometerse. Puedes confirmar en este estado, y si el resultado no vale, te vuelves a una rama y los commits quedan huérfanos.

El peligro real

El peligro no es estar en este estado. Es confirmar en él y luego irse sin dejar rastro:

# En detached HEAD
echo "// prueba de rendimiento" >> app.js
git commit -am "Probar una optimización del pintado"
[detached HEAD 7d2f9a1] Probar una optimización del pintado
 1 file changed, 1 insertion(+)

Ese commit 7d2f9a1 es real y está en la base de datos de objetos, pero ninguna rama lo alcanza. Si Ana ahora hace git switch main:

Warning: you are leaving 1 commit behind, not connected to
any of your branches:

  7d2f9a1 Probar una optimización del pintado

If you want to keep it by creating a new branch, this may be a good time
to do so with:

 git branch <new-branch-name> 7d2f9a1

Switched to branch 'main'

Git avisa, y hasta te da el comando exacto. Pero si no lees ese aviso y cierras la terminal, el hash desaparece de tu vista y el commit queda huérfano. Sigue recuperable durante un tiempo mediante el reflog (módulo 9), pero es una situación que no quieres.

Cómo salir sin perder nada

Si no has confirmado nada (solo mirabas):

git switch -

O git switch main, o cualquier rama. No hay nada que salvar.

Si has confirmado y quieres conservarlo, crea una rama antes de irte:

git switch -c experimento/optimizacion-pintado
Switched to a new branch 'experimento/optimizacion-pintado'

Ahora HEAD vuelve a apuntar a una rama, esa rama apunta a 7d2f9a1, y el commit ya es alcanzable. Ya no hay nada huérfano.

Si ya te has ido y el commit quedó atrás, todavía puedes crear la rama a posteriori, siempre que tengas el hash (aparece en el aviso, y si lo perdiste, en git reflog):

git branch experimento/optimizacion-pintado 7d2f9a1
graph TD
    A["En una rama<br/>HEAD → ref: refs/heads/main"] -->|"git switch --detach abc123"| B["HEAD desacoplado<br/>HEAD → abc123"]
    B -->|"git switch -<br/>(sin confirmar nada)"| A
    B -->|"confirmar aquí"| C["Commits huérfanos<br/>ninguna rama los alcanza"]
    C -->|"git switch -c mi-rama"| D["Trabajo salvado<br/>en una rama nueva"]
    C -->|"irse sin crear rama"| E["Solo recuperable<br/>vía reflog (módulo 9)"]

  1. El repositorio del equipo, ya con ramas

Recapitulemos dónde estamos. Ana tiene su rama con dos commits. Bruno ha estado trabajando en la suya, con los tres commits que ya vimos al final del módulo 2 —el filtro de pendientes, el estilo de las completadas y la corrección del foco—, ahora en funcionalidad/filtro-pendientes en lugar de directamente sobre main.

Para el resto del módulo vamos a razonar como si las tres ramas estuvieran en un mismo repositorio local, el de Ana. Cómo consigue el equipo que el trabajo viaje entre el portátil de Ana y el MacBook de Bruno es precisamente el tema del módulo 4; aquí nos concentramos en la mecánica de las ramas, que es idéntica venga el trabajo de donde venga.

git branch -v
  funcionalidad/contador-tareas   9d1e4b7 Marcar tareas como completadas al hacer clic
  funcionalidad/filtro-pendientes b2e6d3f Corregir el foco del campo tras añadir una tarea
* main                            c5d9b1e Documentar la instalación en el README
git log --oneline --graph --all
* b2e6d3f (funcionalidad/filtro-pendientes) Corregir el foco del campo tras añadir una tarea
* 7c1f4a9 Aplicar estilo a las tareas completadas
* 3d5b8e1 Añadir filtro de tareas pendientes
| * 9d1e4b7 (funcionalidad/contador-tareas) Marcar tareas como completadas al hacer clic
| * 6f2b9d4 Añadir el contador de tareas pendientes
|/
* c5d9b1e (HEAD -> main) Documentar la instalación en el README
* 4e7f2a9 Añadir borrado de tareas al listado
* 8b6d3c2 Añadir estilos base del listado
* 1a4c8d6 Estructura inicial del gestor de tareas
gitGraph
   commit id: "1a4c8d6"
   commit id: "8b6d3c2"
   commit id: "4e7f2a9"
   commit id: "c5d9b1e"
   branch contador-tareas
   checkout contador-tareas
   commit id: "6f2b9d4"
   commit id: "9d1e4b7"
   checkout main
   branch filtro-pendientes
   checkout filtro-pendientes
   commit id: "3d5b8e1"
   commit id: "7c1f4a9"
   commit id: "b2e6d3f"

Tres líneas de trabajo, tres punteros, 123 bytes de coste total. Los cuatro commits de base están una sola vez en la base de datos de objetos y los comparten las tres ramas.

Y aquí está el problema que resolveremos en la lección siguiente: las dos funcionalidades están terminadas y probadas, pero el proyecto realmain— sigue exactamente donde estaba hace una semana. Nada de lo que han hecho Ana y Bruno está en la línea principal.

Errores Comunes y Consejos

Error 1: git branch <nombre> y ponerse a trabajar. Creas la rama, se te olvida el git switch, haces cinco commits y todos caen en main. Prevención doble: usa git switch -c en lugar de git branch, y lee la línea entre corchetes que imprime git commit, que te dice la rama de destino.

Error 2: crear la rama desde el sitio equivocado. git switch -c correccion/urgente estando en una rama de funcionalidad a medias arrastra todo ese trabajo sin terminar dentro de la corrección urgente. Antes de crear una rama, comprueba dónde estás con git branch --show-current, o indica el punto de partida explícitamente: git switch -c correccion/urgente main.

Error 3: entrar en pánico con detached HEAD. No es un error ni una corrupción: es un estado normal. El mensaje es largo porque explica, no porque avise de un desastre. Si solo mirabas, git switch - y listo. Si confirmaste, git switch -c <nombre> antes de irte.

Error 4: usar git checkout -f o --discard-changes para "desatascar". Ese comando destruye los cambios no confirmados de forma irreversible: no están en ningún objeto de Git y no hay reflog que valga. Cuando Git se niegue a cambiar de rama, la respuesta correcta es confirmar, descartar conscientemente con git restore o guardar con git stash.

Error 5: git switch -C en lugar de -c. La mayúscula reinicia una rama existente sin preguntar. Si la rama ya tenía trabajo, mueve el puntero y ese trabajo queda huérfano. -c falla con un error claro, que en el 99 % de los casos es lo que quieres que pase.

Consejo 1: una rama por tarea, y de vida corta. Las ramas son gratis, así que no acumules trabajo en ellas. Una rama que vive tres semanas acumula divergencia y garantiza conflictos; una que vive dos días casi nunca los tiene.

Consejo 2: activa el autocompletado de Git. Con nombres como funcionalidad/filtro-pendientes, escribir git switch fun<TAB> es la diferencia entre trabajar cómodo y equivocarse de rama. El script git-completion.bash viene con la instalación de Git y muchas distribuciones lo activan por defecto.

Consejo 3: comprueba dónde estás antes de confirmar. El coste es un segundo:

git branch --show-current

Consejo 4: no reutilices ramas viejas para tareas nuevas. Es tentador (ya tengo la rama pruebas, la reaprovecho), pero acaba con historiales que no se entienden y con fusiones que arrastran cambios que nadie pidió. Crea una rama nueva y borra la vieja cuando ya no sirva; la limpieza es el tema de la lección 03-06.

Ejercicios

Ejercicio 1: crear ramas sin moverse

En un repositorio de pruebas con al menos tres commits, crea dos ramas —experimento/uno y experimento/dos— sin cambiarte a ninguna de ellas, de forma que:

  • experimento/uno parta del commit actual.
  • experimento/dos parta de dos commits atrás.

Después, demuestra con comandos que HEAD no se ha movido y que las dos ramas apuntan donde corresponde.

Ejercicio 2: provocar y resolver un rechazo de cambio de rama

Diseña la secuencia mínima de comandos que provoque el error Your local changes to the following files would be overwritten by checkout, y después resuélvelo de tres formas distintas, explicando cuándo usarías cada una.

Ejercicio 3: salir de un HEAD desacoplado sin perder trabajo

Sitúate deliberadamente en un HEAD desacoplado, haz un commit ahí y consérvalo en una rama nueva. Después repite el proceso pero abandonando el estado sin crear la rama, y observa qué te dice Git. ¿Qué dato del mensaje es imprescindible anotar?

Soluciones

Solución 1:

# Punto de partida: comprobamos dónde estamos
git branch --show-current
main
git rev-parse HEAD
c5d9b1e7a3f2d8b4e6c1a9f5d3b7e2c8a4f6d1b9
# Las dos ramas, con git branch (que NO mueve HEAD)
git branch experimento/uno
git branch experimento/dos HEAD~2

Comprobación:

git branch -v
  experimento/dos  8b6d3c2 Añadir estilos base del listado
  experimento/uno  c5d9b1e Documentar la instalación en el README
* main             c5d9b1e Documentar la instalación en el README

El asterisco sigue en main: HEAD no se ha movido. experimento/uno coincide con main y experimento/dos está dos commits atrás, como pedía el enunciado.

Verificación byte a byte:

cat .git/HEAD
ref: refs/heads/main
git rev-parse experimento/uno experimento/dos
c5d9b1e7a3f2d8b4e6c1a9f5d3b7e2c8a4f6d1b9
8b6d3c2e1f5a9c3d7b2e6f8a4c1d9b3e5f7a2c6e

Solución 2:

Para provocar el error hacen falta dos condiciones: un fichero que difiera entre las dos ramas y una modificación sin confirmar en ese mismo fichero.

# 1. Rama con un cambio confirmado en app.js
git switch -c rama-x
echo "// cambio de la rama X" >> app.js
git commit -am "Cambiar app.js en la rama X"

# 2. Volvemos a main y tocamos el MISMO fichero, sin confirmar
git switch main
echo "// cambio sin confirmar en main" >> app.js

# 3. Intentamos volver
git switch rama-x
error: Your local changes to the following files would be overwritten by checkout:
	app.js
Please commit your changes or stash them before you switch branches.
Aborting

Las tres resoluciones:

# A) Confirmar: el trabajo pertenece a main y está listo
git commit -am "Añadir una nota en app.js"
git switch rama-x

Cuándo: cuando el cambio es válido y tiene sentido en la rama actual.

# B) Descartar: el cambio no sirve
git restore app.js
git switch rama-x

Cuándo: pruebas, trazas de depuración o cualquier cosa que no quieras conservar. Es irreversible.

# C) Guardar temporalmente: trabajo a medias que quiero conservar
git stash
git switch rama-x
# … trabajo en rama-x …
git switch main
git stash pop

Cuándo: el caso más habitual en el día a día, y el motivo de que git stash exista (lección 05-04).

Solución 3:

# Nos desacoplamos deliberadamente
git switch --detach HEAD~2
HEAD is now at 8b6d3c2 Añadir estilos base del listado
# Confirmamos algo aquí
echo "/* prueba desde HEAD desacoplado */" >> estilos.css
git commit -am "Probar un ajuste de estilos"
[detached HEAD 5f7a9c1] Probar un ajuste de estilos
 1 file changed, 1 insertion(+)
# Lo conservamos ANTES de irnos
git switch -c experimento/ajuste-estilos
Switched to a new branch 'experimento/ajuste-estilos'
git log --oneline -1
5f7a9c1 (HEAD -> experimento/ajuste-estilos) Probar un ajuste de estilos

El commit ya está alcanzable desde una rama: no hay nada huérfano.

Segunda parte, abandonando sin crear rama:

git switch --detach HEAD~2
echo "/* otra prueba */" >> estilos.css
git commit -am "Otra prueba que voy a abandonar"
git switch main
Warning: you are leaving 1 commit behind, not connected to
any of your branches:

  3c9d4a2 Otra prueba que voy a abandonar

If you want to keep it by creating a new branch, this may be a good time
to do so with:

 git branch <new-branch-name> 3c9d4a2

Switched to branch 'main'

El dato imprescindible es el hash: 3c9d4a2. Mientras lo tengas, recuperar el trabajo es un solo comando:

git branch rescate 3c9d4a2

Si además pierdes el hash, la situación no es desesperada: git reflog guarda todos los movimientos de HEAD durante 90 días por defecto y permite encontrarlo. Es el tema de la lección 09-04.

Conclusión

Ya sabes crear ramas y moverte entre ellas con criterio:

  • git branch <nombre> crea el puntero y no toca nada más: ni HEAD, ni el directorio de trabajo, ni el índice. Útil para marcar un punto sin abandonar lo que estás haciendo.
  • git switch <rama> te sitúa en una rama: reescribe .git/HEAD y actualiza el directorio de trabajo al árbol de destino.
  • git switch -c <nombre> [punto-de-partida] es la forma habitual de empezar una tarea: crea y se cambia en un paso, y admite cualquier referencia como origen (otra rama, un hash, HEAD~2…).
  • git switch y git restore sustituyen a git checkout desde Git 2.23, separando lo que antes era un único comando ambiguo: navegar entre ramas y destruir cambios ya no comparten verbo. checkout sigue funcionando y lo seguirás viendo en documentación y scripts.
  • git switch - alterna con la rama anterior, igual que cd -.
  • Al cambiar de rama, Git se lleva los cambios sin confirmar si puede hacerlo sin perder nada, y aborta limpiamente si el fichero difiere entre las dos ramas. Las salidas son confirmar, descartar con git restore o apartar con git stash (lección 05-04).
  • El HEAD desacoplado es HEAD conteniendo un hash en lugar de ref: refs/heads/…. No es un error: sirve para inspeccionar y experimentar. La única precaución es crear una rama con git switch -c antes de abandonarlo si has confirmado algo.

Lo que viene

El equipo tiene ahora tres ramas: main parada donde estaba, funcionalidad/contador-tareas con dos commits de Ana y funcionalidad/filtro-pendientes con tres de Bruno. Dos funcionalidades terminadas que no están en el proyecto.

Aislar el trabajo era la mitad del problema; la otra mitad es volver a juntarlo. En la siguiente lección, Fusionando Ramas, Ana integrará por fin las dos funcionalidades en main. Veremos los dos escenarios que Git distingue —el fast-forward, en el que el puntero simplemente avanza, y la fusión a tres bandas, que crea un commit especial con dos padres—, cómo encuentra Git el ancestro común que estudiamos en la lección anterior, y por qué a veces conviene forzar un commit de fusión aunque no haga falta.

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