Git más allá de lo básico: Workflows que ahorran horas cada semana
Interactive rebase, cherry-pick, bisect, worktrees, rescate con reflog y las estrategias de branching que realmente funcionan. Comandos de Git que uso cada día y que la mayoría de desarrolladores no conoce.
La mayoría de desarrolladores aprende cinco comandos de Git y ahí se detiene. add, commit, push, pull, merge. Quizás checkout y branch si se sienten aventureros. Eso te saca adelante el primer año. Después tu rama tiene 47 commits con mensajes como "fix" y "wip" y "please work", accidentalmente reseteaste algo que no debías, y pasas 40 minutos en Stack Overflow tratando de deshacer un merge que salió mal.
He usado Git durante años. No casualmente — intensivamente. Múltiples ramas, múltiples repos, múltiples colaboradores, todo el día, todos los días. Lo que sigue son los comandos y workflows que realmente uso. No los que quedan bien en un tutorial. Los que me ahorran tiempo real, todas las semanas.
Interactive Rebase: Limpia tu desastre antes de que alguien lo vea#
Tu rama tiene doce commits. La mitad son "fix typo". Uno dice "undo previous commit". Otro dice "actually fix it this time". Estás a punto de abrir un PR. Nadie necesita ver ese historial.
Interactive rebase es cómo reescribes la historia de tu rama antes de compartirla. Te permite aplastar commits, reformular mensajes, reordenarlos o eliminarlos por completo.
El comando básico#
git rebase -i HEAD~5Esto abre un editor mostrando tus últimos 5 commits, del más antiguo al más reciente:
pick a1b2c3d Add user authentication endpoint
pick d4e5f6g Fix typo in auth middleware
pick h7i8j9k Add rate limiting
pick l0m1n2o Fix rate limit bug
pick p3q4r5s Update auth testsCada línea empieza con un comando. Cambia pick por uno de estos:
squash(os) — Fusiona este commit con el de arriba, combina los mensajesfixup(of) — Igual que squash, pero descarta el mensaje de este commitreword(or) — Conserva el commit pero cambia su mensajedrop(od) — Elimina este commit por completoedit(oe) — Pausa el rebase en este commit para que puedas modificarlo
Una sesión de limpieza real#
Esto es lo que realmente hago. Ese historial desordenado de arriba se convierte en:
pick a1b2c3d Add user authentication endpoint
fixup d4e5f6g Fix typo in auth middleware
pick h7i8j9k Add rate limiting
fixup l0m1n2o Fix rate limit bug
pick p3q4r5s Update auth testsGuardar y cerrar. Ahora tienes tres commits limpios en lugar de cinco. El fix del typo se integra en el commit de auth. El fix del bug de rate limit se integra en el commit de rate limit. Tu reviewer del PR ve una progresión limpia y lógica.
Reordenando commits#
Literalmente puedes reorganizar las líneas. Si el commit de tests debería ir antes del commit de rate limiting, simplemente mueve la línea:
pick a1b2c3d Add user authentication endpoint
pick p3q4r5s Update auth tests
pick h7i8j9k Add rate limitingGit reproducirá tus commits en este nuevo orden. Si hay conflictos, pausará y te dejará resolverlos.
El atajo de autosquash#
Si sabes que un commit es una corrección de uno anterior, márcalo al momento de hacer commit:
git commit --fixup=a1b2c3dEsto crea un commit con el mensaje fixup! Add user authentication endpoint. Luego cuando haces rebase:
git rebase -i --autosquash HEAD~5Git automáticamente reordena los commits fixup justo debajo de sus objetivos y los marca como fixup. Solo guardas y cierras. Sin edición manual.
Uso esto constantemente. Es la forma más rápida de iterar en una rama mientras mantienes el historial final limpio.
La regla de oro#
Nunca hagas rebase de commits que ya fueron pusheados a una rama compartida. Si otras personas han basado trabajo en esos commits, reescribir la historia causará problemas reales. Haz rebase de tus propias ramas de features antes de mergear. Nunca hagas rebase de main.
Si ya pusheaste tu rama de feature y necesitas hacer rebase:
git push --force-with-leaseEl flag --force-with-lease es más seguro que --force. Se rehúsa a pushear si alguien más ha pusheado a la misma rama desde tu último fetch. No prevendrá todos los problemas, pero atrapa el más común.
Cherry-Pick: Transferencias quirúrgicas de commits#
Cherry-pick toma un commit específico de una rama y lo aplica en otra. No es un merge. No es un rebase. Solo un commit, aplicado limpiamente.
Cuándo realmente uso esto#
El escenario más común: arreglé un bug en mi rama de feature, pero la corrección también necesita ir a main o a una rama de release ahora mismo. No quiero mergear toda mi rama de feature. Quiero solo esa corrección.
# Find the commit hash of the fix
git log --oneline feature/user-auth
# a1b2c3d Fix null pointer in session validation
# Switch to main and cherry-pick it
git checkout main
git cherry-pick a1b2c3dListo. La corrección está en main como un nuevo commit con los mismos cambios.
Cherry-pick sin hacer commit#
A veces quieres aplicar los cambios pero no hacer commit todavía. Quizás quieres combinar varios cherry-picks en un solo commit, o modificar ligeramente los cambios:
git cherry-pick --no-commit a1b2c3dLos cambios están en staging pero no commiteados. Puedes modificarlos, agregar más cambios, y hacer commit cuando estés listo.
Cherry-pick en rango#
¿Necesitas múltiples commits consecutivos? Usa la sintaxis de rango:
git cherry-pick a1b2c3d..f6g7h8iEsto hace cherry-pick de todo después de a1b2c3d hasta e incluyendo f6g7h8i. Nota que a1b2c3d en sí queda excluido. Si quieres incluirlo:
git cherry-pick a1b2c3d^..f6g7h8iManejando conflictos#
Los conflictos de cherry-pick funcionan como los conflictos de merge. Cuando ocurre uno:
# Git will tell you there's a conflict
# Fix the conflicting files, then:
git add .
git cherry-pick --continueO si cambias de opinión:
git cherry-pick --abortAlgo a tener en cuenta: los commits cherry-pickeados crean nuevos hashes de commit. Si después mergeas la rama original, Git generalmente es lo suficientemente inteligente para manejar la duplicación. Pero si haces cherry-pick agresivamente entre ramas que eventualmente se van a mergear, podrías ver conflictos inesperados. Úsalo quirúrgicamente, no como una estrategia de merge.
Git Bisect: Búsqueda binaria para bugs#
Algo se rompió. Sabes que funcionaba hace dos semanas. Ha habido 200 commits desde entonces. ¿Cuál lo rompió?
Podrías revisar cada commit manualmente. O podrías usar git bisect, que usa búsqueda binaria para encontrar el commit exacto que rompió las cosas en log2(n) pasos. Para 200 commits, eso son unos 7-8 verificaciones en lugar de 200.
El método manual#
# Start bisecting
git bisect start
# Mark the current commit as bad (the bug exists here)
git bisect bad
# Mark a known good commit (the bug didn't exist here)
git bisect good v2.1.0Git hace checkout de un commit a mitad de camino entre good y bad. Pruébalo. Luego:
# If the bug exists at this commit:
git bisect bad
# If the bug doesn't exist at this commit:
git bisect goodGit reduce el rango a la mitad cada vez. Después de 7-8 pasos, te dice:
a1b2c3d4e5f6g7h is the first bad commit
commit a1b2c3d4e5f6g7h
Author: Some Developer <dev@example.com>
Date: Tue Feb 18 14:23:01 2026 +0300
Refactor session handling to use async middlewareAhora sabes exactamente qué commit introdujo el bug. Cuando termines:
git bisect resetEsto te regresa a donde estabas.
El método automatizado (aquí está el verdadero poder)#
Si tienes un test que puede detectar el bug, puedes automatizar todo el proceso:
git bisect start
git bisect bad HEAD
git bisect good v2.1.0
git bisect run npm test -- --grep "session validation"Git automáticamente hará checkout de commits, ejecutará el test, y los marcará como good o bad basándose en el código de salida. Cero significa good, distinto de cero significa bad. Te vas, vuelves, y te dice el commit exacto.
Puedes usar cualquier script:
git bisect run ./test-regression.shDonde test-regression.sh es:
#!/bin/bash
npm run build 2>/dev/null || exit 125 # 125 means "skip this commit"
npm test -- --grep "session" || exit 1 # 1 means "bad"
exit 0 # 0 means "good"El código de salida 125 es especial — le dice a bisect que omita ese commit (útil si un commit no compila). Esta es una de esas funcionalidades que parecen de nicho hasta que las necesitas, y entonces te ahorran toda una tarde.
Una sesión real de bisect#
Así se ve en la práctica:
$ git bisect start
$ git bisect bad HEAD
$ git bisect good abc1234
Bisecting: 97 revisions left to test after this (roughly 7 steps)
[def5678...] Commit message here
$ npm test -- --grep "login flow"
# Tests fail
$ git bisect bad
Bisecting: 48 revisions left to test after this (roughly 6 steps)
[ghi9012...] Another commit message
$ npm test -- --grep "login flow"
# Tests pass
$ git bisect good
Bisecting: 24 revisions left to test after this (roughly 5 steps)
...
# After ~7 iterations:
abc1234def5678ghi is the first bad commitSiete pasos para encontrar una aguja en un pajar de 200 commits.
Git Worktrees: Múltiples ramas, cero stashing#
Esta es la funcionalidad más subutilizada de Git. Estoy convencido de que la mayoría de desarrolladores no saben que existe.
El problema: estás metido en una rama de feature. Hay archivos cambiados por todos lados. Entonces alguien dice "¿puedes ver este bug de producción rapidito?" Tienes tres opciones:
- Stashear todo, cambiar de rama, arreglar, volver a cambiar, pop stash. Esperar que nada salga mal.
- Commitear tu trabajo a medias con un mensaje "wip". Feo pero funcional.
- Clonar el repo de nuevo en otro directorio.
O la opción 4: worktrees.
Qué es un worktree#
Un worktree es un segundo (o tercero, o cuarto) directorio de trabajo vinculado al mismo repositorio. Cada worktree tiene su propia rama checkeada, sus propios archivos de trabajo, su propio index. Pero comparten los mismos datos .git, así que no estás duplicando todo el repo.
Agregando un worktree#
# You're on feature/user-auth, deep in work
# Need to fix a bug on main:
git worktree add ../hotfix-session mainEsto crea un nuevo directorio ../hotfix-session con main checkeado. Tu directorio actual se queda exactamente como estaba. Nada stasheado, nada commiteado, nada interrumpido.
cd ../hotfix-session
# Fix the bug
git add .
git commit -m "Fix null pointer in session validation"
git push origin main
cd ../my-project
# Continue working on your feature as if nothing happenedCreando una nueva rama en un worktree#
git worktree add ../hotfix-nav -b hotfix/nav-crash mainEsto crea el worktree Y crea una nueva rama hotfix/nav-crash basada en main.
Administrando worktrees#
# List all worktrees
git worktree list
# /home/dev/my-project abc1234 [feature/user-auth]
# /home/dev/hotfix-session def5678 [main]
# Remove a worktree when done
git worktree remove ../hotfix-session
# If the directory was already deleted:
git worktree prunePor qué es mejor que el stashing#
El stashing está bien para cambios de contexto rápidos. Pero los worktrees son mejores para cualquier cosa que tome más de cinco minutos:
- Tu IDE se queda abierto en tu rama de feature. Sin reindexar, sin perder tu posición de scroll.
- Puedes correr tests en ambos directorios simultáneamente.
- Sin riesgo de conflictos de stash o de olvidar qué stasheaste.
- Puedes tener un dev server corriendo en un worktree y un build limpio en otro.
Típicamente mantengo dos o tres worktrees activos: mi rama principal de feature, un worktree de main para verificaciones rápidas, y a veces un worktree de review donde hago checkout del PR de alguien más.
La única advertencia#
No puedes tener la misma rama checkeada en dos worktrees. Eso es por diseño — previene que hagas cambios conflictivos a la misma rama en dos lugares. Si lo intentas, Git se rehusará.
Reflog: El botón de deshacer para todo#
Hiciste un hard reset y perdiste commits. Borraste una rama. Hiciste rebase y todo salió terriblemente mal. Piensas que tu trabajo se perdió.
No se perdió. Git casi nunca borra nada realmente. El reflog es tu red de seguridad.
Qué es el reflog#
Cada vez que HEAD se mueve — cada commit, checkout, rebase, reset, merge — Git lo registra en el reflog. Es un log de todos los lugares donde tu HEAD ha estado, en orden.
git reflogSalida:
a1b2c3d (HEAD -> main) HEAD@{0}: reset: moving to HEAD~3
f4e5d6c HEAD@{1}: commit: Add payment processing
b7a8c9d HEAD@{2}: commit: Update user dashboard
e0f1g2h HEAD@{3}: commit: Fix auth token refresh
i3j4k5l HEAD@{4}: checkout: moving from feature/payments to mainCada entrada tiene un índice (HEAD@{0}, HEAD@{1}, etc.) y una descripción de lo que pasó.
Recuperándose después de un hard reset#
Accidentalmente ejecutaste git reset --hard HEAD~3 y perdiste tres commits. Están justo ahí en el reflog:
# See what you lost
git reflog
# The commit before the reset is HEAD@{1}
git reset --hard f4e5d6cLos tres commits están de vuelta. Crisis evitada.
Recuperando una rama borrada#
Borraste una rama que tenía trabajo sin mergear:
git branch -D feature/experimental
# Oh no, that had two weeks of workLos commits siguen existiendo. Encuéntralos:
git reflog | grep "feature/experimental"
# Or just look through the reflog for the last commit on that branch
# Found it. Recreate the branch at that commit:
git branch feature/experimental a1b2c3dLa rama está de vuelta, con todos sus commits.
Recuperándose después de un mal rebase#
Hiciste rebase y todo salió mal. Conflictos por todos lados, commits equivocados, caos:
# The reflog shows where you were before the rebase
git reflog
# a1b2c3d HEAD@{0}: rebase (finish): ...
# ...
# f4e5d6c HEAD@{5}: rebase (start): checkout main
# b7a8c9d HEAD@{6}: commit: Your last good commit
# Go back to before the rebase
git reset --hard b7a8c9dVolviste a exactamente donde estabas antes de que empezara el rebase. Como si nunca hubiera pasado.
La red de seguridad de 30 días#
Por defecto, Git conserva las entradas del reflog durante 30 días (90 días para commits alcanzables). Después de eso, pueden ser recolectados por el garbage collector. Así que tienes un mes para darte cuenta de que cometiste un error. En la práctica, eso es más que suficiente.
Puedes verificar la expiración:
git config gc.reflogExpire
# default: 90.days.ago (for reachable)
git config gc.reflogExpireUnreachable
# default: 30.days.ago (for unreachable)Si eres paranoico, auméntalo:
git config --global gc.reflogExpireUnreachable "180.days.ago"Una regla personal#
Antes de cualquier operación destructiva — hard reset, force push, borrado de ramas — ejecuto git log --oneline -10 primero. Noto mentalmente el HEAD actual. Toma dos segundos y me ha salvado más de una vez de un pánico que no necesitaba tener.
Stash correctamente: No es solo git stash#
La mayoría de gente usa stash así:
git stash
# do something
git stash popEso funciona, pero es el equivalente de tirar todo en una caja etiquetada "cosas". Cuando tienes tres stashes, no tienes idea de cuál es cuál.
Nombra tus stashes#
git stash push -m "WIP: user auth form validation"Ahora cuando listas stashes:
git stash list
# stash@{0}: On feature/auth: WIP: user auth form validation
# stash@{1}: On main: Quick fix attempt for nav bug
# stash@{2}: On feature/payments: Experiment with Stripe webhooksPuedes ver exactamente qué contiene cada stash.
Incluir archivos no rastreados#
Por defecto, git stash solo stashea archivos rastreados. Archivos nuevos que no has agregado se quedan atrás:
# Stash everything, including new files
git stash push --include-untracked -m "WIP: new auth components"
# Or even include ignored files (rarely needed)
git stash push --all -m "Full workspace snapshot"Uso --include-untracked casi siempre. Dejar archivos nuevos atrás cuando cambias de rama causa confusión.
Stashing parcial#
Esta es la que la mayoría no conoce. Puedes stashear archivos específicos:
# Stash only specific files
git stash push -m "Just the auth changes" src/auth/ src/middleware.tsO usar el modo patch para stashear hunks específicos dentro de archivos:
git stash push --patch -m "Partial: only the validation logic"Git recorrerá cada cambio interactivamente y te preguntará si quieres stashearlo. y para sí, n para no, s para dividir el hunk en piezas más pequeñas.
Apply vs Pop#
# Pop: apply and remove from stash list
git stash pop stash@{2}
# Apply: apply but keep in stash list
git stash apply stash@{2}Uso apply cuando no estoy seguro de si el stash se aplicará limpiamente. Si hay un conflicto, el stash se preserva. Con pop, si hay un conflicto, el stash se queda en la lista de todas formas (mucha gente no sabe esto), pero prefiero la intención explícita de apply.
Viendo el contenido de un stash#
# See what files changed in a stash
git stash show stash@{0}
# See the full diff
git stash show -p stash@{0}Creando una rama desde un stash#
Si tu stash creció en algo más sustancial:
git stash branch feature/auth-validation stash@{0}Esto crea una nueva rama desde el commit donde originalmente hiciste stash, aplica el stash, y lo elimina. Limpio.
Estrategias de branching: Cuál realmente funciona#
Hay tres estrategias mainstream de branching. Cada una tiene un contexto donde brilla y contextos donde causa dolor.
Gitflow#
El clásico. main, develop, feature/*, release/*, hotfix/*. Creado por Vincent Driessen en 2010.
# Feature branch
git checkout -b feature/user-auth develop
# ... work ...
git checkout develop
git merge --no-ff feature/user-auth
# Release branch
git checkout -b release/2.1.0 develop
# ... final fixes ...
git checkout main
git merge --no-ff release/2.1.0
git tag -a v2.1.0 -m "Release 2.1.0"
git checkout develop
git merge --no-ff release/2.1.0
# Hotfix
git checkout -b hotfix/session-fix main
# ... fix ...
git checkout main
git merge --no-ff hotfix/session-fix
git checkout develop
git merge --no-ff hotfix/session-fixCuándo funciona: Apps móviles, software de escritorio, cualquier cosa con releases versionados con nombre y múltiples versiones soportadas simultáneamente. Si distribuyes v2.1 y v3.0 y necesitas parchear ambas, Gitflow maneja eso.
Cuándo no funciona: Aplicaciones web con deployment continuo. Si despliegas a producción 5 veces al día, la ceremonia de ramas de release y ramas develop es pura sobrecarga. La mayoría de equipos web que adoptan Gitflow terminan con una rama develop que está perpetuamente rota y ramas de release que nadie entiende.
GitHub Flow#
Simple. Tienes main. Creas ramas de feature. Abres PRs. Mergeas a main. Despliegas main.
git checkout -b feature/user-auth main
# ... work ...
git push origin feature/user-auth
# Open PR, get reviewed, merge
# main is always deployableCuándo funciona: Equipos pequeños a medianos desplegando apps web. Deployment continuo. Si main siempre se despliega, esto es todo lo que necesitas. Es lo que usa este sitio.
Cuándo no funciona: Cuando necesitas mantener múltiples versiones de release, o cuando tienes un ciclo largo de QA antes del deployment. GitHub Flow asume que main va a producción rápidamente.
Trunk-Based Development#
Todos commitean a main (el "trunk") directamente o vía ramas de muy corta duración (menos de un día). Sin ramas de feature de larga duración.
# Short-lived branch (merged same day)
git checkout -b fix/auth-token main
# ... small, focused change ...
git push origin fix/auth-token
# PR reviewed and merged within hours, not daysCuándo funciona: Equipos de alto rendimiento con buen CI/CD, suites de tests completas y feature flags. Google, Meta y la mayoría de grandes empresas tech usan trunk-based development. Fuerza cambios pequeños e incrementales y elimina el infierno de merges.
Cuándo no funciona: Equipos sin buena cobertura de tests o CI. Si mergear al trunk significa desplegar código sin testear, romperás producción constantemente. También necesitas feature flags para cualquier cosa que tome más de un día en construir:
# Feature flag in code
if (featureFlags.isEnabled('new-checkout-flow')) {
renderNewCheckout();
} else {
renderLegacyCheckout();
}Mi recomendación#
Para la mayoría de equipos de desarrollo web: empieza con GitHub Flow. Es simple, funciona, y no requiere herramientas más allá de lo que GitHub/GitLab ya proporciona.
Si tu equipo crece más allá de 15-20 ingenieros y estás desplegando múltiples veces al día, mira trunk-based development con feature flags. La inversión en infraestructura de feature flags se paga sola en menos conflictos de merge e iteración más rápida.
Si estás distribuyendo software versionado (apps móviles, herramientas CLI, librerías): Gitflow o una versión simplificada. Realmente necesitas esas ramas de release.
No elijas una estrategia porque un blog post dijo que es la mejor. Elige la que coincida con cómo realmente distribuyes tu software.
Git Hooks: Automatiza las cosas que sigues olvidando#
Los Git hooks son scripts que se ejecutan automáticamente en puntos específicos del workflow de Git. Son locales a tu máquina (no se pushean al remoto), lo que significa que necesitas una forma de compartirlos con tu equipo.
Los hooks que realmente importan#
pre-commit — Se ejecuta antes de cada commit. Úsalo para linting y formateo:
#!/bin/bash
# .git/hooks/pre-commit
# Run ESLint on staged files only
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts|tsx)$')
if [ -n "$STAGED_FILES" ]; then
echo "Running ESLint on staged files..."
npx eslint $STAGED_FILES --quiet
if [ $? -ne 0 ]; then
echo "ESLint failed. Fix errors before committing."
exit 1
fi
fi
# Run Prettier on staged files
if [ -n "$STAGED_FILES" ]; then
echo "Running Prettier..."
npx prettier --check $STAGED_FILES
if [ $? -ne 0 ]; then
echo "Prettier check failed. Run 'npx prettier --write' first."
exit 1
fi
ficommit-msg — Valida el formato del mensaje de commit. Perfecto para imponer conventional commits:
#!/bin/bash
# .git/hooks/commit-msg
COMMIT_MSG=$(cat "$1")
PATTERN="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{1,72}"
if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
echo "Invalid commit message format."
echo "Expected: type(scope): description"
echo "Example: feat(auth): add session refresh endpoint"
echo ""
echo "Valid types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert"
exit 1
fipre-push — Se ejecuta antes de pushear. Úsalo para tests:
#!/bin/bash
# .git/hooks/pre-push
echo "Running tests before push..."
npm test
if [ $? -ne 0 ]; then
echo "Tests failed. Push aborted."
exit 1
fiHusky vs Hooks nativos#
Los hooks nativos viven en .git/hooks/. El problema: el directorio .git no se rastrea por Git, así que no puedes compartir hooks a través del repo. Todos tienen que configurarlos manualmente.
Husky resuelve esto. Almacena las configuraciones de hooks en el repo y configura .git/hooks automáticamente con npm install:
npx husky initEsto crea un directorio .husky/. Agrega hooks como archivos:
# .husky/pre-commit
npx lint-stagedCombinado con lint-staged, obtienes verificaciones pre-commit rápidas y dirigidas:
{
"lint-staged": {
"*.{js,ts,tsx}": ["eslint --fix", "prettier --write"],
"*.css": ["prettier --write"],
"*.json": ["prettier --write"]
}
}Esto ejecuta ESLint y Prettier solo en los archivos que realmente estás commiteando. No en todo el codebase. Rápido.
Cuándo saltarse los hooks#
A veces necesitas commitear sin que los hooks se ejecuten. Hotfixes de emergencia, commits work-in-progress en tu propia rama:
git commit --no-verify -m "WIP: debugging production issue"Úsalo con moderación. Si te encuentras saltando hooks regularmente, tus hooks probablemente son demasiado lentos o demasiado estrictos.
Aliases que ahorran tiempo#
Mi .gitconfig ha evolucionado durante años. Estos son los aliases que sobrevivieron — los que realmente uso a diario, no los que agregué porque se veían ingeniosos.
Pretty Log#
El git log por defecto es verboso. Esto te da una vista limpia, colorida y basada en grafos:
git config --global alias.lg "log --oneline --graph --all --decorate"Uso:
git lg
# * a1b2c3d (HEAD -> main) Fix session validation
# | * d4e5f6g (feature/payments) Add Stripe integration
# | * h7i8j9k Update payment models
# |/
# * l0m1n2o Merge PR #42
# * p3q4r5s Add user dashboardEjecuto esto 20 veces al día. Es la forma más rápida de entender el estado de tu repositorio.
Deshacer el último commit#
Conserva los cambios, solo deshace el commit:
git config --global alias.undo "reset HEAD~1 --mixed"Uso:
git undo
# Commit is gone, but all changes are still in your working directoryUso esto cuando commiteo demasiado pronto, olvido un archivo, o quiero reestructurar los cambios.
Quitar todo del staging#
git config --global alias.unstage "reset HEAD --"Uso:
git unstage src/auth/session.ts
# File is unstaged but changes are preservedListar todos los aliases#
Porque olvidarás lo que configuraste:
git config --global alias.aliases "config --get-regexp ^alias\\."Más aliases que uso#
# Show what you're about to commit
git config --global alias.staged "diff --staged"
# Short status
git config --global alias.st "status -sb"
# Amend without changing the message
git config --global alias.amend "commit --amend --no-edit"
# Show the last commit
git config --global alias.last "log -1 HEAD --stat"
# Pull with rebase instead of merge
git config --global alias.up "pull --rebase --autostash"
# Delete branches that have been merged to main
git config --global alias.cleanup "!git branch --merged main | grep -v '^[ *]*main$' | xargs git branch -d"El alias up es particularmente bueno. pull --rebase mantiene tu historial lineal en lugar de crear merge commits en cada pull. --autostash automáticamente stashea y restaura tus cambios si tienes archivos sucios. Es lo que pull debería haber sido por defecto.
El alias cleanup borra ramas locales que ya fueron mergeadas a main. Con el tiempo acumulas docenas de ramas obsoletas. Ejecuta esto semanalmente.
Los comandos que uso a diario#
Estos no son aliases ni funcionalidades avanzadas. Son solo comandos que ejecuto constantemente y que muchos desarrolladores parecen no conocer.
El comando de log definitivo#
git log --oneline --graph --allEsto muestra cada rama, cada merge, toda la topología de tu repo en una vista compacta. Es lo primero que ejecuto cuando hago pull de cambios. Responde "¿qué está pasando en este repo ahora mismo?"
Diff de cambios en staging#
git diff --stagedEsto muestra lo que está a punto de ser commiteado. No lo que cambió en tu directorio de trabajo — lo que realmente está en staging. Siempre ejecuto esto antes de commitear. Siempre. Atrapa inclusiones accidentales, sentencias de debug, console.logs que no deberían estar ahí.
# See unstaged changes
git diff
# See staged changes
git diff --staged
# See both
git diff HEADMostrar un commit específico#
git show a1b2c3dMuestra el diff completo de un solo commit. Útil cuando revisas historial, para entender qué cambió realmente un commit.
# Show just the files that changed
git show --stat a1b2c3d
# Show a specific file at a specific commit
git show a1b2c3d:src/auth/session.tsEse último es increíblemente útil. Puedes ver cualquier archivo en cualquier punto de la historia sin hacer checkout de ese commit.
Blame con rangos de líneas#
git blame -L 42,60 src/auth/session.tsMuestra quién modificó por última vez las líneas 42-60. Más útil que hacer blame del archivo completo, que generalmente es abrumador.
# Show the commit message too, not just the hash
git blame -L 42,60 --show-name src/auth/session.ts
# Ignore whitespace changes (very useful)
git blame -w -L 42,60 src/auth/session.ts
# Show the commit before the blamed one (dig deeper)
git log --follow -p -- src/auth/session.tsEl flag -w es importante. Sin él, blame atribuirá líneas a quien sea que haya reformateado el archivo por última vez, que rara vez es la persona que estás buscando.
Buscar una cadena en todo el historial#
# Find when a function was added or removed
git log -S "validateSession" --oneline
# Find when a regex pattern appeared
git log -G "session.*timeout" --oneline-S (el "pickaxe") encuentra commits donde el número de ocurrencias de una cadena cambió. -G encuentra commits donde el diff coincide con un regex. Ambos son poderosos para arqueología — descubrir cuándo algo fue introducido o eliminado.
Mostrar qué cambió entre dos puntos#
# What changed between two branches
git diff main..feature/auth
# What changed on this branch since it diverged from main
git diff main...feature/auth
# List just the changed files
git diff main...feature/auth --name-only
# Stat view (files + insertions/deletions)
git diff main...feature/auth --statDos puntos vs tres puntos importa. Dos puntos muestra la diferencia entre las puntas de ambas ramas. Tres puntos muestra qué cambió en el lado derecho desde que divergió del lado izquierdo. Tres puntos generalmente es lo que quieres cuando revisas una rama de feature.
Limpiar archivos no rastreados#
# See what would be deleted (dry run)
git clean -n
# Delete untracked files
git clean -f
# Delete untracked files and directories
git clean -fd
# Delete untracked and ignored files (nuclear option)
git clean -fdxSiempre ejecuta con -n primero. git clean -fdx borrará tus node_modules, .env, artefactos de build — todo lo que no esté rastreado por Git. Útil para un inicio verdaderamente fresco, pero destructivo.
Restaurar un archivo individual desde otra rama#
# Get the main branch version of a file without switching branches
git restore --source main -- src/config/database.tsO desde un commit específico:
git restore --source a1b2c3d -- src/config/database.tsEsto es más limpio que la vieja sintaxis git checkout main -- path/to/file, y no afecta HEAD.
Juntando todo: Un workflow real#
Así se ve un día típico con estas herramientas:
# Morning: check what's happening
git lg
git fetch --all
# Start a feature
git checkout -b feature/session-refresh main
# Work, commit incrementally
git add -p # Stage specific hunks, not entire files
git commit -m "Add token refresh endpoint"
git commit -m "Add refresh token rotation"
git commit -m "Fix: handle expired refresh tokens"
git commit -m "Add integration tests"
# Interrupted: need to fix a production bug
git worktree add ../hotfix main
cd ../hotfix
# ... fix, commit, push, PR merged ...
cd ../my-project
git worktree remove ../hotfix
# Back to feature work
git commit -m "Fix edge case in token validation"
# Ready for PR: clean up history
git rebase -i main
# Squash the fix commits, reword for clarity
# Push and open PR
git push -u origin feature/session-refresh
# Something broke in staging? Find out which commit:
git bisect start
git bisect bad HEAD
git bisect good main
git bisect run npm test
# Oops, I hard-reset the wrong thing
git reflog
git reset --hard HEAD@{2}Cada uno de estos comandos toma segundos. Juntos, ahorran horas. No horas hipotéticas — horas reales, cada semana, que de otra forma pasaría desenredando problemas de Git, buscando bugs manualmente, o perdiendo trabajo en cambios de contexto.
Git es una herramienta que recompensa la profundidad. Lo básico te saca adelante el día. Pero los comandos de este post son lo que separa "uso Git" de "Git realmente me hace más rápido". Apréndelos incrementalmente. Elige una técnica nueva esta semana y úsala hasta que sea memoria muscular. Luego elige otra.
Tu yo del futuro, mirando fijamente un bug de producción a las 11 PM, te agradecerá por saber git bisect.