Experiencias · Lessons Learned
Cuando falta la copia de seguridad y un script borra
Este informe de experiencia resume un incidente que se produjo durante la consolidación de un gran volumen de documentos financieros y fiscales. Está anonimizado, pero sigue exactamente la cadena de errores técnicos. El objetivo es evitar que los administradores de comparticiones SMB, espejos de Nextcloud y repositorios Git cometan los mismos errores.
Situación inicial
Un autónomo en el ámbito de la investigación y el trabajo freelance quería trasladar sus documentos financieros, acumulados a lo largo de los años, desde una compartición de origen (Samba/NAS) a una nueva estructura de destino organizada por años (carpetas FY2015 … FY2026 y FY_undatiert). El origen contenía copias fuertemente redundantes; como paso final se planeaba una deduplicación («ningún archivo duplicado»).
Lo que salió mal
Dos errores se combinaron para provocar una pérdida masiva de datos:
- No se verificó una copia de seguridad antes de comenzar. Solo después se montó una copia de seguridad externa de 8 TB (del 16.08.). Si se hubiera comprobado antes, el siguiente error apenas habría causado daño.
- Un script de deduplicación con una lista de rutas sin comillas. La lista de directorios a buscar se pasó sin comillas a
find. Dado que algunas rutas contenían espacios (p. ej.Freigabe/Banken/Konto 1234 5678/2024), la shell dividió esas rutas. Esto provocó que los directorios padre e hijo aparecieran múltiples veces como puntos de partida —findenumeró el mismo archivo varias veces.
El script de deduplicación comparaba hashes SHA-256 consecutivos y eliminaba la segunda entrada «idéntica». Dado que el mismo archivo, por la enumeración repetida, aparecía como «duplicado» de sí mismo, se eliminó — junto con todas las demás enumeraciones —. Se destruyeron archivos únicos, no copias redundantes.
(find $SCOPE)"] --> B["Shell trennt Pfade mit
Leerzeichen in Fragmente"] B --> C["Eltern- und Kindverzeichnisse
erscheinen mehrfach als Startpunkte"] C --> D["find listet dieselbe Datei
N-mal (N > 1)"] D --> E["Dedup: aufeinanderfolgende
Hash-Werte vergleichen"] E --> F["h == prev bei wiederholter
Listung derselben Datei"] F --> G["rm der 'Duplikate' =
Löschung einzigartiger Dateien"] G --> H["~6.400 einzigartige Dateien
vernichtet"] style G fill:#c0392b,color:#fff style H fill:#c0392b,color:#fff
El rescate
En cuanto se detectó el error, se detuvo de inmediato la operación en curso. No existía ningún repositorio Git local como fuente, pero había tres copias adicionales en el servidor o en medios externos:
- Espejo de sincronización de Nextcloud de la compartición de origen en el mismo servidor → recuperó ~5.300 archivos.
- Archivos ZIP supervivientes en la estructura de destino → ~70 archivos adicionales mediante una simple extracción.
- Copia de seguridad externa de 8 TB (del 16.08.) → ~600 archivos adicionales.
- Origen Git (Gitea) para un sub-repositorio afectado → se pudo reconstruir el historial completo mediante un clonado.
(~6.400)"] --> M["Nextcloud-Sync-Spiegel
der Quelle"] D --> Z["Überlebende ZIP-Archive"] D --> B["Externes 8-TB-Backup
(16.08.)"] D --> G["Git-Origin (Gitea)
für Teil-Repo"] M -->|"~5.300"| R["Wiederhergestellt"] Z -->|"~70"| R B -->|"~600"| R G -->|"History"| R R --> S["~98 % gerettet
(~6.300 von ~6.400)"] style S fill:#27ae60,color:#fff
En total se pudieron recuperar alrededor del 98 % de los archivos eliminados. El ~2 % restante correspondía a archivos que no existían en ninguna de las copias disponibles (entre otros, una exportación de bandeja de entrada y algunos documentos creados después de la copia de seguridad).
Por qué el concepto de GitCover ayuda aquí
La historia demuestra: quien apuesta por estructuras nativas de Git puede volver a trabajar rápidamente incluso tras errores involuntarios — y al mismo tiempo documenta conforme a GoBD.
Lessons Learned
- Verificar la copia de seguridad antes, no después. Idealmente 3-2-1: 3 copias, 2 medios, 1 offline. Una copia de seguridad que solo se monta después del daño no sirve de nada.
- Procesar siempre las rutas con delimitador null y comillas.
find … -print0 | xargs -0en lugar defind $LISTsin comillas. Los espacios en las rutas son el clásico asesino de scripts. - La deduplicación nunca debe borrar con enumeración repetida. Lo correcto es: conservar exactamente una copia por ruta única + contenido; si el mismo archivo se enumera varias veces, no debe eliminarse nada (decidir basado en inode o hash+ruta).
- Mantener varias fuentes de recuperación independientes. Espejos, archivos y control de versiones se complementan entre sí; ninguna por sí sola es suficiente.
- Proteger con commits los archivos del working tree no versionados. Si solo existen como untracked en el repositorio, basta un
git cleanpara destruirlos. Un commit los convierte en parte del historial.
Conclusión: Una sola llamada a variable sin comillas basta para destruir miles de archivos únicos. El rescate solo fue posible porque existían varias copias independientes. La estructura, el proceso y la disciplina no son temas de lujo — son la única protección que existe.
Parte de la sección Experiencias. Más artículos próximamente.