SXXXXXXX_GitUtility/gitutility/logic/transformation_handler.py

146 lines
6.7 KiB
Python

# -*- coding: utf-8 -*-
"""
Handles the business logic for the repository transformation workflow.
This handler validates user input, confirms destructive actions, and orchestrates
the asynchronous execution of the transformation process, which includes
history rewriting, repository renaming, and updating the local Gitea remote.
"""
from typing import TYPE_CHECKING, Dict, Any, Optional
import os
from gitutility.async_tasks import async_workers
from gitutility.logging_setup import log_handler
from gitutility.commands.git_commands import GitCommandError
# Forward reference for type hinting to avoid circular import
if TYPE_CHECKING:
from ..app import GitSvnSyncApp
class TransformationHandler:
"""
Manages the logic for the repository transformation feature.
"""
def __init__(self, app: "GitSvnSyncApp"):
"""
Initializes the TransformationHandler.
Args:
app: The main application instance.
"""
self.app = app
self.main_frame = app.main_frame
self.git_commands = app.git_commands
self.config_manager = app.config_manager
log_handler.log_debug("TransformationHandler initialized.", func_name="__init__")
def start_transformation(self, target_details: Dict[str, str]) -> None:
"""
Validates inputs and starts the asynchronous repository transformation process.
Args:
target_details: A dictionary containing 'repo_name', 'author_name',
and 'author_email' for the target identity.
"""
func_name = "start_transformation"
log_handler.log_info(f"--- Action Triggered: Start Repository Transformation ---", func_name=func_name)
# --- 1. Validazione degli Input e dello Stato del Sistema ---
# Valida i dettagli forniti dall'utente
target_repo_name = target_details.get("repo_name")
target_author_name = target_details.get("author_name")
target_author_email = target_details.get("author_email")
if not all([target_repo_name, target_author_name, target_author_email]):
self.main_frame.show_error("Input Error", "All target identity fields are required.")
log_handler.log_error("Validation failed: Missing target identity details.", func_name=func_name)
return
# Valida il repository sorgente (quello del profilo corrente)
source_repo_path = self.app._get_and_validate_svn_path("Repository Transformation")
if not source_repo_path or not self.app._is_repo_ready(source_repo_path):
self.main_frame.show_error("Repository Error", "The current profile's repository is not valid or not ready.")
log_handler.log_error("Validation failed: Source repository not valid.", func_name=func_name)
return
source_repo_basename = os.path.basename(source_repo_path)
# If the target name equals the source, allow the user to proceed with
# an author-only transformation (no folder/profile rename) or cancel.
rename_repo = True
if source_repo_basename == target_repo_name:
# Ask the user whether to continue with author-only rewrite
proceed_msg = (
"The target repository name is the SAME as the source repository.\n\n"
"You can either:\n"
" - Cancel and change the target repository name, or\n"
" - Proceed with an AUTHOR-ONLY transformation (this will rewrite commit authors/emails but WILL NOT rename the repository folder nor create a new profile).\n\n"
"Do you want to proceed with AUTHOR-ONLY transformation?"
)
if not self.main_frame.ask_yes_no("Same Repository Name", proceed_msg):
self.main_frame.update_status_bar("Transformation cancelled.")
log_handler.log_info("User cancelled the transformation due to same repository name.", func_name=func_name)
return
rename_repo = False
# Controlla se il repository ha modifiche non committate
try:
if self.git_commands.git_status_has_changes(source_repo_path):
self.main_frame.show_warning(
"Action Blocked",
"The repository has uncommitted changes. Please commit or stash them before starting a transformation."
)
log_handler.log_warning("Validation failed: Repository is not clean.", func_name=func_name)
return
except GitCommandError as e:
self.main_frame.show_error("Git Error", f"Failed to check repository status:\n{e}")
log_handler.log_error(f"Validation failed: Could not get repo status. {e}", func_name=func_name)
return
# --- 2. Dialogo di Conferma Finale ---
confirmation_message = (
f"You are about to perform a PERMANENT and DESTRUCTIVE transformation.\n\n"
f"1. Source Repository: '{source_repo_basename}'\n"
f" at path: '{source_repo_path}'\n\n"
f"2. All commits will be rewritten with author:\n"
f" '{target_author_name} <{target_author_email}>'\n\n"
f"3. The local repository folder will be RENAMED to:\n"
f" '{target_repo_name}'\n\n"
f"4. A new profile '{target_repo_name}' will be created in this tool.\n\n"
f"This action CANNOT be undone. It is strongly advised to have a backup.\n\n"
f"Are you absolutely sure you want to proceed?"
)
if not self.main_frame.ask_yes_no("Confirm Destructive Transformation", confirmation_message):
self.main_frame.update_status_bar("Transformation cancelled.")
log_handler.log_info("User cancelled the transformation.", func_name=func_name)
return
# --- 3. Avvio del Worker Asincrono ---
log_handler.log_info("User confirmed. Starting asynchronous transformation worker.", func_name=func_name)
# Prepara gli argomenti per il worker
args = (
# Passeremo le istanze necessarie, non solo i valori, per dare flessibilità al worker
self.git_commands,
self.config_manager,
source_repo_path,
target_repo_name,
target_author_name,
target_author_email,
rename_repo,
)
self.app._start_async_operation(
worker_func=async_workers.run_repository_transformation_async, # Creeremo questo worker tra poco
args_tuple=args,
context_dict={
"context": "repository_transformation",
"status_msg": f"Transforming '{source_repo_basename}' to '{target_repo_name}'...",
},
)