#!/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