Staging Area
La staging area (también llamada index) es una zona intermedia donde Git prepara qué archivos formarán parte del próximo commit.
Añadir archivos al stage:
git add . # añade todo desde el directorio actual
git add archivo.js # añade un archivo concreto
git add carpeta/ # añade una carpeta entera
Ver el estado del stage:
git status # qué está staged, modificado o untracked
git diff --staged # ver qué cambios concretos hay staged
Sacar archivos del stage:
Hay dos comandos para "vaciar" el stage, pero hacen cosas distintas. Confundirlos es muy común. La elección depende de si el archivo ya estaba en algún commit anterior o no.
| Comando | Qué hace | Cuándo usarlo |
|---|---|---|
git restore --staged . |
Deshace el git add. El archivo sigue tracked si ya estaba en algún commit; si era nuevo, vuelve a untracked. |
Cuando se ha hecho add de algo que aún no se quería commitear. Es el "deshacer un add" del día a día. |
git rm -r --cached . |
Destrackea los archivos: los saca del index y los marca para borrarse del repositorio en el próximo commit. No toca el Working Directory. | Cuando un archivo ya estaba commiteado y se quiere que Git deje de seguirlo (típicamente después de añadirlo al .gitignore). |
Aclaración importante: en la versión anterior de este post se usó git restore --staged . como si destrackeara archivos, pero no es así. restore --staged solo deshace un add reciente; no puede destrackear un archivo que ya está en commits anteriores. Para eso es necesario git rm -r --cached.
Caso típico: .env añadido al .gitignore tarde
# El archivo ya está en commits anteriores
echo ".env" >> .gitignore # añadirlo al gitignore no basta
git rm --cached .env # destrackearlo
git commit -m "Dejar de trackear .env"
# A partir de aquí, .gitignore por fin tiene efecto sobre .env
Esto no elimina el archivo del historial: los commits viejos siguen conteniéndolo. Si era un secreto, hay que rotarlo y reescribir historia con git filter-repo.
Commits
Un commit es una instantánea del proyecto en un momento dado. Forman una cadena que constituye el historial del repositorio.
Crear un commit:
git commit -m "Mensaje descriptivo"
git commit -am "Mensaje" # add + commit de archivos ya tracked (no incluye nuevos)
Ver el historial:
git log # historial completo
git log --oneline # versión compacta, una línea por commit
git log --graph --oneline # visualización del grafo de ramas
Restaurar todo al estado del último commit:
git reset --hard HEAD
No borra archivos untracked (los que Git no conoce). Para limpiar también esos, conviene combinarlo con git clean -fd.
Deshacer commits:
Los tres reset se diferencian en qué zonas afectan:
| Comando | Qué hace | Working Dir | Stage | Commits |
|---|---|---|---|---|
git reset --soft HEAD~1 |
Quita el commit, deja los cambios staged listos para recommittear. | Intacto | Intacto | Retrocede 1 |
git reset HEAD~1 (mixed) |
Quita el commit y saca los cambios del stage. Los cambios quedan como "modified". | Intacto | Vaciado | Retrocede 1 |
git reset --hard HEAD~1 |
Quita el commit y descarta los cambios completamente. Destructivo. | Retrocede 1 | Retrocede 1 | Retrocede 1 |
--hard es irreversible para cambios no commiteados. En caso de duda, conviene usar --soft o --mixed primero.
Alternativa segura: git revert
git revert HEAD # crea un commit NUEVO que deshace el anterior
A diferencia de reset, no reescribe historia. Es lo recomendado en ramas compartidas.
Restaurar temporalmente el área de trabajo
En muchas ocasiones puede ser importante el restaurar el área de trabajo para ver cómo era el estado anterior de algún archivo en particular. Para hacer esto, usar:
git checkout <commit>
Stash
Stash es un almacén temporal donde se pueden guardar estados y cambios de forma aislada. Deja el working tree en el estado del último commit HEAD además de limpiar el stage area (a menos que se use --keep-index).
Comportamiento:
Las versiones guardadas en el stash están aislados ente sí. En cambio cada stash tiene como padre el HEAD de la rama desde donde se crea este. Los stash se guardan en un stack y debido a que no están relacionados entre sí, es posible borrar un stash que está en un nivel inferior sin ningún tipo de consecuencia. Es importante notar que los stash no se guardan en los remotos sino que se guardan exclusivamente de manera local.
Crear un stash:
Para crear un stash se debe usar el siguientre comando:
git stash push
Si se quiere crear un stash sin eliminar el index (como se había mencionado antes):
git stash push --keep-index
Si se desea agregar una descripción, usar:
git stash push -m "My stash"
El Stack de Stash:
El stack de Stash es una pila LIFO (La última en agregarse es la primera en irse). Los nombres de cada stash vienen numerados dinámicamente usando el modelo stash@{x}. El siguiente cuadro ejemplifica el stack de stash:
refs/stash ──> stash@{0} "wip login" ─┐
stash@{1} "pruebas API" ├─ todos cuelgan de HEAD,
stash@{2} "WIP on main" ─┘ no unos de otros
│
└── entradas del reflog de refs/stash
Poniéndonos un poco más técnicos, la pila se guarda en dos archivos, el primero guarda la referencia de la última entrada guardada y el segundo guarda las referencias de todos los stash guardados.
.git/refs/stash ──────────> último stash
.git/logs/refs/stash ────────> todos los stash
Comandos de Stash A continuación se presenta una tabla con algunos de los comandos de stash.
| Comando | Efecto |
|---|---|
git stash push |
apila una entrada nueva en stash@{0} |
git stash list |
muestra la pila entera |
git stash show -p stash@{N} |
el diff de una entrada |
git stash apply stash@{N} |
aplica, sin eliminarla |
git stash pop stash@{N} |
aplica y la elimina |
git stash drop stash@{N} |
la saca sin aplicar |
git stash clear |
vacía la pila |
git reflog stash |
la pila en crudo, con hashes |
Merge: unir ramas
Al incorporar el trabajo de una rama en otra, Git ofrece tres métodos según la situación.
Fast-forward
Posible solo cuando la rama base (master) no tuvo commits propios mientras existía la otra rama. Git mueve el puntero hacia adelante sin crear ningún commit nuevo. El historial queda lineal, como si la rama nunca hubiera existido.
Antes:
commit1 → commit2 (master)
↘
commit3 → commit4 (rama-1)
Después:
commit1 → commit2 → commit3 → commit4
↑
master, rama-1
- A favor: historial limpio.
- En contra: se pierde la información de que existió una rama paralela.
Merge commit
Cuando ambas ramas tienen commits propios, Git crea un commit de merge con dos padres. El historial refleja honestamente que hubo dos líneas paralelas.
Antes:
commit1 → commit2 → commit3 → commit6 → commit7 (master)
↘
commit4 → commit5 (rama-1)
Después:
commit4 → commit5
↗ ↘
commit1 → commit2 → commit3 → commit6 → commit7 → mergeCommit
↑
master
- A favor: historial honesto, refleja la realidad del desarrollo.
- En contra: el grafo se vuelve complejo con muchas ramas.
Rebase
Reescribe los commits de la rama para que parezcan haber salido del último commit de master. El historial queda lineal, pero "miente" sobre cómo ocurrió el desarrollo real.
Antes:
commit1 → commit2 → commit3 → commit6 → commit7 (master)
↘
commit4 → commit5 (rama-1)
Después:
commit1 → commit2 → commit3 → commit6 → commit7 → commit4' → commit5'
↑
rama-1
Los commit4 y commit5 originales quedan huérfanos y git gc los eliminará eventualmente. commit4' y commit5' son nuevos commits con los mismos cambios pero distinto hash.
- A favor: historial lineal, fácil de leer.
- En contra: reescribe historia. Nunca debe rebasearse una rama ya compartida con otros, porque les rompe su copia local.
Regla práctica:
- Rebase para limpiar una rama local antes de mergear.
- Merge commit para integrar trabajo compartido en la rama principal.
Otros comandos útiles
Descartar cambios locales en archivos tracked:
git restore . # versión moderna (Git 2.23+)
git checkout . # versión clásica, sigue funcionando
Sustituye los archivos modificados por la versión del stage (o de HEAD si no hay nada staged).
Eliminar archivos untracked del Working Directory:
git clean -fd
# │└─ d: incluir directorios untracked
# └── f: force (obligatorio por seguridad)
git clean -nd # DRY RUN: muestra qué borraría sin borrar nada
git clean -fdx # también borra archivos ignorados (.env, node_modules, etc.)
Conviene ejecutar siempre git clean -n primero. Los archivos borrados con clean no van a la papelera y, por ser untracked, Git no puede recuperarlos. Con -x se pueden perder archivos como .env con credenciales locales.
Combinación "dejar el repo como recién clonado":
git reset --hard HEAD && git clean -fd
Descarta cambios en archivos tracked y borra los untracked. El Working Directory queda exactamente igual al último commit.