diff --git a/README.md b/README.md index d595c25..46317fe 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ MRDevs Tools utiliza una estructura organizada que separa el código (en ~/devs) | Comando | Descripción | |---------|-------------| +| `bin/backlog.sh ` | Revierte el repositorio o archivos específicos al estado de un commit concreto | | `bin/ghadmin_install.sh` | Instala GitHub CLI (gh) con soporte multiplataforma | | `bin/gladmin_install.sh` | Instala GitLab CLI (glab) con múltiples métodos de instalación | | `bin/fjadmin_install.sh` | Instala Forgejo CLI (berg) para administración de Forgejo/Codeberg | @@ -140,6 +141,31 @@ El directorio `$HOME/devs` sirve como área principal de desarrollo donde se ges Si desea usar Docker en lugar de Podman, debe instalarlo manualmente antes de ejecutar `bootstrap.sh`. +### Gestión Avanzada de Repositorios Git + +```bash +# Revertir todo el repositorio a un commit específico +bin/backlog.sh a1b2c3d + +# Recuperar solo un archivo específico desde un commit +bin/backlog.sh a1b2c3d --file src/config.js + +# Recuperar múltiples archivos usando un patrón +bin/backlog.sh a1b2c3d --files "*.js" + +# Ver solo la lista de archivos modificados en un commit +bin/backlog.sh a1b2c3d --list + +# Ver la ayuda completa del comando +bin/backlog.sh --help +``` + +El script `backlog.sh` permite recuperar estados previos del repositorio con varias opciones de seguridad: +- Crea ramas de respaldo automáticamente antes de realizar cambios +- Ofrece guardar cambios no confirmados en stash +- Proporciona confirmaciones interactivas antes de acciones potencialmente destructivas +- Incluye instrucciones para deshacer los cambios si fuera necesario + ### Configuración de Herramientas de IA MRDevs Tools facilita el trabajo con herramientas de IA: diff --git a/bin/backlog.sh b/bin/backlog.sh new file mode 100755 index 0000000..0cbcecc --- /dev/null +++ b/bin/backlog.sh @@ -0,0 +1,291 @@ +#!/bin/bash +#Script : backlog.sh +#Apps : MRDEVS TOOLS +#Description : Revierte el repositorio o archivos específicos al estado de un commit +#Author : Cortana Rosero One +#Company Email : mauro@rosero.one +#Personal Email : cortana@rosero.one +#Created : 2025-04-01 10:00:00 +#Modified : 2025-04-01 11:30:00 +#Version : 1.3.1 +#Use Notes : ./backlog.sh [--file ] [--files ] +#============================================================================== +# Derechos de Autor 2025 Mauro Rosero P. +#============================================================================== +# Este programa es software libre: usted puede redistribuirlo y/o modificarlo +# bajo los términos de la Licencia Pública Affero General de GNU tal como +# lo publica la Free Software Foundation, ya sea la versión 3 de la licencia, +# o (a su elección) cualquier versión posterior. +# +# Este programa se distribuye con la esperanza de que sea útil, +# pero SIN NINGUNA GARANTÍA; sin siquiera la garantía implícita de +# COMERCIABILIDAD o IDONEIDAD PARA UN PROPÓSITO PARTICULAR. Consulte la +# Licencia Pública Affero General de GNU para obtener más detalles. +# +# Debería haber recibido una copia de la Licencia Pública Affero General +# junto con este programa. Si no la recibió, consulte . + +# Definir variables básicas +BIN_HOME=${HOME}/devs +BIN_BASE=bin + +# Mostrar ayuda +function mostrar_ayuda { + echo "Uso: $0 [opciones]" + echo "" + echo "Opciones:" + echo " --full Revierte todo el repositorio al commit especificado (por defecto)" + echo " --file Revierte un archivo específico al estado del commit" + echo " --files Revierte archivos que coincidan con un patrón" + echo " --list Lista los archivos modificados en el commit sin revertir nada" + echo " --help Muestra esta ayuda" + echo "" + echo "Ejemplos:" + echo " $0 abc123 # Revierte todo el repositorio al commit abc123" + echo " $0 abc123 --file src/main.js # Revierte solo src/main.js al estado del commit abc123" + echo " $0 abc123 --files \"*.js\" # Revierte todos los archivos .js al estado del commit abc123" + echo " $0 abc123 --list # Muestra los archivos que cambiaron en el commit abc123" + exit 0 +} + +# Verificar si hay suficientes parámetros +if [ $# -lt 1 ]; then + echo "Error: Se requiere un commit_id como parámetro." + mostrar_ayuda + exit 1 +fi + +# Inicializar variables +COMMIT_ID="$1" +shift +MODO="full" +ARCHIVO="" +PATRON="" + +# Procesar argumentos adicionales +while [ "$#" -gt 0 ]; do + case "$1" in + --help) + mostrar_ayuda + ;; + --full) + MODO="full" + ;; + --file) + MODO="file" + ARCHIVO="$2" + if [ -z "$ARCHIVO" ]; then + echo "Error: Falta el nombre del archivo después de --file" + exit 1 + fi + shift + ;; + --files) + MODO="files" + PATRON="$2" + if [ -z "$PATRON" ]; then + echo "Error: Falta el patrón después de --files" + exit 1 + fi + shift + ;; + --list) + MODO="list" + ;; + *) + echo "Error: Opción desconocida: $1" + mostrar_ayuda + exit 1 + ;; + esac + shift +done + +# Verificar si estamos en un repositorio git +if ! git rev-parse --is-inside-work-tree > /dev/null 2>&1; then + echo "Error: Este comando debe ejecutarse dentro de un repositorio git." + exit 1 +fi + +# Verificar si el commit_id existe +if ! git rev-parse --verify "$COMMIT_ID^{commit}" >/dev/null 2>&1; then + echo "Error: El commit_id '$COMMIT_ID' no existe en este repositorio." + echo "Buscando referencias a ese commit_id..." + + # Intentar encontrar commits parciales + POSIBLES_COMMITS=$(git log --all --pretty=format:"%h %s" | grep -i "$COMMIT_ID") + if [ -n "$POSIBLES_COMMITS" ]; then + echo "Posibles coincidencias encontradas:" + echo "$POSIBLES_COMMITS" + exit 1 + fi + + echo "No se encontraron referencias al commit proporcionado." + exit 1 +fi + +# Si el modo es "list", mostrar los archivos modificados y salir +if [ "$MODO" = "list" ]; then + echo "📋 Archivos modificados en el commit $COMMIT_ID:" + git show --name-status "$COMMIT_ID" | grep -E "^[AMD]" | sort + exit 0 +fi + +# Verificar si hay cambios sin confirmar +if ! git diff --quiet || ! git diff --cached --quiet; then + echo "⚠️ ADVERTENCIA: Hay cambios sin confirmar en tu repositorio." + if [ "$MODO" = "full" ]; then + echo "Estos cambios se perderán si continúas con la reversión completa." + else + echo "Estos cambios podrían causar conflictos con los archivos que intentas recuperar." + fi + read -p "¿Deseas guardar estos cambios en un stash? (s/n): " respuesta + + if [[ "$respuesta" == "s" || "$respuesta" == "S" ]]; then + STASH_NAME="stash_before_reset_to_${COMMIT_ID}_$(date +%Y%m%d_%H%M%S)" + git stash push -m "$STASH_NAME" + echo "Cambios guardados en stash: $STASH_NAME" + fi +fi + +# Crear una rama de respaldo antes de hacer cualquier cambio (si es reversión completa) +if [ "$MODO" = "full" ]; then + CURRENT_BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse HEAD) + BACKUP_BRANCH="backup_${CURRENT_BRANCH}_before_reset_$(date +%Y%m%d_%H%M%S)" + + echo "Creando rama de respaldo '$BACKUP_BRANCH'..." + git branch "$BACKUP_BRANCH" + + # Obtener información del commit para el resumen + COMMIT_INFO=$(git show --quiet --format="%h - %s (%an, %ad)" --date=format:"%Y-%m-%d %H:%M:%S" "$COMMIT_ID") + echo "⚠️ ADVERTENCIA: Estás a punto de revertir TODO el repositorio al commit:" + echo "$COMMIT_INFO" + echo "Todos los cambios posteriores a este commit se perderán." + echo "Se ha creado una rama de respaldo: $BACKUP_BRANCH" + + read -p "¿Estás seguro de que quieres continuar? (s/n): " confirmacion + if [[ "$confirmacion" != "s" && "$confirmacion" != "S" ]]; then + echo "Operación cancelada por el usuario." + exit 0 + fi + + echo "Revirtiendo todo el repositorio al commit $COMMIT_ID..." + + # Realizar la reversión usando reset (se perderán todos los cambios locales) + git reset --hard "$COMMIT_ID" + + if [ $? -eq 0 ]; then + echo "✅ Repositorio revertido exitosamente al commit: $COMMIT_ID" + echo "Información del commit:" + git show --quiet --format="%h - %s%n%nAutor: %an <%ae>%nFecha: %ad%n%n%b" --date=format:"%Y-%m-%d %H:%M:%S" "$COMMIT_ID" + echo "" + echo "Para recuperar los cambios anteriores, puedes usar la rama de respaldo:" + echo " git checkout $BACKUP_BRANCH" + echo "" + echo "Si necesitas empujar este cambio a un repositorio remoto, usa:" + echo " git push --force origen $CURRENT_BRANCH" + echo "" + echo "⚠️ ADVERTENCIA: El uso de git push --force puede causar problemas en repositorios compartidos." + else + echo "❌ Error al revertir al commit $COMMIT_ID" + echo "Puedes intentar manualmente con:" + echo " git reset --hard $COMMIT_ID" + fi +elif [ "$MODO" = "file" ]; then + # Verificar si el archivo existía en el commit + if ! git ls-tree -r "$COMMIT_ID" --name-only | grep -q "^$ARCHIVO$"; then + echo "⚠️ El archivo '$ARCHIVO' no existía en el commit $COMMIT_ID o la ruta es incorrecta." + echo "Archivos disponibles en ese commit que coinciden parcialmente:" + git ls-tree -r "$COMMIT_ID" --name-only | grep -i "$(basename "$ARCHIVO")" + exit 1 + fi + + echo "Recuperando el archivo '$ARCHIVO' desde el commit $COMMIT_ID..." + + # Crear directorio temporal + TEMP_DIR=$(mktemp -d) + + # Extraer el archivo del commit + git show "$COMMIT_ID:$ARCHIVO" > "$TEMP_DIR/$(basename "$ARCHIVO")" 2>/dev/null + + if [ $? -ne 0 ]; then + echo "❌ Error al recuperar el archivo del commit." + rm -rf "$TEMP_DIR" + exit 1 + fi + + # Crear directorio para el archivo si no existe + ARCHIVO_DIR=$(dirname "$ARCHIVO") + if [ "$ARCHIVO_DIR" != "." ] && [ ! -d "$ARCHIVO_DIR" ]; then + mkdir -p "$ARCHIVO_DIR" + fi + + # Copiar el archivo de vuelta a su ubicación + cp "$TEMP_DIR/$(basename "$ARCHIVO")" "$ARCHIVO" + + # Limpiar + rm -rf "$TEMP_DIR" + + echo "✅ Archivo '$ARCHIVO' recuperado exitosamente desde el commit $COMMIT_ID." + echo "Para confirmar estos cambios, usa:" + echo " git add \"$ARCHIVO\"" + echo " git commit -m \"Recuperado $ARCHIVO desde el commit $COMMIT_ID\"" +elif [ "$MODO" = "files" ]; then + # Obtener la lista de archivos que coinciden con el patrón en el commit + ARCHIVOS_COINCIDENTES=$(git ls-tree -r "$COMMIT_ID" --name-only | grep -E "$PATRON" || echo "") + + if [ -z "$ARCHIVOS_COINCIDENTES" ]; then + echo "❌ No se encontraron archivos que coincidan con el patrón '$PATRON' en el commit $COMMIT_ID." + exit 1 + fi + + echo "Se encontraron $(echo "$ARCHIVOS_COINCIDENTES" | wc -l) archivos que coinciden con el patrón '$PATRON':" + echo "$ARCHIVOS_COINCIDENTES" | sed 's/^/ - /' + + read -p "¿Quieres recuperar todos estos archivos? (s/n): " confirmacion + if [[ "$confirmacion" != "s" && "$confirmacion" != "S" ]]; then + echo "Operación cancelada por el usuario." + exit 0 + fi + + # Crear directorio temporal + TEMP_DIR=$(mktemp -d) + + # Recuperar cada archivo + CONTADOR_EXITO=0 + CONTADOR_ERROR=0 + + echo "$ARCHIVOS_COINCIDENTES" | while read -r archivo; do + # Crear la estructura de directorios necesaria + mkdir -p "$TEMP_DIR/$(dirname "$archivo")" + + # Extraer el archivo del commit + git show "$COMMIT_ID:$archivo" > "$TEMP_DIR/$archivo" 2>/dev/null + + if [ $? -eq 0 ]; then + # Crear directorio para el archivo si no existe + ARCHIVO_DIR=$(dirname "$archivo") + if [ "$ARCHIVO_DIR" != "." ] && [ ! -d "$ARCHIVO_DIR" ]; then + mkdir -p "$ARCHIVO_DIR" + fi + + # Copiar el archivo de vuelta a su ubicación + cp "$TEMP_DIR/$archivo" "$archivo" + echo "✅ Recuperado: $archivo" + CONTADOR_EXITO=$((CONTADOR_EXITO + 1)) + else + echo "❌ Error al recuperar: $archivo" + CONTADOR_ERROR=$((CONTADOR_ERROR + 1)) + fi + done + + # Limpiar + rm -rf "$TEMP_DIR" + + echo "Recuperación finalizada: $CONTADOR_EXITO archivos recuperados, $CONTADOR_ERROR errores." + echo "Para confirmar estos cambios, usa:" + echo " git add -A" + echo " git commit -m \"Recuperados archivos $PATRON desde el commit $COMMIT_ID\"" +fi + +exit 0 \ No newline at end of file