🌿 Git y open source
Git a fondo: el modelo de grafo, objetos y refs, el flujo diario, branchs, merge y rebase, remotes, reflog, flujos de colaboración y contribución a open source. Teoría, comandos y práctica.
Git y open source
Git es, junto al sistema operativo y el editor, la herramienta más transversal del ingeniero. La clave para dominarlo no es memorizar comandos: es entender el modelo de datos. Cuando entiendes que un commit es un snapshot enlazado en un grafo y que el reflog te permite volver de cualquier desastre, dejas de tener miedo al rebase.
Git no guarda diferencias de archivos: guarda snapshots (fotos) del estado completo del proyecto. Cada snapshot (commit) apunta a su padre. Un branch es solo una etiqueta que apunta a un commit. Ese es TODO el modelo: objetos + referencias.
El modelo de grafo
Todo en Git es un grafo de commits. Cada commit tiene:
- El snapshot (los archivos en ese momento).
- Un puntero a su commit padre (o padres, si es un merge).
- Metadatos: autor, mensaje, timestamp.
- Un hash (SHA-1) que lo identifica de forma única.
C1 ──── C2 ──── C3 ──── C4 ← main
\
└── C5 ──── C6 ← feature
C1 = primer commit
C2 = "añade login" (padre: C1)
C5 = "añade carrito" (padre: C2, en otra rama)
💡 El hash es el commit: si dos commits tienen el mismo contenido y metadatos, tienen el mismo hash. Por eso Git detecta si algo se ha corrompido o si dos personas hicieron el mismo cambio. Y por eso nunca edites historia que ya compartiste: cambias los hashes y todos los que la tenían se rompen.
Los objetos internos
Git guarda todo en .git/objects/ como cuatro tipos de objetos:
| Objeto | Qué es |
|---|---|
| Blob | El contenido de un archivo (sin nombre) |
| Tree | Un directorio: lista de blobs y sub-trees con sus nombres |
| Commit | El snapshot: apunta a un tree, a su padre, y tiene metadatos |
| Tag | Una etiqueta a un commit (anotada o simple) |
commit (9fceb2...) tree (a1b2c3...)
│ tree → a1b2c3... ├── src/
│ parent → 2fe31a... ├── README.md → blob (d4f7e1...)
│ autor: Ana └── config.yml → blob (0a9b8c...)
⚠️ Concepto clave: los blobs se guardan una sola vez aunque aparezcan en muchos commits. Si dos commits tienen el mismo
README.md, comparten el mismo blob. Así Git no duplica contenido — esa es la magia del almacenamiento eficiente.
Referencias (refs)
Un branch es una etiqueta movible que apunta a un commit. HEAD es la referencia especial que indica dónde estás.
HEAD → main → 9fceb2 (último commit de main)
git branch # lista de ramas (* = la actual)
git branch feature # crea una rama apuntando a HEAD
git switch feature # muévete a feature (HEAD → feature)
git switch -c nueva # crear y moverte a la vez
git log --oneline # historia como lista
git log --graph --all # el grafo visual
El flujo diario: add, commit, branch, merge
El staging area (índice)
Git tiene tres estados para cada archivo:
Working dir ──git add──▶ Staging (index) ──git commit──▶ Repositorio
(modificado) (listo para commit) (guardado)
git status # ver el estado de todo
git add archivo.txt # mover al staging
git add . # todo lo modificado
git add -p # fragmento a fragmento (arte avanzado)
git commit -m "mensaje" # guardar el snapshot
git commit -am "msg" # add + commit de archivos ya rastreados
git diff # cambios sin stagear
git diff --staged # cambios ya stageados
💡 Commits atómicos: un commit debe ser una unidad lógica (“añade login”), no “cambios del viernes”. Con
git add -pseleccionas qué fragmentos entran en cada commit.
Merge: unir ramas
git switch main
git merge feature # incorpora feature en main
Antes: main: C1 ── C2
feature: └── C3 ── C4
Después: main: C1 ── C2 ── C3 ── C4 (fast-forward)
o
main: C1 ── C2 ────── M
feature: └── C3 ── C4 ─┘ (merge commit, sin ff)
- Fast-forward: si main no avanzó, Git solo mueve la etiqueta (historia lineal).
- Merge commit: si ambas ramas avanzaron, Git crea un commit de fusión con dos padres.
- Conflicto: si las dos ramas tocaron las mismas líneas. Lo resuelves a mano y luego
git add+git commit.
git merge feature
# CONFLICTO en archivo.txt
# → editas archivo.txt dejando lo correcto
git add archivo.txt
git commit # cierra el merge
⚠️ Los conflictos no son malos: son Git pidiéndote que decidas. Cuanto más pequeños los commits y más seguido merges/mrebases, menos conflictos.
Rebase: historia lineal
El rebase reescribe la historia: toma tus commits y los re-planta sobre otro punto.
git switch feature
git rebase main # feature se reconstruye encima de main
Antes: main: C1 ── C2
feature: └── C3 ── C4
Después (rebase): main: C1 ── C2
feature: └── C3' ── C4'
💡 Regla de oro:
rebasepara historia local (no compartida),mergepara integrar ramas compartidas. Rebasear lo que ya pusiste en GitHub reescribe historia pública y rompe a los demás.
Rebase interactivo: limpiar historia
git rebase -i HEAD~3 # reescribe los últimos 3 commits
# pick → keep, squash → fundir con el anterior, reword → renombrar
Remotes y colaboración
Un remote es una copia remota del repo (GitHub, GitLab, tu servidor). Tu repo local y el remoto son repos independientes que se sincronizan.
git remote add origin https://github.com/usuario/proyecto.git
git remote -v # ver remotes
git push origin main # sube tus commits
git pull origin main # baja y mergea (fetch + merge)
git fetch origin # solo baja, sin mergear
git clone url # copia un repo remoto
El flujo de Pull Request
1. git switch -c feature # rama nueva
2. ... trabajo ... commits ...
3. git push origin feature # sube la rama
4. → en GitHub: creas un Pull Request (PR)
5. → code review → ajustes → aprobación
6. → merge del PR
7. git switch main && git pull # actualizas tu main
💡 Pull request es una petición de revisión: no es solo “código nuevo”, es una conversación (código + comentarios + discusión). La revisión de código (code review) es el mecanismo de calidad #1 del open source y de los equipos.
El reflog: tu red de seguridad
El reflog es el historial de dónde han estado tus refs. Aunque borres una rama o hagas un hard reset, Git recuerda por dónde pasó HEAD — y puedes volver.
git reflog # lista: "9fceb2 HEAD@{0}: commit: añade login"
git reset --hard HEAD@{2} # volver a un estado anterior
git reflog --oneline
💡 Regla de seguridad: casi nada en Git es irrecuperable mientras exista el reflog (y lo que borraste sigue en objetos hasta que el GC los limpie). Antes de entrar en pánico, mira el reflog.
Comandos de emergencia
| Situación | Comando |
|---|---|
| Deshacer un commit (manteniendo cambios) | git reset --soft HEAD~1 |
| Deshacer un commit (borrando cambios) | git reset --hard HEAD~1 |
| Arreglar el último commit | git commit --amend |
| Cambios sin commit, quiero borrarlos | git checkout -- archivo |
| Mover trabajo sin commit a otra rama | git stash / git stash pop |
| Encontrar el commit que rompió algo | git bisect |
| Ver quién tocó cada línea | git blame archivo |
| Ver commits de una rama que faltan en main | git log main..feature |
| Dejar de trackear un archivo | git rm --cached archivo |
# git bisect: búsqueda binaria del commit culpable
git bisect start
git bisect bad # el actual está roto
git bisect good <hash> # este commit estaba bien
# → Git te va moviendo a la mitad hasta aislar el culpable
Hooks y automatización
Los hooks son scripts que Git ejecuta en momentos del flujo (client y server).
# .git/hooks/pre-commit — corre ANTES de cada commit
#!/bin/sh
if grep -n "TODO" src/; then
echo "No se permite TODO en el código"; exit 1
fi
| Hook | Momento |
|---|---|
pre-commit |
Antes de commitear (lint, formato, secretos) |
commit-msg |
Validar el mensaje del commit |
pre-push |
Antes de subir (tests, build) |
post-merge |
Después de un merge (npm install, migraciones) |
💡 La forma moderna: pre-commit framework (
.pre-commit-config.yaml) que gestiona hooks de forma compartida entre el equipo, o herramientas como husky (JS).
Flujos de trabajo (workflows)
| Workflow | Cómo funciona | Cuándo |
|---|---|---|
| GitHub Flow | Rama corta por feature → PR → merge a main. Simple. | El estándar moderno (entrega continua) |
| GitFlow | main + develop + feature/release/hotfix |
Releases versionados (legacy, software empaquetado) |
| Trunk-based | Todos trabajan en main, ramas de menos de 1 día, feature flags | Equipos con CI fuerte, despliegue continuo |
💡 Recomendación: GitHub Flow / trunk-based. Las ramas cortas (1-2 días), PRs pequeños y CI verde son la práctica moderna. GitFlow solo tiene sentido para software con releases versionados.
Contribuir a open source
El flujo real de contribución (por email o PR):
1. Fork del repo (copia en tu cuenta)
2. git clone tu-fork
3. git checkout -b mi-cambio
4. ... cambios + commits ...
5. git push origin mi-cambio
6. PR al repo original (upstream)
7. Discusión + revisiones → merge
git remote add upstream https://github.com/original/repo.git
git fetch upstream
git checkout main
git merge upstream/main # mantener tu fork al día
⚠️ Etiqueta del open source: lee el CONTRIBUTING.md, abre un issue antes de cambios grandes, sigue los convenios del proyecto, responde a los reviews, y usa el canal correcto (issues vs PRs vs discussions).
Licencias
Cuando creas o usas open source, la licencia define qué pueden hacer otros con tu código.
| Licencia | Permite | Obliga |
|---|---|---|
| MIT | Uso comercial, modificar, distribuir | Mantener el aviso de copyright |
| Apache 2.0 | Igual + patentes | Aviso + indicar cambios |
| GPL | Uso, modificar, distribuir | Copyleft: derivados también GPL |
| BSD | Igual que MIT | Aviso |
| AGPL | Igual que GPL | Copyleft incluso vía red |
💡 MIT/Apache = permiso de usar sin casi condiciones. GPL/AGPL = copyleft (lo que deriva también debe ser libre). Elige con choosealicense.com.
Cheatsheet
git status / git log --oneline --graph
git add -p && git commit -m "msg"
git switch -c rama && git switch main
git merge rama / git rebase main
git push origin rama / git pull
git fetch && git diff main..origin/main
git stash / git stash pop
git reflog && git reset --hard HEAD@{n}
git bisect start
git blame archivo
Práctica propuesta
- Crea un repo, haz 5 commits, crea una rama, haz un merge y luego un rebase. Compara los grafos.
- Provoca un conflicto a propósito (dos ramas editando la misma línea) y resuélvelo.
- Haz
git reset --harda un commit anterior y recupérate con el reflog. - Reescribe la historia de tus últimos 3 commits con
git rebase -i(squash + reword). - Completa Learn Git Branching completo (incluye niveles de remote).
- Haz tu primer PR real a un proyecto de first-contributions.
- Busca el commit que introdujo un bug en un repo tuyo con
git bisect.
Para profundizar
- Pro Git (Chacon & Straub): el libro oficial, gratis en español. Capítulo 10 (Internals) obligatorio.
- Git from the Inside Out (Mary Rose Cook): el modelo de grafo siguiendo comandos reales.
- Git for Computer Scientists: el grafo en una página.
- git-flight-rules: qué hacer cuando algo sale mal.
- Oh shit, git!: guía rápida de desastres, en español.
- Google Engineering Practices — Code Review: cómo hacer y recibir reviews.
- Trunk Based Development: por qué evitar el merge hell.
- Open Source Guides: contribuir y mantener proyectos.
- Sigue con ⏱️ Algoritmos o vuelve al 🗺️ Plan.