291 lines
No EOL
11 KiB
Bash
Executable file
291 lines
No EOL
11 KiB
Bash
Executable file
#!/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 <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 <commit_id> [--file <archivo>] [--files <patrón>]
|
|
#==============================================================================
|
|
# Derechos de Autor 2025 Mauro Rosero P. <mauro@rosero.one>
|
|
#==============================================================================
|
|
# 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 <https://www.gnu.org/licenses/>.
|
|
|
|
# Definir variables básicas
|
|
BIN_HOME=${HOME}/devs
|
|
BIN_BASE=bin
|
|
|
|
# Mostrar ayuda
|
|
function mostrar_ayuda {
|
|
echo "Uso: $0 <commit_id> [opciones]"
|
|
echo ""
|
|
echo "Opciones:"
|
|
echo " --full Revierte todo el repositorio al commit especificado (por defecto)"
|
|
echo " --file <archivo> Revierte un archivo específico al estado del commit"
|
|
echo " --files <patrón> 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 |