From 69cacd26ec23f9dc334fdbccbde9889d35d1dca6 Mon Sep 17 00:00:00 2001 From: VALLONGOL Date: Wed, 23 Apr 2025 10:38:44 +0200 Subject: [PATCH] add current branch in status --- GitUtility.py | 931 +++++++++++++++----------------------------------- gui.py | 61 ++-- 2 files changed, 303 insertions(+), 689 deletions(-) diff --git a/GitUtility.py b/GitUtility.py index 3aeacc7..e23232a 100644 --- a/GitUtility.py +++ b/GitUtility.py @@ -480,6 +480,8 @@ class GitSvnSyncApp: mf.update_history_display([]) if hasattr(mf, "update_history_branch_filter"): mf.update_history_branch_filter([]) + if hasattr(self.main_frame, "update_ahead_behind_status"): + self.main_frame.update_ahead_behind_status(status_text="Sync Status: (Repo not ready)") # La lista changed files viene già gestita da update_svn_status_indicator # Imposta status bar finale per questo caso mf.update_status_bar( @@ -491,6 +493,8 @@ class GitSvnSyncApp: f"Error applying settings for '{profile_name}': {e}", func_name=func_name, ) + if hasattr(self.main_frame, "update_ahead_behind_status"): + self.main_frame.update_ahead_behind_status(status_text="Sync Status: Error") mf.show_error("Profile Load Error", f"Failed to apply settings:\n{e}") mf.update_status_bar(f"Error loading profile '{profile_name}'.") @@ -2404,32 +2408,16 @@ class GitSvnSyncApp: svn_path, current_branch ) else: - log_handler.log_warning( - "Refresh Status: Cannot get status, currently in detached HEAD state.", - func_name=func_name, - ) + log_handler.log_warning("Refresh Status: Cannot get status, currently in detached HEAD state.", func_name=func_name) if hasattr(self.main_frame, "update_ahead_behind_status"): - self.main_frame.update_ahead_behind_status( - status_text="Sync Status: (Detached HEAD)" - ) + self.main_frame.update_ahead_behind_status(current_branch=None, status_text="Sync Status: (Detached HEAD)") return # Esce se detached if not upstream_branch: - log_handler.log_info( - f"Refresh Status: No upstream configured for branch '{current_branch}'.", - func_name=func_name, - ) + log_handler.log_info(f"Refresh Status: No upstream configured for branch '{current_branch}'.", func_name=func_name) if hasattr(self.main_frame, "update_ahead_behind_status"): - self.main_frame.update_ahead_behind_status( - status_text=f"Sync Status: Upstream not set for '{current_branch}'" - ) - # Abilita/Disabilita pulsanti Push/Pull in base all'upstream? Forse no qui. - # Potremmo disabilitare il pulsante "Refresh Status" stesso? - if hasattr(self.main_frame, "refresh_sync_status_button"): - self.main_frame.refresh_sync_status_button.config( - state=tk.DISABLED - ) # Disabilita refresh se manca upstream - return # Esce se manca upstream + self.main_frame.update_ahead_behind_status(current_branch=current_branch, status_text=f"Sync Status: Upstream not set") + if hasattr(self.main_frame, "refresh_sync_status_button"): self.main_frame.refresh_sync_status_button.config(state=tk.DISABLED) # Se siamo qui, abbiamo branch e upstream, abilita il pulsante refresh (se era disabilitato) if hasattr(self.main_frame, "refresh_sync_status_button"): @@ -2441,9 +2429,7 @@ class GitSvnSyncApp: func_name=func_name, ) if hasattr(self.main_frame, "update_ahead_behind_status"): - self.main_frame.update_ahead_behind_status( - status_text="Sync Status: Error getting info" - ) + self.main_frame.update_ahead_behind_status(status_text="Sync Status: Error getting info") return # --- Avvia worker asincrono --- @@ -2453,9 +2439,7 @@ class GitSvnSyncApp: ) # Aggiorna label GUI a "checking..." if hasattr(self.main_frame, "update_ahead_behind_status"): - self.main_frame.update_ahead_behind_status( - status_text="Sync Status: Checking..." - ) + self.main_frame.update_ahead_behind_status(current_branch=current_branch, status_text="Sync Status: Checking...") args = (self.git_commands, svn_path, current_branch, upstream_branch) self._start_async_operation( @@ -2476,11 +2460,13 @@ class GitSvnSyncApp: if hasattr(self, "main_frame") and self.main_frame.winfo_exists(): # Chiama il metodo della GUI per aggiornare il label self.main_frame._update_auth_status_indicator(status) + if status != 'ok' and hasattr(self.main_frame, "update_ahead_behind_status"): + self.main_frame.update_ahead_behind_status(status_text=f"Sync Status: ({status})") # --- ==== Gestione Coda Risultati ==== --- def _check_completion_queue(self, results_queue: queue.Queue, context: dict): """Checks result queue from async workers, updates GUI accordingly.""" - task_context = context.get("context", "unknown") + task_context = context.get('context', 'unknown') # func_name per i log interni a questa funzione func_name = "_check_completion_queue" # log_handler.log_debug(f"Checking completion queue for context: {task_context}", func_name=func_name) # Log inizio check (opzionale) @@ -2488,35 +2474,20 @@ class GitSvnSyncApp: try: # Tenta di ottenere un risultato dalla coda senza bloccare result_data = results_queue.get_nowait() - log_handler.log_info( - f"Result received for '{task_context}'. Status: {result_data.get('status')}", - func_name=func_name, - ) + log_handler.log_info(f"Result received for '{task_context}'. Status: {result_data.get('status')}", func_name=func_name) # --- Determina se riabilitare subito i widget --- - should_reenable_now = True # Default: riabilita subito - status_from_result = result_data.get( - "status" - ) # Ottieni lo stato dal risultato + should_reenable_now = True # Default: riabilita subito + status_from_result = result_data.get('status') # Ottieni lo stato dal risultato # Non riabilitare subito se l'utente deve interagire o se parte un'altra azione - if ( - task_context == "check_connection" - and status_from_result == "auth_required" - ): + if task_context == "check_connection" and status_from_result == 'auth_required': should_reenable_now = False - log_handler.log_debug( - "Delaying widget re-enable: waiting for auth prompt.", - func_name=func_name, - ) - elif task_context == "interactive_auth" and status_from_result == "success": + log_handler.log_debug("Delaying widget re-enable: waiting for auth prompt.", func_name=func_name) + elif task_context == "interactive_auth" and status_from_result == 'success': should_reenable_now = False - log_handler.log_debug( - "Delaying widget re-enable: re-checking connection after interactive auth.", - func_name=func_name, - ) + log_handler.log_debug("Delaying widget re-enable: re-checking connection after interactive auth.", func_name=func_name) # Non ritardare per errore get_ahead_behind, l'utente può riprovare manualmente - # elif task_context == "get_ahead_behind" and status_from_result == 'error': should_reenable_now = False # Riabilita i widget se non è necessario attendere if should_reenable_now: @@ -2525,683 +2496,319 @@ class GitSvnSyncApp: self.main_frame.set_action_widgets_state(tk.NORMAL) else: # Se la GUI non c'è, non c'è nulla da fare o riabilitare - log_handler.log_warning( - "Cannot re-enable widgets, MainFrame missing.", - func_name=func_name, - ) - return # Esce dalla funzione + log_handler.log_warning("Cannot re-enable widgets, MainFrame missing.", func_name=func_name) + return # Esce dalla funzione # --- Estrai dettagli dal risultato --- - status = status_from_result # Usa la variabile già ottenuta - message = result_data.get( - "message", "Operation finished." - ) # Messaggio di default - result_value = result_data.get("result") # Valore specifico del risultato - exception = result_data.get("exception") # Eventuale eccezione catturata - committed = result_data.get( - "committed", False - ) # Flag per operazioni che committano + status = status_from_result # Usa la variabile già ottenuta + message = result_data.get('message', "Operation finished.") # Messaggio di default + result_value = result_data.get('result') # Valore specifico del risultato + exception = result_data.get('exception') # Eventuale eccezione catturata + committed = result_data.get('committed', False) # Flag per operazioni che committano # Estrai flag conflitto specifico per pull o fetch_bundle - is_conflict = False # Default - repo_path_conflict = None # Default - if task_context == "pull_remote": - is_conflict = status == "conflict" # Determinato dallo stato specifico - repo_path_conflict = context.get( - "repo_path" - ) # Path passato nel contesto originale - elif task_context == "fetch_bundle": - is_conflict = result_data.get( - "conflict", False - ) # Dipende da flag nel risultato - repo_path_conflict = result_data.get("repo_path") # Path nel risultato + is_conflict = False # Default + repo_path_conflict = None # Default + if task_context == 'pull_remote': + is_conflict = (status == 'conflict') # Determinato dallo stato specifico + repo_path_conflict = context.get('repo_path') # Path passato nel contesto originale + elif task_context == 'fetch_bundle': + is_conflict = result_data.get('conflict', False) # Dipende da flag nel risultato + repo_path_conflict = result_data.get('repo_path') # Path nel risultato # Estrai flag rifiuto e nome branch per push - is_rejected = False # Default - rejected_branch = None # Default - if task_context == "push_remote": - is_rejected = status == "rejected" # Determinato dallo stato specifico - rejected_branch = result_data.get( - "branch_name" - ) # Nome branch passato nel risultato + is_rejected = False # Default + rejected_branch = None # Default + if task_context == 'push_remote': + is_rejected = (status == 'rejected') # Determinato dallo stato specifico + rejected_branch = result_data.get('branch_name') # Nome branch passato nel risultato # Altri dati dal contesto originale - new_branch_context = context.get( - "new_branch_name" - ) # Info se si crea branch - remote_name_context = context.get( - "remote_name" - ) # Nome remote dall'azione originale + new_branch_context = context.get('new_branch_name') # Info se si crea branch + remote_name_context = context.get("remote_name") # Nome remote dall'azione originale # --- Aggiorna Status Bar con colore e reset temporizzato --- status_color = None - reset_duration = 5000 # Default reset 5 secondi - if status == "success": - status_color = self.main_frame.STATUS_GREEN - elif status == "warning": - status_color = self.main_frame.STATUS_YELLOW - reset_duration = 7000 - elif status == "auth_required": - status_color = self.main_frame.STATUS_YELLOW - reset_duration = 15000 - elif status == "conflict": - status_color = self.main_frame.STATUS_RED - reset_duration = 15000 - elif status == "rejected": - status_color = self.main_frame.STATUS_RED - reset_duration = 15000 - elif status == "error": - status_color = self.main_frame.STATUS_RED - reset_duration = 10000 + reset_duration = 5000 # Default reset 5 secondi + if status == 'success': status_color = self.main_frame.STATUS_GREEN + elif status == 'warning': status_color = self.main_frame.STATUS_YELLOW; reset_duration = 7000 + elif status == 'auth_required': status_color = self.main_frame.STATUS_YELLOW; reset_duration = 15000 + elif status == 'conflict': status_color = self.main_frame.STATUS_RED; reset_duration = 15000 + elif status == 'rejected': status_color = self.main_frame.STATUS_RED; reset_duration = 15000 + elif status == 'error': status_color = self.main_frame.STATUS_RED; reset_duration = 10000 # Aggiorna la status bar (usa la funzione helper della GUI) if hasattr(self, "main_frame") and self.main_frame.winfo_exists(): - self.main_frame.update_status_bar( - message, bg_color=status_color, duration_ms=reset_duration - ) + self.main_frame.update_status_bar(message, bg_color=status_color, duration_ms=reset_duration) # --- Processa risultato specifico per task --- # Ottieni path corrente per eventuali refresh - repo_path_for_updates = self._get_and_validate_svn_path( - "Post-Action Update Check" - ) + # Usiamo una variabile separata perché il path per i refresh potrebbe differire da repo_path_conflict + repo_path_for_refreshes = self._get_and_validate_svn_path("Post-Action Refresh Check") # Lista per raccogliere funzioni di refresh da chiamare alla fine refresh_list = [] + # Flag per triggerare refresh stato sync post-azione + post_action_sync_refresh_needed = False # --- Gestione specifica per check_connection e interactive_auth --- if task_context == "check_connection": - remote_name = context.get( - "remote_name_checked", remote_name_context or "unknown remote" - ) - if status == "success": - auth_status = "ok" - if result_value == "connected_empty": - log_handler.log_info( - f"Connection check successful for '{remote_name}' (remote empty/unborn).", - func_name=func_name, - ) - else: - log_handler.log_info( - f"Connection check successful for '{remote_name}'.", - func_name=func_name, - ) + remote_name = context.get("remote_name_checked", remote_name_context or "unknown remote") + if status == 'success': + auth_status = 'ok' + log_handler.log_info(f"Connection check successful for '{remote_name}'.", func_name=func_name) self._update_gui_auth_status(auth_status) - elif status == "auth_required": - log_handler.log_warning( - f"Authentication required for remote '{remote_name}'.", - func_name=func_name, - ) - self._update_gui_auth_status("required") + # Dopo un check OK, aggiorna anche lo stato ahead/behind + post_action_sync_refresh_needed = True + elif status == 'auth_required': + log_handler.log_warning(f"Authentication required for remote '{remote_name}'.", func_name=func_name) + self._update_gui_auth_status('required') repo_path_checked = context.get("repo_path_checked") - if ( - repo_path_checked - and hasattr(self, "main_frame") - and self.main_frame.ask_yes_no( - "Authentication Required", - f"Authentication is required to connect to remote '{remote_name}'.\n\n" - f"Do you want to attempt authentication now?\n" - f"(This may open a separate terminal window for credential input.)", - ) + if repo_path_checked and hasattr(self,"main_frame") and self.main_frame.ask_yes_no( + "Authentication Required", + f"Authentication is required to connect to remote '{remote_name}'.\n\n" + f"Do you want to attempt authentication now?\n" + f"(This may open a separate terminal window for credential input.)" ): - log_handler.log_info( - "User requested interactive authentication attempt.", - func_name=func_name, - ) - args_interactive = ( - self.git_commands, - repo_path_checked, - remote_name, - ) + log_handler.log_info("User requested interactive authentication attempt.", func_name=func_name) + args_interactive = (self.git_commands, repo_path_checked, remote_name) self._start_async_operation( async_workers.run_interactive_auth_attempt_async, args_interactive, - { - "context": "interactive_auth", - "status_msg": f"Attempting interactive auth for '{remote_name}'", - "original_context": context, - }, + { "context": "interactive_auth", "status_msg": f"Attempting interactive auth for '{remote_name}'", "original_context": context } ) else: - log_handler.log_info( - "User declined interactive authentication attempt.", - func_name=func_name, - ) - if ( - hasattr(self, "main_frame") - and self.main_frame.winfo_exists() - ): - self.main_frame.set_action_widgets_state(tk.NORMAL) + log_handler.log_info("User declined interactive authentication attempt.", func_name=func_name) + if hasattr(self, "main_frame") and self.main_frame.winfo_exists(): self.main_frame.set_action_widgets_state(tk.NORMAL) - elif status == "error": - error_type = ( - result_value - if result_value - in ["connection_failed", "unknown_error", "worker_exception"] - else "unknown_error" - ) - self._update_gui_auth_status(error_type) - if hasattr(self, "main_frame"): - self.main_frame.show_error("Connection Error", f"{message}") + elif status == 'error': + error_type = result_value if result_value in ['connection_failed', 'unknown_error', 'worker_exception'] else 'unknown_error' + self._update_gui_auth_status(error_type) # Aggiorna stato auth + if hasattr(self.main_frame, "update_ahead_behind_status"): self.main_frame.update_ahead_behind_status(status_text="Sync Status: Error") # Aggiorna stato sync + if hasattr(self, "main_frame"): self.main_frame.show_error("Connection Error", f"{message}") elif task_context == "interactive_auth": - original_context = context.get("original_context", {}) - remote_name = original_context.get( - "remote_name_checked", remote_name_context or "unknown remote" - ) - if status == "success" and result_value == "auth_attempt_success": - log_handler.log_info( - f"Interactive auth attempt for '{remote_name}' successful. Re-checking connection...", - func_name=func_name, - ) - if hasattr(self, "main_frame"): - self.main_frame.update_status_bar( - f"Authentication successful. Checking status..." - ) - self.check_connection_auth() # Ri-avvia check silenzioso - elif status == "error": - log_handler.log_warning( - f"Interactive auth attempt for '{remote_name}' failed or error occurred: {message}", - func_name=func_name, - ) - self._update_gui_auth_status( - "failed" - ) # Imposta indicatore su fallito - if hasattr(self, "main_frame"): - self.main_frame.show_warning( - "Authentication Attempt Failed", f"{message}" - ) - if hasattr(self, "main_frame") and self.main_frame.winfo_exists(): - self.main_frame.set_action_widgets_state(tk.NORMAL) + original_context = context.get("original_context", {}) + remote_name = original_context.get("remote_name_checked", remote_name_context or "unknown remote") + if status == 'success' and result_value == 'auth_attempt_success': + log_handler.log_info(f"Interactive auth attempt for '{remote_name}' successful. Re-checking connection...", func_name=func_name) + if hasattr(self, "main_frame"): self.main_frame.update_status_bar(f"Authentication successful. Checking status...") + self.check_connection_auth() # Ri-avvia check silenzioso + elif status == 'error': + log_handler.log_warning(f"Interactive auth attempt for '{remote_name}' failed or error occurred: {message}", func_name=func_name) + self._update_gui_auth_status('failed') + if hasattr(self.main_frame, "update_ahead_behind_status"): self.main_frame.update_ahead_behind_status(status_text="Sync Status: Auth Failed") + if hasattr(self, "main_frame"): self.main_frame.show_warning("Authentication Attempt Failed", f"{message}") + if hasattr(self, "main_frame") and self.main_frame.winfo_exists(): self.main_frame.set_action_widgets_state(tk.NORMAL) # --- Gestione specifica per PULL CONFLICT --- - elif task_context == "pull_remote" and status == "conflict": - log_handler.log_error( - f"Merge conflict occurred during pull. User needs to resolve manually in '{repo_path_conflict}'.", - func_name=func_name, - ) - if hasattr(self, "main_frame"): - self.main_frame.show_error( - "Merge Conflict", - f"Merge conflict occurred during pull from '{remote_name_context or 'remote'}'.\n\n" - f"Please resolve the conflicts manually in:\n{repo_path_conflict}\n\n" - f"After resolving, stage the changes and commit them.", - ) - # Dopo un conflitto, aggiorna solo la lista dei file modificati - if self.refresh_changed_files_list not in refresh_list: - refresh_list.append(self.refresh_changed_files_list) - if repo_path_for_updates and self.refresh_changed_files_list: - log_handler.log_debug( - f"Triggering changes refresh after pull conflict.", - func_name=func_name, - ) - try: - self.refresh_changed_files_list() - except Exception as ref_e: - log_handler.log_error( - f"Error triggering changes refresh after conflict: {ref_e}", - func_name=func_name, - ) + elif task_context == 'pull_remote' and status == 'conflict': + log_handler.log_error(f"Merge conflict occurred during pull. User needs to resolve manually in '{repo_path_conflict}'.", func_name=func_name) + if hasattr(self, "main_frame"): + self.main_frame.show_error( + "Merge Conflict", + f"Merge conflict occurred during pull from '{remote_name_context or 'remote'}'.\n\n" + f"Please resolve the conflicts manually in:\n{repo_path_conflict}\n\n" + f"After resolving, stage the changes and commit them." + ) + # Aggiorna solo la lista dei file modificati + if self.refresh_changed_files_list not in refresh_list: refresh_list.append(self.refresh_changed_files_list) + # Resetta stato sync a unknown/error dopo conflitto + if hasattr(self.main_frame, "update_ahead_behind_status"): self.main_frame.update_ahead_behind_status(status_text="Sync Status: Conflict") # --- Gestione specifica per PUSH REJECTED --- - elif task_context == "push_remote" and status == "rejected": - log_handler.log_error( - f"Push rejected for branch '{rejected_branch}'. User needs to pull.", - func_name=func_name, - ) - if hasattr(self, "main_frame"): - self.main_frame.show_warning( - "Push Rejected", - f"{message}", # Il messaggio dal worker è già dettagliato - ) - # Dopo un push rifiutato, suggerisci fetch - if self.fetch_remote not in refresh_list: - refresh_list.append(self.fetch_remote) - if repo_path_for_updates and self.fetch_remote: - log_handler.log_debug( - "Triggering fetch after push rejection.", func_name=func_name - ) - try: - self.fetch_remote() - except Exception as ref_e: - log_handler.log_error( - f"Error triggering fetch after rejection: {ref_e}", - func_name=func_name, - ) + elif task_context == 'push_remote' and status == 'rejected': + log_handler.log_error(f"Push rejected for branch '{rejected_branch}'. User needs to pull.", func_name=func_name) + if hasattr(self, "main_frame"): + self.main_frame.show_warning("Push Rejected", f"{message}") + # Dopo un push rifiutato, suggerisci fetch e aggiorna stato sync + if self.fetch_remote not in refresh_list: refresh_list.append(self.fetch_remote) # Fetch aggiornerà lo stato sync indirettamente # --- Gestione specifica per GET_AHEAD_BEHIND --- - elif task_context == "get_ahead_behind": - if status == "success": - ahead, behind = ( - result_value - if isinstance(result_value, tuple) - else (None, None) - ) - log_handler.log_info( - f"Ahead/Behind status updated: Ahead={ahead}, Behind={behind}", - func_name=func_name, - ) - if hasattr(self.main_frame, "update_ahead_behind_status"): - self.main_frame.update_ahead_behind_status( - ahead=ahead, behind=behind - ) - elif status == "error": - log_handler.log_error( - f"Failed to get ahead/behind status: {message}", - func_name=func_name, - ) - if hasattr(self.main_frame, "update_ahead_behind_status"): - self.main_frame.update_ahead_behind_status( - status_text=f"Sync Status: Error" - ) + elif task_context == 'get_ahead_behind': + local_branch_ctx = context.get("local_branch") + if status == 'success': + ahead, behind = result_value if isinstance(result_value, tuple) else (None, None) + log_handler.log_info(f"Ahead/Behind status updated for '{local_branch_ctx}': Ahead={ahead}, Behind={behind}", func_name=func_name) + if hasattr(self.main_frame, "update_ahead_behind_status"): + self.main_frame.update_ahead_behind_status(current_branch=local_branch_ctx, ahead=ahead, behind=behind) + elif status == 'error': + log_handler.log_error(f"Failed to get ahead/behind status for '{local_branch_ctx}': {message}", func_name=func_name) + if hasattr(self.main_frame, "update_ahead_behind_status"): + self.main_frame.update_ahead_behind_status(current_branch=local_branch_ctx, status_text=f"Sync Status: Error") # --- Gestione risultati altri task (successo) --- - elif status == "success": - # Determina quali refresh avviare in base al task completato - post_action_refresh_needed = ( - False # Flag per refresh stato ahead/behind - ) - if task_context in [ - "prepare_repo", - "fetch_bundle", - "commit", - "create_tag", - "checkout_tag", - "create_branch", - "checkout_branch", - "_handle_gitignore_save", - "add_file", - "apply_remote_config", - "fetch_remote", - "pull_remote", - "push_remote", - "push_tags_remote", - ]: + elif status == 'success': + # Determina quali refresh avviare e se aggiornare lo stato sync + if task_context in ['prepare_repo', 'fetch_bundle', 'commit', 'create_tag', + 'checkout_tag', 'create_branch', 'checkout_branch', + '_handle_gitignore_save', 'add_file', 'apply_remote_config', + 'fetch_remote', 'pull_remote', + 'push_remote', 'push_tags_remote']: # Logica per popolare refresh_list - if task_context == "push_remote": - if self.refresh_commit_history not in refresh_list: - refresh_list.append(self.refresh_commit_history) - if self.refresh_branch_list not in refresh_list: - refresh_list.append(self.refresh_branch_list) - post_action_refresh_needed = True - elif task_context == "push_tags_remote": - if self.refresh_tag_list not in refresh_list: - refresh_list.append(self.refresh_tag_list) - post_action_refresh_needed = ( - True # Anche push tags può cambiare stato relativo - ) - elif task_context == "pull_remote": - if self.refresh_commit_history not in refresh_list: - refresh_list.append(self.refresh_commit_history) - if self.refresh_branch_list not in refresh_list: - refresh_list.append(self.refresh_branch_list) - if self.refresh_tag_list not in refresh_list: - refresh_list.append(self.refresh_tag_list) - if self.refresh_changed_files_list not in refresh_list: - refresh_list.append(self.refresh_changed_files_list) - post_action_refresh_needed = True - elif task_context == "fetch_remote": - if self.refresh_commit_history not in refresh_list: - refresh_list.append(self.refresh_commit_history) - if self.refresh_branch_list not in refresh_list: - refresh_list.append(self.refresh_branch_list) - if self.refresh_tag_list not in refresh_list: - refresh_list.append(self.refresh_tag_list) - post_action_refresh_needed = True - elif task_context == "apply_remote_config": - refresh_list.append(self.check_connection_auth) - if self.refresh_commit_history not in refresh_list: - refresh_list.append(self.refresh_commit_history) - if self.refresh_branch_list not in refresh_list: - refresh_list.append(self.refresh_branch_list) - post_action_refresh_needed = True # Controlla stato dopo apply - # Logica refresh per le altre azioni (non remote) + if task_context == 'push_remote': + if self.refresh_commit_history not in refresh_list: refresh_list.append(self.refresh_commit_history) + if self.refresh_branch_list not in refresh_list: refresh_list.append(self.refresh_branch_list) + post_action_sync_refresh_needed = True + elif task_context == 'push_tags_remote': + if self.refresh_tag_list not in refresh_list: refresh_list.append(self.refresh_tag_list) + post_action_sync_refresh_needed = True + elif task_context == 'pull_remote': # Pull successo (non conflitto) + if self.refresh_commit_history not in refresh_list: refresh_list.append(self.refresh_commit_history) + if self.refresh_branch_list not in refresh_list: refresh_list.append(self.refresh_branch_list) + if self.refresh_tag_list not in refresh_list: refresh_list.append(self.refresh_tag_list) + if self.refresh_changed_files_list not in refresh_list: refresh_list.append(self.refresh_changed_files_list) + post_action_sync_refresh_needed = True + elif task_context == 'fetch_remote': # Fetch successo + if self.refresh_commit_history not in refresh_list: refresh_list.append(self.refresh_commit_history) + if self.refresh_branch_list not in refresh_list: refresh_list.append(self.refresh_branch_list) + if self.refresh_tag_list not in refresh_list: refresh_list.append(self.refresh_tag_list) + post_action_sync_refresh_needed = True + elif task_context == 'apply_remote_config': # Apply Config successo + refresh_list.append(self.check_connection_auth) # Controlla connessione dopo apply + if self.refresh_commit_history not in refresh_list: refresh_list.append(self.refresh_commit_history) + if self.refresh_branch_list not in refresh_list: refresh_list.append(self.refresh_branch_list) + post_action_sync_refresh_needed = True + elif task_context == 'checkout_branch' or task_context == 'checkout_tag': # Cambio branch/stato + post_action_sync_refresh_needed = True + # Aggiungi refresh standard dopo checkout + if self.refresh_commit_history not in refresh_list: refresh_list.append(self.refresh_commit_history) + if self.refresh_branch_list not in refresh_list: refresh_list.append(self.refresh_branch_list) + if self.refresh_tag_list not in refresh_list: refresh_list.append(self.refresh_tag_list) + if self.refresh_changed_files_list not in refresh_list: refresh_list.append(self.refresh_changed_files_list) + elif task_context == 'create_branch' and not new_branch_context: # Creazione senza checkout + post_action_sync_refresh_needed = True # Aggiorna stato sync (sarà no upstream) + if self.refresh_commit_history not in refresh_list: refresh_list.append(self.refresh_commit_history) + if self.refresh_branch_list not in refresh_list: refresh_list.append(self.refresh_branch_list) + + # Logica refresh per le altre azioni locali else: - if committed or task_context in [ - "fetch_bundle", - "prepare_repo", - "create_tag", - "_handle_gitignore_save", - ]: - if self.refresh_commit_history not in refresh_list: - refresh_list.append(self.refresh_commit_history) - if task_context != "refresh_changes": - if self.refresh_changed_files_list not in refresh_list: - refresh_list.append(self.refresh_changed_files_list) - if ( - task_context not in ["refresh_tags", "checkout_tag"] - or committed - ): - if self.refresh_tag_list not in refresh_list: - refresh_list.append(self.refresh_tag_list) - if task_context not in ["refresh_branches", "checkout_branch"]: - if self.refresh_branch_list not in refresh_list: - refresh_list.append(self.refresh_branch_list) + if committed or task_context in ['fetch_bundle','prepare_repo','create_tag','_handle_gitignore_save']: + if self.refresh_commit_history not in refresh_list: refresh_list.append(self.refresh_commit_history) + if task_context != 'refresh_changes': + if self.refresh_changed_files_list not in refresh_list: refresh_list.append(self.refresh_changed_files_list) + if task_context not in ['refresh_tags','checkout_tag'] or committed: + if self.refresh_tag_list not in refresh_list: refresh_list.append(self.refresh_tag_list) + if task_context not in ['refresh_branches', 'checkout_branch']: + if self.refresh_branch_list not in refresh_list: refresh_list.append(self.refresh_branch_list) # --- Aggiornamenti diretti GUI (per i task di refresh stessi) --- - if task_context == "refresh_tags": - if hasattr(self, "main_frame"): - self.main_frame.update_tag_list( - result_value if isinstance(result_value, list) else [] - ) - elif task_context == "refresh_branches": - branches, current = ( - result_value - if isinstance(result_value, tuple) and len(result_value) == 2 - else ([], None) - ) + elif task_context == 'refresh_tags': + if hasattr(self, "main_frame"): self.main_frame.update_tag_list(result_value if isinstance(result_value, list) else []) + elif task_context == 'refresh_branches': + branches, current = result_value if isinstance(result_value, tuple) and len(result_value) == 2 else ([], None) if hasattr(self, "main_frame"): self.main_frame.update_branch_list(branches, current) self.main_frame.update_history_branch_filter(branches) - # Dopo refresh branch, aggiorna anche lo stato sync se possibile - post_action_refresh_needed = True - elif task_context == "refresh_history": - if hasattr(self, "main_frame"): - self.main_frame.update_history_display( - result_value if isinstance(result_value, list) else [] - ) - elif task_context == "refresh_changes": - if hasattr(self, "main_frame"): - self.main_frame.update_changed_files_list( - result_value if isinstance(result_value, list) else [] - ) + post_action_sync_refresh_needed = True # Aggiorna stato sync dopo refresh branch + elif task_context == 'refresh_history': + if hasattr(self, "main_frame"): self.main_frame.update_history_display(result_value if isinstance(result_value, list) else []) + elif task_context == 'refresh_changes': + if hasattr(self, "main_frame"): self.main_frame.update_changed_files_list(result_value if isinstance(result_value, list) else []) # --- Azioni post-successo specifiche --- - if task_context == "commit" and committed: - if hasattr(self, "main_frame"): - self.main_frame.clear_commit_message() - if task_context == "create_branch" and new_branch_context: - if hasattr(self, "main_frame") and self.main_frame.ask_yes_no( - "Checkout?", f"Switch to new branch '{new_branch_context}'?" - ): - self.checkout_branch( - branch_to_checkout=new_branch_context, - repo_path_override=repo_path_for_updates, - ) - # Se non fa checkout, assicurati che la history venga aggiornata - # e anche lo stato sync (perché siamo su un nuovo branch senza upstream) - elif self.refresh_commit_history not in refresh_list: - refresh_list.append(self.refresh_commit_history) - post_action_refresh_needed = True # Aggiorna stato sync dopo creazione branch (sarà 'no upstream') + if task_context == 'commit' and committed: + if hasattr(self, "main_frame"): self.main_frame.clear_commit_message() + if task_context == 'create_branch' and new_branch_context: + if hasattr(self, "main_frame") and self.main_frame.ask_yes_no("Checkout?", f"Switch to new branch '{new_branch_context}'?"): + # Avvia checkout asincrono (che triggererà i suoi refresh) + self.checkout_branch(branch_to_checkout=new_branch_context, repo_path_override=repo_path_for_refreshes) + post_action_sync_refresh_needed = False # Verrà fatto dopo il checkout + # Se non fa checkout, i refresh sono già in lista e post_action_sync_refresh_needed è True - # --- Trigger finale dei refresh asincroni raccolti --- - if repo_path_for_updates and refresh_list: - log_handler.log_debug( - f"Triggering {len(refresh_list)} async refreshes after '{task_context}'", - func_name=func_name, - ) - for refresh_func in refresh_list: - try: - refresh_func() - except Exception as ref_e: - log_handler.log_error( - f"Error triggering {getattr(refresh_func, '__name__', 'refresh function')}: {ref_e}", - func_name=func_name, - ) - elif refresh_list: - log_handler.log_warning( - "Cannot trigger post-action UI refreshes: Repo path unavailable.", - func_name=func_name, - ) + elif status == 'warning': + # Gestione warning generica: mostra popup + if hasattr(self, "main_frame"): self.main_frame.show_warning("Operation Info", message) + # Logica specifica per warning "already prepared" + if "already prepared" in message: + if self.refresh_changed_files_list not in refresh_list: refresh_list.append(self.refresh_changed_files_list) + if self.refresh_branch_list not in refresh_list: refresh_list.append(self.refresh_branch_list) + post_action_sync_refresh_needed = True - # --- Triggera refresh stato ahead/behind se necessario --- - if post_action_refresh_needed: - # Solo se il repo è ancora valido (potrebbe essere cambiato da un checkout) - current_repo_path = self._get_and_validate_svn_path( - "Post-Action Sync Status Check" - ) - if current_repo_path: # Usa path corrente, non quello vecchio - log_handler.log_debug( - f"Triggering remote sync status refresh after '{task_context}'.", - func_name=func_name, - ) - # Usa after per separarlo leggermente dal ciclo corrente e permettere GUI update - self.master.after( - 100, self.refresh_remote_status - ) # Leggero ritardo + elif status == 'error': + # Gestione errori generica (esclusi check_connection, interactive_auth, pull_conflict, push_rejected, get_ahead_behind) + log_handler.log_error(f"Error reported for task '{task_context}': {message}", func_name=func_name) + error_details = f"{message}\n({type(exception).__name__}: {exception})" if exception else message - elif status == "warning": - # Gestione warning generica: mostra popup - if hasattr(self, "main_frame"): - self.main_frame.show_warning("Operation Info", message) - # Logica specifica per warning "already prepared" - if "already prepared" in message: - if self.refresh_changed_files_list not in refresh_list: - refresh_list.append(self.refresh_changed_files_list) - if self.refresh_branch_list not in refresh_list: - refresh_list.append(self.refresh_branch_list) - post_action_refresh_needed = True # Aggiorna stato sync anche qui - if repo_path_for_updates and refresh_list: - log_handler.log_debug( - "Triggering refreshes after 'already prepared' warning.", - func_name=func_name, - ) - for rf in refresh_list: - rf() - if post_action_refresh_needed: - self.master.after(100, self.refresh_remote_status) + # Gestione errore per fetch_remote, pull (non conflitto), push (non rifiuto), push_tags + if task_context in ['fetch_remote', 'pull_remote', 'push_remote', 'push_tags_remote', 'apply_remote_config']: + auth_related_error = False; conn_related_error = False + if isinstance(exception, GitCommandError) and exception.stderr: stderr_low = exception.stderr.lower(); + if any(e in stderr_low for e in ["authentication failed", "permission denied", "could not read"]): auth_related_error = True; + if any(e in stderr_low for e in ["repository not found", "could not resolve host"]): conn_related_error = True + if auth_related_error: self._update_gui_auth_status('failed') + elif conn_related_error: self._update_gui_auth_status('connection_failed') + else: self._update_gui_auth_status('unknown_error') + # Mostra popup specifico del task + action_name = task_context.replace("_remote", "").replace("_", " ").title() + if hasattr(self, "main_frame"): self.main_frame.show_error(f"{action_name} Error", f"{message}") + # Resetta stato sync su errore + if hasattr(self.main_frame, "update_ahead_behind_status"): self.main_frame.update_ahead_behind_status(status_text="Sync Status: Error") - elif status == "error": - # Gestione errori generica (esclusi check_connection, interactive_auth, pull_conflict, push_rejected, get_ahead_behind) - log_handler.log_error( - f"Error reported for task '{task_context}': {message}", - func_name=func_name, - ) - error_details = ( - f"{message}\n({type(exception).__name__}: {exception})" - if exception - else message - ) - - # --- Gestione errore per fetch_remote --- - if task_context == "fetch_remote": - auth_related_error = False - conn_related_error = False - if isinstance(exception, GitCommandError) and exception.stderr: - stderr_low = exception.stderr.lower() - if any( - e in stderr_low - for e in [ - "authentication failed", - "permission denied", - "could not read", - ] - ): - auth_related_error = True - if any( - e in stderr_low - for e in ["repository not found", "could not resolve host"] - ): - conn_related_error = True - if auth_related_error: - self._update_gui_auth_status("failed") - elif conn_related_error: - self._update_gui_auth_status("connection_failed") - else: - self._update_gui_auth_status("unknown_error") - if hasattr(self, "main_frame"): - self.main_frame.show_error("Fetch Error", f"{message}") - # Resetta stato ahead/behind su errore fetch - if hasattr(self.main_frame, "update_ahead_behind_status"): - self.main_frame.update_ahead_behind_status( - status_text="Sync Status: Error" - ) - - # --- Gestione errore per PULL (non conflitto) --- - elif task_context == "pull_remote": - auth_related_error = False - conn_related_error = False - upst_related_error = False - if isinstance(exception, GitCommandError) and exception.stderr: - stderr_low = exception.stderr.lower() - if any( - e in stderr_low - for e in [ - "authentication failed", - "permission denied", - "could not read", - ] - ): - auth_related_error = True - if any( - e in stderr_low - for e in ["repository not found", "could not resolve host"] - ): - conn_related_error = True - if any( - e in stderr_low - for e in ["no tracking information", "unrelated histories"] - ): - upst_related_error = True - if auth_related_error: - self._update_gui_auth_status("failed") - elif conn_related_error: - self._update_gui_auth_status("connection_failed") - else: - self._update_gui_auth_status("unknown_error") - if hasattr(self, "main_frame"): - self.main_frame.show_error("Pull Error", f"{message}") - if hasattr(self.main_frame, "update_ahead_behind_status"): - self.main_frame.update_ahead_behind_status( - status_text="Sync Status: Error" - ) - - # --- Gestione errore per PUSH (non rifiuto) e PUSH TAGS --- - elif ( - task_context == "push_remote" or task_context == "push_tags_remote" - ): - action_name = ( - "Push Branch" if task_context == "push_remote" else "Push Tags" - ) - auth_related_error = False - conn_related_error = False - if isinstance(exception, GitCommandError) and exception.stderr: - stderr_low = exception.stderr.lower() - if any( - e in stderr_low - for e in [ - "authentication failed", - "permission denied", - "could not read", - ] - ): - auth_related_error = True - if any( - e in stderr_low - for e in ["repository not found", "could not resolve host"] - ): - conn_related_error = True - if auth_related_error: - self._update_gui_auth_status("failed") - elif conn_related_error: - self._update_gui_auth_status("connection_failed") - else: - self._update_gui_auth_status("unknown_error") - if hasattr(self, "main_frame"): - self.main_frame.show_error(f"{action_name} Error", f"{message}") - if hasattr(self.main_frame, "update_ahead_behind_status"): - self.main_frame.update_ahead_behind_status( - status_text="Sync Status: Error" - ) - - # --- Gestione errori per altri task --- - else: - # Mostra popup specifici o generico - if ( - is_conflict - and repo_path_conflict - and task_context == "fetch_bundle" - ): - if hasattr(self, "main_frame"): - self.main_frame.show_error( - "Merge Conflict", - f"Conflict occurred during bundle fetch.\nResolve in:\n{repo_path_conflict}\nThen commit.", - ) + # Gestione errori per altri task + else: + if is_conflict and repo_path_conflict and task_context == 'fetch_bundle': + if hasattr(self, "main_frame"): self.main_frame.show_error("Merge Conflict", f"Conflict occurred during bundle fetch.\nResolve in:\n{repo_path_conflict}\nThen commit.") elif exception and "Uncommitted changes" in str(exception): - if hasattr(self, "main_frame"): - self.main_frame.show_warning( - "Action Blocked", f"{exception}\nCommit or stash first." - ) + if hasattr(self, "main_frame"): self.main_frame.show_warning("Action Blocked", f"{exception}\nCommit or stash first.") else: - if hasattr(self, "main_frame"): - self.main_frame.show_error( - "Error: Operation Failed", error_details - ) + if hasattr(self, "main_frame"): self.main_frame.show_error("Error: Operation Failed", error_details) + + # Aggiorna liste GUI con stato errore + if task_context == 'refresh_tags': + if hasattr(self, "main_frame"): self.main_frame.update_tag_list([("(Error)", "")]) + elif task_context == 'refresh_branches': + if hasattr(self, "main_frame"): + self.main_frame.update_branch_list([], None) + self.main_frame.update_history_branch_filter([]) + if hasattr(self.main_frame, "update_ahead_behind_status"): self.main_frame.update_ahead_behind_status(status_text="Sync Status: Error") + elif task_context == 'refresh_history': + if hasattr(self, "main_frame"): self.main_frame.update_history_display(["(Error retrieving history)"]) + elif task_context == 'refresh_changes': + if hasattr(self, "main_frame"): self.main_frame.update_changed_files_list(["(Error refreshing changes)"]) + # Non serve aggiornare stato auth/sync per errori locali generici + + + # --- Trigger finale dei refresh asincroni raccolti (spostato dopo il blocco if/elif/else principale) --- + if repo_path_for_refreshes and refresh_list: + log_handler.log_debug(f"Triggering {len(refresh_list)} async refreshes after '{task_context}'", func_name=func_name) + # Usa 'after' per separare leggermente l'avvio dei refresh dal ciclo corrente + delay_ms = 50 + for refresh_func in refresh_list: + try: + self.master.after(delay_ms, refresh_func) + delay_ms += 20 # Scaletta leggermente i refresh + except Exception as ref_e: + log_handler.log_error(f"Error scheduling {getattr(refresh_func, '__name__', 'refresh function')}: {ref_e}", func_name=func_name) + elif refresh_list: + log_handler.log_warning("Cannot trigger post-action UI refreshes: Repo path unavailable.", func_name=func_name) + + # Triggera refresh stato ahead/behind SE necessario e non già in refresh_list + if post_action_sync_refresh_needed and self.refresh_remote_status not in refresh_list: + current_repo_path = self._get_and_validate_svn_path("Post-Action Sync Status Check") + if current_repo_path: + log_handler.log_debug(f"Triggering remote sync status refresh after '{task_context}'.", func_name=func_name) + self.master.after(delay_ms + 50, self.refresh_remote_status) # Dopo gli altri refresh - # Aggiorna liste GUI con stato errore (se applicabile al task) - if task_context == "refresh_tags": - if hasattr(self, "main_frame"): - self.main_frame.update_tag_list([("(Error)", "")]) - elif task_context == "refresh_branches": - if hasattr(self, "main_frame"): - self.main_frame.update_branch_list([], None) - self.main_frame.update_history_branch_filter([]) - # Se fallisce refresh branch, stato sync è sconosciuto - if hasattr(self.main_frame, "update_ahead_behind_status"): - self.main_frame.update_ahead_behind_status( - status_text="Sync Status: Unknown" - ) - elif task_context == "refresh_history": - if hasattr(self, "main_frame"): - self.main_frame.update_history_display( - ["(Error retrieving history)"] - ) - elif task_context == "refresh_changes": - if hasattr(self, "main_frame"): - self.main_frame.update_changed_files_list( - ["(Error refreshing changes)"] - ) - elif task_context == "apply_remote_config": - self._update_gui_auth_status( - "unknown_error" - ) # Errore durante apply config - if hasattr(self.main_frame, "update_ahead_behind_status"): - self.main_frame.update_ahead_behind_status( - status_text="Sync Status: Unknown" - ) # Log finale solo se non è stata gestita una ricorsione/nuovo avvio if should_reenable_now: - log_handler.log_debug( - f"Finished processing result for context '{task_context}'.", - func_name=func_name, - ) + log_handler.log_debug(f"Finished processing result for context '{task_context}'.", func_name=func_name) except queue.Empty: # Coda vuota, riprogramma check se la finestra esiste ancora if hasattr(self, "master") and self.master.winfo_exists(): - self.master.after( - self.ASYNC_QUEUE_CHECK_INTERVAL_MS, - self._check_completion_queue, - results_queue, - context, - ) + self.master.after(self.ASYNC_QUEUE_CHECK_INTERVAL_MS, self._check_completion_queue, results_queue, context) except Exception as e: - # Errore critico nel processare la coda stessa - log_handler.log_exception( - f"Critical error processing completion queue for {task_context}: {e}", - func_name=func_name, - ) - # Tenta recupero GUI - try: - if hasattr(self, "main_frame") and self.main_frame.winfo_exists(): - self.main_frame.set_action_widgets_state( - tk.NORMAL - ) # Tenta riabilitazione - self.main_frame.update_status_bar( - "Error processing async result.", - bg_color=self.main_frame.STATUS_RED, - duration_ms=10000, - ) - # Resetta anche stato sync su errore grave - if hasattr(self.main_frame, "update_ahead_behind_status"): - self.main_frame.update_ahead_behind_status( - status_text="Sync Status: Error" - ) - except Exception as recovery_e: - log_handler.log_error( - f"Failed to recover GUI after queue processing error: {recovery_e}", - func_name=func_name, - ) + # Errore critico nel processare la coda stessa + log_handler.log_exception(f"Critical error processing completion queue for {task_context}: {e}", func_name=func_name) + # Tenta recupero GUI + try: + if hasattr(self, "main_frame") and self.main_frame.winfo_exists(): + self.main_frame.set_action_widgets_state(tk.NORMAL) # Tenta riabilitazione + self.main_frame.update_status_bar("Error processing async result.", bg_color=self.main_frame.STATUS_RED, duration_ms=10000) + if hasattr(self.main_frame, "update_ahead_behind_status"): self.main_frame.update_ahead_behind_status(status_text="Sync Status: Error") + except Exception as recovery_e: + log_handler.log_error(f"Failed to recover GUI after queue processing error: {recovery_e}", func_name=func_name) # --- Helper Methods (interni alla classe) --- def _generate_next_tag_suggestion(self, svn_path: str) -> str: diff --git a/gui.py b/gui.py index 30d4e11..49ad986 100644 --- a/gui.py +++ b/gui.py @@ -714,18 +714,15 @@ class MainFrame(ttk.Frame): status_frame.columnconfigure(0, weight=1) # Label si espande # Label per Ahead/Behind - self.ahead_behind_label = ttk.Label( + self.sync_status_label = ttk.Label( status_frame, textvariable=self.remote_ahead_behind_var, # Collegato alla variabile anchor=tk.W, # Allinea testo a sinistra # relief=tk.SUNKEN, # Forse senza bordo è meglio? padding=(5, 2), ) - self.ahead_behind_label.grid(row=0, column=0, sticky=tk.EW, padx=(5, 10)) - self.create_tooltip( - self.ahead_behind_label, - "Indicates if the local branch is ahead (needs push) or behind (needs pull) the remote branch.", - ) + self.sync_status_label.grid(row=0, column=0, sticky=tk.EW, padx=(5, 10)) + self.create_tooltip(self.sync_status_label, "Shows the current local branch and its sync status (ahead/behind) relative to its upstream.") # Pulsante Refresh Status self.refresh_sync_status_button = ttk.Button( @@ -737,10 +734,7 @@ class MainFrame(ttk.Frame): self.refresh_sync_status_button.grid( row=0, column=1, sticky=tk.E, padx=(0, 5), pady=2 ) - self.create_tooltip( - self.refresh_sync_status_button, - "Check how many commits the local branch is ahead or behind the remote branch.", - ) + self.create_tooltip(self.refresh_sync_status_button, "Check sync status (ahead/behind) for the current branch.") # --- Sezione Azioni Remote --- actions_frame = ttk.LabelFrame(frame, text="Remote Actions", padding=(10, 5)) @@ -1935,43 +1929,56 @@ class MainFrame(ttk.Frame): def update_ahead_behind_status( self, + current_branch: str | None = None, # Aggiunto parametro branch status_text: str | None = None, ahead: int | None = None, - behind: int | None = None, - ): - """Updates the ahead/behind status label.""" - label = getattr(self, "ahead_behind_label", None) + behind: int | None = None + ): + """Updates the synchronization status label, including the current branch.""" + label = getattr(self, "sync_status_label", None) # Usa nuovo nome label var = getattr(self, "remote_ahead_behind_var", None) if not label or not var or not label.winfo_exists(): return - text_to_display = "Sync Status: Unknown" # Default + # Determina il testo da visualizzare + if current_branch: + branch_part = f"Branch '{current_branch}': " + else: + # Se non conosciamo il branch (es. detached head o errore iniziale) + branch_part = "Current Branch: " + + status_part = "Unknown" # Default if status_text is not None: - # Se viene passato un testo specifico (es. errore, no upstream), usa quello - text_to_display = status_text + # Testo esplicito fornito (es. errore, no upstream, detached) + status_part = status_text # Il testo dovrebbe già includere "Sync Status:" o simile + # Sovrascrivi branch_part se il testo contiene già info sul branch o stato + if "Branch" in status_part or "Detached" in status_part or "Upstream" in status_part: + text_to_display = status_part # Usa direttamente il testo fornito + else: + text_to_display = branch_part + status_part elif ahead is not None and behind is not None: - # Se abbiamo i conteggi, costruisci il messaggio + # Costruisci messaggio da conteggi if ahead == 0 and behind == 0: - text_to_display = "Sync Status: Up to date" + status_part = "Up to date" else: parts = [] if ahead > 0: - plural_a = "s" if ahead > 1 else "" + plural_a = 's' if ahead > 1 else '' parts.append(f"{ahead} commit{plural_a} ahead (Push needed)") if behind > 0: - plural_b = "s" if behind > 1 else "" + plural_b = 's' if behind > 1 else '' parts.append(f"{behind} commit{plural_b} behind (Pull needed)") - text_to_display = "Sync Status: " + ", ".join(parts) - # else: Se non viene passato testo e i conteggi sono None, rimane "Unknown" + status_part = ", ".join(parts) + text_to_display = branch_part + status_part + else: # Caso di default o conteggi None senza testo esplicito + text_to_display = branch_part + "Unknown Status" + try: var.set(text_to_display) except Exception as e: - log_handler.log_error( - f"Failed to update ahead/behind status variable: {e}", - func_name="update_ahead_behind_status", - ) + log_handler.log_error(f"Failed to update sync status variable: {e}", func_name="update_ahead_behind_status") # --- END OF FILE gui.py ---