En la lección 03-02 nos encontramos por primera vez con este mensaje:
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.
Y dijimos que había una tercera salida, además de confirmar o descartar: apartar los cambios. Prometimos una lección entera. Es esta.
git stash es el cajón de sastre de Git: coge todo lo que tienes a medias en el directorio de trabajo y en el índice, lo guarda en un sitio seguro, y te deja la copia de trabajo limpia como si acabaras de clonar. Después, cuando quieras, lo recuperas.
El escenario es siempre el mismo y le pasa a todo el mundo: estás a mitad de algo, con código que no compila y que no merece un commit, y llega una urgencia. Carla lo va a vivir en esta lección. Y al final veremos lo que casi nadie mira: que el stash no tiene nada de mágico, que por dentro son commits normales en una referencia oculta, exactamente los mismos objetos del modelo de datos de la lección 01-04.
Contenido
- El escenario: Carla y la urgencia
git stash: qué guarda y qué no- Ficheros sin seguimiento e ignorados:
-uy-a - La pila:
list,show,apply,pop,drop,clear applyfrente apop- Referirse a una entrada concreta
git stash push: mensajes, rutas y modo interactivo--keep-indexy--stagedgit stash branch: cuando el stash ya no encaja- Cómo funciona por dentro
- Riesgos: stashes olvidados y falsas copias de seguridad
- El escenario: Carla y la urgencia
Carla está en funcionalidad/orden-por-fecha, a media función. Ha tocado app.js y estilos.css, y ha creado un fichero nuevo, utilidades.js, que todavía no ha añadido al repositorio.
Llega un mensaje de Ana: el botón de borrar no funciona en producción y hay que corregirlo ya. Carla necesita cambiar a main, hacer la corrección y volver. Sus opciones:
| Opción | Problema |
|---|---|
Confirmar un wip |
Ensucia el historial (aunque sea corregible con 05-02), y su código no compila |
Descartar con git restore |
Pierde dos horas de trabajo |
Copiar los ficheros a /tmp a mano |
Funciona, pero es artesanía y se olvida |
git stash |
Aparta todo, deja el directorio limpio y lo devuelve después |
Saved working directory and index state On funcionalidad/orden-por-fecha: Ordenación por fecha a medias
Directorio limpio. Carla puede cambiar de rama sin que Git proteste, corregir el fallo, publicarlo y volver:
git switch main
git pull
# ... corrige, confirma, publica ...
git switch funcionalidad/orden-por-fecha
git stash popOn branch funcionalidad/orden-por-fecha
Changes not staged for commit:
modified: app.js
modified: estilos.css
Untracked files:
utilidades.js
Dropped refs/stash@{0} (a7f3c92e8b1d5c4a7f2e9b6d3c8a1f5e7b4d2c9a)Todo vuelve a estar como estaba. Eso es git stash en su uso más básico. Ahora, los detalles que marcan la diferencia.
git stash: qué guarda y qué no
git stash: qué guarda y qué nogit stash sin argumentos es un alias de git stash push. Por defecto guarda:
- Los cambios en ficheros con seguimiento que están modificados en el directorio de trabajo.
- Los cambios en ficheros con seguimiento que están preparados en el índice.
Y no guarda:
- Los ficheros sin seguimiento (los que aparecen como
??engit status). - Los ficheros ignorados por
.gitignore.
Esta es la primera trampa, y es grave. Si Carla hubiera hecho git stash sin -u:
app.js y estilos.css estarían apartados, pero utilidades.js seguiría ahí, sin seguimiento y sin apartar. Y como no lo aparta, tampoco lo devuelve: si Carla lo borrara por error pensando que está en el stash, lo perdería.
La lógica de Git tiene sentido —un fichero sin seguimiento nunca ha formado parte del repositorio, así que Git es conservador y no lo toca— pero la consecuencia práctica sorprende siempre la primera vez.
Recordando las tres zonas de la lección 01-03, el reparto es este:
| Zona | ¿Se aparta con git stash? |
|---|---|
| Directorio de trabajo, ficheros con seguimiento modificados | Sí |
| Índice (área de preparación) | Sí |
| Ficheros sin seguimiento | Solo con -u |
| Ficheros ignorados | Solo con -a |
| Repositorio (commits) | Nunca: el stash no toca commits |
Y un matiz que importa al recuperar: por defecto, el stash no conserva qué estaba preparado y qué no. Al hacer pop o apply, todo vuelve como "modificado sin preparar". Si necesitas conservar esa distinción, existe --index:
Con --index, lo que estaba en el índice vuelve al índice. Puede fallar si el estado actual no lo permite, en cuyo caso Git lo aplica todo sin preparar y te avisa.
- Ficheros sin seguimiento e ignorados:
-u y -a
-u y -a| Opción | Nombre largo | Qué añade a lo que se guarda por defecto |
|---|---|---|
-u |
--include-untracked |
Los ficheros sin seguimiento |
-a |
--all |
Los sin seguimiento y los ignorados |
# Lo habitual cuando has creado ficheros nuevos
git stash -u
# Solo si sabes muy bien lo que haces
git stash -aSobre -a: los ficheros ignorados suelen ser node_modules/, dist/, .env, ficheros de compilación... Apartarlos significa borrarlos del directorio de trabajo y meterlos en el stash. Con node_modules/ eso son decenas de miles de ficheros y una operación lentísima. Y con .env, tus credenciales locales pasan a estar dentro de objetos de Git, que es justo lo que no quieres. Usa -a de forma excepcional y consciente.
En cambio, -u es tan frecuentemente lo que quieres que mucha gente lo configura como comportamiento por defecto mediante un alias:
Los alias se ven a fondo en la lección 06-04, pero este vale la pena desde ya.
- La pila:
list, show, apply, pop, drop, clear
list, show, apply, pop, drop, clearEl stash no es un cajón único: es una pila. Puedes apartar varias veces, y cada nueva entrada se coloca encima.
stash@{0}: On funcionalidad/orden-por-fecha: Ordenación por fecha a medias
stash@{1}: WIP on main: 7d3a8f4 Devolver el foco al campo de texto tras borrar
stash@{2}: On funcionalidad/etiquetas-color: prueba del selectorstash@{0}es siempre la más reciente.- Los números se renumeran cada vez que añades o quitas una entrada.
stash@{1}de hoy no es el mismo que el de mañana. Es un motivo excelente para poner mensajes descriptivos. - El texto
WIP on main: 7d3a8f4 ...es el mensaje automático cuando no le pones uno: la rama y el commit sobre el que se guardó.
Para ver el contenido de una entrada:
diff --git a/app.js b/app.js
index 8c3d5a1..2e7b9f4 100644
--- a/app.js
+++ b/app.js
@@ -22,7 +22,11 @@ function pintarLista() {
const lista = document.getElementById('lista-tareas');
lista.innerHTML = '';
- tareas.forEach(function (t) {
+ const ordenadas = tareas.slice().sort(function (a, b) {
+ return b.creada - a.creada;
+ });
+ ordenadas.forEach(function (t) {
lista.appendChild(crearElementoTarea(t));
});Un detalle: por defecto, git stash show no incluye los ficheros sin seguimiento que hubieras guardado con -u. Para verlos:
El repertorio completo de comandos:
| Comando | Qué hace |
|---|---|
git stash / git stash push |
Aparta los cambios y limpia el directorio |
git stash list |
Lista la pila |
git stash show [-p] [<entrada>] |
Enseña qué hay en una entrada |
git stash apply [<entrada>] |
Aplica los cambios conservando la entrada |
git stash pop [<entrada>] |
Aplica los cambios y elimina la entrada |
git stash drop [<entrada>] |
Elimina la entrada sin aplicarla |
git stash clear |
Vacía la pila entera. Sin confirmación |
git stash branch <rama> [<entrada>] |
Crea una rama a partir de la entrada y la aplica |
git stash create |
Crea el objeto de stash sin tocar la pila ni el directorio |
git stash store <sha> |
Guarda en la pila un objeto creado con create |
git stash clear merece un aviso en negrita: borra toda la pila de golpe y no pregunta. Recuperar algo después es posible pero engorroso (hay que rebuscar objetos huérfanos con git fsck, técnica de la lección 09-04). Trátalo como rm -rf.
apply frente a pop
apply frente a popLas dos aplican los cambios guardados sobre tu directorio de trabajo. La diferencia está en qué pasa con la entrada después:
git stash apply |
git stash pop |
|
|---|---|---|
| Aplica los cambios | Sí | Sí |
| Elimina la entrada de la pila | No | Sí, si la aplicación tuvo éxito |
| Se puede aplicar en varias ramas | Sí | No (desaparece tras el primero) |
| Si hay conflicto | La entrada se conserva | La entrada se conserva |
| Riesgo de duplicar cambios | Sí, si olvidas hacer drop |
No |
| Riesgo de perder el guardado | No | Bajo, pero existe |
En qué se traduce esto en la práctica:
Usa pop en el caso normal: apartaste, hiciste otra cosa, vuelves. Es un solo comando y deja la pila limpia.
Usa apply cuando:
- Quieres aplicar los mismos cambios en dos ramas distintas.
- No estás seguro de que vayan a encajar y prefieres conservar la copia hasta comprobarlo.
- La rama actual ha cambiado mucho y sospechas que habrá conflicto.
El detalle que salva vidas: si pop provoca un conflicto, la entrada NO se elimina. Git aplica lo que puede, deja los marcadores de conflicto y conserva el stash por si acaso. Es un comportamiento deliberado y muy sensato. Pero tiene una consecuencia molesta: después de resolver el conflicto, la entrada sigue en la pila y tienes que borrarla tú:
Auto-merging app.js CONFLICT (content): Merge conflict in app.js The stash entry is kept in case you need it again.
# Resolver el conflicto (mecánica de la lección 03-05)
# ... editar app.js, quitar los marcadores ...
git add app.js
# Y ahora sí, eliminar la entrada a mano
git stash dropAnota ese hash que imprime drop. Es la referencia al objeto del stash, y con él se puede recuperar una entrada borrada por error (lección 09-04). Es el mismo consejo que dimos al borrar ramas en 03-06, y por el mismo motivo.
Un aviso sobre los conflictos de stash: a diferencia de un merge o un rebase, aquí no hay --abort. Si pop conflictúa, estás en medio de la resolución y la única forma de volver atrás es descartar los cambios del directorio de trabajo (git checkout -- . o git reset --hard, con el cuidado que eso exige) sabiendo que el stash sigue a salvo en la pila.
- Referirse a una entrada concreta
Casi todos los comandos aceptan una entrada. Si la omites, se usa stash@{0}.
La notación stash@{N} es la misma sintaxis de reflog que viste en 02-06 aplicada a la referencia refs/stash. Y admite formas por tiempo:
En algunos intérpretes de comandos las llaves necesitan comillas o escape:
Desde Git 2.11 también se admite el número a secas, que es más cómodo:
Y recuerda: los números se renumeran. Si tienes tres entradas y borras la del medio, la que era stash@{2} pasa a ser stash@{1}. Nunca guardes un número apuntado en un papel; guarda el mensaje.
git stash push: mensajes, rutas y modo interactivo
git stash push: mensajes, rutas y modo interactivogit stash push es la forma moderna y completa. git stash save "mensaje" es la forma antigua, todavía funciona, pero está desaconsejada y no admite rutas.
Mensaje descriptivo:
Es la mejora de calidad de vida más barata que existe con esta herramienta. Un git stash list con tres entradas llamadas WIP on main es inútil; con tres mensajes descriptivos, es un plan de trabajo.
Guardar solo unas rutas:
git stash push -m "Solo los estilos" estilos.css
git stash push -m "Todo lo del directorio de informes" informes/Los cambios de esos ficheros se apartan; el resto se queda en el directorio de trabajo. Es muy útil cuando has mezclado dos tareas en la misma sesión y quieres separar una para trabajar con la otra tranquilo.
Modo interactivo:
Trozo a trozo, igual que git add -p (lección 02-04), Git te pregunta por cada bloque de cambios si quieres apartarlo:
@@ -22,7 +22,11 @@ function pintarLista() {
const lista = document.getElementById('lista-tareas');
lista.innerHTML = '';
- tareas.forEach(function (t) {
+ const ordenadas = tareas.slice().sort(function (a, b) {
...
(1/3) Stash this hunk [y,n,q,a,d,j,J,g,/,e,?]?Las teclas son las mismas de siempre: y sí, n no, q salir, s dividir el trozo, e editarlo a mano.
Otras opciones de push:
| Opción | Qué hace |
|---|---|
-m <mensaje> |
Mensaje descriptivo |
-u / -a |
Incluir sin seguimiento / también ignorados |
-p |
Elegir trozo a trozo |
-k / --keep-index |
Deja intacto lo que estaba preparado (apartado 8) |
-S / --staged |
Aparta solo lo preparado (apartado 8) |
-q |
Silencioso |
--pathspec-from-file=<f> |
Lee la lista de rutas de un fichero |
--keep-index y --staged
--keep-index y --stagedDos opciones parecidas de nombre y muy distintas de efecto. La tabla lo aclara todo; supongamos que tienes app.js preparado y estilos.css modificado sin preparar:
| Opción | Qué se guarda en el stash | Qué queda en el directorio de trabajo |
|---|---|---|
| (ninguna) | app.js + estilos.css |
Nada: directorio limpio |
--keep-index |
app.js + estilos.css |
app.js preparado (lo demás, limpio) |
--staged |
Solo app.js |
estilos.css modificado |
--keep-index guarda todo pero restaura en el directorio lo que estaba en el índice. Su uso clásico es probar un commit antes de hacerlo:
# Tengo preparado exactamente lo que quiero confirmar
git add app.js
git stash push --keep-index -m "Lo que no va en este commit"
# El directorio contiene SOLO lo que voy a confirmar: puedo probarlo de verdad
npm test
# Si pasa, confirmo con la seguridad de haber probado eso y solo eso
git commit -m "Añadir la ordenación por fecha de creación"
# Y recupero el resto
git stash popEs la forma correcta de asegurarse de que un commit es autocontenido y no depende de cambios que se han quedado fuera. Mucha gente descubre así que su commit "listo" no compilaba por sí solo.
Nota: con
--keep-index, el stash contiene también lo preparado. Al hacerpopdespués del commit, esos cambios ya están confirmados y pueden conflictuar. En la práctica se suele combinar con--include-untrackedy se acepta que elpopposterior a veces requiere un--skipmental: revisa congit stash show -pantes de recuperar.
--staged (desde Git 2.35) es más simple y más nueva: aparta solo lo que está en el índice y deja el resto. Es lo contrario del caso anterior, y sirve para "esto que ya tenía preparado me lo llevo a otra rama":
git add utilidades.js
git stash push --staged -m "La utilidad va en otra rama"
git switch funcionalidad/utilidades
git stash pop
git stash branch: cuando el stash ya no encaja
git stash branch: cuando el stash ya no encajaProblema clásico: guardaste un stash hace tres días, la rama ha avanzado mucho desde entonces y ahora git stash pop da un conflicto tras otro.
La causa es que el stash se guardó sobre un commit concreto y ahora se está aplicando sobre otro completamente distinto. La solución es aplicarlo donde encajaba:
Este comando hace cuatro cosas de una vez:
- Crea una rama nueva en el commit sobre el que se guardó el stash.
- Se cambia a ella.
- Aplica el stash (que encaja perfectamente, porque el contexto es el original).
- Elimina la entrada de la pila, ya que se ha aplicado con éxito.
Switched to a new branch 'funcionalidad/rescate'
On branch funcionalidad/rescate
Changes not staged for commit:
modified: app.js
Dropped refs/stash@{1} (5c8e2d1f9a3b7e4c6d1f8a2b5e9c3d7f4a1b8e6c)Desde ahí puedes confirmar tranquilamente y después integrar la rama con merge o rebase, resolviendo los conflictos una sola vez y con contexto, en lugar de pelearte con un pop a ciegas.
Es la mejor salida cuando un stash "no entra". Y también la mejor forma de convertir un stash que se ha vuelto importante en trabajo de verdad.
- Cómo funciona por dentro
Aquí es donde el stash deja de parecer magia. Retomamos el modelo de datos de la lección 01-04.
Cuando haces git stash, Git crea commits normales:
- Un commit con el estado del índice.
- Opcionalmente, un commit con los ficheros sin seguimiento (si usaste
-u). - Un commit de fusión cuyo primer padre es el
HEADactual, el segundo el commit del índice, y el tercero (si existe) el de los sin seguimiento. Este es el commit del stash.
Y guarda la referencia a ese commit en refs/stash. Comprobémoslo:
Un commit corriente. Veamos su interior con las mismas herramientas de 01-04:
tree 3f8b1c7e2d9a5b4f6c1e8a3d7b2f5c9e4a1d6b8f parent 7d3a8f4c9b1e5d2a8f7c3b6e9d4a1c8f5b2e7d3a parent 8e2c5f1a9d3b7e4c1f6a8d2b5e9c3f7a4d1b8e6c parent 1c9e4b7f2a8d5c3e6b1f9a4d7c2e5b8f3a6d1c9e author Carla Vidal <[email protected]> 1753959200 +0200 committer Carla Vidal <[email protected]> 1753959200 +0200 On funcionalidad/orden-por-fecha: Prueba de anatomía
Tres padres:
| Padre | Qué contiene |
|---|---|
1.º (7d3a8f4) |
El HEAD de cuando guardaste: la base |
2.º (8e2c5f1) |
El estado del índice |
3.º (1c9e4b7) |
Los ficheros sin seguimiento (solo con -u) |
Y el tree del propio commit es el estado del directorio de trabajo. Con esos cuatro árboles, Git puede reconstruir exactamente lo que tenías y aplicarlo como una fusión a tres bandas. De ahí que los conflictos de stash pop sean conflictos de fusión normales y corrientes.
La pila, por su parte, es el reflog de refs/stash:
a7f3c92 stash@{0}: On funcionalidad/orden-por-fecha: Prueba de anatomía
5c8e2d1 stash@{1}: WIP on main: 7d3a8f4 Devolver el foco al campo de texto
9b4f7e3 stash@{2}: On funcionalidad/etiquetas-color: prueba del selectorEso explica de golpe tres cosas que antes parecían arbitrarias:
- Por qué la sintaxis es
stash@{N}: es exactamente la sintaxis del reflog. - Por qué los números se renumeran: son posiciones en un registro, no identificadores.
- Por qué se pueden recuperar stashes borrados: el objeto sigue en la base de datos hasta que pase el recolector de basura.
Y una consecuencia práctica muy útil: como el stash es un commit, puedes usar cualquier comando de commits con él.
git show stash@{0} # el commit del stash
git diff stash@{0}^ stash@{0} # su diff contra la base
git diff main stash@{0} -- app.js # comparar con otra rama
git log --oneline stash@{0}^..stash@{0} # rangos, aunque aquí aporte poco
- Riesgos: stashes olvidados y falsas copias de seguridad
Riesgo 1: el stash olvidado. Es, con diferencia, el problema más frecuente. Apartas algo, la urgencia se alarga, pasan tres semanas y el trabajo sigue ahí. Cuando lo encuentras, ya no encaja con nada.
El stash no aparece en git status, no aparece en git log, no sale en ninguna interfaz gráfica por defecto y no se envía nunca al servidor. Es invisible.
Medidas:
# Mira la pila de vez en cuando
git stash list
# Mejor: añádelo al mensaje de tu prompt o a un alias que uses a diario
git config --global alias.st '!git status && echo "--- stash ---" && git stash list'Y la medida real: el stash es para minutos u horas, no para días. Si el trabajo va a esperar más de una jornada, es una rama. git stash branch está justo para eso.
Riesgo 2: creer que es una copia de seguridad. No lo es, por tres motivos:
- Es local. No se envía nunca con
git push. El refspec por defecto solo cubrerefs/heads/*(lección 04-05), yrefs/stashqueda fuera. Si se te muere el disco, el stash muere con él. - No se clona.
git cloneno trae los stashes de nadie. Ni siquiera ungit clone --mirrorlos replica de forma útil. - Es frágil.
git stash clearlo borra todo sin preguntar. Y los commits del stash, al no estar referenciados por ninguna rama, son candidatos al recolector de basura cuando se borran de la pila.
Una copia de seguridad de verdad es un commit en una rama publicada. Si el trabajo importa, confírmalo —aunque sea con un mensaje provisional que luego arreglarás con rebase -i (lección 05-02)— y publícalo.
Riesgo 3: aplicar el stash en la rama equivocada. El stash no está atado a una rama: puedes hacer pop en cualquiera. A veces es exactamente lo que quieres (moverte de rama con el trabajo a cuestas); a veces es un accidente que llena main de cambios que no le tocaban. git stash list te dice en qué rama se guardó cada entrada; léelo antes de recuperar.
Riesgo 4: los ficheros sin seguimiento. Ya lo vimos: sin -u no se guardan. El error concreto y peligroso es hacer git stash seguido de git clean -fd para "dejar todo limpio": el clean borra los ficheros sin seguimiento que el stash no guardó, y esos sí se pierden de verdad.
Errores Comunes y Consejos
Error 1: git stash sin -u cuando hay ficheros nuevos. Se quedan fuera. Es la sorpresa número uno con esta herramienta. Mira siempre git status --short antes de apartar.
Error 2: git stash clear para "ordenar". Borra toda la pila sin preguntar y sin confirmación. Usa git stash drop <entrada> de una en una, después de mirar cada una con show -p.
Error 3: pensar que pop siempre elimina la entrada. Si hay conflicto, la conserva a propósito. Tras resolver, tienes que hacer git stash drop tú.
Error 4: acumular stashes sin mensaje. Cinco entradas llamadas WIP on main son cinco incógnitas. git stash push -m "..." siempre.
Error 5: usar el stash como sistema de ramas. Si vas a tardar más de una jornada, haz una rama. El stash no sobrevive a la memoria de nadie.
Error 6: fiarse de los números. stash@{2} cambia de significado en cuanto añades o quitas entradas. Identifica por mensaje, no por número.
Error 7: combinar git stash con git clean -fd sin pensar. El primero no guarda lo sin seguimiento; el segundo lo borra. La combinación destruye ficheros nuevos.
Consejo 1: alias con -u incorporado. git config --global alias.guardar 'stash push -u -m' y a partir de ahí git guardar "lo que sea".
Consejo 2: --keep-index antes de confirmar. Un git stash push --keep-index && npm test te dice si tu commit es realmente autocontenido. Se tarda un minuto y evita commits rotos.
Consejo 3: git stash show -p antes de pop. Especialmente si la entrada tiene más de un día. Saber qué va a llegar evita sustos.
Consejo 4: git stash branch en cuanto haya conflicto. No pelees con un pop que no encaja: crea la rama en el punto original, aplica limpiamente y fusiona con calma.
Consejo 5: revisa la pila los viernes. Un git stash list semanal es suficiente para que no se te quede nada tres meses.
Ejercicios
Ejercicio 1: la trampa de los ficheros sin seguimiento
En un repositorio de pruebas:
- Modifica un fichero con seguimiento y crea uno nuevo sin añadir.
- Haz
git stashsin-uy comprueba congit statusqué ha pasado con cada uno. - Recupera, y repite la operación con
-u. - Demuestra con
git stash show -p -uque en el segundo caso el fichero nuevo sí está dentro.
Ejercicio 2: --keep-index para validar un commit
Prepara un escenario donde tengas dos cambios: uno preparado (que compila por sí solo) y otro sin preparar (que rompe el fichero). Usando --keep-index:
- Aparta lo que no va en el commit.
- Comprueba que el fichero es válido (
node --checko similar). - Confirma.
- Recupera el resto y observa qué pasa.
Ejercicio 3: anatomía de un stash
Crea un stash con -u y demuestra con comandos de bajo nivel:
- Que
refs/stashapunta a un commit. - Que ese commit tiene tres padres.
- Qué contiene cada uno de los tres.
- Que
git reflog stashygit stash listmuestran la misma información.
Soluciones
Solución 1:
mkdir /tmp/practica-stash && cd /tmp/practica-stash
git init -b main
echo "original" > seguido.txt && git add . && git commit -m "Base"
echo "modificado" > seguido.txt
echo "soy nuevo" > sin-seguir.txt
git status --shortseguido.txt ha vuelto a su versión original; sin-seguir.txt sigue exactamente donde estaba. No se ha guardado.
Ahora sí: el directorio está limpio de verdad y el fichero nuevo ha desaparecido (está en el stash).
diff --git a/seguido.txt b/seguido.txt
--- a/seguido.txt
+++ b/seguido.txt
@@ -1 +1 @@
-original
+modificado
diff --git a/sin-seguir.txt b/sin-seguir.txt
new file mode 100644
--- /dev/null
+++ b/sin-seguir.txt
@@ -0,0 +1 @@
+soy nuevoSolución 2:
mkdir /tmp/practica-keepindex && cd /tmp/practica-keepindex
git init -b main
echo "const a = 1;" > app.js && git add . && git commit -m "Base"
# Cambio bueno, preparado
echo "const b = 2;" >> app.js
git add app.js
# Cambio malo, sin preparar
echo "const c = ;" >> app.js
git status --short(El doble M significa: modificado en el índice y modificado además en el directorio de trabajo.)
# 1. Apartar lo que no va en el commit
git stash push --keep-index -m "El cambio a medias"
cat app.jsEl fichero contiene solo lo preparado.
El pop ha devuelto el estado completo que había antes. Como el commit ya contiene la línea de b, en este caso simple no hay conflicto; con cambios que se solapan en las mismas líneas sí lo habría, y es la razón por la que conviene revisar con git stash show -p antes.
Solución 3:
mkdir /tmp/practica-anatomia && cd /tmp/practica-anatomia
git init -b main
echo "base" > f.txt && git add . && git commit -m "Base"
echo "cambio en el directorio" > f.txt
echo "preparado" > g.txt && git add g.txt
echo "sin seguir" > h.txt
git stash push -u -m "Anatomía"parent 4b8e1c7f2a9d5e3b6c1f8a4d7b2e5c9f3a6d1b8e parent 9d2f6a3c8b1e5f7d4a2c9e6b3f8d1a5c7e4b2f9d parent 6c1a8f4d3e7b2c5a9f1d6b8e3c7a4f2d5b9e1c8a
# 3. Qué contiene cada uno
git show --stat refs/stash^1 | head -3 # la base: el commit HEAD original
git ls-tree refs/stash^2 # el índice: incluye g.txt preparado
git ls-tree refs/stash^3 # los sin seguimiento: h.txtLa misma información, presentada de dos formas. git stash list es, literalmente, una vista del reflog de refs/stash.
Conclusión
git stash es una herramienta pequeña con más matices de los que aparenta. Lo esencial:
- Aparta los cambios sin confirmar y deja el directorio limpio, para que puedas cambiar de rama, atender una urgencia o probar algo, y recuperarlos después.
- Por defecto NO guarda los ficheros sin seguimiento ni los ignorados: para eso están
-u(sin seguimiento, la opción que querrás casi siempre) y-a(también los ignorados, que solo se usa muy conscientemente). - Es una pila:
git stash listla enumera,stash@{0}es la más reciente y los números se renumeran, así que hay que identificar por mensaje.git stash push -m "..."es obligatorio en la práctica. popaplica y elimina;applyaplica y conserva. Si hay conflicto,popconserva la entrada y tienes que hacerdropa mano tras resolver.--keep-indexdeja en el directorio solo lo que ibas a confirmar (para probarlo de verdad);--stagedaparta solo lo preparado. Ypush -ppermite elegir trozo a trozo, ypush <ruta>apartar solo unos ficheros.git stash branch <rama>crea una rama en el commit original del stash y lo aplica allí: la mejor salida cuando un stash ya no encaja.- Por dentro no hay magia: son commits normales en
refs/stash, con elHEADoriginal, el índice y los sin seguimiento como padres, y la pila es el reflog de esa referencia. De ahí vienen la sintaxisstash@{N}y la renumeración. - No es una copia de seguridad: es local, no se envía, no se clona y
clearlo borra sin preguntar. Para minutos y horas, no para días.
Lo que viene
Hasta aquí, todo el módulo ha ido de modificar el historial: reaplicarlo, reorganizarlo, copiarlo, apartarlo. Ahora vamos a hacer lo contrario: fijar un punto en él para siempre.
gestor-tareas está a punto de tener su primera versión estable. El equipo necesita poder decir "esto es la 1.0.0" y que dentro de dos años, cuando llegue una incidencia de un cliente que sigue con esa versión, alguien pueda situarse exactamente en ese código sin depender de recordar un hash de cuarenta caracteres.
Para eso están las etiquetas, el cuarto tipo de objeto de la base de datos de Git que conocimos en la lección 01-04 y del que apenas hemos hablado. Lo vemos en la lección 05-05: Etiquetando Confirmaciones.
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
