1762 lines
68 KiB
Python
1762 lines
68 KiB
Python
# --- FILE: async_workers.py ---
|
|
|
|
import os
|
|
import queue
|
|
import logging # Usato solo per i livelli, non per loggare direttamente
|
|
import datetime # Necessario per alcuni messaggi
|
|
|
|
# Importa i moduli necessari per la logica interna e le dipendenze
|
|
import log_handler
|
|
from git_commands import GitCommands, GitCommandError
|
|
from action_handler import ActionHandler
|
|
from backup_handler import BackupHandler
|
|
from remote_actions import RemoteActionHandler
|
|
|
|
# Nota: Queste sono funzioni standalone, non metodi di una classe.
|
|
|
|
# === Worker per Refresh GUI ===
|
|
|
|
|
|
def run_refresh_tags_async(
|
|
git_commands: GitCommands, repo_path: str, results_queue: queue.Queue
|
|
):
|
|
"""Worker to fetch tag list asynchronously."""
|
|
func_name = "run_refresh_tags_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Refresh Tags for '{repo_path}'", func_name=func_name
|
|
)
|
|
try:
|
|
# Chiama il metodo corretto in GitCommands
|
|
tags_data = git_commands.list_tags(repo_path)
|
|
count = len(tags_data)
|
|
message = f"Tags refreshed ({count} found)."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
# Metti il risultato nella coda
|
|
results_queue.put(
|
|
{"status": "success", "result": tags_data, "message": message}
|
|
)
|
|
except (GitCommandError, Exception) as e:
|
|
log_handler.log_exception(
|
|
f"[Worker] EXCEPTION refreshing tags: {e}", func_name=func_name
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"result": [
|
|
("(Error)", "")
|
|
], # Formato atteso dalla GUI in caso di errore
|
|
"message": f"Error refreshing tags: {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug("[Worker] Finished: Refresh Tags", func_name=func_name)
|
|
|
|
|
|
def run_refresh_branches_async(
|
|
git_commands: GitCommands, repo_path: str, results_queue: queue.Queue
|
|
):
|
|
"""Worker to fetch branch list asynchronously."""
|
|
func_name = "run_refresh_branches_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Refresh Branches for '{repo_path}'", func_name=func_name
|
|
)
|
|
try:
|
|
# Chiama il metodo corretto in GitCommands
|
|
branches, current = git_commands.list_branches(repo_path)
|
|
count = len(branches)
|
|
curr_disp = current if current else "None (Detached?)"
|
|
message = f"Branches refreshed ({count} found). Current: {curr_disp}"
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
# Metti il risultato (una tupla) nella coda
|
|
results_queue.put(
|
|
{"status": "success", "result": (branches, current), "message": message}
|
|
)
|
|
except (GitCommandError, Exception) as e:
|
|
log_handler.log_exception(
|
|
f"[Worker] EXCEPTION refreshing branches: {e}", func_name=func_name
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"result": (
|
|
["(Error)"],
|
|
None,
|
|
), # Formato atteso dalla GUI in caso di errore
|
|
"message": f"Error refreshing branches: {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug(
|
|
"[Worker] Finished: Refresh Branches", func_name=func_name
|
|
)
|
|
|
|
|
|
def run_refresh_history_async(
|
|
git_commands: GitCommands,
|
|
repo_path: str,
|
|
branch_filter: str | None,
|
|
log_scope: str, # Descrizione per i log (es. 'All History' o "'branch_name'")
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""Worker to fetch commit history asynchronously."""
|
|
func_name = "run_refresh_history_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Refresh History ({log_scope}) for '{repo_path}'",
|
|
func_name=func_name,
|
|
)
|
|
try:
|
|
# Chiama il metodo corretto in GitCommands
|
|
log_data = git_commands.get_commit_log(
|
|
repo_path, max_count=200, branch=branch_filter
|
|
)
|
|
count = len(log_data)
|
|
message = f"History refreshed ({count} entries for {log_scope})."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
# Metti il risultato (lista di stringhe) nella coda
|
|
results_queue.put({"status": "success", "result": log_data, "message": message})
|
|
except (GitCommandError, Exception) as e:
|
|
log_handler.log_exception(
|
|
f"[Worker] EXCEPTION refreshing history: {e}", func_name=func_name
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"result": ["(Error retrieving history)"], # Formato atteso dalla GUI
|
|
"message": f"Error refreshing history: {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug(
|
|
f"[Worker] Finished: Refresh History ({log_scope})", func_name=func_name
|
|
)
|
|
|
|
|
|
def run_refresh_changes_async(
|
|
git_commands: GitCommands, repo_path: str, results_queue: queue.Queue
|
|
):
|
|
"""Worker to get status of changed files asynchronously."""
|
|
func_name = "run_refresh_changes_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Refresh Changes for '{repo_path}'", func_name=func_name
|
|
)
|
|
try:
|
|
# Chiama il metodo corretto in GitCommands
|
|
files_status_list = git_commands.get_status_short(repo_path)
|
|
count = len(files_status_list)
|
|
log_handler.log_info(f"[Worker] Found {count} changes.", func_name=func_name)
|
|
message = (
|
|
f"Ready ({count} changes detected)."
|
|
if count > 0
|
|
else "Ready (No changes detected)."
|
|
)
|
|
# Logga prima di mettere in coda per debug
|
|
log_handler.log_debug(
|
|
f"[Worker] Preparing result for queue. Status: success, Count: {count}",
|
|
func_name=func_name,
|
|
)
|
|
results_queue.put(
|
|
{"status": "success", "result": files_status_list, "message": message}
|
|
)
|
|
log_handler.log_debug(
|
|
"[Worker] Successfully PUT result in queue.", func_name=func_name
|
|
)
|
|
except (GitCommandError, ValueError, Exception) as e:
|
|
log_handler.log_exception(
|
|
f"[Worker] EXCEPTION refreshing changes: {e}", func_name=func_name
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"result": ["(Error refreshing changes)"], # Formato atteso
|
|
"message": f"Error refreshing changes: {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug("[Worker] Finished: Refresh Changes", func_name=func_name)
|
|
|
|
|
|
# === Worker per Azioni Principali ===
|
|
|
|
|
|
def run_prepare_async(
|
|
action_handler: ActionHandler, repo_path: str, results_queue: queue.Queue
|
|
):
|
|
"""Worker to prepare repository asynchronously."""
|
|
func_name = "run_prepare_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Prepare Repo for '{repo_path}'", func_name=func_name
|
|
)
|
|
try:
|
|
# Chiama il metodo corretto in ActionHandler
|
|
success = action_handler.execute_prepare_repo(repo_path)
|
|
message = "Repository prepared successfully."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put({"status": "success", "result": success, "message": message})
|
|
except ValueError as e:
|
|
# Gestione specifica per "already prepared"
|
|
if "already prepared" in str(e).lower():
|
|
log_handler.log_warning(f"[Worker] Warning: {e}", func_name=func_name)
|
|
results_queue.put(
|
|
{
|
|
"status": "warning",
|
|
"result": True,
|
|
"message": str(e),
|
|
} # Segnala come warning
|
|
)
|
|
else:
|
|
# Rilancia altri ValueError
|
|
log_handler.log_exception(
|
|
f"[Worker] VALUE ERROR preparing repo: {e}", func_name=func_name
|
|
)
|
|
results_queue.put(
|
|
{"status": "error", "exception": e, "message": f"Error preparing: {e}"}
|
|
)
|
|
except (GitCommandError, IOError, Exception) as e:
|
|
log_handler.log_exception(
|
|
f"[Worker] EXCEPTION preparing repo: {e}", func_name=func_name
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Error preparing repository: {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug("[Worker] Finished: Prepare Repo", func_name=func_name)
|
|
|
|
|
|
def run_create_bundle_async(
|
|
action_handler: ActionHandler,
|
|
repo_path: str,
|
|
bundle_full_path: str,
|
|
profile_name: str,
|
|
autobackup_enabled: bool,
|
|
backup_base_dir: str,
|
|
autocommit_enabled: bool,
|
|
commit_message: str,
|
|
excluded_extensions: set,
|
|
excluded_dirs: set,
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""Worker to create Git bundle asynchronously."""
|
|
func_name = "run_create_bundle_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Create Bundle '{os.path.basename(bundle_full_path)}' from '{repo_path}'",
|
|
func_name=func_name,
|
|
)
|
|
try:
|
|
# Chiama il metodo corretto in ActionHandler
|
|
result_path = action_handler.execute_create_bundle(
|
|
repo_path=repo_path,
|
|
bundle_full_path=bundle_full_path,
|
|
profile_name=profile_name,
|
|
autobackup_enabled=autobackup_enabled,
|
|
backup_base_dir=backup_base_dir,
|
|
autocommit_enabled=autocommit_enabled,
|
|
commit_message=commit_message,
|
|
excluded_extensions=excluded_extensions,
|
|
excluded_dirs=excluded_dirs,
|
|
)
|
|
# Determina messaggio successo
|
|
if result_path:
|
|
message = f"Bundle created: {os.path.basename(result_path)}"
|
|
else:
|
|
# Potrebbe non essere stato creato per repo vuoto o nessun cambiamento + no commit
|
|
message = "Bundle creation finished (no file generated - repo empty or no changes?)."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put(
|
|
{
|
|
"status": "success",
|
|
"result": result_path, # Può essere None
|
|
"message": message,
|
|
"committed": autocommit_enabled, # Indica se è stato TENTATO un autocommit
|
|
}
|
|
)
|
|
except (IOError, GitCommandError, ValueError, Exception) as e:
|
|
log_handler.log_exception(
|
|
f"[Worker] EXCEPTION creating bundle: {e}", func_name=func_name
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Error creating bundle: {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug("[Worker] Finished: Create Bundle", func_name=func_name)
|
|
|
|
|
|
def run_fetch_bundle_async(
|
|
action_handler: ActionHandler,
|
|
target_repo_path_str: str, # Può essere dir non esistente
|
|
bundle_full_path: str,
|
|
profile_name: str,
|
|
autobackup_enabled: bool,
|
|
backup_base_dir: str,
|
|
excluded_extensions: set,
|
|
excluded_dirs: set,
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""Worker to fetch/clone from Git bundle asynchronously."""
|
|
func_name = "run_fetch_bundle_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Fetch Bundle '{os.path.basename(bundle_full_path)}' into '{target_repo_path_str}'",
|
|
func_name=func_name,
|
|
)
|
|
try:
|
|
# Chiama il metodo corretto in ActionHandler
|
|
success = action_handler.execute_fetch_bundle(
|
|
target_repo_path_str=target_repo_path_str,
|
|
bundle_full_path=bundle_full_path,
|
|
profile_name=profile_name,
|
|
autobackup_enabled=autobackup_enabled,
|
|
backup_base_dir=backup_base_dir,
|
|
excluded_extensions=excluded_extensions,
|
|
excluded_dirs=excluded_dirs,
|
|
)
|
|
message = "Fetch/Clone from bundle completed successfully."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put({"status": "success", "result": success, "message": message})
|
|
except (FileNotFoundError, IOError, GitCommandError, ValueError, Exception) as e:
|
|
log_handler.log_exception(
|
|
f"[Worker] EXCEPTION fetching bundle: {e}", func_name=func_name
|
|
)
|
|
# Controlla se è un errore di conflitto merge
|
|
is_conflict = False
|
|
if isinstance(e, GitCommandError) and "merge conflict" in str(e).lower():
|
|
is_conflict = True
|
|
log_handler.log_error(
|
|
"[Worker] Merge conflict detected during fetch.", func_name=func_name
|
|
)
|
|
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Error fetching from bundle: {type(e).__name__}",
|
|
"conflict": is_conflict, # Flag per gestione specifica errore
|
|
"repo_path": target_repo_path_str, # Passa path per messaggio conflitto
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug("[Worker] Finished: Fetch Bundle", func_name=func_name)
|
|
|
|
|
|
def run_manual_backup_async(
|
|
backup_handler: BackupHandler,
|
|
repo_path: str,
|
|
backup_base_dir: str,
|
|
profile_name: str,
|
|
excluded_extensions: set,
|
|
excluded_dirs: set,
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""Worker to create manual backup asynchronously."""
|
|
func_name = "run_manual_backup_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Manual Backup for '{repo_path}' (Profile: {profile_name})",
|
|
func_name=func_name,
|
|
)
|
|
try:
|
|
# Chiama il metodo corretto in BackupHandler
|
|
result_path = backup_handler.create_zip_backup(
|
|
source_repo_path=repo_path,
|
|
backup_base_dir=backup_base_dir,
|
|
profile_name=profile_name,
|
|
excluded_extensions=excluded_extensions,
|
|
excluded_dirs_base=excluded_dirs,
|
|
)
|
|
# Messaggio successo
|
|
ts = datetime.datetime.now().strftime("%H:%M:%S")
|
|
if result_path:
|
|
message = f"Manual backup created: {os.path.basename(result_path)} ({ts})."
|
|
else:
|
|
message = (
|
|
f"Manual backup finished (no file generated - empty/excluded?) ({ts})."
|
|
)
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put(
|
|
{"status": "success", "result": result_path, "message": message}
|
|
)
|
|
except (IOError, ValueError, PermissionError, Exception) as e:
|
|
log_handler.log_exception(
|
|
f"[Worker] EXCEPTION creating manual backup: {e}", func_name=func_name
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Error creating backup: {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug("[Worker] Finished: Manual Backup", func_name=func_name)
|
|
|
|
|
|
def run_commit_async(
|
|
action_handler: ActionHandler,
|
|
repo_path: str,
|
|
commit_message: str,
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""Worker to perform manual commit asynchronously."""
|
|
func_name = "run_commit_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Commit for '{repo_path}'", func_name=func_name
|
|
)
|
|
try:
|
|
# Chiama il metodo corretto in ActionHandler
|
|
committed = action_handler.execute_manual_commit(repo_path, commit_message)
|
|
# Messaggio basato sull'esito
|
|
if committed:
|
|
message = "Commit successful."
|
|
else:
|
|
message = "Commit finished (no changes detected)."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put(
|
|
{
|
|
"status": "success",
|
|
"result": committed,
|
|
"message": message,
|
|
"committed": committed, # Flag esplicito se commit avvenuto
|
|
}
|
|
)
|
|
except (GitCommandError, ValueError, Exception) as e:
|
|
log_handler.log_exception(
|
|
f"[Worker] EXCEPTION committing: {e}", func_name=func_name
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Error committing changes: {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug("[Worker] Finished: Commit", func_name=func_name)
|
|
|
|
|
|
def run_untrack_async(
|
|
action_handler: ActionHandler, repo_path: str, results_queue: queue.Queue
|
|
):
|
|
"""Worker to untrack files based on .gitignore asynchronously."""
|
|
func_name = "run_untrack_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Untrack Files Check for '{repo_path}'", func_name=func_name
|
|
)
|
|
try:
|
|
# Chiama il metodo corretto in ActionHandler
|
|
committed = action_handler.execute_untrack_files_from_gitignore(repo_path)
|
|
# Messaggio basato sull'esito
|
|
if committed:
|
|
message = "Untracking complete: Automatic commit created."
|
|
else:
|
|
message = "Untrack check complete (no tracked files matched .gitignore)."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put(
|
|
{
|
|
"status": "success",
|
|
"result": committed,
|
|
"message": message,
|
|
"committed": committed, # Flag se commit avvenuto
|
|
}
|
|
)
|
|
except (GitCommandError, ValueError, Exception) as e:
|
|
log_handler.log_exception(
|
|
f"[Worker] EXCEPTION untracking files: {e}", func_name=func_name
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Error during untracking operation: {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug(
|
|
"[Worker] Finished: Untrack Files Check", func_name=func_name
|
|
)
|
|
|
|
|
|
def run_add_file_async(
|
|
git_commands: GitCommands,
|
|
repo_path: str,
|
|
relative_path: str,
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""Worker to add a file to staging asynchronously."""
|
|
func_name = "run_add_file_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Add File '{relative_path}' in '{repo_path}'",
|
|
func_name=func_name,
|
|
)
|
|
try:
|
|
# Chiama il metodo corretto in GitCommands
|
|
success = git_commands.add_file(repo_path, relative_path)
|
|
message = f"File '{os.path.basename(relative_path)}' added to staging area."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put({"status": "success", "result": success, "message": message})
|
|
except (GitCommandError, ValueError, Exception) as e:
|
|
log_handler.log_exception(
|
|
f"[Worker] EXCEPTION adding file: {e}", func_name=func_name
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Error adding file '{os.path.basename(relative_path)}': {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug(
|
|
f"[Worker] Finished: Add File '{relative_path}'", func_name=func_name
|
|
)
|
|
|
|
|
|
def run_create_tag_async(
|
|
action_handler: ActionHandler,
|
|
repo_path: str,
|
|
tag_name: str,
|
|
tag_message: str,
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""Worker to create an annotated tag asynchronously."""
|
|
func_name = "run_create_tag_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Create Tag '{tag_name}' in '{repo_path}'",
|
|
func_name=func_name,
|
|
)
|
|
try:
|
|
# Chiama il metodo corretto in ActionHandler (passa None per arg 'ignored')
|
|
success = action_handler.execute_create_tag(
|
|
repo_path, None, tag_name, tag_message
|
|
)
|
|
message = f"Tag '{tag_name}' created successfully."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put(
|
|
{
|
|
"status": "success",
|
|
"result": success,
|
|
"message": message,
|
|
"committed": True, # Tag annotato implica commit
|
|
}
|
|
)
|
|
except (GitCommandError, ValueError, Exception) as e:
|
|
log_handler.log_exception(
|
|
f"[Worker] EXCEPTION creating tag: {e}", func_name=func_name
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Error creating tag '{tag_name}': {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug(
|
|
f"[Worker] Finished: Create Tag '{tag_name}'", func_name=func_name
|
|
)
|
|
|
|
|
|
def run_checkout_tag_async(
|
|
action_handler: ActionHandler,
|
|
repo_path: str,
|
|
tag_name: str,
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""Worker to checkout a tag asynchronously."""
|
|
func_name = "run_checkout_tag_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Checkout Tag '{tag_name}' in '{repo_path}'",
|
|
func_name=func_name,
|
|
)
|
|
try:
|
|
# Chiama il metodo corretto in ActionHandler
|
|
success = action_handler.execute_checkout_tag(repo_path, tag_name)
|
|
message = f"Checked out tag '{tag_name}' (Detached HEAD)."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put({"status": "success", "result": success, "message": message})
|
|
except (ValueError, GitCommandError, Exception) as e:
|
|
# Cattura ValueError specificamente per 'uncommitted changes'
|
|
log_handler.log_exception(
|
|
f"[Worker] EXCEPTION checking out tag: {e}", func_name=func_name
|
|
)
|
|
if isinstance(e, ValueError) and "Uncommitted changes" in str(e):
|
|
msg = f"Checkout failed: Uncommitted changes exist."
|
|
else:
|
|
msg = f"Error checking out tag '{tag_name}': {type(e).__name__}"
|
|
results_queue.put({"status": "error", "exception": e, "message": msg})
|
|
finally:
|
|
log_handler.log_debug(
|
|
f"[Worker] Finished: Checkout Tag '{tag_name}'", func_name=func_name
|
|
)
|
|
|
|
|
|
def run_create_branch_async(
|
|
action_handler: ActionHandler,
|
|
repo_path: str,
|
|
branch_name: str,
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""Worker to create a branch asynchronously."""
|
|
func_name = "run_create_branch_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Create Branch '{branch_name}' in '{repo_path}'",
|
|
func_name=func_name,
|
|
)
|
|
try:
|
|
# Chiama il metodo corretto in ActionHandler
|
|
success = action_handler.execute_create_branch(repo_path, branch_name)
|
|
message = f"Branch '{branch_name}' created successfully."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put({"status": "success", "result": success, "message": message})
|
|
except (GitCommandError, ValueError, Exception) as e:
|
|
log_handler.log_exception(
|
|
f"[Worker] EXCEPTION creating branch: {e}", func_name=func_name
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Error creating branch '{branch_name}': {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug(
|
|
f"[Worker] Finished: Create Branch '{branch_name}'", func_name=func_name
|
|
)
|
|
|
|
|
|
def run_checkout_branch_async(
|
|
action_handler: ActionHandler,
|
|
repo_path: str,
|
|
branch_name: str,
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""Worker to checkout a branch asynchronously."""
|
|
func_name = "run_checkout_branch_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Checkout Branch '{branch_name}' in '{repo_path}'",
|
|
func_name=func_name,
|
|
)
|
|
try:
|
|
# Chiama il metodo corretto in ActionHandler
|
|
success = action_handler.execute_switch_branch(repo_path, branch_name)
|
|
message = f"Switched to branch '{branch_name}'."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put({"status": "success", "result": success, "message": message})
|
|
except (ValueError, GitCommandError, Exception) as e:
|
|
# Cattura ValueError per 'uncommitted changes'
|
|
log_handler.log_exception(
|
|
f"[Worker] EXCEPTION checking out branch: {e}", func_name=func_name
|
|
)
|
|
if isinstance(e, ValueError) and "Uncommitted changes" in str(e):
|
|
msg = f"Checkout failed: Uncommitted changes exist."
|
|
else:
|
|
msg = f"Error checking out branch '{branch_name}': {type(e).__name__}"
|
|
results_queue.put({"status": "error", "exception": e, "message": msg})
|
|
finally:
|
|
log_handler.log_debug(
|
|
f"[Worker] Finished: Checkout Branch '{branch_name}'", func_name=func_name
|
|
)
|
|
|
|
|
|
# === Worker per Azioni Remote (Nuove) ===
|
|
|
|
|
|
def run_apply_remote_config_async(
|
|
remote_action_handler: RemoteActionHandler,
|
|
repo_path: str,
|
|
remote_name: str,
|
|
remote_url: str,
|
|
results_queue: queue.Queue,
|
|
):
|
|
# (Implementazione precedente invariata)
|
|
func_name = "run_apply_remote_config_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Apply Remote Config for '{remote_name}' in '{repo_path}'",
|
|
func_name=func_name,
|
|
)
|
|
try:
|
|
success = remote_action_handler.apply_remote_config(
|
|
repo_path, remote_name, remote_url
|
|
)
|
|
message = f"Remote '{remote_name}' configuration applied successfully."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put({"status": "success", "result": success, "message": message})
|
|
except (GitCommandError, ValueError) as e:
|
|
log_handler.log_error(
|
|
f"[Worker] EXCEPTION applying remote config: {e}", func_name=func_name
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Error applying remote config: {e}",
|
|
}
|
|
)
|
|
except Exception as e:
|
|
log_handler.log_exception(
|
|
f"[Worker] UNEXPECTED EXCEPTION applying remote config: {e}",
|
|
func_name=func_name,
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Unexpected error applying remote config: {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug(
|
|
f"[Worker] Finished: Apply Remote Config for '{remote_name}'",
|
|
func_name=func_name,
|
|
)
|
|
|
|
|
|
def run_check_connection_async(
|
|
git_commands: GitCommands,
|
|
repo_path: str,
|
|
remote_name: str,
|
|
results_queue: queue.Queue
|
|
):
|
|
"""
|
|
Worker to check remote connection and potential auth issues using 'git ls-remote'.
|
|
Does NOT prompt for credentials.
|
|
"""
|
|
func_name = "run_check_connection_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Check Connection/Auth for '{remote_name}' in '{repo_path}'",
|
|
func_name=func_name
|
|
)
|
|
try:
|
|
# Esegui ls-remote catturando output, senza check=True
|
|
result = git_commands.git_ls_remote(repo_path, remote_name)
|
|
|
|
# Analizza il risultato
|
|
if result.returncode == 0:
|
|
# Successo: connessione e autenticazione (se necessaria) OK
|
|
message = f"Connection to remote '{remote_name}' successful."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put({
|
|
"status": "success",
|
|
"result": "connected",
|
|
"message": message
|
|
})
|
|
elif result.returncode == 2:
|
|
# RC=2: Connesso con successo, ma il remote è vuoto (no refs) o "unborn"
|
|
message = f"Connected to remote '{remote_name}' (Note: Repository might be empty or unborn)."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put({
|
|
"status": "success",
|
|
"result": "connected_empty",
|
|
"message": message
|
|
})
|
|
else:
|
|
# Errore: analizza stderr per capire la causa
|
|
stderr_lower = result.stderr.lower() if result.stderr else ""
|
|
log_handler.log_warning(
|
|
f"[Worker] ls-remote failed (RC={result.returncode}). Stderr: {stderr_lower}",
|
|
func_name=func_name
|
|
)
|
|
|
|
# Controlla errori comuni di autenticazione/permessi
|
|
auth_errors = [
|
|
"authentication failed",
|
|
"permission denied",
|
|
"could not read username",
|
|
"fatal: could not read password",
|
|
]
|
|
# ---<<< MODIFICA: Aggiorna connection_errors >>>---
|
|
connection_errors = [
|
|
"repository not found",
|
|
"could not resolve host",
|
|
"name or service not known",
|
|
"network is unreachable",
|
|
# Aggiunti basati sui log e casi comuni
|
|
"failed to connect", # <<< AGGIUNTO
|
|
"unable to access", # <<< AGGIUNTO
|
|
"could not connect", # <<< AGGIUNTO (più specifico)
|
|
"connection timed out", # <<< AGGIUNTO (altro errore comune)
|
|
]
|
|
# ---<<< FINE MODIFICA >>>---
|
|
|
|
if any(err in stderr_lower for err in auth_errors):
|
|
message = f"Authentication required or failed for remote '{remote_name}'."
|
|
log_handler.log_warning(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put({
|
|
"status": "auth_required",
|
|
"result": "authentication needed",
|
|
"message": message,
|
|
"exception": GitCommandError(message, stderr=result.stderr)
|
|
})
|
|
elif any(err in stderr_lower for err in connection_errors): # Ora dovrebbe matchare l'errore dei log
|
|
message = f"Failed to connect to remote '{remote_name}': Repository or host not found/reachable."
|
|
log_handler.log_error(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put({
|
|
"status": "error", # Manteniamo 'error' come status principale
|
|
"result": "connection_failed", # Ma usiamo un 'result' specifico
|
|
"message": message,
|
|
"exception": GitCommandError(message, stderr=result.stderr)
|
|
})
|
|
else:
|
|
# Errore generico di Git (ora meno probabile per errori di connessione)
|
|
message = f"Failed to check remote '{remote_name}'. Check logs for details. (RC={result.returncode})" # Aggiunto RC al messaggio
|
|
log_handler.log_error(f"[Worker] Unknown error checking remote. Stderr: {result.stderr}", func_name=func_name)
|
|
results_queue.put({
|
|
"status": "error",
|
|
"result": "unknown_error",
|
|
"message": message,
|
|
"exception": GitCommandError(message, stderr=result.stderr)
|
|
})
|
|
|
|
except Exception as e:
|
|
# Errore imprevisto nell'esecuzione del worker stesso
|
|
log_handler.log_exception(f"[Worker] UNEXPECTED EXCEPTION checking connection: {e}", func_name=func_name)
|
|
results_queue.put({
|
|
"status": "error",
|
|
"exception": e,
|
|
"result": "worker_exception",
|
|
"message": f"Unexpected error checking connection: {type(e).__name__}"
|
|
})
|
|
finally:
|
|
log_handler.log_debug(f"[Worker] Finished: Check Connection/Auth for '{remote_name}'", func_name=func_name)
|
|
|
|
|
|
def run_interactive_auth_attempt_async(
|
|
git_commands: GitCommands,
|
|
repo_path: str,
|
|
remote_name: str,
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""
|
|
Worker to attempt an interactive Git operation (fetch) to trigger credential prompts.
|
|
This worker intentionally does NOT capture output or hide the console.
|
|
"""
|
|
func_name = "run_interactive_auth_attempt_async"
|
|
log_handler.log_info(
|
|
f"[Worker] Started: Interactive Auth Attempt for '{remote_name}' via Fetch in '{repo_path}'",
|
|
func_name=func_name,
|
|
)
|
|
try:
|
|
# Esegui git fetch in modalità interattiva (no capture, no hide)
|
|
result = git_commands.git_fetch_interactive(repo_path, remote_name)
|
|
|
|
# Controlla solo il codice di ritorno
|
|
if result.returncode == 0:
|
|
# Successo (presumibilmente l'utente ha autenticato)
|
|
message = f"Interactive authentication attempt for '{remote_name}' seems successful."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put(
|
|
{
|
|
"status": "success",
|
|
"result": "auth_attempt_success",
|
|
"message": message,
|
|
}
|
|
)
|
|
else:
|
|
# Fallimento (utente ha annullato, credenziali errate, altro errore)
|
|
message = f"Interactive authentication attempt for '{remote_name}' failed or was cancelled (RC={result.returncode})."
|
|
log_handler.log_warning(f"[Worker] {message}", func_name=func_name)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"result": "auth_attempt_failed",
|
|
"message": message,
|
|
"exception": GitCommandError(message, stderr=None),
|
|
} # Non abbiamo stderr qui
|
|
)
|
|
|
|
except Exception as e:
|
|
# Errore imprevisto nell'esecuzione del worker
|
|
log_handler.log_exception(
|
|
f"[Worker] UNEXPECTED EXCEPTION during interactive auth attempt: {e}",
|
|
func_name=func_name,
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"result": "worker_exception",
|
|
"message": f"Unexpected error during interactive auth: {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug(
|
|
f"[Worker] Finished: Interactive Auth Attempt for '{remote_name}'",
|
|
func_name=func_name,
|
|
)
|
|
|
|
|
|
# --- Placeholder per future funzioni worker remote ---
|
|
def run_fetch_remote_async(
|
|
remote_action_handler: RemoteActionHandler, # Dipendenza dall'handler delle azioni remote
|
|
repo_path: str,
|
|
remote_name: str,
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""
|
|
Worker function to execute 'git fetch' asynchronously.
|
|
Executed in a separate thread.
|
|
"""
|
|
func_name = "run_fetch_remote_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Fetch Remote '{remote_name}' for '{repo_path}'",
|
|
func_name=func_name,
|
|
)
|
|
|
|
try:
|
|
# Chiama il metodo execute_remote_fetch che contiene la logica e l'analisi dell'errore
|
|
result_info = remote_action_handler.execute_remote_fetch(repo_path, remote_name)
|
|
|
|
# result_info contiene già {'status': '...', 'message': '...', 'exception': ...}
|
|
log_handler.log_info(
|
|
f"[Worker] Fetch result status for '{remote_name}': {result_info.get('status')}",
|
|
func_name=func_name,
|
|
)
|
|
|
|
# Metti il dizionario del risultato direttamente nella coda
|
|
results_queue.put(result_info)
|
|
|
|
except Exception as e:
|
|
# Cattura eccezioni impreviste sollevate da execute_remote_fetch stesso
|
|
# (es., errori di validazione o eccezioni non gestite all'interno)
|
|
log_handler.log_exception(
|
|
f"[Worker] UNEXPECTED EXCEPTION during fetch execution: {e}",
|
|
func_name=func_name,
|
|
)
|
|
# Metti un risultato di errore generico nella coda
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Unexpected error during fetch operation: {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug(
|
|
f"[Worker] Finished: Fetch Remote '{remote_name}'", func_name=func_name
|
|
)
|
|
|
|
|
|
def run_pull_remote_async(
|
|
remote_action_handler: RemoteActionHandler, # Dipendenza per eseguire il pull
|
|
git_commands: GitCommands, # Necessario per ottenere il branch corrente
|
|
repo_path: str,
|
|
remote_name: str,
|
|
# branch_name non è più necessario passarlo qui, lo otteniamo nel worker
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""
|
|
Worker function to execute 'git pull' asynchronously.
|
|
Executed in a separate thread.
|
|
"""
|
|
func_name = "run_pull_remote_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Pull Remote '{remote_name}' for '{repo_path}'",
|
|
func_name=func_name,
|
|
)
|
|
|
|
try:
|
|
# --- Ottieni il branch corrente ---
|
|
# È necessario conoscere il branch corrente per passarlo a execute_remote_pull
|
|
# (anche se git pull di base non lo richiede, la nostra logica di action lo usa)
|
|
# e per log/messaggi più chiari.
|
|
current_branch_name = git_commands.get_current_branch_name(
|
|
repo_path
|
|
) # Assumendo che questo metodo esista/venga aggiunto a GitCommands
|
|
if not current_branch_name:
|
|
# Se non riusciamo a determinare il branch (es. detached HEAD), non possiamo fare pull standard
|
|
raise ValueError(
|
|
"Cannot perform pull: Unable to determine current branch (possibly detached HEAD)."
|
|
)
|
|
|
|
log_handler.log_debug(
|
|
f"[Worker] Current branch identified as: '{current_branch_name}'",
|
|
func_name=func_name,
|
|
)
|
|
|
|
# --- Chiama l'Action Handler ---
|
|
# execute_remote_pull contiene la logica per:
|
|
# 1. Controllare modifiche non committate
|
|
# 2. Eseguire git pull
|
|
# 3. Analizzare l'esito (successo, conflitto, errore auth/conn/altro)
|
|
result_info = remote_action_handler.execute_remote_pull(
|
|
repo_path, remote_name, current_branch_name
|
|
)
|
|
|
|
# result_info è il dizionario restituito da execute_remote_pull
|
|
log_handler.log_info(
|
|
f"[Worker] Pull result status for '{remote_name}': {result_info.get('status')}",
|
|
func_name=func_name,
|
|
)
|
|
|
|
# --- Aggiungi informazioni extra per la gestione dei conflitti ---
|
|
if result_info.get("status") == "conflict":
|
|
result_info["repo_path"] = (
|
|
repo_path # Assicura che il path sia nel risultato per il messaggio GUI
|
|
)
|
|
|
|
# Metti il dizionario del risultato direttamente nella coda
|
|
results_queue.put(result_info)
|
|
|
|
except (GitCommandError, ValueError) as e:
|
|
# Cattura errori dalla determinazione del branch o altri errori noti
|
|
log_handler.log_error(
|
|
f"[Worker] Handled EXCEPTION during pull setup/execution: {e}",
|
|
func_name=func_name,
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Pull failed: {e}", # Usa messaggio eccezione
|
|
}
|
|
)
|
|
except Exception as e:
|
|
# Cattura eccezioni impreviste
|
|
log_handler.log_exception(
|
|
f"[Worker] UNEXPECTED EXCEPTION during pull operation: {e}",
|
|
func_name=func_name,
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Unexpected error during pull operation: {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug(
|
|
f"[Worker] Finished: Pull Remote '{remote_name}'", func_name=func_name
|
|
)
|
|
|
|
|
|
def run_push_remote_async(
|
|
remote_action_handler: RemoteActionHandler, # Dipendenza per eseguire push
|
|
git_commands: GitCommands, # Necessario per ottenere il branch corrente
|
|
repo_path: str,
|
|
remote_name: str,
|
|
# branch_name viene determinato internamente
|
|
# force flag non necessario qui, gestito da execute_remote_push se necessario
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""
|
|
Worker function to execute 'git push' for the current branch asynchronously.
|
|
Executed in a separate thread. Handles upstream setting.
|
|
"""
|
|
func_name = "run_push_remote_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Push Remote '{remote_name}' for '{repo_path}'",
|
|
func_name=func_name,
|
|
)
|
|
|
|
try:
|
|
# --- Ottieni il branch corrente ---
|
|
current_branch_name = git_commands.get_current_branch_name(repo_path)
|
|
if not current_branch_name:
|
|
raise ValueError(
|
|
"Cannot perform push: Unable to determine current branch (possibly detached HEAD)."
|
|
)
|
|
|
|
log_handler.log_debug(
|
|
f"[Worker] Current branch identified as: '{current_branch_name}'",
|
|
func_name=func_name,
|
|
)
|
|
|
|
# --- Chiama l'Action Handler ---
|
|
# execute_remote_push contiene la logica per:
|
|
# 1. Controllare e impostare l'upstream se necessario
|
|
# 2. Eseguire git push
|
|
# 3. Analizzare l'esito (successo, rifiutato, errore auth/conn/altro)
|
|
# NOTA: Il flag 'force' qui è sempre False per il push standard dal pulsante GUI.
|
|
# Un eventuale push forzato richiederebbe un pulsante/conferma separata.
|
|
result_info = remote_action_handler.execute_remote_push(
|
|
repo_path=repo_path,
|
|
remote_name=remote_name,
|
|
current_branch_name=current_branch_name,
|
|
force=False, # Push standard
|
|
)
|
|
|
|
# result_info è il dizionario restituito da execute_remote_push
|
|
log_handler.log_info(
|
|
f"[Worker] Push result status for '{current_branch_name}' to '{remote_name}': {result_info.get('status')}",
|
|
func_name=func_name,
|
|
)
|
|
|
|
# --- Aggiungi informazioni extra per la gestione GUI ---
|
|
if result_info.get("status") == "rejected":
|
|
result_info["branch_name"] = (
|
|
current_branch_name # Passa il nome del branch nel risultato
|
|
)
|
|
|
|
# Metti il dizionario del risultato direttamente nella coda
|
|
results_queue.put(result_info)
|
|
|
|
except (GitCommandError, ValueError) as e:
|
|
# Cattura errori dalla determinazione del branch o altri errori noti
|
|
log_handler.log_error(
|
|
f"[Worker] Handled EXCEPTION during push setup/execution: {e}",
|
|
func_name=func_name,
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Push failed: {e}", # Usa messaggio eccezione
|
|
}
|
|
)
|
|
except Exception as e:
|
|
# Cattura eccezioni impreviste
|
|
log_handler.log_exception(
|
|
f"[Worker] UNEXPECTED EXCEPTION during push operation: {e}",
|
|
func_name=func_name,
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Unexpected error during push operation: {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug(
|
|
f"[Worker] Finished: Push Remote '{remote_name}'", func_name=func_name
|
|
)
|
|
|
|
|
|
def run_push_tags_async(
|
|
remote_action_handler: RemoteActionHandler, # Dipendenza per eseguire push tags
|
|
repo_path: str,
|
|
remote_name: str,
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""
|
|
Worker function to execute 'git push --tags' asynchronously.
|
|
Executed in a separate thread.
|
|
"""
|
|
func_name = "run_push_tags_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Push Tags to '{remote_name}' for '{repo_path}'",
|
|
func_name=func_name,
|
|
)
|
|
|
|
try:
|
|
# --- Chiama l'Action Handler ---
|
|
result_info = remote_action_handler.execute_push_tags(repo_path, remote_name)
|
|
|
|
# result_info è il dizionario restituito da execute_push_tags
|
|
log_handler.log_info(
|
|
f"[Worker] Push tags result status for '{remote_name}': {result_info.get('status')}",
|
|
func_name=func_name,
|
|
)
|
|
|
|
# Metti il dizionario del risultato direttamente nella coda
|
|
results_queue.put(result_info)
|
|
|
|
except (GitCommandError, ValueError) as e:
|
|
# Cattura errori noti sollevati da execute_push_tags (principalmente validazione)
|
|
log_handler.log_error(
|
|
f"[Worker] Handled EXCEPTION during push tags execution: {e}",
|
|
func_name=func_name,
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Push tags failed: {e}",
|
|
}
|
|
)
|
|
except Exception as e:
|
|
# Cattura eccezioni impreviste
|
|
log_handler.log_exception(
|
|
f"[Worker] UNEXPECTED EXCEPTION during push tags operation: {e}",
|
|
func_name=func_name,
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Unexpected error during push tags operation: {type(e).__name__}",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug(
|
|
f"[Worker] Finished: Push Tags to '{remote_name}'", func_name=func_name
|
|
)
|
|
|
|
|
|
def run_get_ahead_behind_async(
|
|
git_commands: GitCommands, # Dipendenza
|
|
repo_path: str,
|
|
local_branch: str,
|
|
upstream_branch: str,
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""
|
|
Worker function to get ahead/behind commit counts asynchronously.
|
|
Executed in a separate thread.
|
|
"""
|
|
func_name = "run_get_ahead_behind_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Get Ahead/Behind for '{local_branch}' vs '{upstream_branch}' in '{repo_path}'",
|
|
func_name=func_name,
|
|
)
|
|
|
|
ahead_count = None
|
|
behind_count = None
|
|
status = "error" # Default a errore
|
|
message = f"Could not determine ahead/behind status for branch '{local_branch}'."
|
|
exception_obj = None
|
|
|
|
try:
|
|
# Chiama il metodo in GitCommands per ottenere i conteggi
|
|
ahead_count, behind_count = git_commands.get_ahead_behind_count(
|
|
working_directory=repo_path,
|
|
local_branch=local_branch,
|
|
upstream_branch=upstream_branch,
|
|
)
|
|
|
|
# Verifica se il comando ha restituito valori validi (non None)
|
|
if ahead_count is not None and behind_count is not None:
|
|
status = "success"
|
|
# Costruisci un messaggio descrittivo
|
|
if ahead_count == 0 and behind_count == 0:
|
|
message = (
|
|
f"Branch '{local_branch}' is up to date with '{upstream_branch}'."
|
|
)
|
|
else:
|
|
parts = []
|
|
if ahead_count > 0:
|
|
plural_a = "s" if ahead_count > 1 else ""
|
|
parts.append(f"{ahead_count} commit{plural_a} ahead")
|
|
if behind_count > 0:
|
|
plural_b = "s" if behind_count > 1 else ""
|
|
parts.append(f"{behind_count} commit{plural_b} behind")
|
|
message = (
|
|
f"Branch '{local_branch}' is "
|
|
+ " and ".join(parts)
|
|
+ f" of '{upstream_branch}'."
|
|
)
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
|
|
else:
|
|
# Se get_ahead_behind_count ha restituito None, significa che c'è stato un errore
|
|
# nel comando git o nell'analisi. Il messaggio di default va bene.
|
|
log_handler.log_warning(
|
|
f"[Worker] Failed to get valid ahead/behind counts for '{local_branch}'.",
|
|
func_name=func_name,
|
|
)
|
|
status = "error" # Mantiene lo stato di errore
|
|
# Potremmo cercare di recuperare un'eccezione se disponibile, ma è complesso qui
|
|
|
|
except Exception as e:
|
|
# Cattura eccezioni impreviste nel worker stesso
|
|
log_handler.log_exception(
|
|
f"[Worker] UNEXPECTED EXCEPTION getting ahead/behind: {e}",
|
|
func_name=func_name,
|
|
)
|
|
status = "error"
|
|
message = f"Unexpected error getting ahead/behind status: {type(e).__name__}"
|
|
exception_obj = e
|
|
|
|
# Metti il risultato nella coda
|
|
results_queue.put(
|
|
{
|
|
"status": status,
|
|
"result": (ahead_count, behind_count), # La tupla con i conteggi (o None)
|
|
"message": message,
|
|
"exception": exception_obj, # Passa l'eccezione se c'è stata
|
|
}
|
|
)
|
|
|
|
log_handler.log_debug(
|
|
f"[Worker] Finished: Get Ahead/Behind for '{local_branch}'", func_name=func_name
|
|
)
|
|
|
|
|
|
def run_clone_remote_async(
|
|
git_commands: GitCommands, # Dipendenza per eseguire clone
|
|
remote_url: str,
|
|
local_clone_path: str, # Path completo dove clonare
|
|
profile_name_to_create: str, # Nome del profilo da creare post-clone
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""
|
|
Worker function to execute 'git clone' asynchronously.
|
|
Executed in a separate thread.
|
|
|
|
Args:
|
|
git_commands (GitCommands): Instance to execute git commands.
|
|
remote_url (str): URL of the repository to clone.
|
|
local_clone_path (str): Full path to the target directory for the clone.
|
|
profile_name_to_create (str): The name for the new profile upon success.
|
|
results_queue (queue.Queue): Queue to put the result dictionary.
|
|
"""
|
|
func_name = "run_clone_remote_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Clone from '{remote_url}' into '{local_clone_path}'",
|
|
func_name=func_name,
|
|
)
|
|
|
|
result_info = {"status": "unknown", "message": "Clone not completed."} # Default
|
|
|
|
try:
|
|
# Il controllo sull'esistenza della directory di destinazione
|
|
# è stato fatto PRIMA di avviare questo worker (in GitUtility.py).
|
|
# Qui eseguiamo direttamente il comando.
|
|
|
|
# Chiama il metodo git_clone (che ha check=False)
|
|
clone_result = git_commands.git_clone(remote_url, local_clone_path)
|
|
|
|
# Analizza il risultato del comando clone
|
|
if clone_result.returncode == 0:
|
|
# Successo
|
|
result_info["status"] = "success"
|
|
result_info["message"] = (
|
|
f"Repository cloned successfully into '{os.path.basename(local_clone_path)}'."
|
|
)
|
|
# Passa i dati necessari per creare il profilo nel risultato
|
|
result_info["result"] = {
|
|
"cloned_path": local_clone_path,
|
|
"profile_name": profile_name_to_create,
|
|
"remote_url": remote_url,
|
|
}
|
|
log_handler.log_info(
|
|
f"[Worker] Clone successful: {result_info['message']}",
|
|
func_name=func_name,
|
|
)
|
|
|
|
else:
|
|
# Errore durante il clone
|
|
result_info["status"] = "error"
|
|
stderr_full = clone_result.stderr if clone_result.stderr else ""
|
|
stderr_lower = stderr_full.lower()
|
|
log_handler.log_error(
|
|
f"Clone command failed (RC={clone_result.returncode}). Stderr: {stderr_lower}",
|
|
func_name=func_name,
|
|
)
|
|
|
|
# Controlla errori specifici noti
|
|
auth_errors = [
|
|
"authentication failed",
|
|
"permission denied",
|
|
"could not read username",
|
|
"fatal: could not read password",
|
|
]
|
|
connection_errors = [
|
|
"repository not found",
|
|
"could not resolve host",
|
|
"name or service not known",
|
|
"network is unreachable",
|
|
]
|
|
path_errors = [
|
|
"already exists and is not an empty directory",
|
|
"could not create work tree",
|
|
] # Anche se controllato prima, può succedere
|
|
|
|
if any(err in stderr_lower for err in auth_errors):
|
|
result_info["message"] = (
|
|
f"Authentication required or failed for cloning '{remote_url}'."
|
|
)
|
|
result_info["exception"] = GitCommandError(
|
|
result_info["message"], stderr=clone_result.stderr
|
|
)
|
|
# Potremmo impostare uno stato specifico 'auth_required' se vogliamo distinguerlo
|
|
# result_info['result'] = 'authentication needed' # Opzionale
|
|
elif any(err in stderr_lower for err in connection_errors):
|
|
result_info["message"] = (
|
|
f"Failed to connect while cloning: Repository or host '{remote_url}' not found/reachable."
|
|
)
|
|
result_info["exception"] = GitCommandError(
|
|
result_info["message"], stderr=clone_result.stderr
|
|
)
|
|
# result_info['result'] = 'connection_failed' # Opzionale
|
|
elif any(err in stderr_lower for err in path_errors):
|
|
result_info["message"] = (
|
|
f"Clone failed: Target directory '{local_clone_path}' already exists and is not empty, or could not be created."
|
|
)
|
|
result_info["exception"] = GitCommandError(
|
|
result_info["message"], stderr=clone_result.stderr
|
|
)
|
|
# result_info['result'] = 'path_error' # Opzionale
|
|
else:
|
|
# Errore generico di Git
|
|
result_info["message"] = (
|
|
f"Clone from '{remote_url}' failed (RC={clone_result.returncode}). Check logs."
|
|
)
|
|
result_info["exception"] = GitCommandError(
|
|
result_info["message"], stderr=clone_result.stderr
|
|
)
|
|
# result_info['result'] = 'unknown_error' # Opzionale
|
|
|
|
# Metti il risultato in coda
|
|
results_queue.put(result_info)
|
|
|
|
except Exception as e:
|
|
# Cattura eccezioni impreviste nel worker stesso
|
|
log_handler.log_exception(
|
|
f"[Worker] UNEXPECTED EXCEPTION during clone operation: {e}",
|
|
func_name=func_name,
|
|
)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Unexpected error during clone operation: {type(e).__name__}",
|
|
"result": "worker_exception", # Stato specifico per errore worker
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug(
|
|
f"[Worker] Finished: Clone Remote '{remote_url}'", func_name=func_name
|
|
)
|
|
|
|
|
|
def run_refresh_remote_branches_async(
|
|
git_commands: GitCommands, # Dipendenza
|
|
repo_path: str,
|
|
remote_name: str,
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""
|
|
Worker function to get the list of remote branches asynchronously.
|
|
Executed in a separate thread.
|
|
"""
|
|
func_name = "run_refresh_remote_branches_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Refresh Remote Branches for '{remote_name}' in '{repo_path}'",
|
|
func_name=func_name,
|
|
)
|
|
|
|
remote_branches = [] # Default a lista vuota
|
|
status = "error" # Default a errore
|
|
message = f"Could not retrieve remote branches for '{remote_name}'."
|
|
exception_obj = None
|
|
|
|
try:
|
|
# Chiama il metodo in GitCommands per ottenere la lista
|
|
remote_branches = git_commands.git_list_remote_branches(repo_path, remote_name)
|
|
|
|
# Se siamo qui senza eccezioni, il comando è andato a buon fine
|
|
# (anche se la lista potrebbe essere vuota)
|
|
status = "success"
|
|
count = len(remote_branches)
|
|
if count > 0:
|
|
message = f"Found {count} remote branches for '{remote_name}'."
|
|
else:
|
|
message = (
|
|
f"No remote branches found for '{remote_name}' (or remote invalid)."
|
|
)
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
|
|
except Exception as e:
|
|
# Cattura eccezioni impreviste (GitCommandError dovrebbe essere gestito dentro git_list_remote_branches restituendo [])
|
|
log_handler.log_exception(
|
|
f"[Worker] UNEXPECTED EXCEPTION refreshing remote branches: {e}",
|
|
func_name=func_name,
|
|
)
|
|
status = "error"
|
|
message = f"Unexpected error listing remote branches: {type(e).__name__}"
|
|
exception_obj = e
|
|
remote_branches = ["(Error)"] # Segnaposto per la GUI
|
|
|
|
# Metti il risultato nella coda
|
|
# 'result' contiene la lista dei nomi dei branch (o ['(Error)'])
|
|
results_queue.put(
|
|
{
|
|
"status": status,
|
|
"result": remote_branches,
|
|
"message": message,
|
|
"exception": exception_obj,
|
|
}
|
|
)
|
|
|
|
log_handler.log_debug(
|
|
f"[Worker] Finished: Refresh Remote Branches for '{remote_name}'",
|
|
func_name=func_name,
|
|
)
|
|
|
|
|
|
def run_checkout_tracking_branch_async(
|
|
action_handler: ActionHandler, # Dipendenza dall'ActionHandler locale
|
|
repo_path: str,
|
|
new_local_branch_name: str,
|
|
remote_tracking_branch_full_name: str, # Es. 'origin/main'
|
|
results_queue: queue.Queue,
|
|
):
|
|
"""
|
|
Worker function to checkout a remote branch as a new local tracking branch asynchronously.
|
|
Executed in a separate thread.
|
|
"""
|
|
func_name = "run_checkout_tracking_branch_async"
|
|
log_handler.log_debug(
|
|
f"[Worker] Started: Checkout Remote Branch '{remote_tracking_branch_full_name}' "
|
|
f"as Local '{new_local_branch_name}' in '{repo_path}'",
|
|
func_name=func_name,
|
|
)
|
|
|
|
try:
|
|
# Chiama il metodo execute_checkout_tracking_branch che contiene la logica
|
|
# (controllo stato, esecuzione git checkout -b, analisi risultato)
|
|
result_info = action_handler.execute_checkout_tracking_branch(
|
|
repo_path=repo_path,
|
|
new_local_branch_name=new_local_branch_name,
|
|
remote_tracking_branch_full_name=remote_tracking_branch_full_name,
|
|
)
|
|
|
|
# result_info contiene già {'status': '...', 'message': '...', 'exception': ...}
|
|
log_handler.log_info(
|
|
f"[Worker] Checkout tracking branch result status: {result_info.get('status')}",
|
|
func_name=func_name,
|
|
)
|
|
|
|
# Metti il dizionario del risultato direttamente nella coda
|
|
results_queue.put(result_info)
|
|
|
|
except Exception as e:
|
|
# Cattura eccezioni impreviste sollevate da execute_checkout_tracking_branch
|
|
# (es., errori di validazione iniziali o eccezioni non gestite all'interno)
|
|
log_handler.log_exception(
|
|
f"[Worker] UNEXPECTED EXCEPTION during checkout tracking branch execution: {e}",
|
|
func_name=func_name,
|
|
)
|
|
# Metti un risultato di errore generico nella coda
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Unexpected error during checkout operation: {type(e).__name__}",
|
|
"result": "worker_exception",
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug(
|
|
f"[Worker] Finished: Checkout Remote Branch '{remote_tracking_branch_full_name}' as Local '{new_local_branch_name}'",
|
|
func_name=func_name,
|
|
)
|
|
|
|
def run_delete_local_branch_async(
|
|
action_handler: ActionHandler, # Dipendenza
|
|
repo_path: str,
|
|
branch_name: str,
|
|
force: bool,
|
|
results_queue: queue.Queue
|
|
):
|
|
"""
|
|
Worker function to delete a local branch asynchronously.
|
|
Executed in a separate thread.
|
|
"""
|
|
func_name = "run_delete_local_branch_async"
|
|
action_type = "Force delete" if force else "Delete"
|
|
log_handler.log_debug(f"[Worker] Started: {action_type} Local Branch '{branch_name}' in '{repo_path}'", func_name=func_name)
|
|
|
|
try:
|
|
# Chiama il metodo execute_delete_local_branch che contiene la logica
|
|
# (controllo branch corrente, esecuzione git branch -d/-D, analisi risultato)
|
|
result_info = action_handler.execute_delete_local_branch(
|
|
repo_path=repo_path,
|
|
branch_name=branch_name,
|
|
force=force
|
|
)
|
|
|
|
# result_info contiene già {'status': '...', 'message': '...', 'exception': ...}
|
|
log_handler.log_info(
|
|
f"[Worker] Delete local branch '{branch_name}' result status: {result_info.get('status')}",
|
|
func_name=func_name
|
|
)
|
|
|
|
# Metti il dizionario del risultato direttamente nella coda
|
|
results_queue.put(result_info)
|
|
|
|
except ValueError as ve:
|
|
# Cattura specificamente il ValueError sollevato se si tenta di cancellare il branch corrente
|
|
log_handler.log_error(f"[Worker] Handled EXCEPTION during delete branch setup: {ve}", func_name=func_name)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": ve, # Passa l'eccezione originale
|
|
"message": str(ve), # Usa il messaggio del ValueError
|
|
}
|
|
)
|
|
except Exception as e:
|
|
# Cattura eccezioni impreviste sollevate da execute_delete_local_branch
|
|
log_handler.log_exception(f"[Worker] UNEXPECTED EXCEPTION during delete local branch: {e}", func_name=func_name)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Unexpected error deleting local branch: {type(e).__name__}",
|
|
"result": "worker_exception"
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug(f"[Worker] Finished: {action_type} Local Branch '{branch_name}'", func_name=func_name)
|
|
|
|
def run_merge_local_branch_async(
|
|
action_handler: ActionHandler, # Dipendenza per eseguire merge
|
|
git_commands: GitCommands, # Dipendenza per ottenere branch corrente
|
|
repo_path: str,
|
|
branch_to_merge: str,
|
|
no_ff: bool,
|
|
results_queue: queue.Queue
|
|
):
|
|
"""
|
|
Worker function to merge a local branch into the current branch asynchronously.
|
|
Executed in a separate thread.
|
|
"""
|
|
func_name = "run_merge_local_branch_async"
|
|
log_handler.log_debug(f"[Worker] Started: Merge Local Branch '{branch_to_merge}' into current for '{repo_path}'", func_name=func_name)
|
|
|
|
result_info = {} # Inizializza dizionario risultato
|
|
|
|
try:
|
|
# --- Ottieni il branch corrente ---
|
|
# Necessario per passarlo a execute_merge_local_branch per validazione
|
|
current_branch_name = git_commands.get_current_branch_name(repo_path)
|
|
if not current_branch_name:
|
|
# Caso Detached HEAD
|
|
raise ValueError("Cannot perform merge: Currently in detached HEAD state.")
|
|
|
|
log_handler.log_debug(f"[Worker] Current branch for merge validation: '{current_branch_name}'", func_name=func_name)
|
|
|
|
# --- Chiama l'Action Handler ---
|
|
# execute_merge_local_branch contiene la logica per:
|
|
# 1. Validare input e stato (branch corrente, branch esiste, no modifiche)
|
|
# 2. Eseguire git merge
|
|
# 3. Analizzare l'esito (successo, conflitto, errore)
|
|
result_info = action_handler.execute_merge_local_branch(
|
|
repo_path=repo_path,
|
|
branch_to_merge=branch_to_merge,
|
|
current_branch=current_branch_name,
|
|
no_ff=no_ff
|
|
)
|
|
|
|
# Logga l'esito
|
|
log_handler.log_info(
|
|
f"[Worker] Merge local branch '{branch_to_merge}' result status: {result_info.get('status')}",
|
|
func_name=func_name
|
|
)
|
|
|
|
# --- Aggiungi informazioni extra per gestione GUI in caso di conflitto ---
|
|
if result_info.get('status') == 'conflict':
|
|
result_info['repo_path'] = repo_path # Path per messaggio utente
|
|
result_info['branch_merged_into'] = current_branch_name # Branch dove risolvere
|
|
|
|
# Metti il dizionario del risultato nella coda
|
|
results_queue.put(result_info)
|
|
|
|
except ValueError as ve:
|
|
# Cattura specificamente i ValueError sollevati dai check in action_handler
|
|
# (es. merge into self, branch non esiste, detached HEAD, uncommitted changes)
|
|
log_handler.log_error(f"[Worker] Handled VALIDATION EXCEPTION during merge setup: {ve}", func_name=func_name)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": ve,
|
|
"message": f"Merge failed: {ve}", # Usa il messaggio chiaro del ValueError
|
|
}
|
|
)
|
|
except Exception as e:
|
|
# Cattura eccezioni impreviste
|
|
log_handler.log_exception(f"[Worker] UNEXPECTED EXCEPTION during merge operation: {e}", func_name=func_name)
|
|
results_queue.put(
|
|
{
|
|
"status": "error",
|
|
"exception": e,
|
|
"message": f"Unexpected error during merge operation: {type(e).__name__}",
|
|
"result": "worker_exception"
|
|
}
|
|
)
|
|
finally:
|
|
log_handler.log_debug(f"[Worker] Finished: Merge Local Branch '{branch_to_merge}'", func_name=func_name)
|
|
|
|
def run_compare_branches_async(
|
|
git_commands: GitCommands, # Dipendenza
|
|
repo_path: str,
|
|
ref1: str, # Riferimento 1 (di solito il branch corrente)
|
|
ref2: str, # Riferimento 2 (il branch selezionato dall'utente)
|
|
results_queue: queue.Queue
|
|
):
|
|
"""
|
|
Worker function to get the list of changed files between two references
|
|
using 'git diff-tree'. Executed in a separate thread.
|
|
"""
|
|
func_name = "run_compare_branches_async"
|
|
log_handler.log_debug(f"[Worker] Started: Compare Branches '{ref1}' vs '{ref2}' in '{repo_path}'", func_name=func_name)
|
|
|
|
changed_files = [] # Lista dei file cambiati "Status\tPath"
|
|
status = 'error' # Default a errore
|
|
message = f"Could not compare '{ref1}' and '{ref2}'."
|
|
exception_obj = None
|
|
|
|
try:
|
|
# --- Opzionale: Eseguire Fetch prima del confronto? ---
|
|
# Se ref2 è un branch remoto, potremmo voler fare fetch prima per avere lo stato più recente.
|
|
# Questo però rallenta l'operazione. Per ora, confrontiamo con lo stato locale conosciuto.
|
|
# if '/' in ref2: # Heuristica semplice per branch remoto
|
|
# try:
|
|
# remote_name = ref2.split('/')[0]
|
|
# log_handler.log_debug(f"[Worker] Fetching '{remote_name}' before comparison.", func_name=func_name)
|
|
# fetch_res = git_commands.git_fetch(repo_path, remote_name)
|
|
# if fetch_res.returncode != 0:
|
|
# log_handler.log_warning(f"[Worker] Pre-compare fetch failed (RC={fetch_res.returncode}). Comparing with potentially stale data.", func_name=func_name)
|
|
# except Exception as fetch_e:
|
|
# log_handler.log_warning(f"[Worker] Error during pre-compare fetch: {fetch_e}. Comparing with potentially stale data.", func_name=func_name)
|
|
# --- Fine Fetch Opzionale ---
|
|
|
|
|
|
# Chiama il metodo in GitCommands per ottenere la lista dei file differenti
|
|
return_code, changed_files_list = git_commands.git_diff_tree(
|
|
working_directory=repo_path,
|
|
ref1=ref1,
|
|
ref2=ref2
|
|
)
|
|
|
|
# Verifica l'esito del comando git diff-tree
|
|
if return_code == 0:
|
|
status = 'success'
|
|
changed_files = changed_files_list # La lista dei file nel formato "Status\tPath"
|
|
count = len(changed_files)
|
|
if count > 0:
|
|
message = f"Comparison complete: Found {count} differences between '{ref1}' and '{ref2}'."
|
|
else:
|
|
message = f"No differences found between '{ref1}' and '{ref2}'."
|
|
log_handler.log_info(f"[Worker] {message}", func_name=func_name)
|
|
else:
|
|
# Errore durante l'esecuzione di git diff-tree (ref invalidi?)
|
|
status = 'error'
|
|
message = f"Failed to compare '{ref1}' and '{ref2}'. Invalid reference(s)?"
|
|
log_handler.log_error(f"[Worker] git diff-tree command failed (RC={return_code})", func_name=func_name)
|
|
# L'errore specifico è già stato loggato da git_diff_tree
|
|
exception_obj = GitCommandError(message, stderr="See previous logs") # stderr non disponibile qui direttamente
|
|
|
|
except Exception as e:
|
|
# Cattura eccezioni impreviste nel worker stesso
|
|
log_handler.log_exception(f"[Worker] UNEXPECTED EXCEPTION during branch comparison: {e}", func_name=func_name)
|
|
status = 'error'
|
|
message = f"Unexpected error comparing branches: {type(e).__name__}"
|
|
exception_obj = e
|
|
changed_files = ["(Error)"] # Segnaposto per la GUI
|
|
|
|
# Metti il risultato nella coda
|
|
# 'result' contiene la lista dei file nel formato "Status\tPath"
|
|
results_queue.put(
|
|
{
|
|
"status": status,
|
|
"result": changed_files, # Lista di stringhe
|
|
"message": message,
|
|
"exception": exception_obj
|
|
}
|
|
)
|
|
|
|
log_handler.log_debug(f"[Worker] Finished: Compare Branches '{ref1}' vs '{ref2}'", func_name=func_name)
|
|
|
|
# --- END OF FILE async_workers.py ---
|