add Ahead/Behind function
This commit is contained in:
parent
bebfb9a22a
commit
af83b49380
250
GitUtility.py
250
GitUtility.py
@ -164,6 +164,7 @@ class GitSvnSyncApp:
|
|||||||
# === Dati / Istanze per la GUI ===
|
# === Dati / Istanze per la GUI ===
|
||||||
config_manager_instance=self.config_manager,
|
config_manager_instance=self.config_manager,
|
||||||
profile_sections_list=self.config_manager.get_profile_sections(),
|
profile_sections_list=self.config_manager.get_profile_sections(),
|
||||||
|
refresh_remote_status_cb=self.refresh_remote_status,
|
||||||
)
|
)
|
||||||
print("MainFrame GUI created.")
|
print("MainFrame GUI created.")
|
||||||
log_handler.log_debug(
|
log_handler.log_debug(
|
||||||
@ -2371,6 +2372,104 @@ class GitSvnSyncApp:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def refresh_remote_status(self):
|
||||||
|
"""Starts the async check for ahead/behind status."""
|
||||||
|
func_name = "refresh_remote_status"
|
||||||
|
log_handler.log_info(
|
||||||
|
f"--- Action Triggered: Refresh Remote Sync Status ---", func_name=func_name
|
||||||
|
)
|
||||||
|
|
||||||
|
# Validazioni
|
||||||
|
if not hasattr(self, "main_frame") or not self.main_frame.winfo_exists():
|
||||||
|
return
|
||||||
|
svn_path = self._get_and_validate_svn_path("Refresh Sync Status")
|
||||||
|
if not svn_path or not self._is_repo_ready(svn_path):
|
||||||
|
log_handler.log_warning(
|
||||||
|
"Refresh Status skipped: Repo not ready.", func_name=func_name
|
||||||
|
)
|
||||||
|
# Aggiorna label a stato neutro/sconosciuto se repo non pronto
|
||||||
|
if hasattr(self.main_frame, "update_ahead_behind_status"):
|
||||||
|
self.main_frame.update_ahead_behind_status(
|
||||||
|
status_text="Sync Status: (Repo not ready)"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# --- Ottieni branch corrente e upstream ---
|
||||||
|
current_branch = None
|
||||||
|
upstream_branch = None
|
||||||
|
try:
|
||||||
|
current_branch = self.git_commands.get_current_branch_name(svn_path)
|
||||||
|
if current_branch:
|
||||||
|
upstream_branch = self.git_commands.get_branch_upstream(
|
||||||
|
svn_path, current_branch
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
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)"
|
||||||
|
)
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
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
|
||||||
|
|
||||||
|
# Se siamo qui, abbiamo branch e upstream, abilita il pulsante refresh (se era disabilitato)
|
||||||
|
if hasattr(self.main_frame, "refresh_sync_status_button"):
|
||||||
|
self.main_frame.refresh_sync_status_button.config(state=tk.NORMAL)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log_handler.log_exception(
|
||||||
|
f"Error getting branch/upstream before status check: {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 getting info"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# --- Avvia worker asincrono ---
|
||||||
|
log_handler.log_info(
|
||||||
|
f"Checking ahead/behind status for '{current_branch}' vs '{upstream_branch}'...",
|
||||||
|
func_name=func_name,
|
||||||
|
)
|
||||||
|
# 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..."
|
||||||
|
)
|
||||||
|
|
||||||
|
args = (self.git_commands, svn_path, current_branch, upstream_branch)
|
||||||
|
self._start_async_operation(
|
||||||
|
async_workers.run_get_ahead_behind_async, # Worker esterno
|
||||||
|
args,
|
||||||
|
{
|
||||||
|
"context": "get_ahead_behind", # Contesto per il risultato
|
||||||
|
"status_msg": f"Checking sync status for '{current_branch}'",
|
||||||
|
# Passa nomi branch nel contesto per riferimento nel risultato
|
||||||
|
"local_branch": current_branch,
|
||||||
|
"upstream_branch": upstream_branch,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def _update_gui_auth_status(self, status: str):
|
def _update_gui_auth_status(self, status: str):
|
||||||
"""Updates internal state and calls GUI update for auth indicator."""
|
"""Updates internal state and calls GUI update for auth indicator."""
|
||||||
self.remote_auth_status = status # Aggiorna stato interno
|
self.remote_auth_status = status # Aggiorna stato interno
|
||||||
@ -2416,6 +2515,8 @@ class GitSvnSyncApp:
|
|||||||
"Delaying widget re-enable: re-checking connection after interactive auth.",
|
"Delaying widget re-enable: re-checking connection after interactive auth.",
|
||||||
func_name=func_name,
|
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
|
# Riabilita i widget se non è necessario attendere
|
||||||
if should_reenable_now:
|
if should_reenable_now:
|
||||||
@ -2423,11 +2524,12 @@ class GitSvnSyncApp:
|
|||||||
log_handler.log_debug("Re-enabling widgets.", func_name=func_name)
|
log_handler.log_debug("Re-enabling widgets.", func_name=func_name)
|
||||||
self.main_frame.set_action_widgets_state(tk.NORMAL)
|
self.main_frame.set_action_widgets_state(tk.NORMAL)
|
||||||
else:
|
else:
|
||||||
|
# Se la GUI non c'è, non c'è nulla da fare o riabilitare
|
||||||
log_handler.log_warning(
|
log_handler.log_warning(
|
||||||
"Cannot re-enable widgets, MainFrame missing.",
|
"Cannot re-enable widgets, MainFrame missing.",
|
||||||
func_name=func_name,
|
func_name=func_name,
|
||||||
)
|
)
|
||||||
return # Esce dalla funzione se la GUI non c'è più
|
return # Esce dalla funzione
|
||||||
|
|
||||||
# --- Estrai dettagli dal risultato ---
|
# --- Estrai dettagli dal risultato ---
|
||||||
status = status_from_result # Usa la variabile già ottenuta
|
status = status_from_result # Usa la variabile già ottenuta
|
||||||
@ -2513,10 +2615,18 @@ class GitSvnSyncApp:
|
|||||||
)
|
)
|
||||||
if status == "success":
|
if status == "success":
|
||||||
auth_status = "ok"
|
auth_status = "ok"
|
||||||
# (...) log info (...)
|
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,
|
||||||
|
)
|
||||||
self._update_gui_auth_status(auth_status)
|
self._update_gui_auth_status(auth_status)
|
||||||
elif status == "auth_required":
|
elif status == "auth_required":
|
||||||
# (...) log warning, update gui status, askyesno (...)
|
|
||||||
log_handler.log_warning(
|
log_handler.log_warning(
|
||||||
f"Authentication required for remote '{remote_name}'.",
|
f"Authentication required for remote '{remote_name}'.",
|
||||||
func_name=func_name,
|
func_name=func_name,
|
||||||
@ -2526,8 +2636,13 @@ class GitSvnSyncApp:
|
|||||||
if (
|
if (
|
||||||
repo_path_checked
|
repo_path_checked
|
||||||
and hasattr(self, "main_frame")
|
and hasattr(self, "main_frame")
|
||||||
and self.main_frame.ask_yes_no(...)
|
and self.main_frame.ask_yes_no(
|
||||||
): # Messaggio invariato
|
"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(
|
log_handler.log_info(
|
||||||
"User requested interactive authentication attempt.",
|
"User requested interactive authentication attempt.",
|
||||||
func_name=func_name,
|
func_name=func_name,
|
||||||
@ -2558,7 +2673,6 @@ class GitSvnSyncApp:
|
|||||||
self.main_frame.set_action_widgets_state(tk.NORMAL)
|
self.main_frame.set_action_widgets_state(tk.NORMAL)
|
||||||
|
|
||||||
elif status == "error":
|
elif status == "error":
|
||||||
# (... gestione errori connessione/sconosciuto e aggiornamento indicatore ...)
|
|
||||||
error_type = (
|
error_type = (
|
||||||
result_value
|
result_value
|
||||||
if result_value
|
if result_value
|
||||||
@ -2570,7 +2684,6 @@ class GitSvnSyncApp:
|
|||||||
self.main_frame.show_error("Connection Error", f"{message}")
|
self.main_frame.show_error("Connection Error", f"{message}")
|
||||||
|
|
||||||
elif task_context == "interactive_auth":
|
elif task_context == "interactive_auth":
|
||||||
# (... gestione risultato interactive_auth e riavvio check ...)
|
|
||||||
original_context = context.get("original_context", {})
|
original_context = context.get("original_context", {})
|
||||||
remote_name = original_context.get(
|
remote_name = original_context.get(
|
||||||
"remote_name_checked", remote_name_context or "unknown remote"
|
"remote_name_checked", remote_name_context or "unknown remote"
|
||||||
@ -2584,13 +2697,15 @@ class GitSvnSyncApp:
|
|||||||
self.main_frame.update_status_bar(
|
self.main_frame.update_status_bar(
|
||||||
f"Authentication successful. Checking status..."
|
f"Authentication successful. Checking status..."
|
||||||
)
|
)
|
||||||
self.check_connection_auth()
|
self.check_connection_auth() # Ri-avvia check silenzioso
|
||||||
elif status == "error":
|
elif status == "error":
|
||||||
log_handler.log_warning(
|
log_handler.log_warning(
|
||||||
f"Interactive auth attempt for '{remote_name}' failed or error occurred: {message}",
|
f"Interactive auth attempt for '{remote_name}' failed or error occurred: {message}",
|
||||||
func_name=func_name,
|
func_name=func_name,
|
||||||
)
|
)
|
||||||
self._update_gui_auth_status("failed")
|
self._update_gui_auth_status(
|
||||||
|
"failed"
|
||||||
|
) # Imposta indicatore su fallito
|
||||||
if hasattr(self, "main_frame"):
|
if hasattr(self, "main_frame"):
|
||||||
self.main_frame.show_warning(
|
self.main_frame.show_warning(
|
||||||
"Authentication Attempt Failed", f"{message}"
|
"Authentication Attempt Failed", f"{message}"
|
||||||
@ -2607,7 +2722,7 @@ class GitSvnSyncApp:
|
|||||||
if hasattr(self, "main_frame"):
|
if hasattr(self, "main_frame"):
|
||||||
self.main_frame.show_error(
|
self.main_frame.show_error(
|
||||||
"Merge Conflict",
|
"Merge Conflict",
|
||||||
f"Merge conflict occurred during pull from '{remote_name_context or 'remote'}'.\n\n" # Usa nome dal contesto se disponibile
|
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"Please resolve the conflicts manually in:\n{repo_path_conflict}\n\n"
|
||||||
f"After resolving, stage the changes and commit them.",
|
f"After resolving, stage the changes and commit them.",
|
||||||
)
|
)
|
||||||
@ -2653,9 +2768,38 @@ class GitSvnSyncApp:
|
|||||||
func_name=func_name,
|
func_name=func_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# --- 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"
|
||||||
|
)
|
||||||
|
|
||||||
# --- Gestione risultati altri task (successo) ---
|
# --- Gestione risultati altri task (successo) ---
|
||||||
elif status == "success":
|
elif status == "success":
|
||||||
# Determina quali refresh avviare in base al task completato
|
# Determina quali refresh avviare in base al task completato
|
||||||
|
post_action_refresh_needed = (
|
||||||
|
False # Flag per refresh stato ahead/behind
|
||||||
|
)
|
||||||
if task_context in [
|
if task_context in [
|
||||||
"prepare_repo",
|
"prepare_repo",
|
||||||
"fetch_bundle",
|
"fetch_bundle",
|
||||||
@ -2673,15 +2817,19 @@ class GitSvnSyncApp:
|
|||||||
"push_tags_remote",
|
"push_tags_remote",
|
||||||
]:
|
]:
|
||||||
# Logica per popolare refresh_list
|
# Logica per popolare refresh_list
|
||||||
if task_context == "push_remote": # Push Branch successo
|
if task_context == "push_remote":
|
||||||
if self.refresh_commit_history not in refresh_list:
|
if self.refresh_commit_history not in refresh_list:
|
||||||
refresh_list.append(self.refresh_commit_history)
|
refresh_list.append(self.refresh_commit_history)
|
||||||
if self.refresh_branch_list not in refresh_list:
|
if self.refresh_branch_list not in refresh_list:
|
||||||
refresh_list.append(self.refresh_branch_list)
|
refresh_list.append(self.refresh_branch_list)
|
||||||
elif task_context == "push_tags_remote": # Push Tags successo
|
post_action_refresh_needed = True
|
||||||
|
elif task_context == "push_tags_remote":
|
||||||
if self.refresh_tag_list not in refresh_list:
|
if self.refresh_tag_list not in refresh_list:
|
||||||
refresh_list.append(self.refresh_tag_list)
|
refresh_list.append(self.refresh_tag_list)
|
||||||
elif task_context == "pull_remote": # Pull successo (non conflitto)
|
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:
|
if self.refresh_commit_history not in refresh_list:
|
||||||
refresh_list.append(self.refresh_commit_history)
|
refresh_list.append(self.refresh_commit_history)
|
||||||
if self.refresh_branch_list not in refresh_list:
|
if self.refresh_branch_list not in refresh_list:
|
||||||
@ -2690,22 +2838,23 @@ class GitSvnSyncApp:
|
|||||||
refresh_list.append(self.refresh_tag_list)
|
refresh_list.append(self.refresh_tag_list)
|
||||||
if self.refresh_changed_files_list not in refresh_list:
|
if self.refresh_changed_files_list not in refresh_list:
|
||||||
refresh_list.append(self.refresh_changed_files_list)
|
refresh_list.append(self.refresh_changed_files_list)
|
||||||
elif task_context == "fetch_remote": # Fetch successo
|
post_action_refresh_needed = True
|
||||||
|
elif task_context == "fetch_remote":
|
||||||
if self.refresh_commit_history not in refresh_list:
|
if self.refresh_commit_history not in refresh_list:
|
||||||
refresh_list.append(self.refresh_commit_history)
|
refresh_list.append(self.refresh_commit_history)
|
||||||
if self.refresh_branch_list not in refresh_list:
|
if self.refresh_branch_list not in refresh_list:
|
||||||
refresh_list.append(self.refresh_branch_list)
|
refresh_list.append(self.refresh_branch_list)
|
||||||
if self.refresh_tag_list not in refresh_list:
|
if self.refresh_tag_list not in refresh_list:
|
||||||
refresh_list.append(self.refresh_tag_list)
|
refresh_list.append(self.refresh_tag_list)
|
||||||
elif task_context == "apply_remote_config": # Apply Config successo
|
post_action_refresh_needed = True
|
||||||
refresh_list.append(
|
elif task_context == "apply_remote_config":
|
||||||
self.check_connection_auth
|
refresh_list.append(self.check_connection_auth)
|
||||||
) # Controlla connessione dopo apply
|
|
||||||
if self.refresh_commit_history not in refresh_list:
|
if self.refresh_commit_history not in refresh_list:
|
||||||
refresh_list.append(self.refresh_commit_history)
|
refresh_list.append(self.refresh_commit_history)
|
||||||
if self.refresh_branch_list not in refresh_list:
|
if self.refresh_branch_list not in refresh_list:
|
||||||
refresh_list.append(self.refresh_branch_list)
|
refresh_list.append(self.refresh_branch_list)
|
||||||
# Logica refresh per le altre azioni
|
post_action_refresh_needed = True # Controlla stato dopo apply
|
||||||
|
# Logica refresh per le altre azioni (non remote)
|
||||||
else:
|
else:
|
||||||
if committed or task_context in [
|
if committed or task_context in [
|
||||||
"fetch_bundle",
|
"fetch_bundle",
|
||||||
@ -2743,6 +2892,8 @@ class GitSvnSyncApp:
|
|||||||
if hasattr(self, "main_frame"):
|
if hasattr(self, "main_frame"):
|
||||||
self.main_frame.update_branch_list(branches, current)
|
self.main_frame.update_branch_list(branches, current)
|
||||||
self.main_frame.update_history_branch_filter(branches)
|
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":
|
elif task_context == "refresh_history":
|
||||||
if hasattr(self, "main_frame"):
|
if hasattr(self, "main_frame"):
|
||||||
self.main_frame.update_history_display(
|
self.main_frame.update_history_display(
|
||||||
@ -2766,8 +2917,11 @@ class GitSvnSyncApp:
|
|||||||
branch_to_checkout=new_branch_context,
|
branch_to_checkout=new_branch_context,
|
||||||
repo_path_override=repo_path_for_updates,
|
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:
|
elif self.refresh_commit_history not in refresh_list:
|
||||||
refresh_list.append(self.refresh_commit_history)
|
refresh_list.append(self.refresh_commit_history)
|
||||||
|
post_action_refresh_needed = True # Aggiorna stato sync dopo creazione branch (sarà 'no upstream')
|
||||||
|
|
||||||
# --- Trigger finale dei refresh asincroni raccolti ---
|
# --- Trigger finale dei refresh asincroni raccolti ---
|
||||||
if repo_path_for_updates and refresh_list:
|
if repo_path_for_updates and refresh_list:
|
||||||
@ -2789,6 +2943,22 @@ class GitSvnSyncApp:
|
|||||||
func_name=func_name,
|
func_name=func_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# --- 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 == "warning":
|
elif status == "warning":
|
||||||
# Gestione warning generica: mostra popup
|
# Gestione warning generica: mostra popup
|
||||||
if hasattr(self, "main_frame"):
|
if hasattr(self, "main_frame"):
|
||||||
@ -2799,6 +2969,7 @@ class GitSvnSyncApp:
|
|||||||
refresh_list.append(self.refresh_changed_files_list)
|
refresh_list.append(self.refresh_changed_files_list)
|
||||||
if self.refresh_branch_list not in refresh_list:
|
if self.refresh_branch_list not in refresh_list:
|
||||||
refresh_list.append(self.refresh_branch_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:
|
if repo_path_for_updates and refresh_list:
|
||||||
log_handler.log_debug(
|
log_handler.log_debug(
|
||||||
"Triggering refreshes after 'already prepared' warning.",
|
"Triggering refreshes after 'already prepared' warning.",
|
||||||
@ -2806,9 +2977,11 @@ class GitSvnSyncApp:
|
|||||||
)
|
)
|
||||||
for rf in refresh_list:
|
for rf in refresh_list:
|
||||||
rf()
|
rf()
|
||||||
|
if post_action_refresh_needed:
|
||||||
|
self.master.after(100, self.refresh_remote_status)
|
||||||
|
|
||||||
elif status == "error":
|
elif status == "error":
|
||||||
# Gestione errori generica (esclusi check_connection, interactive_auth, pull_conflict, push_rejected)
|
# Gestione errori generica (esclusi check_connection, interactive_auth, pull_conflict, push_rejected, get_ahead_behind)
|
||||||
log_handler.log_error(
|
log_handler.log_error(
|
||||||
f"Error reported for task '{task_context}': {message}",
|
f"Error reported for task '{task_context}': {message}",
|
||||||
func_name=func_name,
|
func_name=func_name,
|
||||||
@ -2819,7 +2992,7 @@ class GitSvnSyncApp:
|
|||||||
else message
|
else message
|
||||||
)
|
)
|
||||||
|
|
||||||
# Gestione errore per fetch_remote
|
# --- Gestione errore per fetch_remote ---
|
||||||
if task_context == "fetch_remote":
|
if task_context == "fetch_remote":
|
||||||
auth_related_error = False
|
auth_related_error = False
|
||||||
conn_related_error = False
|
conn_related_error = False
|
||||||
@ -2847,13 +3020,17 @@ class GitSvnSyncApp:
|
|||||||
self._update_gui_auth_status("unknown_error")
|
self._update_gui_auth_status("unknown_error")
|
||||||
if hasattr(self, "main_frame"):
|
if hasattr(self, "main_frame"):
|
||||||
self.main_frame.show_error("Fetch Error", f"{message}")
|
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)
|
# --- Gestione errore per PULL (non conflitto) ---
|
||||||
elif task_context == "pull_remote":
|
elif task_context == "pull_remote":
|
||||||
auth_related_error = False
|
auth_related_error = False
|
||||||
conn_related_error = False
|
conn_related_error = False
|
||||||
upst_related_error = False
|
upst_related_error = False
|
||||||
stderr_low = ""
|
|
||||||
if isinstance(exception, GitCommandError) and exception.stderr:
|
if isinstance(exception, GitCommandError) and exception.stderr:
|
||||||
stderr_low = exception.stderr.lower()
|
stderr_low = exception.stderr.lower()
|
||||||
if any(
|
if any(
|
||||||
@ -2865,7 +3042,6 @@ class GitSvnSyncApp:
|
|||||||
]
|
]
|
||||||
):
|
):
|
||||||
auth_related_error = True
|
auth_related_error = True
|
||||||
|
|
||||||
if any(
|
if any(
|
||||||
e in stderr_low
|
e in stderr_low
|
||||||
for e in ["repository not found", "could not resolve host"]
|
for e in ["repository not found", "could not resolve host"]
|
||||||
@ -2884,8 +3060,12 @@ class GitSvnSyncApp:
|
|||||||
self._update_gui_auth_status("unknown_error")
|
self._update_gui_auth_status("unknown_error")
|
||||||
if hasattr(self, "main_frame"):
|
if hasattr(self, "main_frame"):
|
||||||
self.main_frame.show_error("Pull Error", f"{message}")
|
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
|
# --- Gestione errore per PUSH (non rifiuto) e PUSH TAGS ---
|
||||||
elif (
|
elif (
|
||||||
task_context == "push_remote" or task_context == "push_tags_remote"
|
task_context == "push_remote" or task_context == "push_tags_remote"
|
||||||
):
|
):
|
||||||
@ -2918,8 +3098,12 @@ class GitSvnSyncApp:
|
|||||||
self._update_gui_auth_status("unknown_error")
|
self._update_gui_auth_status("unknown_error")
|
||||||
if hasattr(self, "main_frame"):
|
if hasattr(self, "main_frame"):
|
||||||
self.main_frame.show_error(f"{action_name} Error", f"{message}")
|
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
|
# --- Gestione errori per altri task ---
|
||||||
else:
|
else:
|
||||||
# Mostra popup specifici o generico
|
# Mostra popup specifici o generico
|
||||||
if (
|
if (
|
||||||
@ -2951,6 +3135,11 @@ class GitSvnSyncApp:
|
|||||||
if hasattr(self, "main_frame"):
|
if hasattr(self, "main_frame"):
|
||||||
self.main_frame.update_branch_list([], None)
|
self.main_frame.update_branch_list([], None)
|
||||||
self.main_frame.update_history_branch_filter([])
|
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":
|
elif task_context == "refresh_history":
|
||||||
if hasattr(self, "main_frame"):
|
if hasattr(self, "main_frame"):
|
||||||
self.main_frame.update_history_display(
|
self.main_frame.update_history_display(
|
||||||
@ -2965,6 +3154,10 @@ class GitSvnSyncApp:
|
|||||||
self._update_gui_auth_status(
|
self._update_gui_auth_status(
|
||||||
"unknown_error"
|
"unknown_error"
|
||||||
) # Errore durante apply config
|
) # 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
|
# Log finale solo se non è stata gestita una ricorsione/nuovo avvio
|
||||||
if should_reenable_now:
|
if should_reenable_now:
|
||||||
@ -2999,6 +3192,11 @@ class GitSvnSyncApp:
|
|||||||
bg_color=self.main_frame.STATUS_RED,
|
bg_color=self.main_frame.STATUS_RED,
|
||||||
duration_ms=10000,
|
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:
|
except Exception as recovery_e:
|
||||||
log_handler.log_error(
|
log_handler.log_error(
|
||||||
f"Failed to recover GUI after queue processing error: {recovery_e}",
|
f"Failed to recover GUI after queue processing error: {recovery_e}",
|
||||||
|
|||||||
@ -1205,4 +1205,93 @@ def run_push_tags_async(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# --- END OF FILE async_workers.py ---
|
# --- END OF FILE async_workers.py ---
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import re
|
|||||||
|
|
||||||
# Importa il nuovo gestore della coda log
|
# Importa il nuovo gestore della coda log
|
||||||
import log_handler
|
import log_handler
|
||||||
|
from typing import Tuple, Dict, List
|
||||||
|
|
||||||
|
|
||||||
# --- Custom Exception Definition (invariata) ---
|
# --- Custom Exception Definition (invariata) ---
|
||||||
@ -1656,5 +1657,90 @@ class GitCommands:
|
|||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_ahead_behind_count(
|
||||||
|
self, working_directory: str, local_branch: str, upstream_branch: str
|
||||||
|
) -> Tuple[int | None, int | None]:
|
||||||
|
"""
|
||||||
|
Gets the number of commits the local branch is ahead and behind its upstream counterpart.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
working_directory (str): Path to the repository.
|
||||||
|
local_branch (str): The name of the local branch.
|
||||||
|
upstream_branch (str): The full name of the upstream branch (e.g., 'origin/main').
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[int | None, int | None]: A tuple containing (ahead_count, behind_count).
|
||||||
|
Returns (None, None) if the command fails or
|
||||||
|
branches are invalid/unrelated.
|
||||||
|
"""
|
||||||
|
func_name = "get_ahead_behind_count"
|
||||||
|
log_handler.log_debug(
|
||||||
|
f"Getting ahead/behind count for '{local_branch}'...'{upstream_branch}' in '{working_directory}'",
|
||||||
|
func_name=func_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Comando: git rev-list --count --left-right <local_branch>...<upstream_branch>
|
||||||
|
# L'output è nel formato: <behind_count>\t<ahead_count>
|
||||||
|
# Usiamo '...' per la differenza simmetrica.
|
||||||
|
cmd = [
|
||||||
|
"git",
|
||||||
|
"rev-list",
|
||||||
|
"--count",
|
||||||
|
"--left-right",
|
||||||
|
f"{local_branch}...{upstream_branch}",
|
||||||
|
]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Esegui catturando output, nascondendo console. check=False perché potrebbe fallire
|
||||||
|
# se i branch non esistono o non hanno un antenato comune.
|
||||||
|
result = self.log_and_execute(
|
||||||
|
cmd,
|
||||||
|
working_directory,
|
||||||
|
check=False,
|
||||||
|
capture=True,
|
||||||
|
hide_console=True,
|
||||||
|
log_output_level=logging.DEBUG,
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode == 0 and result.stdout:
|
||||||
|
output = result.stdout.strip()
|
||||||
|
parts = output.split() # Divide per spazio o tab
|
||||||
|
if len(parts) == 2:
|
||||||
|
try:
|
||||||
|
behind_count = int(
|
||||||
|
parts[0]
|
||||||
|
) # Il primo numero è 'behind' (--left-right A...B -> A è left)
|
||||||
|
ahead_count = int(parts[1]) # Il secondo numero è 'ahead'
|
||||||
|
log_handler.log_info(
|
||||||
|
f"Ahead/Behind for '{local_branch}': Ahead={ahead_count}, Behind={behind_count}",
|
||||||
|
func_name=func_name,
|
||||||
|
)
|
||||||
|
return ahead_count, behind_count
|
||||||
|
except ValueError:
|
||||||
|
log_handler.log_error(
|
||||||
|
f"Failed to parse rev-list count output: '{output}'",
|
||||||
|
func_name=func_name,
|
||||||
|
)
|
||||||
|
return None, None
|
||||||
|
else:
|
||||||
|
log_handler.log_error(
|
||||||
|
f"Unexpected output format from rev-list count: '{output}'",
|
||||||
|
func_name=func_name,
|
||||||
|
)
|
||||||
|
return None, None
|
||||||
|
else:
|
||||||
|
# Il comando potrebbe fallire se i branch non sono validi, non hanno antenato comune, ecc.
|
||||||
|
log_handler.log_warning(
|
||||||
|
f"Failed to get ahead/behind count (RC={result.returncode}). Maybe invalid branches or no common history? Stderr: {result.stderr.strip() if result.stderr else 'N/A'}",
|
||||||
|
func_name=func_name,
|
||||||
|
)
|
||||||
|
return None, None # Segnala fallimento
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log_handler.log_exception(
|
||||||
|
f"Unexpected error getting ahead/behind count: {e}", func_name=func_name
|
||||||
|
)
|
||||||
|
return None, None # Segnala fallimento
|
||||||
|
|
||||||
|
|
||||||
# --- END OF FILE git_commands.py ---
|
# --- END OF FILE git_commands.py ---
|
||||||
|
|||||||
80
gui.py
80
gui.py
@ -448,6 +448,7 @@ class MainFrame(ttk.Frame):
|
|||||||
pull_remote_cb,
|
pull_remote_cb,
|
||||||
push_remote_cb,
|
push_remote_cb,
|
||||||
push_tags_remote_cb,
|
push_tags_remote_cb,
|
||||||
|
refresh_remote_status_cb,
|
||||||
):
|
):
|
||||||
"""Initializes the MainFrame."""
|
"""Initializes the MainFrame."""
|
||||||
super().__init__(master)
|
super().__init__(master)
|
||||||
@ -483,6 +484,7 @@ class MainFrame(ttk.Frame):
|
|||||||
self.pull_remote_callback = pull_remote_cb
|
self.pull_remote_callback = pull_remote_cb
|
||||||
self.push_remote_callback = push_remote_cb
|
self.push_remote_callback = push_remote_cb
|
||||||
self.push_tags_remote_callback = push_tags_remote_cb
|
self.push_tags_remote_callback = push_tags_remote_cb
|
||||||
|
self.refresh_remote_status_callback = refresh_remote_status_cb
|
||||||
|
|
||||||
# Configure style (invariato)
|
# Configure style (invariato)
|
||||||
self.style = ttk.Style()
|
self.style = ttk.Style()
|
||||||
@ -513,6 +515,7 @@ class MainFrame(ttk.Frame):
|
|||||||
self.remote_url_var = tk.StringVar()
|
self.remote_url_var = tk.StringVar()
|
||||||
self.remote_name_var = tk.StringVar()
|
self.remote_name_var = tk.StringVar()
|
||||||
self.remote_auth_status_var = tk.StringVar(value="Status: Unknown")
|
self.remote_auth_status_var = tk.StringVar(value="Status: Unknown")
|
||||||
|
self.remote_ahead_behind_var = tk.StringVar(value="Sync Status: Unknown")
|
||||||
|
|
||||||
# --- Create UI Elements ---
|
# --- Create UI Elements ---
|
||||||
self._create_profile_frame()
|
self._create_profile_frame()
|
||||||
@ -703,6 +706,42 @@ class MainFrame(ttk.Frame):
|
|||||||
# Imposta colore iniziale (es. grigio)
|
# Imposta colore iniziale (es. grigio)
|
||||||
self._update_auth_status_indicator("unknown")
|
self._update_auth_status_indicator("unknown")
|
||||||
|
|
||||||
|
# --- Sync Status ---
|
||||||
|
status_frame = ttk.LabelFrame(
|
||||||
|
frame, text="Synchronization Status", padding=(10, 5)
|
||||||
|
)
|
||||||
|
status_frame.pack(pady=(5, 5), fill="x", expand=False)
|
||||||
|
status_frame.columnconfigure(0, weight=1) # Label si espande
|
||||||
|
|
||||||
|
# Label per Ahead/Behind
|
||||||
|
self.ahead_behind_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.",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Pulsante Refresh Status
|
||||||
|
self.refresh_sync_status_button = ttk.Button(
|
||||||
|
status_frame,
|
||||||
|
text="Refresh Status",
|
||||||
|
command=self.refresh_remote_status_callback, # Nuovo callback
|
||||||
|
state=tk.DISABLED, # Abilitato quando repo pronto e upstream configurato?
|
||||||
|
)
|
||||||
|
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.",
|
||||||
|
)
|
||||||
|
|
||||||
# --- Sezione Azioni Remote ---
|
# --- Sezione Azioni Remote ---
|
||||||
actions_frame = ttk.LabelFrame(frame, text="Remote Actions", padding=(10, 5))
|
actions_frame = ttk.LabelFrame(frame, text="Remote Actions", padding=(10, 5))
|
||||||
actions_frame.pack(pady=10, fill="x", expand=False)
|
actions_frame.pack(pady=10, fill="x", expand=False)
|
||||||
@ -1862,6 +1901,7 @@ class MainFrame(ttk.Frame):
|
|||||||
self.pull_button,
|
self.pull_button,
|
||||||
self.push_button,
|
self.push_button,
|
||||||
self.push_tags_button,
|
self.push_tags_button,
|
||||||
|
self.refresh_sync_status_button,
|
||||||
]
|
]
|
||||||
# log_handler.log_debug(f"Setting action widgets state to: {state}", func_name="set_action_widgets_state") # Usa log_handler
|
# log_handler.log_debug(f"Setting action widgets state to: {state}", func_name="set_action_widgets_state") # Usa log_handler
|
||||||
for widget in widgets:
|
for widget in widgets:
|
||||||
@ -1893,5 +1933,45 @@ class MainFrame(ttk.Frame):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass # log_handler.log_error(f"Error setting state for profile dropdown: {e}", func_name="set_action_widgets_state")
|
pass # log_handler.log_error(f"Error setting state for profile dropdown: {e}", func_name="set_action_widgets_state")
|
||||||
|
|
||||||
|
def update_ahead_behind_status(
|
||||||
|
self,
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
|
||||||
|
if status_text is not None:
|
||||||
|
# Se viene passato un testo specifico (es. errore, no upstream), usa quello
|
||||||
|
text_to_display = status_text
|
||||||
|
elif ahead is not None and behind is not None:
|
||||||
|
# Se abbiamo i conteggi, costruisci il messaggio
|
||||||
|
if ahead == 0 and behind == 0:
|
||||||
|
text_to_display = "Sync Status: Up to date"
|
||||||
|
else:
|
||||||
|
parts = []
|
||||||
|
if ahead > 0:
|
||||||
|
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 ""
|
||||||
|
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"
|
||||||
|
|
||||||
|
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",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# --- END OF FILE gui.py ---
|
# --- END OF FILE gui.py ---
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user