Git no es solo add, commit, push. Su modelo de objetos interno — blobs, trees, commits, tags — es un grafo acíclico dirigido (DAG) que, cuando lo entiendes, te da control total sobre el historial de tu proyecto. Aquí no cubro lo básico; cubro lo que marca la diferencia entre "usar Git" y "dominar Git".
El modelo de objetos de Git
Git es un sistema de archivos direccionable por contenido. Cada objeto tiene un hash SHA-1 que depende de su contenido:
| Objeto | Qué almacena | Ejemplo |
|---|---|---|
| Blob | Contenido de archivo | git hash-object archivo.txt |
| Tree | Lista de blobs + otros trees (directorios) | git ls-tree HEAD |
| Commit | Snapshot del árbol + padre + mensaje + autor | git cat-file -p HEAD |
| Tag | Referencia a un commit (anotado o ligero) | git show v1.0 |
# Ver el árbol del commit actual
$ git cat-file -p HEAD^{tree}
# Ver qué archivos componen ese árbol
$ git ls-tree HEAD
# Ver el tipo de cualquier objeto por su hash
$ git cat-file -t a1b2c3dReflog — tu red de seguridad
Cada acción en Git queda registrada en el reflog local durante 90 días:
$ git reflog
# a1b2c3d HEAD@{0}: commit: fix: login validation
# e4f5g6h HEAD@{1}: reset: moving to HEAD~1
# i7j8k9l HEAD@{2}: commit: wip: refactoring auth
# Recuperar un commit "perdido" después de un reset
$ git reset --hard HEAD@{2}
# o por hash
$ git checkout a1b2c3dSin reflog, un git reset --hard equivocado sería desastre. Con reflog, es solo un paso atrás.
Interactive rebase — historia limpia
$ git rebase -i HEAD~5Esto abre un editor donde puedes reordenar, fusionar (squash), editar, o eliminar commits. Los comandos clave:
pick a1b2c3 feat: add login form
squash d4e5f6 fix: typo in login form → se fusiona con el anterior
reword g7h8i9 chore: update deps → permite editar mensaje
edit j0k1l2 refactor: extract validator → pausa para dividir en más commits
drop m3n4o5 temp: debug output → elimina el commitNunca hagas rebase en ramas compartidas (main, develop) — solo en ramas locales o feature branches.
Git bisect — cazando bugs en el historial
$ git bisect start
$ git bisect bad # commit actual tiene el bug
$ git bisect good v1.0 # esta versión funcionaba
# Git hace checkout binario hasta encontrar el commit culpable
# En cada paso: prueba y marca
$ git bisect good # o bad
$ git bisect reset # al terminarPuedes automatizarlo:
$ git bisect run npm test
# Git ejecuta el comando en cada paso: 0 = good, 1 = badEn un proyecto con 2000 commits, git bisect encuentra el culpable en ~11 pasos (log₂ 2000).
Worktrees — múltiples ramas simultáneas
# Trabajar en una rama sin perder el estado actual
$ git worktree add ../project-feature feature-x
$ git worktree list
# Útil para: revisar PRs, hotfixes, o contextos paralelos
$ git worktree add -b hotfix/critical ../project-hotfix main
# Editas en ../project-hotfix mientras mantienes tu trabajo actualCada worktree es un checkout independiente que comparte el objeto store — no duplica el .git.
Hooks — automatización local
Los hooks en .git/hooks/ se ejecutan en eventos específicos:
#!/bin/sh
# .git/hooks/pre-commit — lint + test antes de cada commit
npm run lint
npm run typecheck
if [ $? -ne 0 ]; then
echo "❌ Lint o typecheck fallaron. Commit cancelado."
exit 1
fiHooks útiles: pre-commit (lint/test), prepare-commit-msg (prefix automático), pre-push (test suite), post-checkout (instalar dependencias).
Para compartir hooks con el equipo, usa core.hooksPath y versionalos en .githooks/:
$ git config core.hooksPath .githooksFirmar commits con SSH
Desde Git 2.34, puedes firmar commits con SSH en vez de GPG:
$ git config --global gpg.format ssh
$ git config --global user.signingkey ~/.ssh/id_ed25519.pub
$ git commit -S -m "feat: audit trail"Verificar firmas: git log --show-signature. La ventaja de SSH sobre GPG: ya tienes las llaves, no necesitas un keyring separado.
Git merge strategies
# Estrategias de merge: ort (default desde Git 2.33) vs recursive vs ours
$ git merge -X theirs feature-x # en conflicto, gana su versión
$ git merge -X ours feature-x # en conflicto, gana mi versión
# Squash merge — fusiona todo en un solo commit
$ git merge --squash feature-x && git commit| Estrategia | Cuándo usar |
|---|---|
| merge regular | Preservar historial completo, ramas compartidas |
| squash merge | Feature branches temporales, PRs que no necesitan cada commit individual |
| rebase | Historia lineal, ramas locales, antes de abrir PR |
| cherry-pick | Portar commits específicos entre ramas |
Git para forensia
# Quién modificó cada línea de un archivo
$ git blame --date=short src/lib/auth.ts
# Buscar cuándo se introdujo (o eliminó) una string específica
$ git log -S "SECRET_KEY" -- "*.ts"
$ git log -G "password" -- "*.{ts,js}"
# Buscar por contenido en mensajes de commit
$ git log --all --grep="security"Estos comandos son esenciales para auditoría de seguridad y análisis de regresiones. Combinados con git bisect, son el equivalente forense de Git.
Buenas prácticas consolidadas
- Commits atómicos: cada commit es un cambio lógico único. Si necesitas describirlo con "y", deberían ser dos commits.
- Conventional Commits:
feat:,fix:,chore:,refactor:,docs:son prefijos que permiten generar changelogs automáticos. - Branch naming:
feat/login-form,fix/auth-null,chore/deps-2026. - Pull con rebase:
git pull --rebaseevita merge commits innecesarios. Configúralo por defecto:git config --global pull.rebase true. - .gitattributes: define normalización de line endings, detecta archivos binarios, asigna diff drivers para archivos específicos:
*.ts diff=typescript linguist-language=TypeScript
*.svg -diff binary
* text=auto eol=lfDomina estos patrones y Git pasa de ser un "sistema de backup" a ser una herramienta quirúrgica para navegar y manipular el historial de tu proyecto.