DISASTER_RECOVERY_PHILOSOPHY.md
╔══════════════════════════════════════════════════════════════════════════════╗ ║ ║ ║ ⚠️ DISASTER RECOVERY PHILOSOPHY ║ ║ La Psicología de la Crisis en Git ║ ║ ║ ║ "Nada se pierde hasta que Git lo olvida" ║ ║ ║ ╚══════════════════════════════════════════════════════════════════════════════╝
═══════════════════════════════════════════════════════════════════════════════ 🎭 CAPÍTULO 1: TAXONOMÍA DEL DESASTRE ═══════════════════════════════════════════════════════════════════════════════
LA NATURALEZA DE LOS ERRORES ────────────────────────────────────────────────────────────────────────────
En Git, los "desastres" tienen una característica única: casi todos son REVERSIBLES. El pánico viene de no entender QUÉ pasó, no de la irreversibilidad del daño.
PRINCIPIO FUNDAMENTAL: ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ Git es una BASE DE DATOS de contenido direccionado por hash. │ │ Los "deletes" solo eliminan REFERENCIAS, no contenido. │ │ El contenido persiste hasta el garbage collection (~90 días). │ │ │ └─────────────────────────────────────────────────────────────────────────┘
⚠️ SEVERITY PYRAMID: CLASIFICACIÓN POR GRAVEDAD ────────────────────────────────────────────────────────────────────────────
╱╲
╱ ╲
╱ N4 ╲ NIVEL 4: CATASTRÓFICO
╱──────╲ • Pérdida total de repositorio
╱ ╲ • Corrupción de .git/
╱ N 3 ╲ • Todos los clones afectados
╱──────────╲
╱ ╲ NIVEL 3: CRÍTICO
╱ N 2 ╲ • Pérdida parcial de historia
╱──────────────╲ • Force push destructivo en main
╱ ╲• Múltiples commits perdidos
╱ N 1 ╲
╱──────────────────╱ NIVEL 2: MAYOR
╱ ╱ • Estado inconsistente
╱ ╱ • Merge complejo fallido
╱ ╱ • Branch en estado inválido ╱__________________╱ NIVEL 1: MENOR • Commit message incorrecto • Cambios en branch equivocado • Staging area desorganizado
╔═══════════════════════════════════════════════════════════════════════════╗ ║ MATRIZ DE DESASTRES ║ ╠═══════════════════════════════════════════════════════════════════════════╣ ║ ║ ║ DESASTRE │ NIVEL │ REVERSIBLE │ PREVENCIÓN │ TIME TO FIX ║ ╠═══════════════════════════════════════════════════════════════════════════╣ ║ ║ ║ 🔴 Corrupción .git/ │ 4 │ Imposible │ Backups │ Horas ║ ║ │ │ sin backup │ │ ║ ║ ║ ║ 🔴 Borrado de main │ 4 │ Difícil │ Protection │ 30-60 min ║ ║ con GC ejecutado │ │ │ rules │ ║ ║ ║ ║ 🟠 Force push en main │ 3 │ Posible │ Hooks + │ 15-30 min ║ ║ (historia perdida) │ │ │ Branch │ ║ ║ │ │ │ protection │ ║ ║ ║ ║ 🟠 Reset hard sin │ 3 │ Posible │ Commit │ 10-20 min ║ ║ reflog │ │ │ frecuente │ ║ ║ ║ ║ 🟡 Merge conflict │ 2 │ Fácil │ Feature │ 5-15 min ║ ║ masivo │ │ │ branches │ ║ ║ ║ ║ 🟡 Commit a branch │ 2 │ Fácil │ Awareness │ 2-5 min ║ ║ incorrecta │ │ │ │ ║ ║ ║ ║ 🟡 Rebase que rompe │ 2 │ Fácil │ Testing │ 5-10 min ║ ║ build │ │ │ │ ║ ║ ║ ║ 🟢 Typo en commit msg │ 1 │ Trivial │ Review │ 30 seg ║ ║ ║ ║ 🟢 Archivo staged │ 1 │ Trivial │ git status │ 10 seg ║ ║ por error │ │ │ frecuente │ ║ ║ ║ ║ 🟢 Forgot to pull │ 1 │ Trivial │ git fetch │ 1 min ║ ║ before push │ │ │ hábito │ ║ ║ ║ ╚═══════════════════════════════════════════════════════════════════════════╝
ANATOMÍA DE UN DESASTRE: LAS TRES FASES ────────────────────────────────────────────────────────────────────────────
FASE 1: EL ERROR │ ├─ Ejecución de comando peligroso ├─ Malentendido de consecuencias ├─ Typo en comando crítico └─ Assumption incorrecta sobre estado
↓
FASE 2: EL PÁNICO │ ├─ Realización del error ├─ Intento de "arreglo rápido" (empeora situación) ├─ Ejecución de comandos sin entender └─ Pedir ayuda en Stack Overflow sin contexto
↓
FASE 3: LA RECUPERACIÓN │ ├─ STOP: No hacer nada más ├─ DIAGNÓSTICO: Entender exactamente qué pasó ├─ PLAN: Estrategia de recuperación └─ EJECUCIÓN: Pasos medidos y verificados
LA REGLA DE ORO DE RECOVERY: ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ "FIRST, DO NO HARM" (Primero, no hacer daño) │ │ │ │ Cuando descubres un error, tu PRIMER instinto es arreglarlo │ │ inmediatamente. RESISTE ese instinto. │ │ │ │ Cada comando adicional puede: │ │ • Sobrescribir evidencia en reflog │ │ • Hacer la situación irreversible │ │ • Complicar el diagnóstico │ │ │ │ El mejor "arreglo" a menudo es: NADA, hasta entender completamente. │ │ │ └─────────────────────────────────────────────────────────────────────────┘
═══════════════════════════════════════════════════════════════════════════════ 🧠 CAPÍTULO 2: EL REFLOG COMO MÁQUINA DEL TIEMPO ═══════════════════════════════════════════════════════════════════════════════
EL CONCEPTO FUNDAMENTAL ────────────────────────────────────────────────────────────────────────────
Git mantiene DOS historias paralelas:
1️⃣ GIT LOG: La historia OFICIAL • Lo que "existe" según el proyecto • Lo que otros ven • Historia lineal y limpia • Commits conectados por parentesco
2️⃣ REFLOG: El cuaderno de bitácora PERSONAL • Lo que TÚ hiciste localmente • Todas las movidas de HEAD • Commits "huérfanos" preservados • Timeline de tus acciones
VISUALIZACIÓN: DOS REALIDADES PARALELAS ────────────────────────────────────────────────────────────────────────────
GIT LOG (público) REFLOG (privado) ═════════════════ ════════════════
main Todas tus movidas:
│ │
○ C3 HEAD@{0} ─○ commit C4
│ │
○ C2 HEAD@{1} ─○ reset --hard C3
│ │
○ C1 HEAD@{2} ─○ commit C4 (experimental)
│
Solo ves C1→C2→C3 HEAD@{3} ─○ checkout -b experiment │ HEAD@{4} ─○ commit C3 │ HEAD@{5} ─○ checkout main │ ⋮
NOTA: C4 "no existe" en log, pero EXISTE en reflog!
EL REFLOG COMO RED DE SEGURIDAD ────────────────────────────────────────────────────────────────────────────
PRINCIPIO: ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ "En Git, DELETE no significa DESTRUIR, │ │ significa OLVIDAR DÓNDE ESTÁ" │ │ │ │ Los commits son como ISLAS en un océano. │ │ Los branches son PUENTES hacia esas islas. │ │ Borrar un branch ELIMINA EL PUENTE, no la isla. │ │ Reflog es el MAPA que recuerda dónde están todas las islas. │ │ │ └─────────────────────────────────────────────────────────────────────────┘
ANALOGÍA: EL ARCHIVERO OBSESIVO ────────────────────────────────────────────────────────────────────────────
Imagina que Git es un archivero que:
1️⃣ NUNCA tira documentos a la basura • Los guarda en un archivo "temporal" • Los marca con fecha de "posible descarte" • Default: 90 días de retención
2️⃣ SIEMPRE registra cada movimiento • "10:30 AM: Usuario abrió carpeta X" • "10:32 AM: Usuario movió documento Y a carpeta Z" • "10:35 AM: Usuario tiró carpeta Z a la basura" • "10:36 AM: ¡Usuario busca desesperadamente carpeta Z!"
3️⃣ PUEDE reconstruir cualquier estado pasado • "Muéstrame mi escritorio como estaba a las 10:31 AM" • El archivero saca documentos del archivo temporal • Recrea la estructura exacta
ACCIONES CAPTURADAS POR REFLOG ────────────────────────────────────────────────────────────────────────────
✅ REGISTRA: │ ├─ commit │ └─ Cada commit que creas │ ├─ checkout │ └─ Cada cambio de branch o commit │ ├─ merge │ └─ Merge exitosos Y fallidos │ ├─ reset │ └─ Todos los tipos (soft/mixed/hard) │ ├─ rebase │ └─ Rebase completos y abortados │ ├─ cherry-pick │ └─ Cada cherry-pick aplicado │ ├─ pull │ └─ Actualizaciones desde remote │ ├─ revert │ └─ Commits de reversión │ └─ branch -D └─ Incluso branches borrados!
❌ NO REGISTRA: │ ├─ Cambios NO commiteados │ └─ Working directory sin commit = perdido │ ├─ Archivos untracked borrados │ └─ Si nunca lo commiteaste, no existe │ ├─ Stash drops antiguos │ └─ Stash tiene SU PROPIO reflog │ └─ Acciones en OTHER repos └─ Cada repo tiene reflog independiente
EJEMPLO DETALLADO: VIDA DE UN COMMIT "PERDIDO" ────────────────────────────────────────────────────────────────────────────
TIMELINE:
10:00 AM │ main@{0} │ ○ C1 ← Commit inicial ↓
10:15 AM │ main@{1} │ ○ C2 ← Agrego feature A │ │ │ ○ C1 ↓
10:30 AM │ main@{2} │ ○ C3 ← Agrego feature B (experimental) │ │ │ ○ C2 │ │ │ ○ C1 ↓
10:35 AM │ main@{3} │ ○ C2 ← RESET HARD! (C3 desaparece del log) │ │ │ ○ C1 │ │ Pero C3 EXISTE en reflog: │ HEAD@{2} apunta a C3 ↓
10:45 AM │ Pánico: "¿Dónde está mi trabajo?" │ Reflog dice: "HEAD@{2} tiene tu commit" │ C3 sigue en la base de datos, solo sin branch ↓
10:50 AM │ Recuperación: │ Crear nuevo branch apuntando a HEAD@{2} │ ○ C3 ← nueva-branch │ │ │ ○ C2 ← main │ │ │ ○ C1
REFLOG: NOTACIÓN Y SINTAXIS ────────────────────────────────────────────────────────────────────────────
FORMATO DE REFERENCIA:
HEAD@{n} │ n movidas atrás desde ahora │ HEAD@{0} = posición actual │ HEAD@{1} = posición anterior │ HEAD@{5} = 5 posiciones atrás │ branch@{n} │ n movidas atrás en BRANCH específico │ main@{3} = main hace 3 movidas │ develop@{1} = develop posición anterior │ HEAD@{timestamp} │ Posición en TIEMPO específico │ HEAD@{2.days.ago} │ HEAD@{yesterday} │ HEAD@{2023-12-01} │ main@{1.week.ago}
EJEMPLO DE REFLOG REAL: ────────────────────────────────────────────────────────────────────────────
HEAD@{0} abc1234 commit: Add audio processing module HEAD@{1} def5678 commit: Fix buffer overflow HEAD@{2} 9ab0123 reset: moving to HEAD~1 HEAD@{3} 4cd5678 commit: Experimental feature (deleted!) HEAD@{4} 7ef8901 checkout: moving from develop to main HEAD@{5} 2ab3456 merge feature-x: Fast-forward HEAD@{6} cde7890 commit: Initial work on feature-x HEAD@{7} f123456 checkout: moving from main to feature-x
INTERPRETACIÓN: • HEAD@{3} tiene un commit "experimental" que ya no está en ningún branch • Puedo recuperarlo creando branch en HEAD@{3} • Todas estas referencias son VÁLIDAS por ~90 días
REFLOG POR BRANCH ────────────────────────────────────────────────────────────────────────────
CONCEPTO IMPORTANTE: ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ Cada branch tiene SU PROPIO reflog. │ │ HEAD tiene un reflog de TODOS los checkouts. │ │ │ │ HEAD reflog = "¿Dónde he estado?" │ │ Branch reflog = "¿Qué le ha pasado a ESTE branch?" │ │ │ └─────────────────────────────────────────────────────────────────────────┘
EJEMPLO:
HEAD reflog: main reflog: │ │ HEAD@{0} main commit main@{0} commit C HEAD@{1} checkout main main@{1} merge feature-x HEAD@{2} feature-x commit main@{2} commit B HEAD@{3} checkout feature-x main@{3} commit A HEAD@{4} main commit HEAD@{5} checkout main ⋮ ⋮
USO: Si main fue corrompido, main@{} me muestra SU historia específica.
LÍMITES DEL REFLOG: LO QUE NECESITAS SABER ────────────────────────────────────────────────────────────────────────────
⏰ EXPIRACIÓN: │ ├─ Entradas unreachable: 90 días (default) ├─ Entradas reachable: 30 días (default) ├─ Garbage collection ejecuta periódicamente └─ Puedes configurar estos tiempos
🌐 SCOPE: │ ├─ Reflog es LOCAL solamente ├─ NO se pushea a remotes ├─ Cada clone tiene su propio reflog └─ Tu reflog no ayuda a otros developers
💾 PERSISTENCIA: │ ├─ Sobrevive rebases ├─ Sobrevive resets ├─ NO sobrevive clone fresh └─ NO sobrevive rm -rf .git/
ESTRATEGIA DE BÚSQUEDA EN REFLOG ────────────────────────────────────────────────────────────────────────────
METODOLOGÍA:
1️⃣ IDENTIFICAR TIPO DE DESASTRE ├─ ¿Commit perdido? ├─ ¿Branch borrado? └─ ¿Reset accidental?
2️⃣ BUSCAR TIMESTAMP APROXIMADO ├─ ¿Cuándo hiciste el commit? ├─ ¿Antes o después de qué evento? └─ Reflog está ORDENADO cronológicamente
3️⃣ IDENTIFICAR SHA DEL COMMIT DESEADO ├─ Buscar por mensaje de commit ├─ Buscar por timestamp └─ Buscar por acción (reset, merge, etc.)
4️⃣ VERIFICAR CONTENIDO ├─ Inspeccionar commit antes de recuperar ├─ Asegurar es el correcto └─ Crear branch de respaldo
5️⃣ RECUPERAR ├─ Crear nuevo branch apuntando a commit ├─ Cherry-pick si solo necesitas cambios └─ Reset si quieres restaurar branch completo
═══════════════════════════════════════════════════════════════════════════════ 🔧 CAPÍTULO 3: RESET VS REVERT - FILOSOFÍAS OPUESTAS ═══════════════════════════════════════════════════════════════════════════════
LA DICOTOMÍA FUNDAMENTAL ────────────────────────────────────────────────────────────────────────────
Git ofrece DOS filosofías completamente diferentes para "deshacer":
🔷 RESET: Reescribir el pasado • "Esto nunca pasó" • Historia alterada • Desaparece del log público
🔷 REVERT: Aceptar el pasado, corregir el futuro • "Esto pasó, pero fue un error, así que lo corrijo" • Historia preservada • Nuevo commit que cancela el anterior
RESET: LA FILOSOFÍA DEL "NUNCA PASÓ" ────────────────────────────────────────────────────────────────────────────
CONCEPTO: ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ Reset MUEVE el pointer del branch hacia atrás en el tiempo. │ │ Es literalmente TIME TRAVEL. │ │ Los commits "futuros" quedan huérfanos (pero recuperables via reflog). │ │ │ └─────────────────────────────────────────────────────────────────────────┘
VISUALIZACIÓN: ANATOMÍA DE UN RESET
ESTADO INICIAL: main ↓ ○────○────○────○────●────●────● A B C D E F G ↑ "Good" "Bad commits"
DESPUÉS DE: reset --hard D
main
↓
○────○────○────○ ● ● ●
A B C D E F G
↑ ↑
"Good" "Huérfanos"
(Recuperables
via reflog)
EFECTO: • main ahora apunta a D • E, F, G no aparecen en log • E, F, G EXISTEN en .git/objects • E, F, G listados en reflog • E, F, G serán garbage collected en ~90 días
LAS TRES MODALIDADES DE RESET ────────────────────────────────────────────────────────────────────────────
Git tiene tres "niveles" de reset, dependiendo de QUÉ quieres preservar:
┌──────────┐
│ COMMIT │
│ HISTORY │
└─────┬────┘
│
↓
┌──────────┐
│ STAGING │
│ AREA │
└─────┬────┘
│
↓
┌──────────┐
│ WORKING │
│DIRECTORY │
└──────────┘
Cada tipo de reset "deshace" hasta cierto nivel.
╔═══════════════════════════════════════════════════════════════════════════╗ ║ MATRIZ DE RESET MODES ║ ╠═══════════════════════════════════════════════════════════════════════════╣ ║ ║ ║ MODE │ COMMITS │ STAGING │ WORKING │ RESULTADO │ USO ║ ║ │ │ AREA │ DIR │ │ ║ ╠═══════════════════════════════════════════════════════════════════════════╣ ║ ║ ║ --soft │ ✗ │ ✓ │ ✓ │ Commits deshacen, │ "Re- ║ ║ │ Remove │ Keep │ Keep │ cambios quedan staged │hacer ║ ║ │ │ │ │ listos para recommit │ msg" ║ ║ ║ ║ --mixed │ ✗ │ ✗ │ ✓ │ Commits deshacen, │ "Re- ║ ║ (DEFAULT) │ Remove │ Clear │ Keep │ cambios quedan │hacer ║ ║ │ │ │ │ unstaged en working │todo" ║ ║ ║ ║ --hard │ ✗ │ ✗ │ ✗ │ TODO se pierde, │"Nuke ║ ║ │ Remove │ Clear │ Clear │ vuelves a estado │ it" ║ ║ │ │ │ │ de commit target │ ║ ║ ║ ╚═══════════════════════════════════════════════════════════════════════════╝
EJEMPLO DETALLADO: LOS TRES RESETS EN ACCIÓN ────────────────────────────────────────────────────────────────────────────
SITUACIÓN INICIAL: main ↓ ○────○────●────● A B C D
Working directory: file.txt modificado (cambios no commiteados)
Staging area: file2.txt staged
Commits: D = "Add feature X" (último commit)
ESCENARIO 1: reset --soft C ────────────────────────────────────────────────────────────────────────────
main
↓
○────○────○ ●
A B C D (huérfano)
Commits: D removido de main, en reflog
Staging area: file2.txt SIGUE staged
+ cambios de D AHORA staged también
Working directory: file.txt modificado SIGUE ahí
+ contenido de D SIGUE ahí
RESULTADO: Puedes re-commitear D con mejor mensaje o cambios adicionales.
ESCENARIO 2: reset --mixed C (DEFAULT) ────────────────────────────────────────────────────────────────────────────
main
↓
○────○────○ ●
A B C D (huérfano)
Commits: D removido de main, en reflog
Staging area: ¡VACÍA! (file2.txt UNstaged)
Working directory: file.txt modificado SIGUE ahí
file2.txt SIGUE ahí
+ cambios de D SIGUE ahí como archivos modificados
RESULTADO: Puedes reorganizar qué va en qué commit, re-stage selectivamente.
ESCENARIO 3: reset --hard C ────────────────────────────────────────────────────────────────────────────
main
↓
○────○────○ ●
A B C D (huérfano)
Commits: D removido de main, en reflog
Staging area: ¡VACÍA!
Working directory: ¡LIMPIO! Todo vuelve a estado de C
file.txt = versión en C
file2.txt = BORRADO si no existía en C
Cambios locales = PERDIDOS (si no commiteados)
RESULTADO: Estado EXACTO del commit C. Cambios no commiteados PERDIDOS.
⚠️ PELIGRO: Únicos cambios NO recuperables via reflog!
CUÁNDO USAR CADA TIPO ────────────────────────────────────────────────────────────────────────────
--soft: ├─ Cambiar mensaje de último commit (mejor que amend si múltiples commits) ├─ Combinar varios commits en uno ├─ Reorganizar último commit en varios más pequeños └─ "Deshacer commit, mantener todo lo demás"
--mixed: ├─ Unstage archivos accidentalmente staged ├─ Reorganizar cambios entre commits ├─ "Empezar de nuevo" el staging pero mantener trabajo └─ DEFAULT si no especificas tipo
--hard: ├─ ELIMINAR completamente trabajo reciente ├─ Volver a estado limpio conocido ├─ Descartar experimentos fallidos └─ "Nuclear option" - úsalo con CERTEZA
REVERT: LA FILOSOFÍA DEL "CORRECCIÓN HACIA ADELANTE" ────────────────────────────────────────────────────────────────────────────
CONCEPTO: ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ Revert NO reescribe historia. │ │ Crea un NUEVO commit que aplica los cambios OPUESTOS. │ │ Es "undo" mediante "redo del opuesto". │ │ │ └─────────────────────────────────────────────────────────────────────────┘
VISUALIZACIÓN: ANATOMÍA DE UN REVERT
ESTADO INICIAL: main ↓ ○────○────○────○────● A B C D E ↑ "Bug introducido"
DESPUÉS DE: revert E
main
↓
○────○────○────○────●────◉
A B C D E E'
↑ ↑
"Bug" "Bug fix"
(Deshace E)
COMPARACIÓN DE CONTENIDO:
E commit: + Línea que causa bug + Archivo nuevo buggy.txt - Línea de código buena
E' commit (revert): - Línea que causa bug ← Opuesto de E - Archivo nuevo buggy.txt + Línea de código buena
RESULTADO: • Contenido de proyecto = como antes de E • Historia MUESTRA que E existió • Historia MUESTRA que E fue revertido • Trazabilidad completa
REVERT VS RESET: LA DECISIÓN CRUCIAL ────────────────────────────────────────────────────────────────────────────
╔═══════════════════════════════════════════════════════════════════════════╗ ║ RESET vs REVERT: GUÍA DE DECISIÓN ║ ╠═══════════════════════════════════════════════════════════════════════════╣ ║ ║ ║ CRITERIO │ RESET │ REVERT ║ ╠═══════════════════════════════════════════════════════════════════════════╣ ║ ║ ║ ¿Commits ya pusheados? │ ❌ NO │ ✅ SÍ ║ ║ ║ ║ ¿Branch compartido con otros? │ ❌ NO │ ✅ SÍ ║ ║ ║ ║ ¿Necesitas auditoría completa? │ ❌ NO │ ✅ SÍ ║ ║ ║ ║ ¿Branch personal/experimental? │ ✅ SÍ │ ⚪ Opcional ║ ║ ║ ║ ¿Quieres "historia limpia"? │ ✅ SÍ │ ❌ NO ║ ║ ║ ║ ¿Múltiples commits a deshacer? │ ✅ Más fácil │ ⚪ Posible pero ║ ║ │ │ verboso ║ ║ ║ ║ ¿Compliance/regulación requiere │ ❌ NO │ ✅ SÍ ║ ║ historia completa? ║ ║ ║ ╚═══════════════════════════════════════════════════════════════════════════╝
REGLA DE ORO: ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ RESET = Private history (solo tú) │ │ REVERT = Public history (compartida) │ │ │ │ Si hay CUALQUIER posibilidad de que alguien más tenga los commits, │ │ usa REVERT, no RESET. │ │ │ └─────────────────────────────────────────────────────────────────────────┘
ESCENARIOS COMPLEJOS: REVERT ────────────────────────────────────────────────────────────────────────────
CASO 1: Revertir múltiples commits ────────────────────────────────────────────────────────────────────────────
SITUACIÓN: main ↓ ○────○────●────●────●────● A B C D E F
Commits C, D, E, F todos tienen bugs, necesito volver a B.
OPCIÓN 1: Revert individual (preserva cada paso)
revert F → F'
revert E → E'
revert D → D'
revert C → C'
○────○────●────●────●────●────◉────◉────◉────◉
A B C D E F F' E' D' C'
RESULTADO: Historia verbose pero completa.
OPCIÓN 2: Revert en batch (commit único)
revert C..F (un solo commit que deshace todos)
○────○────●────●────●────●────◉
A B C D E F "Revert C-F"
RESULTADO: Más limpio, pero menos granular.
CASO 2: Revert de un merge ────────────────────────────────────────────────────────────────────────────
SITUACIÓN: main ↓ ○────●────◎────○ A B ╱ M D ╱ ○────○ C1 C2 feature
M = Merge de feature en main
Merge resultó ser problemático, necesito revertirlo.
PROBLEMA: Merge tiene DOS parents: B (main) y C2 (feature) ¿Qué "deshacer"? ¿Volver a B? ¿Eliminar cambios de C1-C2?
SOLUCIÓN: revert -m 1 M (Mantener parent 1 = main)
Significa: "Deshaz los cambios que VINIERON de feature"
RESULTADO: main ↓ ○────●────◎────○────◉ A B ╱ M D M' ╱ ↑ ○────○ "Revert merge" C1 C2
Contenido vuelve a como estaba en D (antes de merge).
FILOSOFÍA SUBYACENTE: ¿POR QUÉ DOS SISTEMAS? ────────────────────────────────────────────────────────────────────────────
La existencia de RESET y REVERT refleja dos necesidades diferentes:
RESET = Workflow tool ├─ Para el PROCESO de desarrollo ├─ Permite iteración rápida ├─ "Borrador" que puedes editar └─ Local, privado, flexible
REVERT = Audit trail tool ├─ Para PRODUCCIÓN y colaboración ├─ Mantiene registro completo ├─ "Publicación" inmutable └─ Público, compartido, accountable
ANALOGÍA: ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ RESET es como borrar y reescribir en tu CUADERNO PERSONAL. │ │ REVERT es como publicar una ERRATA en un periódico. │ │ │ │ Cuaderno: Solo tú lo ves, puedes borrar y rehacer libremente. │ │ Periódico: Ya está publicado, solo puedes corregir con nueva edición. │ │ │ └─────────────────────────────────────────────────────────────────────────┘
═══════════════════════════════════════════════════════════════════════════════ 🚨 CAPÍTULO 4: RECOVERY PLAYBOOKS ═══════════════════════════════════════════════════════════════════════════════
INTRODUCCIÓN A LOS PLAYBOOKS ────────────────────────────────────────────────────────────────────────────
Un "playbook" es un plan de acción paso a paso para situaciones específicas. Como un manual de procedimientos de emergencia en un avión.
ESTRUCTURA DE CADA PLAYBOOK: ├─ 🎯 SÍNTOMAS: ¿Cómo sabes que tienes este problema? ├─ 🔍 DIAGNÓSTICO: ¿Qué exactamente pasó? ├─ 💊 TRATAMIENTO: Pasos específicos de recuperación └─ 🛡️ PREVENCIÓN: Cómo evitar en el futuro
PLAYBOOK 1: "BORRÉ COMMITS IMPORTANTES" ════════════════════════════════════════════════════════════════════════════
🎯 SÍNTOMAS: ──────────────────────────────────────────────────────────────────────────── ├─ Hiciste reset --hard ├─ Commits desaparecieron del log ├─ Branch parece haber "retrocedido" ├─ Trabajo reciente no visible └─ Pánico intenso
ESCENARIO TÍPICO: "Tenía 5 commits en feature-x, hice reset --hard por error, ahora feature-x solo tiene 2 commits. ¡3 días de trabajo perdidos!"
🔍 DIAGNÓSTICO: ────────────────────────────────────────────────────────────────────────────
PREGUNTA 1: ¿Los commits estaban commiteados? ├─ ✅ SÍ → Recuperables via reflog └─ ❌ NO → No recuperables (solo en working directory)
PREGUNTA 2: ¿Cuánto tiempo pasó? ├─ < 90 días → Probablemente en reflog └─ > 90 días → Posiblemente garbage collected
PREGUNTA 3: ¿Tienes idea del commit SHA o mensaje? ├─ ✅ SÍ → Búsqueda más fácil └─ ❌ NO → Necesitas buscar por timestamp/acción
ESTADO INTERNO DE GIT:
ANTES del reset: feature-x ↓ ○────○────●────●────● C1 C2 C3 C4 C5
DESPUÉS del reset --hard C2: feature-x ↓ ○────○ ● ● ● C1 C2 C3 C4 C5 ↑ "Huérfanos"
REFLOG muestra: feature-x@{0} C2 reset: moving to C2 feature-x@{1} C5 commit: Add final tests feature-x@{2} C4 commit: Refactor logic feature-x@{3} C3 commit: Implement core feature ⋮
C3, C4, C5 EXISTEN, solo necesitas ENCONTRARLOS.
💊 TRATAMIENTO: ────────────────────────────────────────────────────────────────────────────
PASO 1: STOP - No hagas nada más ├─ No hagas más commits ├─ No hagas más resets ├─ No cierres la terminal (si tienes SHAs visibles) └─ Respira profundo
PASO 2: Invocar el reflog ├─ Examinar HEAD@{} para acciones recientes ├─ Examinar branch@{} para historia del branch específico └─ Buscar por mensaje de commit familiar
PASO 3: Identificar commit objetivo ├─ Ver el SHA del commit que necesitas ├─ Nota CUÁNDO fue (HEAD@{n} o timestamp) └─ Verificar mensaje/fecha coinciden
PASO 4: Crear branch de rescate ├─ Crear nuevo branch apuntando al commit recuperado ├─ NO muevas feature-x todavía (por si te equivocas) └─ Nombre descriptivo: "feature-x-recovery"
PASO 5: Verificar contenido ├─ Checkout al branch de rescate ├─ Verificar archivos están correctos ├─ Revisar log muestra commits esperados └─ Confirmar nada más se perdió
PASO 6: Restaurar branch original (opcional) ├─ Si rescate es correcto, mover feature-x ├─ O mantener ambos branches └─ O merge de rescate a feature-x
EJEMPLO PASO A PASO: ────────────────────────────────────────────────────────────────────────────
Situación: Hice "reset --hard HEAD~3" en feature-audio, perdí 3 commits.
PASO 1: STOP • Terminal abierta • No más comandos git
PASO 2: Invocar reflog └─ Buscar en feature-audio@{} o HEAD@{}
Output hipotético:
HEAD@{0} abc1234 reset: moving to HEAD~3
HEAD@{1} def5678 commit: Final polish
HEAD@{2} 9ab0123 commit: Add tests
HEAD@{3} 4cd5678 commit: Implement audio DSP
HEAD@{4} ...
PASO 3: Identificar objetivo • Quiero HEAD@{1} (def5678) que tiene "Final polish" • Ese era mi último commit antes del reset
PASO 4: Crear branch de rescate └─ Nueva branch llamada "feature-audio-recovery" apuntando a def5678
PASO 5: Verificar └─ Checkout a feature-audio-recovery └─ Log muestra los 3 commits recuperados └─ Archivos correctos presentes
PASO 6: Restaurar └─ Mover feature-audio a def5678 └─ O trabajar desde recovery branch
🛡️ PREVENCIÓN: ──────────────────────────────────────────────────────────────────────────── ├─ NUNCA uses --hard sin estar 100% seguro ├─ Usa --soft por default, escalas a --hard si necesitas ├─ Crea branch de backup antes de operaciones peligrosas ├─ Push frecuentemente (remote es backup automático) └─ Usa aliases que confirmen antes de --hard
PLAYBOOK 2: "FORCE PUSH DESTRUYÓ HISTORIA REMOTA" ════════════════════════════════════════════════════════════════════════════
🎯 SÍNTOMAS: ──────────────────────────────────────────────────────────────────────────── ├─ Remote tiene menos commits que antes ├─ Otros developers reportan "missing commits" ├─ GitHub/GitLab muestra "force-pushed" ├─ Commits de otros desaparecieron └─ Equipo en pánico
ESCENARIO TÍPICO: "Hice force push de mi branch local a main, pero mi branch estaba desactualizado. Ahora main en el server no tiene los últimos 10 commits del equipo."
🔍 DIAGNÓSTICO: ────────────────────────────────────────────────────────────────────────────
SEVERIDAD ASSESSMENT:
PREGUNTA 1: ¿Qué branch fue afectado? ├─ main/master/production → ⚠️ CRÍTICO ├─ develop/staging → 🟠 SERIO └─ feature-personal → 🟡 MENOR
PREGUNTA 2: ¿Alguien más tiene los commits perdidos? ├─ ✅ SÍ → Recuperable desde su clone ├─ ❌ NO → Necesitas reflog del pusher └─ ❓ NO SÉ → Pregunta al equipo URGENTE
PREGUNTA 3: ¿Tienes backup/mirror del repo? ├─ ✅ SÍ → Recuperación fácil └─ ❌ NO → Recuperación de clones locales
ESTADO DEL SISTEMA:
REMOTE ANTES: origin/main ↓ ○──○──○──●──●──●──●──●──●──● A B C D E F G H I J
TU LOCAL (desactualizado): main ↓ ○──○──○──● A B C X ↑ "Divergente"
DESPUÉS DE force push: origin/main ↓ ○──○──○──● ●──●──●──●──●──● A B C X D E F G H I J ↑ "Huérfanos en server"
D-J están PERDIDOS del remote (pero EXISTEN en clones que los tenían).
💊 TRATAMIENTO: ────────────────────────────────────────────────────────────────────────────
⚠️ PRIORIDAD MÁXIMA: STOP MORE DAMAGE
PASO 0: EMERGENCY STOP ├─ Notificar equipo INMEDIATAMENTE ├─ Nadie más debe pushear ├─ Nadie debe borrar sus clones locales └─ Congelar actividad en branch afectado
PASO 1: Encontrar commits perdidos ├─ OPCIÓN A: Otro developer tiene los commits ├─ OPCIÓN B: Tu reflog local (si tú hiciste fetch antes) └─ OPCIÓN C: Backup/mirror del remote
PASO 2: Identificar último commit "bueno" ├─ ¿Cuál era el HEAD correcto de main antes del force push? ├─ Buscar SHA del último commit que debería estar └─ Confirmar con equipo
PASO 3: Crear branch de rescate ├─ En tu local o en clone de otro developer ├─ Branch apuntando al último commit bueno └─ Nombre: "main-rescue-YYYY-MM-DD"
PASO 4: Verificar integridad ├─ Comparar main-rescue con main actual ├─ Confirmar tiene TODOS los commits necesarios ├─ Revisar con autor de commits perdidos └─ Test si es código crítico
PASO 5: Restaurar remote ├─ Force push del branch de rescate a main ├─ Sí, otro force push (ironía del proceso) └─ Documentar en mensaje de commit/push
PASO 6: Sincronizar equipo ├─ Notificar restauración completa ├─ Todos deben hacer reset --hard origin/main └─ Verificar cada developer tiene estado correcto
EJEMPLO DETALLADO: ────────────────────────────────────────────────────────────────────────────
Situación: Alice hizo force push a main, perdió commits de Bob y Carol.
PASO 0: EMERGENCY • Alice notifica en Slack: "🚨 FORCE PUSH ACCIDENT ON MAIN - STOP WORK" • Todos pausan
PASO 1: Encontrar commits • Bob tiene los commits en su local • Su main local apunta a commit "j5k6l7" (el correcto)
PASO 2: Identificar bueno • Equipo confirma: j5k6l7 "Merge PR #234" era último en main • Este es el objetivo
PASO 3: Crear rescate • Bob crea branch: "main-rescue-2025-10-03" • Apunta a j5k6l7
PASO 4: Verificar • Log de main-rescue tiene todos los commits esperados • Carol confirma sus commits están ahí • Tests pasan
PASO 5: Restaurar • Bob hace force push de main-rescue a origin/main • Main restaurado a j5k6l7
PASO 6: Sincronizar • Alice, Carol, y otros: git reset --hard origin/main • Verifican log coincide • Trabajo continúa
🛡️ PREVENCIÓN: ──────────────────────────────────────────────────────────────────────────── ├─ Branch protection en GitHub/GitLab (REQUIERE pull request) ├─ Deshabilitar force push en main/develop ├─ Pre-push hooks que bloquean force push ├─ SIEMPRE fetch antes de push ├─ Usar push --force-with-lease (verifica no hay cambios remotos) └─ Educación: "Force push = última opción, no default"
PLAYBOOK 3: "MERGE CONFLICT IMPOSIBLE DE RESOLVER" ════════════════════════════════════════════════════════════════════════════
🎯 SÍNTOMAS: ──────────────────────────────────────────────────────────────────────────── ├─ Merge tiene cientos/miles de conflictos ├─ No está claro qué versión es correcta ├─ Archivos completamente divergentes ├─ Ambas branches son importantes └─ Resolver manualmente tomaría días
ESCENARIO TÍPICO: "Intenté mergear feature-refactor en main después de 3 meses. 457 archivos en conflicto. No sé por dónde empezar."
🔍 DIAGNÓSTICO: ────────────────────────────────────────────────────────────────────────────
ANÁLISIS DE COMPLEJIDAD:
PREGUNTA 1: ¿Cuántos archivos en conflicto? ├─ < 10 → Resoluble manualmente ├─ 10-50 → Laborioso pero viable ├─ 50-200 → Necesitas estrategia └─ > 200 → Merge approach está mal
PREGUNTA 2: ¿Qué tipo de conflictos? ├─ Mismas líneas modificadas → Requiere juicio humano ├─ Archivos movidos/renombrados → Puede automatizarse parcialmente └─ Cambios estructurales masivos → Necesita rebase incremental
PREGUNTA 3: ¿Las branches son compatibles conceptualmente? ├─ ✅ SÍ → Conflictos son técnicos, resoluble └─ ❌ NO → Branches divergieron conceptualmente, necesitas re-architecture
ANATOMÍA DEL PROBLEMA:
SITUACIÓN: main ↓ ○──○──○──●──●──●──●──●──●──●──●──● ╲ A1 A2 A3 A4 A5 A6 A7 A8 ╲ ●──●──●──●──●──●──●──●──●──● B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 ↓ feature-refactor
Base común: 3 meses atrás Divergencia: 8 commits en main, 10 en feature Conflictos: Cambios en mismos archivos con diferentes objetivos
💊 TRATAMIENTO: ────────────────────────────────────────────────────────────────────────────
ESTRATEGIA: NO intentes merge directo. Usa approach incremental.
PASO 1: Abortar merge actual ├─ Volver a estado limpio ├─ Analizar situación con calma └─ No intentar forzar resolución
PASO 2: Elegir estrategia de integración
OPCIÓN A: Rebase incremental (preferido para feature branches)
────────────────────────────────────────────────────────────
├─ Rebase feature sobre main en PASOS PEQUEÑOS
├─ Resolver conflictos de 5-10 commits a la vez
├─ Validar tests después de cada paso
└─ Si un paso falla, abortar ese rebase y ajustar
OPCIÓN B: Merge con estrategia específica
────────────────────────────────────────────────────────────
├─ Merge con -X theirs o -X ours para ciertos archivos
├─ Acepta una versión wholesale, luego cherry-pick cambios específicos
└─ Útil si un lado es claramente "correcto" para ciertos archivos
OPCIÓN C: Cherry-pick selectivo
────────────────────────────────────────────────────────────
├─ No merger TODO feature-refactor
├─ Cherry-pick solo commits que agregan valor
├─ Descartar commits que duplican trabajo de main
└─ Reconstruir feature incrementalmente
OPCIÓN D: Fresh merge con archivos específicos
────────────────────────────────────────────────────────────
├─ Hacer merge
├─ Para archivos conflictivos imposibles, elegir versión completa
├─ Marcar para revisión manual posterior
└─ Iterar sobre archivos marcados
PASO 3: Ejecutar estrategia elegida (Ejemplo: Rebase incremental)
SUB-PASO 3.1: Dividir feature en segmentos
feature-refactor tiene commits B1...B10
Segmento 1: B1-B3 (setup inicial)
Segmento 2: B4-B6 (core changes)
Segmento 3: B7-B10 (finalización)
SUB-PASO 3.2: Rebase segmento 1
├─ Crear branch temporal feature-part1
├─ Cherry-pick B1-B3 sobre main
├─ Resolver conflictos (solo 3 commits)
└─ Validar tests
SUB-PASO 3.3: Rebase segmento 2
├─ Crear branch feature-part2 desde feature-part1
├─ Cherry-pick B4-B6
├─ Resolver conflictos
└─ Validar tests
SUB-PASO 3.4: Rebase segmento 3
├─ Crear branch feature-part3 desde feature-part2
├─ Cherry-pick B7-B10
├─ Resolver conflictos
└─ Validar tests
SUB-PASO 3.5: Consolidar
├─ feature-part3 ES el feature completo rebased
├─ Renombrar a feature-refactor-rebased
└─ Merge a main (ahora sin conflictos o mínimos)
PASO 4: Validación final ├─ Tests completos ├─ Code review de cambios integrados ├─ Comparación con ambas branches originales └─ Confirmar funcionalidad preservada
EJEMPLO DETALLADO: ────────────────────────────────────────────────────────────────────────────
Situación: feature-x tiene 457 conflictos con main.
PASO 1: Abortar • Abort merge • Analizar log de ambas branches
PASO 2: Elegir estrategia • Opción A: Rebase incremental • feature-x tiene 30 commits • Dividir en 3 segmentos de 10 commits
PASO 3: Ejecutar Segmento 1 (commits 1-10): • Cherry-pick a main • 45 conflictos (manejable) • Resolver en 2 horas • Tests pasan
Segmento 2 (commits 11-20):
• Cherry-pick desde segmento 1
• 78 conflictos
• Resolver en 3 horas
• Tests pasan
Segmento 3 (commits 21-30):
• Cherry-pick desde segmento 2
• 23 conflictos (muchos ya resueltos)
• Resolver en 1 hora
• Tests pasan
PASO 4: Validar • feature-x-rebased completo • Todos tests pasan • Code review aprueba • Merge a main limpio
🛡️ PREVENCIÓN: ──────────────────────────────────────────────────────────────────────────── ├─ Merge/rebase frecuentemente (diario/semanal) ├─ Feature branches no deben vivir > 2 semanas ├─ Sync con main regularmente durante desarrollo ├─ CI/CD detecta merge conflicts temprano └─ Comunicación de cambios estructurales
PLAYBOOK 4: "COMMITEÉ ARCHIVO SECRETO/SENSITIVO" ════════════════════════════════════════════════════════════════════════════
🎯 SÍNTOMAS: ──────────────────────────────────────────────────────────────────────────── ├─ .env con passwords commiteado ├─ API keys en código ├─ Certificados SSL en repo ├─ Datos sensibles de clientes └─ Ya pusheado a remote público
ESCENARIO CRÍTICO: "Commiteé .env con database password a GitHub. El repo es público. ¿Están mis credenciales comprometidas?"
🔍 DIAGNÓSTICO: ────────────────────────────────────────────────────────────────────────────
SEVERIDAD:
PREGUNTA 1: ¿Ya fue pusheado a remote? ├─ ❌ NO → Solo local, fácil de arreglar └─ ✅ SÍ → 🚨 CRÍTICO
PREGUNTA 2: ¿El remote es público? ├─ ✅ SÍ → 🚨 ASSUME COMPROMISED └─ ❌ NO (privado) → 🟠 Serio pero contenido
PREGUNTA 3: ¿Cuánto tiempo lleva pusheado? ├─ < 1 hora → Posiblemente no scraped └─ > 1 hora → Probablemente scraped por bots
REALIDAD DURA: ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ Si pusheaste secreto a repo PÚBLICO, asume que está COMPROMETIDO. │ │ Bots scrapean GitHub en MINUTOS buscando API keys. │ │ ELIMINAR el commit NO elimina la exposición. │ │ Historia de Git es PÚBLICA y CACHEADA por múltiples servicios. │ │ │ └─────────────────────────────────────────────────────────────────────────┘
💊 TRATAMIENTO: ────────────────────────────────────────────────────────────────────────────
⚠️ FASE 1: CONTENCIÓN INMEDIATA (Minutos cuentan)
PASO 1: ROTAR CREDENCIALES (PRIORIDAD #1) ├─ Cambiar passwords inmediatamente ├─ Revocar API keys ├─ Regenerar tokens ├─ Invalidar certificados └─ Hacer ANTES de eliminar de Git
RATIONALE: Eliminar de Git NO revoca acceso si alguien ya copió.
PASO 2: Notificar stakeholders ├─ Equipo de seguridad ├─ Managers ├─ Clientes (si datos de ellos) └─ Seguir protocolo de incident response
PASO 3: Monitorear por uso no autorizado ├─ Logs de acceso a servicios ├─ Alerts de actividad inusual └─ Audit trails
⚠️ FASE 2: LIMPIEZA DEL REPOSITORIO
PASO 4: Eliminar de historia
OPCIÓN A: Si es commit reciente NO pusheado
└─ Reset o amend simple
OPCIÓN B: Si ya pusheado pero repo privado
└─ Reescribir historia con filter-repo o BFG
└─ Force push
└─ Notificar equipo para re-clone
OPCIÓN C: Si ya pusheado a repo público
└─ Reescribir historia igual (limita daño futuro)
└─ Pero asume secreto comprometido permanentemente
PASO 5: Prevenir re-introducción ├─ Agregar archivo a .gitignore ├─ Agregar pre-commit hook para detectar secretos └─ Usar herramientas como git-secrets, truffleHog
EJEMPLO DE LIMPIEZA (usando concepto): ────────────────────────────────────────────────────────────────────────────
Situación: .env con DB password en commit C5, ya pusheado.
ESTADO: ○──○──○──○──● C1 C2 C3 C4 C5 (contiene .env)
PROCESO: 1. ROTAR password de DB (hacer PRIMERO) 2. Reescribir historia para eliminar .env de C5 3. Force push de historia reescrita 4. Todos los collaborators deben re-clone o reset --hard
🛡️ PREVENCIÓN: ──────────────────────────────────────────────────────────────────────────── ├─ .gitignore completo desde día 1 ├─ Template .env.example (sin valores reales) ├─ Pre-commit hooks con git-secrets ├─ Code review checklist include "no secrets" ├─ Secret management tools (Vault, AWS Secrets Manager) └─ Educación de equipo sobre riesgos
PLAYBOOK 5: "BRANCH BORRADO ACCIDENTALMENTE" ════════════════════════════════════════════════════════════════════════════
🎯 SÍNTOMAS: ──────────────────────────────────────────────────────────────────────────── ├─ Branch desapareció de lista ├─ Comando branch -D ejecutado por error ├─ Trabajo "perdido" └─ Pánico proporcional a importancia del branch
ESCENARIO: "Quería borrar old-feature, pero escribí feature-current por error. ¡3 semanas de trabajo!"
🔍 DIAGNÓSTICO: ────────────────────────────────────────────────────────────────────────────
PREGUNTA 1: ¿El branch estaba pusheado a remote? ├─ ✅ SÍ → Trivial, solo fetch de nuevo └─ ❌ NO → Necesitas reflog
PREGUNTA 2: ¿Cuánto tiempo pasó? ├─ Reciente → Definitivamente en reflog └─ > 90 días → Posible garbage collection
💊 TRATAMIENTO: ────────────────────────────────────────────────────────────────────────────
CASO A: Branch estaba en remote ──────────────────────────────────────────────────────────────────────────── PASO 1: Fetch del remote PASO 2: Crear branch local tracking remote branch PASO 3: Continuar trabajo
¡Eso es todo! Remote es backup automático.
CASO B: Branch solo local ──────────────────────────────────────────────────────────────────────────── PASO 1: Invocar reflog • Buscar última acción en ese branch • Identificar SHA del último commit
PASO 2: Recrear branch • Crear nuevo branch con mismo nombre • Apuntar al SHA encontrado
PASO 3: Verificar contenido • Confirmar commits esperados están ahí
EJEMPLO: ────────────────────────────────────────────────────────────────────────────
Borraste feature-audio accidentalmente.
REFLOG muestra: HEAD@{0} abc1234 checkout: moving to main HEAD@{1} def5678 commit: Final audio tests ← feature-audio estaba aquí HEAD@{2} 9ab0123 commit: Add DSP logic ⋮
RECUPERACIÓN: Crear branch feature-audio apuntando a def5678 Verificar log tiene commits esperados ¡Recuperado!
🛡️ PREVENCIÓN: ──────────────────────────────────────────────────────────────────────────── ├─ Push branches importantes a remote ├─ Aliases que confirman antes de branch -D ├─ Usar branch -d (sin force) por default └─ Workflow de branch archival en vez de delete
═══════════════════════════════════════════════════════════════════════════════ 🎓 CAPÍTULO 5: PREVENCIÓN > CURA ═══════════════════════════════════════════════════════════════════════════════
LA FILOSOFÍA DE DEFENSA EN PROFUNDIDAD ────────────────────────────────────────────────────────────────────────────
Los mejores desastres son los que NUNCA PASAN. Recovery es vital, pero prevención es SUPERIOR.
PRINCIPIO: ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ "An ounce of prevention is worth a pound of cure" │ │ │ │ Cada minuto invertido en prevención ahorra HORAS de recovery. │ │ Cada hook implementado previene DOCENAS de errores. │ │ Cada regla de protección evita CRISIS de equipo. │ │ │ └─────────────────────────────────────────────────────────────────────────┘
ARQUITECTURA DE SEGURIDAD: CINCO LÍNEAS DE DEFENSA ────────────────────────────────────────────────────────────────────────────
🎯 ASSET (CÓDIGO)
│
│
┌────────────┴────────────┐
│ │
🛡️ LÍNEA 5 │
(Recovery Drills) │
│ │
🛡️ LÍNEA 4 │
(Backups) │
│ │
🛡️ LÍNEA 3 │
(Branch Protection) │
│ │
🛡️ LÍNEA 2 │
(Tooling) │
│ │
🛡️ LÍNEA 1 │
(Education) │
│ │
└─────────────────────────┘
AMENAZA
(Errores humanos)
Cada línea es independiente. Si una falla, las otras aún protegen.
🛡️ LÍNEA 1: EDUCACIÓN ────────────────────────────────────────────────────────────────────────────
PRINCIPIO: Developers que ENTIENDEN Git cometen menos errores.
ELEMENTOS:
📚 Documentación interna ├─ Guías de workflow del equipo ├─ Playbooks de recovery ├─ FAQs de problemas comunes └─ Glossario de conceptos Git
👥 Onboarding de nuevos developers ├─ Git training obligatorio ├─ Práctica en repo de sandbox ├─ Buddy system con developer experimentado └─ Checklist de competencias Git
🎓 Workshops y brown bags ├─ Sesiones mensuales de Git avanzado ├─ Post-mortems de incidents (aprender de errores) ├─ Demos de features Git útiles └─ Lightning talks de tips & tricks
📖 README de emergencia ├─ "Si cometiste un error, LEE ESTO PRIMERO" ├─ Quick reference de comandos de recovery ├─ Contactos de personas expertas en Git └─ Enlace a playbooks completos
MÉTRICAS DE ÉXITO: ├─ Reducción en incidents de Git ├─ Time to resolution más rápido ├─ Menos preguntas repetitivas en Slack └─ Developers confident con Git
🛡️ LÍNEA 2: TOOLING ────────────────────────────────────────────────────────────────────────────
PRINCIPIO: Herramientas que previenen errores automáticamente.
GIT HOOKS: La primera línea de defensa técnica ────────────────────────────────────────────────────────────────────────────
HOOKS ÚTILES:
pre-commit: ├─ Detectar secretos (API keys, passwords) ├─ Linting de código ├─ Format checking ├─ Prohibir commits a main/master directamente └─ Verificar tests pasan
pre-push: ├─ Bloquear force push a branches protegidos ├─ Verificar CI pasa localmente ├─ Confirmar branch está actualizado con remote └─ Advertir si push es a main
commit-msg: ├─ Validar formato de mensaje ├─ Requerir ticket/issue number ├─ Prohibir mensajes vacíos o genéricos └─ Spell checking de mensajes
EJEMPLO: pre-push hook que previene force push a main ────────────────────────────────────────────────────────────────────────────
CONCEPTO: Hook intercepta push command Verifica si es force push Verifica si target es main/master Si ambos = TRUE, BLOQUEA y muestra error
MENSAJE: "🚫 Force push to 'main' is not allowed. This would rewrite shared history. If you must force push, contact DevOps team."
ALIASES: Comandos más seguros por default ────────────────────────────────────────────────────────────────────────────
EJEMPLOS:
safe-reset: • reset --soft en vez de --hard por default • Require flag adicional para --hard
confirm-force-push: • Pide confirmación antes de force push • Muestra diff de lo que se sobrescribirá
backup-branch: • Crea branch de backup antes de operaciones peligrosas • Nombra automáticamente con timestamp
HERRAMIENTAS EXTERNAS: ────────────────────────────────────────────────────────────────────────────
git-secrets: • Escanea commits por secretos • Integra con pre-commit hook
husky: • Gestión de hooks en proyectos Node.js • Compartir hooks entre equipo via package.json
pre-commit framework: • Framework para gestionar hooks • Librería de hooks predefinidos
🛡️ LÍNEA 3: BRANCH PROTECTION ────────────────────────────────────────────────────────────────────────────
PRINCIPIO: Platforms (GitHub/GitLab) enforce políticas a nivel servidor.
GITHUB BRANCH PROTECTION RULES: ────────────────────────────────────────────────────────────────────────────
PARA main/master:
✅ Require pull request before merging • NO se puede pushear directamente • Todo cambio via PR • Code review obligatorio
✅ Require status checks to pass • CI/CD debe estar verde • Tests deben pasar • Coverage debe cumplir threshold
✅ Require branches to be up to date • No se puede mergear si desactualizado • Force sync con main antes de merge
✅ Require signed commits • Commits deben estar firmados GPG • Verificación de identidad
❌ Do not allow force pushes • Force push BLOQUEADO a nivel servidor • Imposible reescribir historia
❌ Do not allow deletions • Branch main no puede borrarse • Protección contra accidentes catastróficos
JERARQUÍA DE PROTECCIÓN: ────────────────────────────────────────────────────────────────────────────
╔═══════════════════════════════════════════════════════════════════════════╗ ║ BRANCH │ PROTECTION LEVEL │ RULES ║ ╠═══════════════════════════════════════════════════════════════════════════╣ ║ ║ ║ main/master │ MÁXIMO │ • Require PR ║ ║ │ │ • Require reviews (2+) ║ ║ │ │ • Require CI green ║ ║ │ │ • No force push ║ ║ │ │ • No delete ║ ║ │ │ • Require signed commits ║ ║ ║ ║ develop/staging │ ALTO │ • Require PR ║ ║ │ │ • Require reviews (1+) ║ ║ │ │ • Require CI green ║ ║ │ │ • No force push ║ ║ ║ ║ release/* │ MEDIO │ • Require PR ║ ║ │ │ • Require CI green ║ ║ ║ ║ feature/* │ BAJO │ • Ninguna restricción ║ ║ │ │ • Developers tienen control total ║ ║ ║ ╚═══════════════════════════════════════════════════════════════════════════╝
RATIONALE: main = Production, máxima protección develop = Integration, alta protección feature = Experimental, libertad para developers
🛡️ LÍNEA 4: BACKUPS ────────────────────────────────────────────────────────────────────────────
PRINCIPIO: Si todo falla, un backup puede salvarte.
ESTRATEGIA DE BACKUP:
1️⃣ Remote como backup primario ├─ Push frecuentemente a GitHub/GitLab ├─ Remote = automatic backup └─ Distribuido: cada clone es backup
2️⃣ Mirrors adicionales ├─ Mirror en segundo servicio (GitLab + GitHub) ├─ Self-hosted GitLab como backup └─ Sync automático via hooks/CI
3️⃣ Snapshots periódicos ├─ Backup completo de .git/ diario/semanal ├─ Stored en storage separado (S3, NAS) └─ Versioning de backups
4️⃣ Critical branches tagged ├─ Tags en releases importantes ├─ Tags = inmutable, permanente └─ Reference points para recovery
ARQUITECTURA DE BACKUP:
LOCAL REPO
│
├─→ origin (GitHub) ← Primary remote
│
├─→ mirror (GitLab) ← Secondary remote
│
└─→ Daily snapshot ← Disk backup
(S3/NAS)
FRECUENCIA: ├─ Push: Varias veces al día (automático en workflow) ├─ Mirror sync: Cada push (automatic) ├─ Snapshot: Diario (cron job) └─ Archive: Mensual (long-term storage)
TESTING DE BACKUPS: ────────────────────────────────────────────────────────────────────────────
REGLA: ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ "Un backup no probado NO ES un backup." │ │ │ │ Backups deben ser PROBADOS regularmente para confirmar funcionan. │ │ │ └─────────────────────────────────────────────────────────────────────────┘
DRILL: ├─ Mensualmente: Restaurar de backup en entorno test ├─ Verificar integridad ├─ Documentar time to restore └─ Ajustar procedimientos si hay issues
🛡️ LÍNEA 5: RECOVERY DRILLS ────────────────────────────────────────────────────────────────────────────
PRINCIPIO: Practice makes perfect. Practicar recovery antes de necesitarla.
TIPOS DE DRILLS:
1️⃣ Tabletop exercises ├─ Reunión de equipo ├─ Escenario hipotético presentado ├─ Equipo discute cómo respondería └─ Identificar gaps en procedimientos
2️⃣ Simulated incidents ├─ Crear "desastre" en repo de test ├─ Developer practica recovery ├─ Medir time to resolution └─ Revisar procedimientos
3️⃣ Game days ├─ Evento de equipo completo ├─ Múltiples escenarios de desastre ├─ Competencia friendly └─ Aprendizaje compartido
EJEMPLO DE DRILL: "Deleted main branch" ────────────────────────────────────────────────────────────────────────────
PREPARACIÓN: • Crear repo de test • Poblar con commits de ejemplo • Borrar main intencionalmente
EJERCICIO: • Developer intenta recuperar • Documentar pasos • Medir tiempo • Comparar con playbook
POST-MORTEM: • ¿Qué funcionó? • ¿Qué fue confuso? • ¿Playbook necesita actualización? • ¿Tooling adicional ayudaría?
BENEFICIOS: ├─ Confidence en crisis real ├─ Muscle memory de comandos ├─ Identificación de gaps └─ Team bonding
REGLAS DE ORO DE GIT SAFETY ════════════════════════════════════════════════════════════════════════════
╔═══════════════════════════════════════════════════════════════════════════╗ ║ 10 MANDAMIENTOS DE GIT ║ ╠═══════════════════════════════════════════════════════════════════════════╣ ║ ║ ║ 1️⃣ NEVER force push to shared branches ║ ║ (main, develop, release) ║ ║ ║ ║ 2️⃣ ALWAYS work on feature branches ║ ║ (Never commit directly to main) ║ ║ ║ ║ 3️⃣ COMMIT often, PUSH regularly ║ ║ (Remote is your backup) ║ ║ ║ ║ 4️⃣ READ error messages COMPLETELY ║ ║ (Git tells you what's wrong) ║ ║ ║ ║ 5️⃣ When in doubt, CREATE backup branch ║ ║ (Branches are cheap, mistakes are expensive) ║ ║ ║ ║ 6️⃣ TEST recovery procedures REGULARLY ║ ║ (Practice before you need it) ║ ║ ║ ║ 7️⃣ NEVER commit secrets ║ ║ (.env, keys, passwords stay out of Git) ║ ║ ║ ║ 8️⃣ FETCH before you work ║ ║ (Stay synced with team) ║ ║ ║ ║ 9️⃣ REBASE personal branches, MERGE shared branches ║ ║ (Clean history personal, preserve history shared) ║ ║ ║ ║ 🔟 ASK before doing destructive operations ║ ║ (--hard, --force, filter-branch = ask first) ║ ║ ║ ╚═══════════════════════════════════════════════════════════════════════════╝
═══════════════════════════════════════════════════════════════════════════════ 🎯 EPÍLOGO: LA MENTALIDAD DEL DISASTER RECOVERY ═══════════════════════════════════════════════════════════════════════════════
LECCIONES FUNDAMENTALES ────────────────────────────────────────────────────────────────────────────
1️⃣ Git está diseñado para SER RECUPERABLE • Casi todo tiene solución • Reflog es tu red de seguridad • 90 días para recuperar es MUCHO tiempo
2️⃣ El pánico es el enemigo • STOP antes de hacer más daño • THINK antes de ejecutar comandos • VERIFY antes de force push
3️⃣ Prevención es inversión, no costo • Hooks toman minutos, saves horas • Protection rules evitan crisis • Education reduce incidents
4️⃣ Community es recurso • Tu equipo tiene experiencia • Stack Overflow tiene respuestas • Documentación es completa
5️⃣ Errores son oportunidades • Post-mortems mejoran procesos • Cada incident → playbook update • Team aprende junto
LA EVOLUCIÓN DE UN GIT USER ────────────────────────────────────────────────────────────────────────────
NIVEL 1: NOVICE ├─ Comando básicos (add, commit, push) ├─ Terror de errores ├─ No entiende internals └─ "Git es magia negra"
NIVEL 2: COMPETENT ├─ Branches, merges, rebases ├─ Puede resolver conflictos ├─ Usa algunos commands avanzados └─ "Git tiene sentido... a veces"
NIVEL 3: PROFICIENT ├─ Entiende modelo de objetos ├─ Usa reflog cómodamente ├─ Puede recovery de la mayoría de errores └─ "Git es lógico y predecible"
NIVEL 4: EXPERT ├─ Entiende plumbing commands ├─ Puede explicar internals ├─ Crea tooling y hooks └─ "Git es una base de datos hermosa"
NIVEL 5: MASTER ├─ Contribuye a Git mismo ├─ Diseña workflows para organizaciones ├─ Mentorea otros developers └─ "Git es una extensión de mi pensamiento"
Tu objetivo: Llegar a PROFICIENT. Ahí tienes confidence para manejar disasters.
PENSAMIENTOS FINALES ────────────────────────────────────────────────────────────────────────────
Git puede parecer intimidante. Sus errores pueden parecer catastróficos. Pero la VERDAD es:
• Git fue diseñado por desarrolladores, para desarrolladores
• Sus creadores sabían que humanos cometen errores
• Por eso construyeron MÚLTIPLES sistemas de recovery
• Reflog, garbage collection delay, distributed nature
• Todo está ahí para PROTEGERTE
Tu trabajo es: 1. Entender cómo funcionan esas protecciones 2. Usar las herramientas disponibles 3. No entrar en pánico cuando algo va mal 4. Seguir los playbooks 5. Aprender de cada incident
Cada disaster es una lección. Cada recovery exitoso es más confidence. Cada playbook ejecutado es más experiencia.
Con tiempo, Git dejará de ser fuente de anxiety y se convertirá en HERRAMIENTA PODEROSA que dominas completamente.
Este documento es tu mapa. Los playbooks son tu GPS. Reflog es tu red de seguridad. Tu equipo es tu soporte.
Ahora sal y usa Git con CONFIDENCE. Y cuando algo vaya mal (porque eventualmente va mal para todos), vuelve aquí, respira, y sigue el playbook.
You got this. 🚀
═══════════════════════════════════════════════════════════════════════════════ FIN DEL DOCUMENTO ═══════════════════════════════════════════════════════════════════════════════