add autocommit when create tag

This commit is contained in:
VALLONGOL 2025-04-18 13:29:55 +02:00
parent e3bf97eeb5
commit 96a916ca09
3 changed files with 161 additions and 45 deletions

View File

@ -6,7 +6,7 @@ import datetime
import tkinter as tk import tkinter as tk
from tkinter import messagebox, filedialog # Assicurati che filedialog sia importato from tkinter import messagebox, filedialog # Assicurati che filedialog sia importato
import logging import logging
import re
# Rimosso zipfile, threading, queue # Rimosso zipfile, threading, queue
# Import application modules # Import application modules
@ -987,27 +987,98 @@ class GitSvnSyncApp:
# Aggiorna GUI in ogni caso (anche con lista vuota o errore) # Aggiorna GUI in ogni caso (anche con lista vuota o errore)
if hasattr(self.main_frame, "update_tag_list"): if hasattr(self.main_frame, "update_tag_list"):
self.main_frame.update_tag_list(tags_data) self.main_frame.update_tag_list(tags_data)
def _generate_next_tag_suggestion(self, svn_path):
"""
Generates a suggested tag name based on the latest tag matching v.X.X.X.X pattern.
Args:
svn_path (str): Path to the repository.
Returns:
str: The suggested tag name (e.g., "v.0.0.0.1" or incremented version).
"""
self.logger.debug("Generating next tag suggestion...")
default_suggestion = "v.0.0.0.1"
latest_valid_tag = None
tag_pattern = re.compile(r"^v\.(\d{1,2})\.(\d{1,2})\.(\d{1,2})\.(\d{1,2})$")
try:
# Ottieni i tag ordinati, dal più recente
# list_tags restituisce [(name, subject), ...]
tags_data = self.git_commands.list_tags(svn_path)
if not tags_data:
self.logger.debug("No existing tags found. Suggesting default.")
return default_suggestion
# Cerca il primo tag che corrisponde al pattern
for tag_name, _ in tags_data:
match = tag_pattern.match(tag_name)
if match:
latest_valid_tag = tag_name
self.logger.debug(f"Found latest tag matching pattern: {latest_valid_tag}")
break # Trovato il più recente, esci
if not latest_valid_tag:
self.logger.debug("No tags matched the pattern v.X.X.X.X. Suggesting default.")
return default_suggestion
# Estrai e incrementa i numeri
match = tag_pattern.match(latest_valid_tag) # Riesegui match per sicurezza
if not match: # Non dovrebbe succedere, ma controllo difensivo
self.logger.error(f"Internal error: Could not re-match tag {latest_valid_tag}")
return default_suggestion
v1, v2, v3, v4 = map(int, match.groups())
# Incrementa gestendo i riporti da 99 a 0
v4 += 1
if v4 > 99:
v4 = 0
v3 += 1
if v3 > 99:
v3 = 0
v2 += 1
if v2 > 99:
v2 = 0
v1 += 1
# Non mettiamo limiti a v1 per ora (può diventare > 99)
next_tag = f"v.{v1}.{v2}.{v3}.{v4}"
self.logger.debug(f"Generated suggestion: {next_tag}")
return next_tag
except Exception as e:
self.logger.error(f"Error generating tag suggestion: {e}", exc_info=True)
return default_suggestion # Ritorna il default in caso di errore
def create_tag(self): def create_tag(self):
"""Handles 'Create Tag' (Synchronous after dialog).""" """Handles 'Create Tag' (Synchronous after dialog), suggesting the next tag name."""
self.logger.info("--- Action Triggered: Create Tag ---") self.logger.info("--- Action Triggered: Create Tag ---")
svn_path = self._get_and_validate_svn_path("Create Tag") svn_path = self._get_and_validate_svn_path("Create Tag")
if not svn_path: if not svn_path:
return return
# --- MODIFICA: Genera suggerimento PRIMA di aprire il dialogo ---
suggested_tag = self._generate_next_tag_suggestion(svn_path)
# --- FINE MODIFICA ---
# Ottieni input utente (Dialog sincrono) # Ottieni input utente (Dialog sincrono)
dialog = CreateTagDialog(self.master) # --- MODIFICA: Passa il suggerimento al Dialog ---
dialog = CreateTagDialog(self.master, suggested_tag_name=suggested_tag)
# --- FINE MODIFICA ---
tag_info = dialog.result tag_info = dialog.result
if tag_info: if tag_info:
tag_name, tag_message = tag_info tag_name, tag_message = tag_info
if ( # (Logica commit pre-tag e chiamata ad action_handler invariata)
not self.save_profile_settings() if not self.save_profile_settings():
): # Salva messaggio commit per pre-commit
if not self.main_frame.ask_yes_no( if not self.main_frame.ask_yes_no(
"Save Warning", "Could not save profile settings.\nContinue anyway?" "Save Warning", "Could not save profile settings.\nContinue anyway?"
): ):
return return
commit_message = self.main_frame.get_commit_message() commit_message = self.main_frame.get_commit_message()
# Esecuzione sincrona
try: try:
success = self.action_handler.execute_create_tag( success = self.action_handler.execute_create_tag(
svn_path, commit_message, tag_name, tag_message svn_path, commit_message, tag_name, tag_message
@ -1016,7 +1087,7 @@ class GitSvnSyncApp:
self.main_frame.show_info("Success", f"Tag '{tag_name}' created.") self.main_frame.show_info("Success", f"Tag '{tag_name}' created.")
self.refresh_tag_list() self.refresh_tag_list()
self.refresh_commit_history() self.refresh_commit_history()
self.refresh_branch_list() # Aggiorna UI self.refresh_branch_list()
except (GitCommandError, ValueError) as e: except (GitCommandError, ValueError) as e:
self.logger.error(f"Failed create tag '{tag_name}': {e}", exc_info=True) self.logger.error(f"Failed create tag '{tag_name}': {e}", exc_info=True)
self.main_frame.show_error("Tag Error", f"Failed:\n{e}") self.main_frame.show_error("Tag Error", f"Failed:\n{e}")

View File

@ -390,41 +390,74 @@ class ActionHandler:
self.logger.exception(f"Unexpected manual commit error: {e}") self.logger.exception(f"Unexpected manual commit error: {e}")
raise Exception("Unexpected manual commit error") from e raise Exception("Unexpected manual commit error") from e
def execute_create_tag(self, svn_path, commit_message, tag_name, tag_message): def execute_create_tag(self, svn_path, commit_message_ignored, tag_name, tag_message):
"""Executes tag creation, including pre-commit using commit_message if needed.""" """
# (Keep original or improved version - no backup involved) Executes tag creation. Always attempts to commit staged changes using
the tag message before creating the annotated tag.
Args:
svn_path (str): Path to the repository.
commit_message_ignored (str): Commit message from GUI (IGNORED for this function).
tag_name (str): The name for the new tag.
tag_message (str): The annotation message for the tag (also used for the commit).
Returns:
bool: True if the tag was created successfully.
Raises:
ValueError: If tag_name or tag_message are empty/invalid (checked by GitCommands).
GitCommandError/Exception: If commit or tag creation fails.
"""
# 1. Validazione Input Essenziali
# La validità del formato tag_name sarà controllata da git_commands.create_tag
# Ma controlliamo che non siano vuoti qui, specialmente tag_message che serve per il commit.
if not tag_name or tag_name.isspace():
# Questo check è leggermente ridondante perché git lo bloccherebbe, ma è buona pratica validare prima.
raise ValueError("Tag name cannot be empty.")
if not tag_message or tag_message.isspace(): if not tag_message or tag_message.isspace():
raise ValueError("Tag annotation message cannot be empty.") # Fondamentale perché ora serve anche per il commit.
raise ValueError("Tag message cannot be empty (used for commit and tag).")
self.logger.info(f"Executing create tag '{tag_name}' for: {svn_path}") self.logger.info(f"Executing create tag '{tag_name}' for: {svn_path}")
try: # Pre-commit self.logger.info("Attempting pre-tag commit using tag message...")
has_changes = self.git_commands.git_status_has_changes(svn_path)
if has_changes: try:
self.logger.info("Uncommitted changes detected before tagging.") # 2. Esegui Commit (con il messaggio del tag)
if not commit_message or commit_message.isspace(): # La funzione git_commit in GitCommands dovrebbe già gestire:
raise ValueError( # - Staging di tutte le modifiche ('git add .')
"Changes exist. Commit message required before tagging." # - Esecuzione di 'git commit -m "tag_message"'
) # - Gestione del caso "nothing to commit" (restituendo False)
self.logger.info(f"Performing pre-tag commit: '{commit_message}'") # - Sollevare GitCommandError in caso di fallimento reale del commit
self.git_commands.git_commit(svn_path, commit_message) commit_made = self.git_commands.git_commit(svn_path, tag_message)
if commit_made:
self.logger.info(f"Pre-tag commit successful with message: '{tag_message}'")
else: else:
self.logger.info("No uncommitted changes detected.") self.logger.info("No changes detected; no pre-tag commit was necessary.")
except (GitCommandError, ValueError) as e:
self.logger.error(f"Pre-tag commit error: {e}", exc_info=True) # 3. Crea il Tag Annotato (usando lo stesso messaggio)
raise e # Procedi indipendentemente dal fatto che il commit abbia effettivamente
except Exception as e: # committato qualcosa, purché non abbia fallito.
self.logger.exception(f"Unexpected pre-tag commit error: {e}") self.logger.info(f"Creating annotated tag '{tag_name}'...")
raise Exception("Unexpected pre-tag commit error") from e # La funzione create_tag in GitCommands dovrebbe gestire:
try: # Create Tag # - Validazione del formato del tag_name (regex)
self.logger.info(f"Proceeding to create tag '{tag_name}'...") # - Esecuzione di 'git tag -a <tag_name> -m "tag_message"'
# - Gestione dell'errore "tag already exists"
self.git_commands.create_tag(svn_path, tag_name, tag_message) self.git_commands.create_tag(svn_path, tag_name, tag_message)
self.logger.info(f"Tag '{tag_name}' created successfully.") self.logger.info(f"Tag '{tag_name}' created successfully.")
return True return True # L'operazione complessiva è riuscita
except (GitCommandError, ValueError) as e: except (GitCommandError, ValueError) as e:
self.logger.error(f"Failed create tag '{tag_name}': {e}", exc_info=True) # Cattura errori specifici da commit o tag
# ValueError potrebbe venire dalla validazione iniziale o da git_commit
self.logger.error(f"Failed to commit or create tag '{tag_name}': {e}", exc_info=True)
# Rilancia l'errore specifico per essere gestito dalla GUI
raise e raise e
except Exception as e: except Exception as e:
self.logger.exception(f"Unexpected tag error: {e}") # Cattura qualsiasi altro errore imprevisto
raise Exception("Unexpected tag creation error") from e self.logger.exception(f"Unexpected error during commit/tag process for '{tag_name}': {e}")
# Rilancia un'eccezione generica ma informativa
raise Exception(f"Unexpected error creating tag '{tag_name}'") from e
def execute_checkout_tag(self, svn_path, tag_name): def execute_checkout_tag(self, svn_path, tag_name):
"""Executes checkout for the specified tag after checking changes.""" """Executes checkout for the specified tag after checking changes."""

32
gui.py
View File

@ -249,10 +249,14 @@ class GitignoreEditorWindow(tk.Toplevel):
# --- Create Tag Dialog --- # --- Create Tag Dialog ---
# (No changes needed here) # (No changes needed here)
class CreateTagDialog(simpledialog.Dialog): class CreateTagDialog(simpledialog.Dialog):
def __init__(self, parent, title="Create New Tag"): def __init__(self, parent, title="Create New Tag", suggested_tag_name=""):
self.tag_name_var = tk.StringVar() self.tag_name_var = tk.StringVar()
self.tag_message_var = tk.StringVar() self.tag_message_var = tk.StringVar()
self.result = None self.result = None
# --- MODIFICA: Memorizza il suggerimento ---
self.suggested_tag_name = suggested_tag_name
# --- FINE MODIFICA ---
# Chiamare super() alla fine o dopo aver inizializzato le variabili usate in body/validate
super().__init__(parent, title=title) super().__init__(parent, title=title)
def body(self, master): def body(self, master):
@ -264,6 +268,11 @@ class CreateTagDialog(simpledialog.Dialog):
) )
self.name_entry = ttk.Entry(frame, textvariable=self.tag_name_var, width=40) self.name_entry = ttk.Entry(frame, textvariable=self.tag_name_var, width=40)
self.name_entry.grid(row=0, column=1, padx=5, pady=5, sticky="ew") self.name_entry.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
# --- MODIFICA: Imposta valore iniziale ---
if self.suggested_tag_name:
self.tag_name_var.set(self.suggested_tag_name)
# --- FINE MODIFICA ---
ttk.Label(frame, text="Tag Message:").grid( ttk.Label(frame, text="Tag Message:").grid(
row=1, column=0, padx=5, pady=5, sticky="w" row=1, column=0, padx=5, pady=5, sticky="w"
) )
@ -271,24 +280,27 @@ class CreateTagDialog(simpledialog.Dialog):
frame, textvariable=self.tag_message_var, width=40 frame, textvariable=self.tag_message_var, width=40
) )
self.message_entry.grid(row=1, column=1, padx=5, pady=5, sticky="ew") self.message_entry.grid(row=1, column=1, padx=5, pady=5, sticky="ew")
return self.name_entry # Ritorna il widget che deve avere il focus iniziale
return self.name_entry # O self.message_entry se si preferisce
# La validazione del nome tag ora avviene in GitCommands.create_tag
# Manteniamo solo i controlli per non vuoto.
def validate(self): def validate(self):
name = self.tag_name_var.get().strip() name = self.tag_name_var.get().strip()
msg = self.tag_message_var.get().strip() msg = self.tag_message_var.get().strip()
if not name: if not name:
messagebox.showwarning("Input Error", "Tag name empty.", parent=self) messagebox.showwarning("Input Error", "Tag name cannot be empty.", parent=self)
return 0 return 0
if not msg: if not msg:
messagebox.showwarning("Input Error", "Tag message empty.", parent=self) messagebox.showwarning("Input Error", "Tag message cannot be empty.", parent=self)
return 0
pattern = r"^(?![./]|.*([./]{2,}|[.]$|\.lock$))[^ \t\n\r\f\v~^:?*[\\]+(?<!\.)$"
if not re.match(pattern, name):
messagebox.showwarning(
"Input Error", "Invalid tag name format.", parent=self
)
return 0 return 0
# Rimuovi il controllo regex da qui, verrà fatto da GitCommands
# pattern = r"^(?![./]|.*([./]{2,}|[.]$|\.lock$))[^ \t\n\r\f\v~^:?*[\\]+(?<!\.)$"
# if not re.match(pattern, name):
# messagebox.showwarning("Input Error", "Invalid tag name format.", parent=self)
# return 0
return 1 return 1
# --- FINE MODIFICA ---
def apply(self): def apply(self):
self.result = ( self.result = (