prima versione

This commit is contained in:
VALLONGOL 2025-03-24 11:31:44 +01:00
commit 5b195acca8

309
Git_Utility.py Normal file
View File

@ -0,0 +1,309 @@
import os
import subprocess
import logging
import tkinter as tk
from tkinter import ttk, scrolledtext, filedialog, messagebox
import configparser
# Configurazione
CONFIG_FILE = "git_svn_sync.ini"
LOG_FILE = "git_svn_sync.log"
DEFAULT_PROFILE = "default"
GREEN = "#90EE90" # Light Green
RED = "#F08080" # Light Coral
class GitSvnSyncApp:
def __init__(self, master):
self.master = master
master.title("Git SVN Sync")
# Configurazione del logging (come nella versione precedente)
self.logger = logging.getLogger(__name__)
self.logger.setLevel(logging.INFO)
self.log_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
self.file_handler = logging.FileHandler(LOG_FILE)
self.file_handler.setLevel(logging.INFO)
self.file_handler.setFormatter(self.log_formatter)
self.logger.addHandler(self.file_handler)
self.log_text = scrolledtext.ScrolledText(master, height=10, width=80)
self.log_text.pack(pady=10)
self.log_text.config(state=tk.DISABLED)
self.text_handler = TextHandler(self.log_text)
self.text_handler.setLevel(logging.INFO)
self.text_handler.setFormatter(self.log_formatter)
self.logger.addHandler(self.text_handler)
# Carica le configurazioni
self.config = configparser.ConfigParser()
self.load_config()
# Frame per la selezione del profilo
self.profile_frame = ttk.Frame(master)
self.profile_frame.pack(pady=5)
self.profile_label = ttk.Label(self.profile_frame, text="Profile:")
self.profile_label.pack(side=tk.LEFT)
self.profile_var = tk.StringVar()
self.profile_dropdown = ttk.Combobox(self.profile_frame, textvariable=self.profile_var, state="readonly")
self.profile_dropdown.pack(side=tk.LEFT)
self.profile_dropdown['values'] = self.config.sections()
if DEFAULT_PROFILE in self.config.sections():
self.profile_var.set(DEFAULT_PROFILE)
else:
self.profile_var.set(self.config.sections()[0] if self.config.sections() else "")
self.profile_var.trace("w", self.load_profile_settings) # Chiama load_profile_settings quando il profilo cambia
self.add_profile_button = ttk.Button(self.profile_frame, text="Add Profile", command=self.add_profile)
self.add_profile_button.pack(side=tk.LEFT, padx=5)
self.remove_profile_button = ttk.Button(self.profile_frame, text="Remove Profile", command=self.remove_profile)
self.remove_profile_button.pack(side=tk.LEFT)
# Etichette e campi di input (organizzati in un frame)
self.settings_frame = ttk.Frame(master)
self.settings_frame.pack(pady=5)
self.svn_path_label = ttk.Label(self.settings_frame, text="SVN Working Copy Path:")
self.svn_path_label.grid(row=0, column=0, sticky=tk.W)
self.svn_path_entry = ttk.Entry(self.settings_frame, width=60)
self.svn_path_entry.grid(row=0, column=1, sticky=tk.W)
self.svn_path_browse_button = ttk.Button(self.settings_frame, text="Browse",
command=lambda: self.browse_folder(self.svn_path_entry))
self.svn_path_browse_button.grid(row=0, column=2, sticky=tk.W)
# Indicatore dello stato del repository SVN
self.svn_status_indicator = ttk.Label(self.settings_frame, text="", width=2)
self.svn_status_indicator.grid(row=0, column=3, sticky=tk.W)
self.usb_path_label = ttk.Label(self.settings_frame, text="USB Drive Path:")
self.usb_path_label.grid(row=1, column=0, sticky=tk.W)
self.usb_path_entry = ttk.Entry(self.settings_frame, width=60)
self.usb_path_entry.grid(row=1, column=1, sticky=tk.W)
self.usb_path_browse_button = ttk.Button(self.settings_frame, text="Browse",
command=lambda: self.browse_folder(self.usb_path_entry))
self.usb_path_browse_button.grid(row=1, column=2, sticky=tk.W)
self.bundle_name_label = ttk.Label(self.settings_frame, text="Bundle Name:")
self.bundle_name_label.grid(row=2, column=0, sticky=tk.W)
self.bundle_name_entry = ttk.Entry(self.settings_frame, width=60)
self.bundle_name_entry.grid(row=2, column=1, sticky=tk.W)
self.bundle_updated_name_label = ttk.Label(self.settings_frame, text="Updated Bundle Name:")
self.bundle_updated_name_label.grid(row=3, column=0, sticky=tk.W)
self.bundle_updated_name_entry = ttk.Entry(self.settings_frame, width=60)
self.bundle_updated_name_entry.grid(row=3, column=1, sticky=tk.W)
# Pulsanti
self.prepare_svn_button = ttk.Button(master, text="Prepare SVN for Git", command=self.prepare_svn_for_git)
self.prepare_svn_button.pack(pady=5)
self.create_bundle_button = ttk.Button(master, text="Create Bundle", command=self.create_git_bundle)
self.create_bundle_button.pack(pady=5)
self.fetch_bundle_button = ttk.Button(master, text="Fetch Bundle", command=self.fetch_from_git_bundle)
self.fetch_bundle_button.pack(pady=5)
# Inizializza i campi con le impostazioni del profilo corrente
self.load_profile_settings()
# Log iniziale
self.logger.info("Applicazione avviata.")
def load_config(self):
"""Carica le configurazioni dal file."""
self.config.read(CONFIG_FILE)
# Crea la sezione di default se non esiste
if DEFAULT_PROFILE not in self.config.sections():
self.config.add_section(DEFAULT_PROFILE)
self.config.set(DEFAULT_PROFILE, "svn_working_copy_path", "/path/to/svn/working/copy")
self.config.set(DEFAULT_PROFILE, "usb_drive_path", "/media/usb")
self.config.set(DEFAULT_PROFILE, "bundle_name", "mio_bundle.bundle")
self.config.set(DEFAULT_PROFILE, "bundle_name_updated", "mio_bundle_aggiornato.bundle")
self.save_config()
def load_profile_settings(self, *args):
"""Carica le impostazioni del profilo selezionato nei campi di input."""
profile = self.profile_var.get()
if profile and profile in self.config.sections():
svn_path = self.config.get(profile, "svn_working_copy_path", fallback="")
self.svn_path_entry.delete(0, tk.END)
self.svn_path_entry.insert(0, svn_path)
self.usb_path_entry.delete(0, tk.END)
self.usb_path_entry.insert(0, self.config.get(profile, "usb_drive_path", fallback=""))
self.bundle_name_entry.delete(0, tk.END)
self.bundle_name_entry.insert(0, self.config.get(profile, "bundle_name", fallback=""))
self.bundle_updated_name_entry.delete(0, tk.END)
self.bundle_updated_name_entry.insert(0, self.config.get(profile, "bundle_name_updated", fallback=""))
self.update_svn_status(svn_path) # Aggiorna lo stato del repository SVN
def save_config(self):
"""Salva le configurazioni nel file."""
with open(CONFIG_FILE, "w") as configfile:
self.config.write(configfile)
def save_profile_settings(self):
"""Salva le impostazioni del profilo corrente."""
profile = self.profile_var.get()
if profile:
self.config.set(profile, "svn_working_copy_path", self.svn_path_entry.get())
self.config.set(profile, "usb_drive_path", self.usb_path_entry.get())
self.config.set(profile, "bundle_name", self.bundle_name_entry.get())
self.config.set(profile, "bundle_name_updated", self.bundle_updated_name_entry.get())
self.save_config()
self.logger.info(f"Impostazioni del profilo '{profile}' salvate.")
def add_profile(self):
"""Aggiunge un nuovo profilo."""
new_profile_name = tk.simpledialog.askstring("Add Profile", "Enter new profile name:")
if new_profile_name:
if new_profile_name in self.config.sections():
messagebox.showerror("Error", "Profile name already exists.")
else:
self.config.add_section(new_profile_name)
self.save_config()
self.profile_dropdown['values'] = self.config.sections()
self.profile_var.set(new_profile_name)
self.load_profile_settings()
self.logger.info(f"Profilo '{new_profile_name}' aggiunto.")
def remove_profile(self):
"""Rimuove il profilo corrente."""
profile = self.profile_var.get()
if profile and profile != DEFAULT_PROFILE: # Previeni la cancellazione del profilo di default
if messagebox.askyesno("Remove Profile", f"Are you sure you want to remove profile '{profile}'?"):
self.config.remove_section(profile)
self.save_config()
self.profile_dropdown['values'] = self.config.sections()
if self.config.sections():
self.profile_var.set(self.config.sections()[0]) # Seleziona il primo profilo disponibile
else:
self.profile_var.set("") # Nessun profilo disponibile
self.load_profile_settings()
self.logger.info(f"Profilo '{profile}' rimosso.")
else:
messagebox.showerror("Error", "Cannot remove the default profile.")
def browse_folder(self, entry):
"""Apre una finestra di dialogo per la selezione di una cartella."""
filename = filedialog.askdirectory()
entry.delete(0, tk.END)
entry.insert(0, filename)
if entry == self.svn_path_entry:
self.update_svn_status(filename) # Aggiorna lo stato se il percorso SVN è stato cambiato
def update_svn_status(self, svn_path):
"""Verifica se il repository SVN è già stato preparato per Git e aggiorna l'indicatore."""
if os.path.exists(os.path.join(svn_path, ".git")):
self.svn_status_indicator.config(background=GREEN)
self.prepare_svn_button.config(state=tk.DISABLED)
else:
self.svn_status_indicator.config(background=RED)
self.prepare_svn_button.config(state=tk.NORMAL)
def prepare_svn_for_git(self):
"""Prepara il repository SVN per l'uso con Git (crea .gitignore, esegue git init)."""
self.save_profile_settings() # Salva le impostazioni del profilo corrente
svn_path = self.svn_path_entry.get()
# Verifica se .git esiste già (anche se non dovrebbe essere possibile arrivare qui con il pulsante disabilitato)
if os.path.exists(os.path.join(svn_path, ".git")):
messagebox.showinfo("Info", "Il repository SVN è già stato preparato per Git.")
return
# Crea .gitignore
gitignore_path = os.path.join(svn_path, ".gitignore")
if not os.path.exists(gitignore_path):
with open(gitignore_path, "w") as f:
f.write(".svn\n")
self.logger.info("Creato file .gitignore.")
# Esegue git init
try:
command = ["git", "init"]
self.log_and_execute(command, check=True)
self.logger.info("Repository Git inizializzato con successo.")
messagebox.showinfo("Success", "Repository SVN preparato per Git con successo.")
self.update_svn_status(svn_path) # Aggiorna lo stato dopo la preparazione
except Exception as e:
self.logger.error(f"Errore durante l'inizializzazione del repository Git: {e}")
messagebox.showerror("Error", f"Errore durante l'inizializzazione del repository Git: {e}")
def log_and_execute(self, command, check=True):
"""Esegue un comando, lo logga e gestisce gli errori."""
log_message = f"Esecuzione comando: {' '.join(command)}"
self.logger.info(log_message)
try:
svn_path = self.svn_path_entry.get()
cwd = os.path.abspath(svn_path) # Ottieni il percorso assoluto
self.logger.info(f"Directory di lavoro: {cwd}") # Aggiungi questa riga per il debug
result = subprocess.run(command, cwd=cwd, capture_output=True, text=True, check=check)
if result.returncode == 0:
self.logger.info(f"Comando eseguito con successo.\nOutput:\n{result.stdout}")
else:
self.logger.error(f"Comando fallito. Codice di ritorno: {result.returncode}\nOutput:\n{result.stderr}")
return result
except subprocess.CalledProcessError as e:
self.logger.exception(f"Errore durante l'esecuzione del comando: {e}")
raise
def create_git_bundle(self):
"""Crea un bundle del repository Git."""
self.save_profile_settings() # Salva le impostazioni del profilo corrente
self.logger.info("Creazione del bundle Git...")
svn_path = self.svn_path_entry.get()
if not os.path.exists(svn_path):
self.logger.error(f"Il percorso SVN '{svn_path}' non esiste.")
messagebox.showerror("Error", f"Il percorso SVN '{svn_path}' non esiste.")
return
bundle_path = os.path.join(self.usb_path_entry.get(), self.bundle_name_entry.get()).replace("\\", "/") # Modifica qui
command = ["git", "bundle", "create", bundle_path, "--all"]
try:
self.log_and_execute(command)
self.logger.info("Bundle Git creato con successo.")
except Exception as e:
self.logger.error(f"Errore durante la creazione del bundle: {e}")
messagebox.showerror("Error", f"Errore durante la creazione del bundle: {e}")
def fetch_from_git_bundle(self):
"""Recupera le modifiche dal bundle nel repository Git locale."""
self.save_profile_settings() # Salva le impostazioni del profilo corrente
self.logger.info("Recupero delle modifiche dal bundle Git...")
bundle_path = os.path.join(self.usb_path_entry.get(), self.bundle_updated_name_entry.get())
try:
fetch_command = ["git", "fetch", bundle_path, "--all"]
self.log_and_execute(fetch_command)
merge_command = ["git", "merge", "FETCH_HEAD"]
self.log_and_execute(merge_command)
self.logger.info("Modifiche recuperate e integrate con successo.")
except Exception as e:
self.logger.error(f"Errore durante il recupero delle modifiche dal bundle: {e}")
messagebox.showerror("Error", f"Errore durante il recupero delle modifiche dal bundle: {e}")
class TextHandler(logging.Handler):
"""Handler per inviare i log a un widget Text di Tkinter."""
def __init__(self, text):
super().__init__()
self.text = text
def emit(self, record):
msg = self.format(record)
self.text.config(state=tk.NORMAL)
self.text.insert(tk.END, msg + "\n")
self.text.config(state=tk.DISABLED)
self.text.see(tk.END) # Autoscroll
def main():
root = tk.Tk()
app = GitSvnSyncApp(root)
root.mainloop()
if __name__ == "__main__":
main()