408 lines
10 KiB
Markdown
408 lines
10 KiB
Markdown
# Command: git-recovery
|
||
|
||
Recupera el repositorio de estados inconsistentes causados por worktrees huérfanos, branches bloqueados o conflictos de git. Ejecuta automáticamente cuando se detectan errores git durante operaciones de issues.
|
||
|
||
## Para el usuario
|
||
|
||
### Cuándo usar este comando
|
||
|
||
- Cuando hay errores "exit status 128" al crear worktrees
|
||
- Cuando git reporta "worktree already exists"
|
||
- Cuando hay branches que no se pueden eliminar
|
||
- Cuando `git worktree list` muestra worktrees huérfanos
|
||
- Cuando el orquestador paralelo falla por problemas git
|
||
- **Automáticamente:** El sistema lo invoca cuando detecta errores git
|
||
|
||
### Sintaxis
|
||
|
||
```bash
|
||
/git:recovery [--aggressive]
|
||
```
|
||
|
||
### Parámetros
|
||
|
||
- `--aggressive` (opcional): Fuerza limpieza agresiva incluyendo branches remotas y reset de estado
|
||
|
||
### Ejemplos
|
||
|
||
**Ejemplo 1: Recuperación estándar (recomendado)**
|
||
```bash
|
||
/git:recovery
|
||
```
|
||
|
||
Limpia worktrees huérfanos, verifica estado, sincroniza con remoto.
|
||
|
||
**Ejemplo 2: Recuperación agresiva**
|
||
```bash
|
||
/git:recovery --aggressive
|
||
```
|
||
|
||
Incluye force-prune, eliminación de branches huérfanas, reset de índice.
|
||
|
||
## Para Claude
|
||
|
||
### Precondiciones
|
||
|
||
Verificar antes de ejecutar:
|
||
|
||
- [ ] Estamos en un repositorio git válido
|
||
- [ ] Hay permisos para modificar .git/
|
||
- [ ] No hay operaciones git críticas en progreso (rebase, merge)
|
||
|
||
### Inputs
|
||
|
||
- `--aggressive` (opcional): Modo agresivo de limpieza
|
||
|
||
### Flujo obligatorio
|
||
|
||
#### 1. Diagnóstico inicial
|
||
|
||
```bash
|
||
echo "🔍 Diagnosticando estado del repositorio..."
|
||
echo ""
|
||
|
||
# Verificar que estamos en repo git
|
||
if [ ! -d ".git" ]; then
|
||
echo "❌ Error: No estamos en un repositorio git"
|
||
exit 1
|
||
fi
|
||
|
||
# Guardar estado actual
|
||
git branch --show-current > /tmp/git-recovery-current-branch.txt
|
||
git status --porcelain > /tmp/git-recovery-status.txt
|
||
|
||
echo "Rama actual: $(cat /tmp/git-recovery-current-branch.txt)"
|
||
echo "Cambios pendientes: $(wc -l < /tmp/git-recovery-status.txt) archivos"
|
||
echo ""
|
||
```
|
||
|
||
#### 2. Análisis de problemas
|
||
|
||
```bash
|
||
echo "📋 Analizando problemas..."
|
||
echo ""
|
||
|
||
# Listar worktrees (puede fallar si hay huérfanos)
|
||
echo "Worktrees actuales:"
|
||
git worktree list 2>&1 || echo " (error listando worktrees - probablemente hay huérfanos)"
|
||
echo ""
|
||
|
||
# Verificar branches locales
|
||
echo "Branches locales:"
|
||
git branch --list
|
||
echo ""
|
||
|
||
# Verificar branches remotas
|
||
echo "Estado remoto:"
|
||
git remote -v
|
||
git fetch --dry-run 2>&1 || echo " (problemas con fetch)"
|
||
echo ""
|
||
```
|
||
|
||
#### 3. Limpieza de worktrees huérfanos
|
||
|
||
```bash
|
||
echo "🧹 Limpiando worktrees huérfanos..."
|
||
echo ""
|
||
|
||
# Ejecutar prune (elimina referencias a worktrees que ya no existen)
|
||
git worktree prune -v 2>&1
|
||
|
||
# Verificar si aún hay worktrees huérfanos en disco
|
||
if [ -d "worktrees" ]; then
|
||
echo ""
|
||
echo "Verificando directorio worktrees/..."
|
||
|
||
# Listar directorios en worktrees/
|
||
for worktree_dir in worktrees/issue-*; do
|
||
if [ -d "$worktree_dir" ]; then
|
||
worktree_name=$(basename "$worktree_dir")
|
||
|
||
# Verificar si git lo conoce
|
||
if ! git worktree list | grep -q "$worktree_dir"; then
|
||
echo " ⚠️ Worktree huérfano detectado: $worktree_name"
|
||
|
||
# Eliminar directorio huérfano
|
||
rm -rf "$worktree_dir"
|
||
echo " ✓ Eliminado: $worktree_dir"
|
||
fi
|
||
fi
|
||
done
|
||
fi
|
||
|
||
echo ""
|
||
echo "✓ Limpieza de worktrees completada"
|
||
echo ""
|
||
```
|
||
|
||
#### 4. Verificación de branches bloqueados
|
||
|
||
```bash
|
||
echo "🔓 Verificando branches bloqueados..."
|
||
echo ""
|
||
|
||
# Listar branches que podrían estar bloqueados
|
||
git branch --list 'issue/*' 'quick/*' | while read -r branch; do
|
||
branch=$(echo "$branch" | sed 's/^\* //' | xargs)
|
||
|
||
# Verificar si la branch está asociada a un worktree
|
||
if git worktree list | grep -q "$branch"; then
|
||
echo " ℹ️ Branch activa en worktree: $branch"
|
||
else
|
||
# Branch no está en worktree, podría estar mergeada
|
||
if git branch --merged master | grep -q "$branch"; then
|
||
echo " ✓ Branch mergeada, puede eliminarse: $branch"
|
||
git branch -d "$branch" 2>&1 || echo " (no se pudo eliminar automáticamente)"
|
||
else
|
||
echo " ⚠️ Branch NO mergeada: $branch"
|
||
fi
|
||
fi
|
||
done
|
||
|
||
echo ""
|
||
```
|
||
|
||
#### 5. Sincronización con remoto
|
||
|
||
```bash
|
||
echo "🔄 Sincronizando con remoto..."
|
||
echo ""
|
||
|
||
# Volver a master si no estamos ahí
|
||
current_branch=$(git branch --show-current)
|
||
if [ "$current_branch" != "master" ]; then
|
||
echo "Cambiando a master desde $current_branch..."
|
||
git checkout master 2>&1 || {
|
||
echo "❌ No se pudo cambiar a master"
|
||
echo " Quedando en rama: $current_branch"
|
||
}
|
||
fi
|
||
|
||
# Fetch y pull
|
||
echo "Actualizando desde origin/master..."
|
||
git fetch origin 2>&1
|
||
git pull --rebase origin master 2>&1 || {
|
||
echo "⚠️ Problemas al hacer pull, continuando..."
|
||
}
|
||
|
||
echo ""
|
||
echo "✓ Sincronización completada"
|
||
echo ""
|
||
```
|
||
|
||
#### 6. Modo agresivo (solo si --aggressive)
|
||
|
||
```bash
|
||
if [ "$AGGRESSIVE" = "true" ]; then
|
||
echo "⚡ MODO AGRESIVO ACTIVADO"
|
||
echo ""
|
||
|
||
echo "Limpiando branches remotas obsoletas..."
|
||
git remote prune origin -v 2>&1
|
||
echo ""
|
||
|
||
echo "Verificando integridad del repositorio..."
|
||
git fsck --full 2>&1 | head -20
|
||
echo ""
|
||
|
||
echo "Limpiando objetos no referenciados..."
|
||
git gc --prune=now 2>&1
|
||
echo ""
|
||
|
||
echo "Verificando índice..."
|
||
if [ -f ".git/index.lock" ]; then
|
||
echo " ⚠️ Encontrado index.lock (proceso git interrumpido)"
|
||
rm -f .git/index.lock
|
||
echo " ✓ Eliminado .git/index.lock"
|
||
fi
|
||
|
||
echo ""
|
||
echo "✓ Limpieza agresiva completada"
|
||
echo ""
|
||
fi
|
||
```
|
||
|
||
#### 7. Verificación final
|
||
|
||
```bash
|
||
echo "✅ Verificación final..."
|
||
echo ""
|
||
|
||
# Estado limpio
|
||
echo "Estado del repositorio:"
|
||
git status
|
||
echo ""
|
||
|
||
# Worktrees finales
|
||
echo "Worktrees activos:"
|
||
git worktree list
|
||
echo ""
|
||
|
||
# Branches locales
|
||
echo "Branches locales:"
|
||
git branch --list
|
||
echo ""
|
||
```
|
||
|
||
#### 8. Resumen
|
||
|
||
```bash
|
||
echo "════════════════════════════════════════════════════════"
|
||
echo "RECUPERACIÓN COMPLETADA"
|
||
echo "════════════════════════════════════════════════════════"
|
||
echo ""
|
||
|
||
# Comparar estado antes/después
|
||
CHANGES_BEFORE=$(wc -l < /tmp/git-recovery-status.txt)
|
||
CHANGES_AFTER=$(git status --porcelain | wc -l)
|
||
|
||
echo "Resumen:"
|
||
echo " Rama actual: $(git branch --show-current)"
|
||
echo " Cambios pendientes: $CHANGES_AFTER (antes: $CHANGES_BEFORE)"
|
||
echo " Worktrees activos: $(git worktree list | grep -v bare | wc -l)"
|
||
echo " Sincronizado con origin: $(git rev-parse HEAD) = $(git rev-parse origin/master 2>/dev/null || echo 'N/A')"
|
||
echo ""
|
||
|
||
if [ "$AGGRESSIVE" = "true" ]; then
|
||
echo "Modo agresivo aplicado: ✓"
|
||
echo ""
|
||
fi
|
||
|
||
echo "Próximos pasos:"
|
||
echo " - Verifica que master esté actualizado: git log --oneline -5"
|
||
echo " - Reintenta la operación que falló"
|
||
echo " - Si persisten errores, reporta el problema"
|
||
echo ""
|
||
|
||
# Limpiar archivos temporales
|
||
rm -f /tmp/git-recovery-*.txt
|
||
```
|
||
|
||
### Detección automática de errores git
|
||
|
||
El orquestador Go debe detectar estos patrones de error:
|
||
|
||
**Patrones de error que activan recovery:**
|
||
- `exit status 128` (error genérico de git)
|
||
- `worktree .* already exists`
|
||
- `reference is not a tree`
|
||
- `cannot lock ref`
|
||
- `index.lock`
|
||
- `fatal: not a git repository`
|
||
|
||
**Flujo de auto-recovery:**
|
||
1. Orquestador detecta error git
|
||
2. Pausa ejecución del grupo actual
|
||
3. Ejecuta `claude -p /git:recovery` automáticamente
|
||
4. Si recovery tiene éxito (exit code 0), reintenta la operación
|
||
5. Si recovery falla, aborta con error descriptivo
|
||
|
||
### Verificación final
|
||
|
||
```bash
|
||
# Verificar que el repo quedó en estado válido
|
||
git status --porcelain
|
||
git worktree list
|
||
|
||
# Exit code 0 si todo OK, 1 si hay problemas
|
||
if [ $? -eq 0 ]; then
|
||
exit 0
|
||
else
|
||
echo "❌ El repositorio sigue con problemas"
|
||
exit 1
|
||
fi
|
||
```
|
||
|
||
## Convenciones
|
||
|
||
- **No destructivo por defecto:** Solo limpia worktrees huérfanos y branches mergeadas
|
||
- **Modo agresivo bajo demanda:** Solo con flag explícito
|
||
- **Siempre sincroniza con remoto:** Garantiza consistencia
|
||
- **Preserva cambios locales:** No hace reset hard sin --aggressive
|
||
- **Logs detallados:** Reporta cada acción para debugging
|
||
|
||
## Troubleshooting
|
||
|
||
### Error: "cannot remove worktree"
|
||
|
||
**Causa:** Proceso usando archivos del worktree
|
||
|
||
**Solución:**
|
||
```bash
|
||
# Verificar procesos
|
||
lsof +D worktrees/ 2>/dev/null
|
||
|
||
# Forzar eliminación con --aggressive
|
||
/git:recovery --aggressive
|
||
```
|
||
|
||
### Error: "index.lock exists"
|
||
|
||
**Causa:** Operación git previa interrumpida
|
||
|
||
**Solución:**
|
||
Automáticamente manejado en modo estándar. Si persiste:
|
||
```bash
|
||
rm -f .git/index.lock
|
||
/git:recovery
|
||
```
|
||
|
||
### Warning: "branch no mergeada"
|
||
|
||
**Causa:** Branch contiene commits no integrados a master
|
||
|
||
**Solución:**
|
||
Verificar manualmente si es seguro eliminarla:
|
||
```bash
|
||
git log master..branch-name
|
||
git branch -D branch-name # Solo si estás seguro
|
||
```
|
||
|
||
## Reglas críticas
|
||
|
||
- **NUNCA hacer git reset --hard sin --aggressive** - puede perder cambios
|
||
- **SIEMPRE hacer backup del estado antes de recovery agresivo**
|
||
- **NUNCA eliminar branches no mergeadas automáticamente**
|
||
- **SIEMPRE sincronizar con remoto después de limpieza**
|
||
- **SIEMPRE verificar que quedó en estado válido**
|
||
- **LOGS completos** - reportar cada acción para auditoría
|
||
|
||
## Integración con orquestador
|
||
|
||
El orquestador Go debe:
|
||
|
||
1. **Capturar stderr** de comandos git
|
||
2. **Detectar patrones** de error conocidos
|
||
3. **Invocar recovery** automáticamente:
|
||
```go
|
||
if isGitError(err) {
|
||
log.Warn("Error git detectado, ejecutando recovery...")
|
||
if err := executeRecovery(); err != nil {
|
||
return fmt.Errorf("recovery falló: %w", err)
|
||
}
|
||
// Reintentar operación original
|
||
return retryOperation()
|
||
}
|
||
```
|
||
4. **Limitar reintentos** a 1 vez por operación
|
||
5. **Abortar si recovery falla**
|
||
|
||
## Ejemplos de uso automático
|
||
|
||
**Escenario 1: Error al crear worktree**
|
||
```
|
||
[ERROR] crear worktree: exit status 128
|
||
[INFO] Detectado error git, ejecutando recovery...
|
||
[INFO] ✓ Recovery completado
|
||
[INFO] Reintentando crear worktree...
|
||
[SUCCESS] ✓ Worktree creado exitosamente
|
||
```
|
||
|
||
**Escenario 2: Recovery falla**
|
||
```
|
||
[ERROR] crear worktree: exit status 128
|
||
[INFO] Detectado error git, ejecutando recovery...
|
||
[ERROR] Recovery falló: problemas persistentes
|
||
[ABORT] Abortando ejecución del grupo
|
||
```
|