From ba6fc818169781f04f98f2a989ca70f51724e9e1 Mon Sep 17 00:00:00 2001 From: Yann Lebrun Date: Fri, 15 Aug 2025 15:37:03 +0200 Subject: [PATCH] Version propre --- PBIDE-GitTool.pb | 799 +++++++++++++++++++++++++++++------------------ 1 file changed, 490 insertions(+), 309 deletions(-) diff --git a/PBIDE-GitTool.pb b/PBIDE-GitTool.pb index d17fd2a..d8a94c5 100644 --- a/PBIDE-GitTool.pb +++ b/PBIDE-GitTool.pb @@ -1,28 +1,44 @@ -; PBIDE-GitTool.pb — Outil externe Git pour l’IDE PureBasic -; - Init, Status, Add+Commit (sélectif), Push/Pull -; - UI : liste avec cases à cocher + actions Inclure/Exclure/Tout -; - Fenêtre "Avancé…" : switch/restore de branche -; - Assistant d’installation IDE si lancé sans paramètre -; Contraintes respectées : pas d'underscore dans nos identifiants, -; Protected uniquement dans les procédures, pas de IIf, déclarations = implémentations, -; comparaisons uniquement dans If/While/Until/For, etc. +; ============================================================================= +; PBIDE-GitTool.pb — Git External Tool for PureBasic IDE +; Outil externe Git pour l'IDE PureBasic +; +; Features / Fonctionnalités : +; - Init, Status, Add+Commit (selective), Push/Pull +; - UI: List with checkboxes + Include/Exclude/All actions +; - Advanced window: branch switch/restore +; - IDE installation wizard if launched without parameters +; +; Constraints respected / Contraintes respectées : +; - No underscores in identifiers / Pas d'underscore dans nos identifiants +; - Protected only in procedures / Protected uniquement dans les procédures +; - No IIf usage / Pas de IIf +; - Declarations = implementations / Déclarations = implémentations +; - Comparisons only in If/While/Until/For / Comparaisons uniquement dans If/While/Until/For +; ============================================================================= EnableExplicit #EnableDebug = #True -; ====== Séparateur de chemin (portable) ====== +; ============================================================================= +; PLATFORM CONFIGURATION / CONFIGURATION PLATEFORME +; ============================================================================= + +; Path separator (portable) / Séparateur de chemin (portable) CompilerIf #PB_Compiler_OS = #PB_OS_Windows #PathSep$ = "\" - ; Chemin Git forcé pour Windows : - #GitExe$ = "C:\Program Files\Git\cmd\git.exe" + ; Forced Git path for Windows / Chemin Git forcé pour Windows + #GitExe$ = "C:\Program Files\Git\cmd\git.exe" CompilerElse #PathSep$ = "/" - ; Sur macOS/Linux on garde 'git' dans le PATH : - #GitExe$ = "git" + ; On macOS/Linux keep 'git' in PATH / Sur macOS/Linux on garde 'git' dans le PATH + #GitExe$ = "git" CompilerEndIf -; ====== IDs UI (placer ce bloc AVANT toutes les procédures) ====== -; Fenêtre principale +; ============================================================================= +; UI IDENTIFIERS / IDENTIFIANTS UI +; ============================================================================= + +; Main window / Fenêtre principale #GWindow = 1 #GLabelRepo = 10 #GStringRepo = 11 @@ -45,18 +61,23 @@ CompilerEndIf #GIncludeAll = 29 #GExcludeAll = 30 #GAdvanced = 31 -; Options d’affichage -#GShowClean = 39 ; Afficher les fichiers suivis à jour (propres) -#GShowIgnored = 40 ; Afficher aussi les fichiers ignorés (.gitignore) +#GDiff = 32 +#GConfig = 34 +#GGuide = 35 +#GRestoreFile = 36 -#GAddBranch = 42 -#GReloadBranches= 43 -#GShowPermanent = 44 -#GExcludeForever = 45 -#GReincludeForever= 46 +; Display options / Options d'affichage +#GShowClean = 39 ; Show tracked files that are up to date / Afficher les fichiers suivis à jour +#GShowIgnored = 40 ; Show ignored files (.gitignore) / Afficher aussi les fichiers ignorés +#GShowPermanent = 44 ; Show permanently excluded files / Afficher les fichiers exclus définitivement +#GExcludeForever = 45 ; Permanently exclude button / Bouton exclure définitivement +#GReincludeForever = 46; Re-include permanently button / Bouton ré-inclure définitivement +; Branch management / Gestion des branches +#GAddBranch = 42 +#GReloadBranches = 43 -; Fenêtre avancée (branches) +; Advanced window (branches) / Fenêtre avancée (branches) #WAdv = 200 #GAdvLabel = 210 #GAdvCombo = 211 @@ -64,7 +85,7 @@ CompilerEndIf #GAdvRestore = 213 #GAdvClose = 214 -; Assistant d’installation +; Installation wizard / Assistant d'installation #WInstall = 100 #GLabelIde = 110 #GStringIde = 111 @@ -79,33 +100,30 @@ CompilerEndIf #GInstallCancel = 120 #GInstallNote = 121 -; --- Nouveaux gadgets --- -#GDiff = 32 -#GConfig = 34 -#GGuide = 35 - -; --- Fenêtre Diff --- +; Diff window / Fenêtre Diff #WDiff = 300 #GDiffText = 301 #GDiffClose = 302 -; Fenêtre de sortie Git +; Git output window / Fenêtre de sortie Git #WOut = 400 #GOutText = 401 #GOutCopy = 402 #GOutClose = 403 -; --- Bouton "Restaurer fichier…" dans la fenêtre principale --- -#GRestoreFile = 36 - -; --- Fenêtre de restauration d’un fichier vers un commit --- +; File restore window / Fenêtre de restauration d'un fichier #WRestore = 500 #GRestList = 501 #GRestOK = 502 #GRestCancel = 503 #GRestInfo = 504 -; Mémoriser / restaurer la sélection autour d'un refresh de la liste +; ============================================================================= +; MACROS FOR SELECTION PERSISTENCE / MACROS POUR PERSISTANCE DE LA SÉLECTION +; ============================================================================= + +; Remember current selection before refreshing list +; Mémoriser la sélection courante avant un refresh de la liste Macro RememberSel() keepIdx.i = GetGadgetState(#GListStatus) keepFile$ = "" @@ -114,89 +132,118 @@ Macro RememberSel() EndIf EndMacro +; Restore selection after list refresh +; Restaurer la sélection après un refresh de la liste Macro RestoreSel() RestoreSelection(keepIdx, keepFile$) EndMacro -; ====== Structures ====== +; ============================================================================= +; STRUCTURES / STRUCTURES +; ============================================================================= + +; Git command execution data / Données d'exécution d'une commande Git Structure GitCall - args.s - workdir.s - output.s - errors.s - exitcode.i + args.s ; Command arguments / Arguments de la commande + workdir.s ; Working directory / Répertoire de travail + output.s ; Standard output / Sortie standard + errors.s ; Error output / Sortie d'erreur + exitcode.i ; Exit code / Code de sortie EndStructure +; File status row in the list / Ligne de statut de fichier dans la liste Structure FileRow - stat.s ; 2 lettres porcelain (" M", "A ", "??", etc.) - file.s ; chemin relatif - include.i ; 1=coché (inclus), 0=exclu + stat.s ; 2-letter porcelain status (" M", "A ", "??", etc.) / Statut porcelain 2 lettres + file.s ; Relative path / Chemin relatif + include.i ; 1=checked (included), 0=excluded / 1=coché (inclus), 0=exclu EndStructure +; Repository preferences / Préférences du dépôt Structure RepoPrefs - remote.s - branch.s + remote.s ; Remote name or URL / Nom ou URL du remote + branch.s ; Branch name / Nom de la branche EndStructure -; ====== Déclarations ====== +; ============================================================================= +; FUNCTION DECLARATIONS / DÉCLARATIONS DE FONCTIONS +; ============================================================================= + +; Core utilities / Utilitaires de base Declare.s TrimNewlines(text$) Declare.i RunGit(*call.GitCall) Declare.s DetectRepoRoot(startDir$) + +; Preferences management / Gestion des préférences Declare.i LoadPrefs(prefsPath$, remote$, branch$) Declare.i SavePrefs(prefsPath$, remote$, branch$) Declare.i SaveRepoPrefs(repoDir$, remote$, branch$) Declare.i LoadRepoPrefs(repoDir$, *prefs.RepoPrefs) + +; UI and main operations / Interface et opérations principales Declare.i OpenGUI(initialDir$, prefsPath$) Declare.i CreateOrTrackBranch(repoDir$, remote$, name$) Declare.i EnsureGitAvailable() -Declare.i OpenGUI(initialDir$, prefsPath$) Declare.s DirFromArgOrFallback() + +; Git operations / Opérations Git Declare.i DoInitRepo(repoDir$) Declare.i DoStatus(repoDir$, out$) Declare.i DoCommit(repoDir$, message$, doPush.i, remote$, branch$) Declare.i DoPush(repoDir$, remote$, branch$) Declare.i DoPull(repoDir$, remote$, branch$) + +; Branch management / Gestion des branches Declare.i ListBranches(repoDir$, List branchesList.s()) +Declare.i SwitchToBranch(repoDir$, branch$) +Declare.i RestoreFromBranch(repoDir$, branch$) + +; File list management / Gestion de la liste de fichiers Declare.i LoadStatusRows(repoDir$, List rows.FileRow()) +Declare.i BuildFullFileList(repoDir$, showClean.i, showIgnored.i, List rows.FileRow()) Declare.i FillStatusList(List rows.FileRow()) Declare.i ToggleIncludeAt(index.i, List rows.FileRow()) Declare.i CollectIncludedFiles(List rows.FileRow(), List files.s()) Declare.i DoCommitSelected(repoDir$, message$, doPush.i, remote$, branch$, List files.s()) +Declare.i SortRowsByInterest(List rows.FileRow()) + +; Advanced features / Fonctionnalités avancées Declare.i OpenAdvancedWindow(repoDir$) -Declare.i SwitchToBranch(repoDir$, branch$) -Declare.i RestoreFromBranch(repoDir$, branch$) -Declare.i InstallPBGitInIDE(ideExe$, toolsPrefs$, themeZip$) -Declare.i OpenInstallWizard() -Declare.s PorcelainToLabel(code$) Declare.i OpenDiffWindow(repoDir$, List rows.FileRow()) +Declare.i OpenRestoreFileWindow(repoDir$, List rows.FileRow()) +Declare.i RestoreFileFromCommit(repoDir$, file$, commit$) + +; Configuration and setup / Configuration et installation Declare.i ConfigIdentityWizard(repoDir$) Declare.i MakeDefaultGitignore(repoDir$) -Declare.i UpdateGuide(repoDir$, List rows.FileRow(), branch$, remote$) +Declare.i InstallPBGitInIDE(ideExe$, toolsPrefs$, themeZip$) +Declare.i OpenInstallWizard() + +; Helper functions / Fonctions d'aide +Declare.s PorcelainToLabel(code$) Declare.s GetLocalConfig(repoDir$, key$) Declare.i IsGitRepo(dir$) Declare.i UpdateGuide(repoDir$, List rows.FileRow(), branch$, remote$) -Declare.i OpenRestoreFileWindow(repoDir$, List rows.FileRow()) -Declare.i RestoreFileFromCommit(repoDir$, file$, commit$) -Declare.i BuildFullFileList(repoDir$, showClean.i, showIgnored.i, List rows.FileRow()) -Declare.s FileFromRowsByIndex(index.i, List rows.FileRow()) -Declare.i OpenRestoreFileWindow(repoDir$, List rows.FileRow()) Declare.s NormalizeGitFilePath(repoDir$, raw$) Declare.i RepoFileExists(repoDir$, rel$) -Declare.i BuildFullFileList(repoDir$, showClean.i, showIgnored.i, List rows.FileRow()) Declare.i RestoreSelection(oldIndex.i, oldFile$) +Declare.s FileFromRowsByIndex(index.i, List rows.FileRow()) + +; Exclusion management / Gestion des exclusions Declare.s LocalExcludePath(repoDir$) Declare.i EnsureToolFilesExcluded(repoDir$) Declare.i LoadLocalExcludes(repoDir$, List excl.s()) Declare.i IsPermanentlyExcluded(file$, List excl.s()) Declare.i AddPermanentExclude(repoDir$, file$) Declare.i RemovePermanentExclude(repoDir$, file$) -Declare.i SortRowsByInterest(List rows.FileRow()) +; ============================================================================= +; UTILITY FUNCTIONS / FONCTIONS UTILITAIRES +; ============================================================================= -; ====== Utils ====== -; Convertit le chemin pour Git (Windows ok) : -; - remplace "\" par "/" -; - retire un éventuel préfixe "./" +; Convert path for Git (Windows compatible) +; Convertit le chemin pour Git (compatible Windows) +; - Replace "\" with "/" +; - Remove "./" prefix if present Procedure.s GitPath(file$) Protected p$ = file$ p$ = ReplaceString(p$, "\", "/") @@ -206,14 +253,16 @@ Procedure.s GitPath(file$) ProcedureReturn p$ EndProcedure -; Normalise un chemin renvoyé par Git : -; - enlève espaces/quotes superflus -; - garde la destination en cas de renommage "old -> new" -; - remplace les "/" par le séparateur OS -; - retire un éventuel "./" de tête +; Normalize a path returned by Git +; Normalise un chemin renvoyé par Git +; - Remove extra spaces/quotes +; - Keep destination in case of rename "old -> new" +; - Replace "/" with OS separator +; - Remove leading "./" if present Procedure.s NormalizeGitFilePath(repoDir$, raw$) Protected p$ = Trim(raw$) - ; retirer quotes simples/doubles éventuelles autour + + ; Remove surrounding quotes if present / Retirer quotes éventuelles autour If Left(p$, 1) = Chr(34) And Right(p$, 1) = Chr(34) p$ = Mid(p$, 2, Len(p$)-2) EndIf @@ -221,25 +270,26 @@ Procedure.s NormalizeGitFilePath(repoDir$, raw$) p$ = Mid(p$, 2, Len(p$)-2) EndIf - ; cas "old -> new" : on garde "new" + ; Handle "old -> new" case: keep "new" / Cas "old -> new" : on garde "new" Protected pos.i = FindString(p$, "->", 1) If pos > 0 p$ = Trim(Mid(p$, pos + 2)) EndIf - ; retirer "./" + ; Remove leading "./" / Retirer "./" If Left(p$, 2) = "./" p$ = Mid(p$, 3) EndIf - ; slashes vers séparateur OS + ; Convert slashes to OS separator / Slashes vers séparateur OS p$ = ReplaceString(p$, "/", #PathSep$) ProcedureReturn p$ EndProcedure -; Vérifie l’existence d’un fichier RELATIF au repo (fichier ou dossier) -; renvoie 1 si présent, 0 sinon +; Check existence of a file RELATIVE to repo (file or directory) +; Vérifie l'existence d'un fichier RELATIF au repo (fichier ou dossier) +; Returns 1 if present, 0 otherwise / Renvoie 1 si présent, 0 sinon Procedure.i RepoFileExists(repoDir$, rel$) Protected base$ = repoDir$ If Right(base$, 1) <> #PathSep$ : base$ + #PathSep$ : EndIf @@ -251,7 +301,7 @@ Procedure.i RepoFileExists(repoDir$, rel$) ProcedureReturn 0 EndProcedure - +; Remove trailing newlines from text / Supprime les retours à la ligne en fin de texte Procedure.s TrimNewlines(text$) Protected s$ = text$ While Right(s$, 1) = #LF$ Or Right(s$, 1) = #CR$ @@ -260,7 +310,7 @@ Procedure.s TrimNewlines(text$) ProcedureReturn s$ EndProcedure - +; Detect if 'remote$' looks like a URL (http(s):// or git@…) ; Détecte si 'remote$' ressemble à une URL (http(s):// ou git@…) Procedure.i IsUrlRemote(remote$) If FindString(remote$, "://", 1) Or FindString(remote$, "@", 1) @@ -269,6 +319,7 @@ Procedure.i IsUrlRemote(remote$) ProcedureReturn 0 EndProcedure +; Read entire text file and return its content (with #LF$ between lines) ; Lit tout le fichier texte et renvoie son contenu (avec #LF$ entre les lignes) Procedure.s ReadAllText(path$) Protected out$ = "" @@ -281,6 +332,7 @@ Procedure.s ReadAllText(path$) ProcedureReturn out$ EndProcedure +; Get current branch name (HEAD → returns "" if detached) ; Récupère le nom de branche courante (HEAD → renvoie "" si détachée) Procedure.s GetCurrentBranch(repoDir$) Protected gc.GitCall @@ -295,7 +347,7 @@ Procedure.s GetCurrentBranch(repoDir$) ProcedureReturn "" EndProcedure -; Teste si un remote nommé existe +; Test if a named remote exists / Teste si un remote nommé existe Procedure.i RemoteExists(repoDir$, remoteName$) Protected gc.GitCall, out$, i.i, n.i, line$ gc\workdir = repoDir$ @@ -314,6 +366,7 @@ Procedure.i RemoteExists(repoDir$, remoteName$) ProcedureReturn 0 EndProcedure +; Display Git command output in a window / Affiche la sortie d'une commande Git dans une fenêtre Procedure.i ShowGitOutput(title$, stdOut$, stdErr$) Protected full$ full$ + "=== STDOUT ===" + #LF$ + stdOut$ + #LF$ + "=== STDERR ===" + #LF$ + stdErr$ @@ -339,7 +392,7 @@ Procedure.i ShowGitOutput(title$, stdOut$, stdErr$) ProcedureReturn 1 EndProcedure -; Ajoute un remote +; Add a remote / Ajoute un remote Procedure.i AddRemote(repoDir$, remoteName$, remoteUrl$) Protected gc.GitCall gc\workdir = repoDir$ @@ -351,9 +404,15 @@ Procedure.i AddRemote(repoDir$, remoteName$, remoteUrl$) ProcedureReturn 0 EndProcedure +; ============================================================================= +; CORE GIT OPERATIONS / OPÉRATIONS GIT DE BASE +; ============================================================================= + +; Execute git with stdout/stderr capture (structure passed by pointer) ; Exécute git avec capture stdout/stderr (structure passée par pointeur) Procedure.i RunGit(*call.GitCall) - Protected prg.i, line$,lineError$, out$, err$ + Protected prg.i, line$, lineError$, out$, err$ + If #EnableDebug Debug "[RunGit] " + #GitExe$ + " " + *call\args + " (wd=" + *call\workdir + ")" EndIf @@ -361,33 +420,37 @@ Procedure.i RunGit(*call.GitCall) prg = RunProgram(#GitExe$, *call\args, *call\workdir, #PB_Program_Open | #PB_Program_Read | #PB_Program_Error | #PB_Program_Hide) If prg = 0 *call\output = "" - *call\errors = "Impossible de lancer '" + #GitExe$ + "'." + *call\errors = "Impossible de lancer '" + #GitExe$+"'." *call\exitcode = -1 ProcedureReturn *call\exitcode EndIf + ; Read output while program is running / Lire la sortie pendant l'exécution While ProgramRunning(prg) While AvailableProgramOutput(prg) line$ = ReadProgramString(prg) If line$ <> "" : out$ + line$ + #LF$ : EndIf - lineError$ = ReadProgramError(prg) ; renvoie "" si rien à lire (non bloquant) + lineError$ = ReadProgramError(prg) If lineError$ <> "" : err$ + lineError$ + #LF$ : EndIf Wend Delay(5) Wend + ; Read remaining output / Lire le reste de la sortie While AvailableProgramOutput(prg) line$ = ReadProgramString(prg) If line$ <> "" : out$ + line$ + #LF$ : EndIf Wend + + ; Read remaining errors / Lire le reste des erreurs Repeat line$ = ReadProgramError(prg) If line$ = "" : Break : EndIf err$ + line$ + #LF$ ForEver - *call\output = out$ - *call\errors = err$ + *call\output = out$ + *call\errors = err$ *call\exitcode = ProgramExitCode(prg) CloseProgram(prg) @@ -400,6 +463,7 @@ Procedure.i RunGit(*call.GitCall) ProcedureReturn *call\exitcode EndProcedure +; Detect Git repository root / Détecte la racine du dépôt Git Procedure.s DetectRepoRoot(startDir$) Protected gc.GitCall gc\args = "rev-parse --show-toplevel" @@ -410,6 +474,11 @@ Procedure.s DetectRepoRoot(startDir$) ProcedureReturn startDir$ EndProcedure +; ============================================================================= +; PREFERENCES MANAGEMENT / GESTION DES PRÉFÉRENCES +; ============================================================================= + +; Load global preferences / Charge les préférences globales Procedure.i LoadPrefs(prefsPath$, remote$, branch$) If OpenPreferences(prefsPath$) PreferenceGroup("git") @@ -426,6 +495,7 @@ Procedure.i LoadPrefs(prefsPath$, remote$, branch$) ProcedureReturn 0 EndProcedure +; Save global preferences / Sauve les préférences globales Procedure.i SavePrefs(prefsPath$, remote$, branch$) If CreatePreferences(prefsPath$) PreferenceGroup("git") @@ -440,6 +510,7 @@ Procedure.i SavePrefs(prefsPath$, remote$, branch$) ProcedureReturn 0 EndProcedure +; Save remote/branch for THIS repository (.pbide-gittool.prefs at repo root) ; Sauvegarde remote/branch pour CE dépôt (.pbide-gittool.prefs à la racine du repo) Procedure.i SaveRepoPrefs(repoDir$, remote$, branch$) Protected base$ = repoDir$ @@ -461,7 +532,7 @@ Procedure.i SaveRepoPrefs(repoDir$, remote$, branch$) ProcedureReturn 0 EndProcedure - +; Load repository remote/branch into *prefs (returns 1 if found) ; Charge remote/branch du dépôt dans *prefs (retourne 1 si trouvé) Procedure.i LoadRepoPrefs(repoDir$, *prefs.RepoPrefs) If *prefs = 0 : ProcedureReturn 0 : EndIf @@ -485,15 +556,21 @@ Procedure.i LoadRepoPrefs(repoDir$, *prefs.RepoPrefs) ProcedureReturn 0 EndProcedure +; ============================================================================= +; SELECTION PERSISTENCE / PERSISTANCE DE LA SÉLECTION +; ============================================================================= +; Restore selection after FillStatusList()/rebuild ; Restaure la sélection après un FillStatusList()/rebuild -; Essaie d'abord l'index, puis cherche par nom de fichier (colonne 1). +; Try index first, then search by filename (column 1) +; Essaie d'abord l'index, puis cherche par nom de fichier (colonne 1) Procedure.i RestoreSelection(oldIndex.i, oldFile$) Protected count.i = CountGadgetItems(#GListStatus) If count = 0 ProcedureReturn 0 EndIf + ; Try to restore by index if file matches / Essaie de restaurer par index si le fichier correspond If oldIndex >= 0 And oldIndex < count If GetGadgetItemText(#GListStatus, oldIndex, 1) = oldFile$ SetGadgetState(#GListStatus, oldIndex) @@ -501,6 +578,7 @@ Procedure.i RestoreSelection(oldIndex.i, oldFile$) EndIf EndIf + ; Search by filename / Recherche par nom de fichier Protected i.i, name$ For i = 0 To count - 1 name$ = GetGadgetItemText(#GListStatus, i, 1) @@ -513,19 +591,24 @@ Procedure.i RestoreSelection(oldIndex.i, oldFile$) ProcedureReturn 0 EndProcedure -; Chemin de .git/info/exclude +; ============================================================================= +; EXCLUSION MANAGEMENT / GESTION DES EXCLUSIONS +; ============================================================================= + +; Path to .git/info/exclude / Chemin de .git/info/exclude Procedure.s LocalExcludePath(repoDir$) Protected base$ = repoDir$ If Right(base$, 1) <> #PathSep$ : base$ + #PathSep$ : EndIf ProcedureReturn base$ + ".git" + #PathSep$ + "info" + #PathSep$ + "exclude" EndProcedure +; Add our internal prefs file to local exclusions if missing ; Ajoute notre fichier prefs interne aux exclusions locales si absent -; Ajoute .pbide-gittool.prefs dans .git/info/exclude si absent +; Add .pbide-gittool.prefs to .git/info/exclude if missing Procedure.i EnsureToolFilesExcluded(repoDir$) Protected excl$ = LocalExcludePath(repoDir$) Protected line$ = ".pbide-gittool.prefs" - Protected txt$ = ReadAllText(excl$) + Protected txt$ = ReadAllText(excl$) Protected found.i = 0 If txt$ <> "" @@ -536,12 +619,12 @@ Procedure.i EnsureToolFilesExcluded(repoDir$) If found = 0 If CreateFile(0, excl$) - ; réécrit l’existant tel quel + ; Rewrite existing content as is / Réécrit l'existant tel quel If txt$ <> "" WriteString(0, txt$) If Right(txt$, 1) <> #LF$ : WriteString(0, #LF$) : EndIf EndIf - ; ajoute notre ligne + ; Add our line / Ajoute notre ligne WriteString(0, line$ + #LF$) CloseFile(0) If #EnableDebug @@ -559,7 +642,7 @@ Procedure.i EnsureToolFilesExcluded(repoDir$) ProcedureReturn 1 EndProcedure - +; Load .git/info/exclude (non-empty / non-commented lines) ; Charge .git/info/exclude (lignes non vides / non commentées) Procedure.i LoadLocalExcludes(repoDir$, List excl.s()) ClearList(excl()) @@ -577,6 +660,7 @@ Procedure.i LoadLocalExcludes(repoDir$, List excl.s()) ProcedureReturn 0 EndProcedure +; Simple test: exclusion by exact equality (no wildcards) ; Test simple : exclusion par égalité exacte (sans wildcards) Procedure.i IsPermanentlyExcluded(file$, List excl.s()) ForEach excl() @@ -587,16 +671,16 @@ Procedure.i IsPermanentlyExcluded(file$, List excl.s()) ProcedureReturn 0 EndProcedure -; Ajoute file$ dans .git/info/exclude (si suivi: rm --cached d'abord) -; Ajoute file$ à .git/info/exclude (et le retire de l’index si suivi) +; Add file$ to .git/info/exclude (and remove from index if tracked) +; Ajoute file$ à .git/info/exclude (et le retire de l'index si suivi) Procedure.i AddPermanentExclude(repoDir$, file$) Protected q$ = Chr(34) Protected gc.GitCall Protected excl$ = LocalExcludePath(repoDir$) - Protected txt$ = ReadAllText(excl$) + Protected txt$ = ReadAllText(excl$) Protected found.i = 0 - ; 1) Si suivi → le retirer de l’index + ; 1) If tracked → remove from index / Si suivi → le retirer de l'index gc\workdir = repoDir$ gc\args = "ls-files -- " + q$ + file$ + q$ If RunGit(@gc) = 0 And Trim(gc\output) <> "" @@ -607,7 +691,7 @@ Procedure.i AddPermanentExclude(repoDir$, file$) EndIf EndIf - ; 2) Si déjà présent dans exclude → rien à faire + ; 2) If already in exclude → nothing to do / Si déjà présent dans exclude → rien à faire If txt$ <> "" If FindString(#LF$ + txt$ + #LF$, #LF$ + file$ + #LF$, 1) > 0 found = 1 @@ -618,7 +702,7 @@ Procedure.i AddPermanentExclude(repoDir$, file$) ProcedureReturn 1 EndIf - ; 3) Écrire + ; 3) Write / Écrire If CreateFile(0, excl$) If txt$ <> "" WriteString(0, txt$) @@ -634,13 +718,14 @@ Procedure.i AddPermanentExclude(repoDir$, file$) ProcedureReturn 0 EndProcedure -; Retire file$ de .git/info/exclude et le ré-inclut (git add -f) +; Remove file$ from .git/info/exclude and force re-inclusion (git add -f) ; Retire file$ de .git/info/exclude et force la ré-inclusion (git add -f) Procedure.i RemovePermanentExclude(repoDir$, file$) Protected excl$ = LocalExcludePath(repoDir$) - Protected out$ = "" + Protected out$ = "" Protected l$, removed.i = 0 + ; Read line by line and rewrite without target line ; On relit ligne à ligne et on réécrit sans la ligne ciblée If ReadFile(0, excl$) = 0 MessageRequester("Ré-inclure (permanent)", "Fichier d'exclusion introuvable : " + excl$, #PB_MessageRequester_Error) @@ -656,7 +741,7 @@ Procedure.i RemovePermanentExclude(repoDir$, file$) out$ + l$ + #LF$ EndIf Else - ; on conserve les lignes vides/commentées telles quelles + ; Keep empty/commented lines as is / Conserve les lignes vides/commentées telles quelles out$ + l$ + #LF$ EndIf Wend @@ -675,7 +760,7 @@ Procedure.i RemovePermanentExclude(repoDir$, file$) ProcedureReturn 0 EndIf - ; Force l’ajout même s’il y a des règles d’ignore ailleurs + ; Force add even with ignore rules elsewhere / Force l'ajout même s'il y a des règles d'ignore ailleurs Protected gc.GitCall, q$ = Chr(34) gc\workdir = repoDir$ gc\args = "add -f -- " + q$ + file$ + q$ @@ -688,6 +773,11 @@ Procedure.i RemovePermanentExclude(repoDir$, file$) ProcedureReturn 1 EndProcedure +; ============================================================================= +; BRANCH MANAGEMENT / GESTION DES BRANCHES +; ============================================================================= + +; Create local branch or track remote branch "remote/branch" ; Crée une branche locale ou suit une branche distante "remote/branch" ; - name$ = "featureX" → git switch -c featureX ; - name$ = "origin/main" → git fetch origin + git switch -c main --track origin/main @@ -696,15 +786,15 @@ Procedure.i CreateOrTrackBranch(repoDir$, remote$, name$) If Trim(name$) = "" : ProcedureReturn 0 : EndIf - ; Détection "remote/branch" + ; Detect "remote/branch" / Détection "remote/branch" pos = FindString(name$, "/", 1) If pos > 0 rem$ = Left(name$, pos - 1) - br$ = Mid(name$, pos + 1) + br$ = Mid(name$, pos + 1) local$ = br$ If #EnableDebug : Debug "[Branch] track " + rem$ + "/" + br$ + " as " + local$ : EndIf - ; fetch d'abord pour avoir la ref + ; Fetch first to have the ref / Fetch d'abord pour avoir la ref gc\workdir = repoDir$ gc\args = "fetch " + rem$ If RunGit(@gc) <> 0 @@ -712,7 +802,7 @@ Procedure.i CreateOrTrackBranch(repoDir$, remote$, name$) ProcedureReturn 0 EndIf - ; création locale en suivant la remote + ; Create local following remote / Création locale en suivant la remote gc\args = "switch -c " + q$ + local$ + q$ + " --track " + rem$ + "/" + br$ If RunGit(@gc) = 0 MessageRequester("Branche", "Branche locale '" + local$ + "' suivant " + rem$ + "/" + br$ + ".", #PB_MessageRequester_Info) @@ -722,7 +812,7 @@ Procedure.i CreateOrTrackBranch(repoDir$, remote$, name$) ProcedureReturn 0 EndIf - ; Création d’une branche locale simple + ; Create simple local branch / Création d'une branche locale simple gc\workdir = repoDir$ gc\args = "switch -c " + q$ + name$ + q$ If RunGit(@gc) = 0 @@ -734,17 +824,18 @@ Procedure.i CreateOrTrackBranch(repoDir$, remote$, name$) ProcedureReturn 0 EndProcedure - +; Check if Git is available / Vérifie si Git est disponible Procedure.i EnsureGitAvailable() Protected gc.GitCall gc\args = "--version" If RunGit(@gc) = 0 And FindString(LCase(gc\output), "git version", 1) ProcedureReturn 1 EndIf - MessageRequester("PBIDE-GitTool", "Git n'est pas détecté à l’emplacement prévu : " + #GitExe$, #PB_MessageRequester_Warning) + MessageRequester("PBIDE-GitTool", "Git n'est pas détecté à l'emplacement prévu : " + #GitExe$, #PB_MessageRequester_Warning) ProcedureReturn 0 EndProcedure +; Determine relevant directory (project → env → --repo → exe) ; Détermine un répertoire pertinent (project → env → --repo → exe) Procedure.s DirFromArgOrFallback() Protected n.i = CountProgramParameters() @@ -774,6 +865,7 @@ Procedure.s DirFromArgOrFallback() ProcedureReturn dir$ EndIf + ; Try environment variable / Essaie la variable d'environnement Protected projFile$ = GetEnvironmentVariable("PB_TOOL_Project") If projFile$ <> "" Protected projDir$ = GetPathPart(projFile$) @@ -783,12 +875,17 @@ Procedure.s DirFromArgOrFallback() EndIf EndIf + ; Fallback to exe directory / Repli sur le répertoire de l'exe dir$ = GetPathPart(ProgramFilename()) If #EnableDebug : Debug "[Dir] fallback exe dir: " + dir$ : EndIf ProcedureReturn dir$ EndProcedure -; ====== Opérations Git de base ====== +; ============================================================================= +; BASIC GIT OPERATIONS / OPÉRATIONS GIT DE BASE +; ============================================================================= + +; Initialize Git repository / Initialise un dépôt Git Procedure.i DoInitRepo(repoDir$) Protected gc.GitCall gc\args = "init" @@ -801,6 +898,7 @@ Procedure.i DoInitRepo(repoDir$) ProcedureReturn 0 EndProcedure +; Get Git status / Récupère le statut Git Procedure.i DoStatus(repoDir$, out$) Protected gc.GitCall gc\args = "status --porcelain" @@ -814,10 +912,12 @@ Procedure.i DoStatus(repoDir$, out$) ProcedureReturn 1 EndProcedure +; Commit all changes / Valide tous les changements Procedure.i DoCommit(repoDir$, message$, doPush.i, remote$, branch$) Protected gc.GitCall, code.i gc\workdir = repoDir$ + ; Add all changes / Ajouter tous les changements gc\args = "add -A" code = RunGit(@gc) If code <> 0 @@ -825,6 +925,7 @@ Procedure.i DoCommit(repoDir$, message$, doPush.i, remote$, branch$) ProcedureReturn 0 EndIf + ; Commit with message / Valider avec un message gc\args = "commit -m " + Chr(34) + message$ + Chr(34) code = RunGit(@gc) If code <> 0 @@ -840,7 +941,10 @@ Procedure.i DoCommit(repoDir$, message$, doPush.i, remote$, branch$) ProcedureReturn 1 EndProcedure -; Push amélioré : +; Enhanced Push operation / Push amélioré : +; - Accepts 'Remote' as URL: creates/uses 'origin' automatically +; - If no upstream: retry with 'push -u ' +; - Shows full output on error ; - Accepte 'Remote' comme URL : crée/emploie 'origin' automatiquement ; - Si pas d'upstream : retente avec 'push -u ' ; - Affiche sorties complètes en cas d'erreur @@ -850,11 +954,12 @@ Procedure.i DoPush(repoDir$, remote$, branch$) Protected remoteUrl$ = "" Protected curBranch$ - ; Remote vide → suppose 'origin' + ; Empty remote → assume 'origin' / Remote vide → suppose 'origin' If Trim(remoteName$) = "" remoteName$ = "origin" EndIf + ; If user entered URL as 'remote', create/use 'origin' ; Si l'utilisateur a saisi une URL comme 'remote', crée/emploie 'origin' If IsUrlRemote(remoteName$) remoteUrl$ = remoteName$ @@ -866,7 +971,7 @@ Procedure.i DoPush(repoDir$, remote$, branch$) EndIf EndIf - ; Branche à utiliser : champ ou branche courante + ; Branch to use: field or current branch / Branche à utiliser : champ ou branche courante If Trim(branch$) = "" curBranch$ = GetCurrentBranch(repoDir$) Else @@ -878,7 +983,7 @@ Procedure.i DoPush(repoDir$, remote$, branch$) ProcedureReturn 0 EndIf - ; Tentative de push normal + ; Normal push attempt / Tentative de push normal gc\workdir = repoDir$ gc\args = "push " + remoteName$ + " " + curBranch$ code = RunGit(@gc) @@ -887,7 +992,7 @@ Procedure.i DoPush(repoDir$, remote$, branch$) ProcedureReturn 1 EndIf - ; Si pas d'upstream, retente avec -u (premier push) + ; If no upstream, retry with -u (first push) / Si pas d'upstream, retente avec -u (premier push) If FindString(LCase(gc\errors), "no upstream branch", 1) Or FindString(LCase(gc\errors), "set the remote as upstream", 1) gc\args = "push -u " + remoteName$ + " " + curBranch$ code = RunGit(@gc) @@ -897,12 +1002,12 @@ Procedure.i DoPush(repoDir$, remote$, branch$) EndIf EndIf - ; Autre erreur → fenêtre complète + ; Other error → full window / Autre erreur → fenêtre complète ShowGitOutput("Git push — erreur", gc\output, gc\errors) ProcedureReturn 0 EndProcedure - +; Pull from remote / Tire depuis le remote Procedure.i DoPull(repoDir$, remote$, branch$) Protected gc.GitCall gc\args = "pull " + remote$ + " " + branch$ @@ -915,9 +1020,11 @@ Procedure.i DoPull(repoDir$, remote$, branch$) ProcedureReturn 0 EndProcedure -; ------------------------------------------------------------------ -; Ouvre une fenêtre affichant le diff du fichier sélectionné -; ------------------------------------------------------------------ +; ============================================================================= +; DIFF WINDOW / FENÊTRE DIFF +; ============================================================================= + +; Open window showing diff of selected file / Ouvre une fenêtre affichant le diff du fichier sélectionné Procedure.i OpenDiffWindow(repoDir$, List rows.FileRow()) Protected idx.i = GetGadgetState(#GListStatus) If idx < 0 @@ -925,7 +1032,7 @@ Procedure.i OpenDiffWindow(repoDir$, List rows.FileRow()) ProcedureReturn 0 EndIf - ; Retrouver le chemin du fichier depuis la liste rows() + ; Find file path from rows() list / Retrouver le chemin du fichier depuis la liste rows() Protected j.i = 0, target$ ForEach rows() If j = idx @@ -940,10 +1047,10 @@ Procedure.i OpenDiffWindow(repoDir$, List rows.FileRow()) ProcedureReturn 0 EndIf - ; Exécuter 'git diff -- ""' + ; Execute 'git diff -- ""' / Exécuter 'git diff -- ""' Protected gc.GitCall gc\workdir = repoDir$ - gc\args = "diff -- " + Chr(34) + target$ + Chr(34) + gc\args = "diff -- " + Chr(34) + target$ + Chr(34) If RunGit(@gc) <> 0 MessageRequester("Diff", "Échec: " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error) ProcedureReturn 0 @@ -954,7 +1061,7 @@ Procedure.i OpenDiffWindow(repoDir$, List rows.FileRow()) diff$ = "Aucune différence détectée pour ce fichier." EndIf - ; Fenêtre d’affichage + ; Display window / Fenêtre d'affichage Protected title$ = "Diff — " + target$ If OpenWindow(#WDiff, 0, 0, 800, 500, title$, #PB_Window_SystemMenu | #PB_Window_ScreenCentered) EditorGadget(#GDiffText, 10, 10, 780, 440) @@ -973,7 +1080,11 @@ Procedure.i OpenDiffWindow(repoDir$, List rows.FileRow()) ProcedureReturn 1 EndProcedure +; ============================================================================= +; BRANCH LISTING / LISTAGE DES BRANCHES +; ============================================================================= +; List local branches / Liste les branches locales Procedure.i ListBranches(repoDir$, List branchesList.s()) ClearList(branchesList()) Protected gc.GitCall @@ -981,6 +1092,7 @@ Procedure.i ListBranches(repoDir$, List branchesList.s()) gc\workdir = repoDir$ If RunGit(@gc) <> 0 + ; Fallback defaults / Valeurs par défaut de repli AddElement(branchesList()) : branchesList() = "main" AddElement(branchesList()) : branchesList() = "master" ProcedureReturn 0 @@ -998,10 +1110,16 @@ Procedure.i ListBranches(repoDir$, List branchesList.s()) ProcedureReturn 1 EndProcedure -; ====== Status → lignes et gestion des coches ====== +; ============================================================================= +; STATUS & FILE LIST MANAGEMENT / GESTION DU STATUS ET DE LA LISTE DE FICHIERS +; ============================================================================= + +; Load status into list of rows (stat, file, include=0) ; Charge le status dans une liste de lignes (stat, file, include=0) -; Parsing robuste : on prend tout ce qui suit le premier séparateur (espace/tab) -; après les 2 lettres de statut ; gère aussi "old -> new" (renommage). +; Robust parsing: take everything after first separator (space/tab) after 2 status letters +; Parsing robuste : on prend tout ce qui suit le premier séparateur après les 2 lettres de statut +; Also handles "old -> new" (rename) +; Gère aussi "old -> new" (renommage) Procedure.i LoadStatusRows(repoDir$, List rows.FileRow()) Protected gc.GitCall, text$, line$, code$, file$ Protected i.i, n.i, start.i, ch$, pos.i @@ -1022,9 +1140,10 @@ Procedure.i LoadStatusRows(repoDir$, List rows.FileRow()) line$ = Trim(line$) If line$ = "" : Continue : EndIf - ; 2 premières colonnes = statut (XY) + ; First 2 columns = status (XY) / 2 premières colonnes = statut (XY) code$ = Left(line$, 2) + ; Find path start: after spaces/tabs following column 3 ; Cherche le début du chemin : après les espaces/tabs suivant la colonne 3 start = 3 While start <= Len(line$) @@ -1036,13 +1155,13 @@ Procedure.i LoadStatusRows(repoDir$, List rows.FileRow()) Wend file$ = Mid(line$, start) - ; Renommage "old -> new" : garder la destination + ; Rename "old -> new": keep destination / Renommage "old -> new" : garder la destination pos = FindString(file$, "->", 1) If pos > 0 file$ = Trim(Mid(file$, pos + 2)) EndIf - ; (Optionnel) normalisation simple + ; Optional simple normalization / Normalisation simple optionnelle If Left(file$, 2) = "./" : file$ = Mid(file$, 3) : EndIf file$ = ReplaceString(file$, "/", #PathSep$) @@ -1055,10 +1174,10 @@ Procedure.i LoadStatusRows(repoDir$, List rows.FileRow()) ProcedureReturn ListSize(rows()) EndProcedure - -; Remplit #GListStatus sans #LF$ (évite la perte du 1er caractère en col. 2) +; Fill #GListStatus without #LF$ (avoids losing 1st character in col. 2) ; Remplit la liste (colonne 0 = état lisible, colonne 1 = chemin) -; Ajoute " — Exclu" si l’élément a des changements mais n’est pas coché +; Add " — Exclu" if element has changes but isn't checked +; Ajoute " — Exclu" si l'élément a des changements mais n'est pas coché Procedure.i FillStatusList(List rows.FileRow()) ClearGadgetItems(#GListStatus) Protected idx.i = 0 @@ -1066,7 +1185,8 @@ Procedure.i FillStatusList(List rows.FileRow()) ForEach rows() label$ = PorcelainToLabel(rows()\stat) - ; Si ce n’est pas un fichier "OK/ignoré" et que include=0, on indique "Exclu" + ; If not "OK/ignored" file and include=0, indicate "Exclu" + ; Si ce n'est pas un fichier "OK/ignoré" et que include=0, on indique "Exclu" If rows()\stat <> "OK" And rows()\stat <> "!!" If rows()\include = 0 label$ + " — Exclu" @@ -1089,9 +1209,7 @@ Procedure.i FillStatusList(List rows.FileRow()) ProcedureReturn CountGadgetItems(#GListStatus) EndProcedure - - - +; Toggle include state at given index / Bascule l'état d'inclusion à l'index donné Procedure.i ToggleIncludeAt(index.i, List rows.FileRow()) If index < 0 : ProcedureReturn 0 : EndIf Protected c.i = CountGadgetItems(#GListStatus) @@ -1112,6 +1230,7 @@ Procedure.i ToggleIncludeAt(index.i, List rows.FileRow()) ProcedureReturn 1 EndProcedure +; Collect checked files into list / Collecte les fichiers cochés dans une liste Procedure.i CollectIncludedFiles(List rows.FileRow(), List files.s()) ClearList(files()) ForEach rows() @@ -1123,7 +1242,7 @@ Procedure.i CollectIncludedFiles(List rows.FileRow(), List files.s()) ProcedureReturn ListSize(files()) EndProcedure -; Commit sélectif si des fichiers sont cochés +; Selective commit if files are checked / Commit sélectif si des fichiers sont cochés Procedure.i DoCommitSelected(repoDir$, message$, doPush.i, remote$, branch$, List files.s()) If ListSize(files()) = 0 ProcedureReturn DoCommit(repoDir$, message$, doPush, remote$, branch$) @@ -1159,14 +1278,11 @@ Procedure.i DoCommitSelected(repoDir$, message$, doPush.i, remote$, branch$, Lis ProcedureReturn 1 EndProcedure -; ====== Fenêtre Avancé… (branches) ====== -#WAdv = 200 -#GAdvLabel = 210 -#GAdvCombo = 211 -#GAdvSwitch = 212 -#GAdvRestore = 213 -#GAdvClose = 214 +; ============================================================================= +; ADVANCED WINDOW (BRANCHES) / FENÊTRE AVANCÉE (BRANCHES) +; ============================================================================= +; Open advanced branch operations window / Ouvre la fenêtre d'opérations avancées sur les branches Procedure.i OpenAdvancedWindow(repoDir$) Protected b$ NewList blist.s() @@ -1220,17 +1336,20 @@ Procedure.i OpenAdvancedWindow(repoDir$) ProcedureReturn 0 EndProcedure +; Switch to specified branch / Bascule vers la branche spécifiée Procedure.i SwitchToBranch(repoDir$, branch$) Protected gc.GitCall gc\workdir = repoDir$ gc\args = "switch " + Chr(34) + branch$ + Chr(34) If RunGit(@gc) = 0 : ProcedureReturn 1 : EndIf - gc\args = "checkout " + Chr(34) + branch$ + Chr(34) ; fallback versions anciennes + ; Fallback for older Git versions / Fallback pour versions anciennes + gc\args = "checkout " + Chr(34) + branch$ + Chr(34) If RunGit(@gc) = 0 : ProcedureReturn 1 : EndIf MessageRequester("Branche", "Échec: " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error) ProcedureReturn 0 EndProcedure +; Restore working directory from specified branch / Restaure le répertoire de travail depuis la branche spécifiée Procedure.i RestoreFromBranch(repoDir$, branch$) Protected gc.GitCall gc\workdir = repoDir$ @@ -1240,10 +1359,17 @@ Procedure.i RestoreFromBranch(repoDir$, branch$) ProcedureReturn 0 EndProcedure +; ============================================================================= +; STATUS LABEL CONVERSION / CONVERSION DES LABELS DE STATUT +; ============================================================================= + +; Convert Git porcelain status codes to readable labels +; Convertit les codes de statut porcelain Git en labels lisibles Procedure.s PorcelainToLabel(code$) Protected x$ = Left(code$, 1) Protected y$ = Right(code$, 1) + ; Custom status codes / Codes de statut personnalisés If code$ = "EX" ProcedureReturn "Exclu (permanent)" EndIf @@ -1260,12 +1386,14 @@ Procedure.s PorcelainToLabel(code$) ProcedureReturn "Ignoré (.gitignore)" EndIf + ; Index status (X column) / Statut de l'index (colonne X) If x$ = "M" : ProcedureReturn "Modifié (indexé)" : EndIf If x$ = "A" : ProcedureReturn "Ajouté (indexé)" : EndIf If x$ = "D" : ProcedureReturn "Supprimé (indexé)" : EndIf If x$ = "R" : ProcedureReturn "Renommé (indexé)" : EndIf If x$ = "C" : ProcedureReturn "Copié (indexé)" : EndIf + ; Working tree status (Y column) / Statut du répertoire de travail (colonne Y) If y$ = "M" : ProcedureReturn "Modifié (non indexé)" : EndIf If y$ = "A" : ProcedureReturn "Ajouté (non indexé)" : EndIf If y$ = "D" : ProcedureReturn "Supprimé (non indexé)" : EndIf @@ -1273,7 +1401,11 @@ Procedure.s PorcelainToLabel(code$) ProcedureReturn "Changement" EndProcedure +; ============================================================================= +; GIT CONFIGURATION / CONFIGURATION GIT +; ============================================================================= +; Get local Git configuration value / Récupère une valeur de configuration Git locale Procedure.s GetLocalConfig(repoDir$, key$) Protected gc.GitCall gc\workdir = repoDir$ @@ -1284,10 +1416,11 @@ Procedure.s GetLocalConfig(repoDir$, key$) ProcedureReturn "" EndProcedure +; Configuration wizard for Git identity / Assistant de configuration pour l'identité Git Procedure.i ConfigIdentityWizard(repoDir$) Protected curName$ = GetLocalConfig(repoDir$, "user.name") Protected curMail$ = GetLocalConfig(repoDir$, "user.email") - Protected name$ = InputRequester("Identité Git", "Nom d’auteur (user.name)", curName$) + Protected name$ = InputRequester("Identité Git", "Nom d'auteur (user.name)", curName$) If name$ = "" : ProcedureReturn 0 : EndIf Protected mail$ = InputRequester("Identité Git", "Email (user.email)", curMail$) If mail$ = "" Or FindString(mail$, "@", 1) = 0 @@ -1297,12 +1430,15 @@ Procedure.i ConfigIdentityWizard(repoDir$) Protected gc.GitCall gc\workdir = repoDir$ + + ; Set user name / Définir le nom d'utilisateur gc\args = "config user.name " + Chr(34) + name$ + Chr(34) If RunGit(@gc) <> 0 MessageRequester("Git config", "Échec user.name: " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error) ProcedureReturn 0 EndIf + ; Set user email / Définir l'email d'utilisateur gc\args = "config user.email " + Chr(34) + mail$ + Chr(34) If RunGit(@gc) <> 0 MessageRequester("Git config", "Échec user.email: " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error) @@ -1313,6 +1449,7 @@ Procedure.i ConfigIdentityWizard(repoDir$) ProcedureReturn 1 EndProcedure +; Create default .gitignore file / Crée un fichier .gitignore par défaut Procedure.i MakeDefaultGitignore(repoDir$) Protected base$ = repoDir$ If Right(base$, 1) <> #PathSep$ : base$ + #PathSep$ : EndIf @@ -1324,6 +1461,7 @@ Procedure.i MakeDefaultGitignore(repoDir$) EndIf EndIf + ; Default ignore patterns for PureBasic / Patterns d'ignore par défaut pour PureBasic Protected txt$ = "" txt$ + "# PureBasic / build artefacts" + #LF$ txt$ + "*.exe" + #LF$ @@ -1364,9 +1502,11 @@ Procedure.i MakeDefaultGitignore(repoDir$) ProcedureReturn 0 EndProcedure -; Vérifie si 'dir$' est un dépôt Git -; - essaie via 'git rev-parse --is-inside-work-tree' -; - fallback: présence du dossier .git +; Check if 'dir is a Git repository / Vérifie si 'dir est un dépôt Git +; - Try via 'git rev-parse --is-inside-work-tree' +; - Fallback: presence of .git folder +; - Essaie via 'git rev-parse --is-inside-work-tree' +; - Fallback: présence du dossier .git Procedure.i IsGitRepo(dir$) Protected gc.GitCall Protected isRepo.i = 0 @@ -1384,7 +1524,7 @@ Procedure.i IsGitRepo(dir$) isRepo = 1 EndIf Else - ; Fallback : dossier .git présent ? + ; Fallback: .git folder present? / Fallback : dossier .git présent ? If FileSize(dotGitDir$) = -2 isRepo = 1 EndIf @@ -1397,12 +1537,15 @@ Procedure.i IsGitRepo(dir$) ProcedureReturn isRepo EndProcedure -; Restaure un fichier à l’état d’un commit précis +; ============================================================================= +; FILE RESTORATION / RESTAURATION DE FICHIERS +; ============================================================================= + +; Restore file to specific commit state / Restaure un fichier à l'état d'un commit précis +; - Try 'git restore --source -- ' +; - Fallback 'git checkout -- ' (for older Git) ; - Essaye 'git restore --source -- ' ; - Fallback 'git checkout -- ' (pour Git anciens) -; Restaure un fichier à l’état d’un commit précis -; - utilise GitPath() pour la robustesse -; - d’abord 'git restore', fallback 'git checkout' Procedure.i RestoreFileFromCommit(repoDir$, file$, commit$) Protected gc.GitCall, q$ = Chr(34) Protected path$ = GitPath(file$) @@ -1417,7 +1560,7 @@ Procedure.i RestoreFileFromCommit(repoDir$, file$, commit$) ProcedureReturn 1 EndIf - ; Fallback (compat versions Git) + ; Fallback for Git compatibility / Fallback pour compatibilité Git gc\args = "checkout " + q$ + commit$ + q$ + " -- " + q$ + path$ + q$ If #EnableDebug Debug "[RestoreFile fallback] " + gc\args @@ -1431,21 +1574,15 @@ Procedure.i RestoreFileFromCommit(repoDir$, file$, commit$) ProcedureReturn 0 EndProcedure -; Ouvre une fenêtre listant les commits du fichier sélectionné, -; puis restaure le fichier vers le commit choisi. -; Ouvre une fenêtre listant les commits du fichier sélectionné, -; puis restaure le fichier vers le commit choisi. -; Améliorations : -; - lit le chemin depuis la 2e colonne du gadget (source de vérité) -; - normalise le chemin pour Git via GitPath() -; - utilise --follow et fallback --all pour couvrir les renommages / autres branches -; Fenêtre de sélection de commit puis restauration d’un fichier -; Robuste : -; - lit le nom depuis le gadget ET depuis rows() -; - corrige le cas où la 1re lettre saute (ex: "PBIDE..." → "BIDE...") -; - normalise le chemin (GitPath), suit les renommages (--follow) + fallback --all -; Fenêtre de sélection de commit puis restauration d’un fichier -; ➜ Affiche maintenant date + heure (format ISO, avec fuseau) +; Open window listing commits for selected file, then restore file to chosen commit +; Ouvre une fenêtre listant les commits du fichier sélectionné, puis restaure le fichier vers le commit choisi +; Features / Fonctionnalités : +; - Read path from gadget AND from rows() +; - Normalize path (GitPath), follow renames (--follow) + fallback --all +; - Now shows date + time (ISO format with timezone) +; - Lit le nom depuis le gadget ET depuis rows() +; - Normalise le chemin (GitPath), suit les renommages (--follow) + fallback --all +; - Affiche maintenant date + heure (format ISO, avec fuseau) Procedure.i OpenRestoreFileWindow(repoDir$, List rows.FileRow()) Protected idx.i = GetGadgetState(#GListStatus) If idx < 0 @@ -1453,19 +1590,19 @@ Procedure.i OpenRestoreFileWindow(repoDir$, List rows.FileRow()) ProcedureReturn 0 EndIf - ; Récupérer le nom de fichier depuis la liste (colonne 1 = chemin) + ; Get filename from list (column 1 = path) / Récupérer le nom de fichier depuis la liste (colonne 1 = chemin) Protected target$ = GetGadgetItemText(#GListStatus, idx, 1) If target$ = "" MessageRequester("Restaurer", "Aucun fichier sélectionné.", #PB_MessageRequester_Info) ProcedureReturn 0 EndIf - ; Normaliser pour Git (slashs, ./) + ; Normalize for Git (slashes, ./) / Normaliser pour Git (slashs, ./) Protected fileArg$ = GitPath(target$) Protected gc.GitCall, out$, line$, n.i, i.i, q$ = Chr(34) - ; 1) Historique avec heure (—date=iso affiche YYYY-MM-DD HH:MM:SS +ZZZZ) - ; Note: si tu préfères l’heure locale stricte (Git récent), tu peux essayer --date=iso-strict-local + ; 1) History with time (--date=iso shows YYYY-MM-DD HH:MM:SS +ZZZZ) + ; 1) Historique avec heure (--date=iso affiche YYYY-MM-DD HH:MM:SS +ZZZZ) gc\workdir = repoDir$ gc\args = "log --follow --date=iso --pretty=format:%h%x09%ad%x09%s -n 200 -- " + q$ + fileArg$ + q$ If #EnableDebug : Debug "[Restore log] " + gc\args : EndIf @@ -1475,7 +1612,8 @@ Procedure.i OpenRestoreFileWindow(repoDir$, List rows.FileRow()) EndIf out$ = gc\output - ; 2) Fallback : toutes les refs (utile si l’historique est sur une autre branche) + ; 2) Fallback: all refs (useful if history is on another branch) + ; 2) Fallback : toutes les refs (utile si l'historique est sur une autre branche) If Trim(out$) = "" gc\args = "log --all --follow --date=iso --pretty=format:%h%x09%ad%x09%s -n 200 -- " + q$ + fileArg$ + q$ If #EnableDebug : Debug "[Restore log fallback --all] " + gc\args : EndIf @@ -1486,17 +1624,17 @@ Procedure.i OpenRestoreFileWindow(repoDir$, List rows.FileRow()) If Trim(out$) = "" MessageRequester("Restaurer", "Aucun commit trouvé pour ce fichier." + #LF$ + - "Vérifiez que le fichier est (ou a été) suivi par Git et qu’il a déjà été committé.", + "Vérifiez que le fichier est (ou a été) suivi par Git et qu'il a déjà été committé.", #PB_MessageRequester_Info) ProcedureReturn 0 EndIf - ; 3) UI : liste des commits avec Date/Heure + ; 3) UI: commit list with Date/Time / UI : liste des commits avec Date/Heure NewList hashes.s() If OpenWindow(#WRestore, 0, 0, 780, 440, "Restaurer : " + target$, #PB_Window_SystemMenu | #PB_Window_ScreenCentered) TextGadget(#GRestInfo, 10, 10, 760, 22, "Choisissez le commit vers lequel restaurer le fichier.") ListIconGadget(#GRestList, 10, 40, 760, 340, "Commit", 110, #PB_ListIcon_FullRowSelect) - AddGadgetColumn(#GRestList, 1, "Date / Heure", 190) ; élargi pour l’heure et le fuseau + AddGadgetColumn(#GRestList, 1, "Date / Heure", 190) ; Widened for time and timezone / Élargi pour l'heure et le fuseau AddGadgetColumn(#GRestList, 2, "Message", 440) ButtonGadget(#GRestOK, 550, 390, 100, 28, "Restaurer") ButtonGadget(#GRestCancel, 660, 390, 100, 28, "Annuler") @@ -1505,9 +1643,9 @@ Procedure.i OpenRestoreFileWindow(repoDir$, List rows.FileRow()) For i = 1 To n line$ = StringField(out$, i, #LF$) If Trim(line$) <> "" - Protected h$ = StringField(line$, 1, #TAB$) ; hash abrégé - Protected d$ = StringField(line$, 2, #TAB$) ; date + heure (+ZZZZ) - Protected s$ = StringField(line$, 3, #TAB$) ; message + Protected h$ = StringField(line$, 1, #TAB$) ; Abbreviated hash / Hash abrégé + Protected d$ = StringField(line$, 2, #TAB$) ; Date + time (+ZZZZ) / Date + heure (+ZZZZ) + Protected s$ = StringField(line$, 3, #TAB$) ; Message / Message AddGadgetItem(#GRestList, -1, h$ + #LF$ + d$ + #LF$ + s$) AddElement(hashes()) : hashes() = h$ If #EnableDebug : Debug "[Restore list] " + h$ + " | " + d$ + " | " + s$ : EndIf @@ -1523,7 +1661,7 @@ Procedure.i OpenRestoreFileWindow(repoDir$, List rows.FileRow()) If idx < 0 MessageRequester("Restaurer", "Sélectionnez un commit.", #PB_MessageRequester_Warning) Else - ; Récupérer le hash choisi + ; Get chosen hash / Récupérer le hash choisi Protected k.i = 0, chosen$ ForEach hashes() If k = idx : chosen$ = hashes() : Break : EndIf @@ -1550,8 +1688,15 @@ Procedure.i OpenRestoreFileWindow(repoDir$, List rows.FileRow()) ProcedureReturn 0 EndProcedure +; ============================================================================= +; GUIDE PANEL / PANNEAU GUIDE +; ============================================================================= + +; Update "Guide" panel with step-by-step instructions ; Met à jour le panneau "Guide" avec un pas-à-pas adapté -; - Inclut l’étape “Init repo” si le dossier n’est pas encore un dépôt +; - Include "Init repo" step if folder isn't a repository yet +; - Remind difference between Commit / Push for beginners +; - Inclut l'étape "Init repo" si le dossier n'est pas encore un dépôt ; - Rappelle la différence Commit / Push pour débutants Procedure.i UpdateGuide(repoDir$, List rows.FileRow(), branch$, remote$) Protected tips$ = "" @@ -1560,7 +1705,7 @@ Procedure.i UpdateGuide(repoDir$, List rows.FileRow(), branch$, remote$) Protected mail$ = GetLocalConfig(repoDir$, "user.email") Protected repoReady.i = IsGitRepo(repoDir$) - ; Y a-t-il des lignes dans le status ? + ; Are there lines in status? / Y a-t-il des lignes dans le status ? ForEach rows() hasChanges = 1 Break @@ -1570,6 +1715,7 @@ Procedure.i UpdateGuide(repoDir$, List rows.FileRow(), branch$, remote$) tips$ + "- Ce panneau vous guide pas à pas." + #LF$ + #LF$ If repoReady = 0 + ; ---- Repository not initialized: start with git init ---- ; ---- Dépôt non initialisé : on commence par git init ---- tips$ + "Étapes de départ :" + #LF$ tips$ + "1) " + Chr(149) + " Cliquez sur le bouton 'Init repo' pour INITIALISER le dépôt dans ce dossier." + #LF$ @@ -1579,10 +1725,10 @@ Procedure.i UpdateGuide(repoDir$, List rows.FileRow(), branch$, remote$) tips$ + "5) " + Chr(149) + " Pour partager en ligne : créez un dépôt distant (GitHub/GitLab…) puis utilisez 'Push'." + #LF$ + #LF$ Else - ; ---- Dépôt déjà prêt ---- + ; ---- Repository already ready ---- / ---- Dépôt déjà prêt ---- tips$ + "Étapes :" + #LF$ tips$ + "1) " + Chr(149) + " (Optionnel) Créez/complétez un '.gitignore' si nécessaire." + #LF$ - tips$ + "2) " + Chr(149) + " Vérifiez l’identité Git locale." + #LF$ + tips$ + "2) " + Chr(149) + " Vérifiez l'identité Git locale." + #LF$ If name$ = "" Or mail$ = "" tips$ + " → Identité : INCOMPLÈTE (utilisez 'Configurer identité…')." + #LF$ Else @@ -1592,7 +1738,7 @@ Procedure.i UpdateGuide(repoDir$, List rows.FileRow(), branch$, remote$) If hasChanges tips$ + " → Des modifications sont détectées ci-dessus." + #LF$ Else - tips$ + " → Aucune modification détectée pour l’instant." + #LF$ + tips$ + " → Aucune modification détectée pour l'instant." + #LF$ EndIf tips$ + "4) " + Chr(149) + " Saisissez un message, cliquez 'Add + Commit' (et cochez 'Pousser après' si vous voulez envoyer au serveur)." + #LF$ tips$ + "5) " + Chr(149) + " Pour changer/restaurer une branche : 'Avancé…'." + #LF$ + #LF$ @@ -1600,11 +1746,11 @@ Procedure.i UpdateGuide(repoDir$, List rows.FileRow(), branch$, remote$) tips$ + "Infos :" + #LF$ tips$ + "• Remote : " + remote$ + " • Branche : " + branch$ + #LF$ - tips$ + "• 'Diff…' affiche le détail d’un fichier sélectionné." + #LF$ + #LF$ + tips$ + "• 'Diff…' affiche le détail d'un fichier sélectionné." + #LF$ + #LF$ - ; Rappel pédagogique (commit vs push) + ; Educational reminder (commit vs push) / Rappel pédagogique (commit vs push) tips$ + "Rappel : différence entre Commit et Push" + #LF$ - tips$ + "• Commit : enregistre localement un instantané de vos fichiers (dans l’historique du dépôt sur votre machine)." + #LF$ + tips$ + "• Commit : enregistre localement un instantané de vos fichiers (dans l'historique du dépôt sur votre machine)." + #LF$ tips$ + "• Push : envoie vos commits locaux vers un serveur distant (GitHub, GitLab, etc.)." + #LF$ tips$ + "→ On peut faire des commits hors-ligne ; le push nécessite un dépôt distant configuré (ex. 'origin') et une branche (ex. 'main')." + #LF$ @@ -1617,13 +1763,19 @@ Procedure.i UpdateGuide(repoDir$, List rows.FileRow(), branch$, remote$) ProcedureReturn 1 EndProcedure -; Affiche stdout + stderr dans une fenêtre (évite le texte tronqué) - +; ============================================================================= +; FULL FILE LIST BUILDING / CONSTRUCTION DE LA LISTE COMPLÈTE DE FICHIERS +; ============================================================================= +; Build rows() list with all files (according to options), WITHOUT name correction ; Construit la liste rows() avec tous les fichiers (selon options), SANS correction de nom +; - showClean=1 → include tracked "clean" files (OK) +; - showIgnored=1 → also include ignored files (!!) +; - Check disk-side existence; if missing and not a delete, mark "NF" (Not Found) +; - rows()\include = 1 for elements potentially to commit (≠ OK/!!) ; - showClean=1 → inclut les fichiers suivis "propres" (OK) ; - showIgnored=1 → inclut aussi les fichiers ignorés (!!) -; - Vérifie l’existence côté disque ; si absent et pas un delete, marque "NF" (Introuvable) +; - Vérifie l'existence côté disque ; si absent et pas un delete, marque "NF" (Introuvable) ; - rows()\include = 1 pour les éléments potentiellement à committer (≠ OK/!!) Procedure.i BuildFullFileList(repoDir$, showClean.i, showIgnored.i, List rows.FileRow()) Protected gc.GitCall @@ -1632,7 +1784,7 @@ Procedure.i BuildFullFileList(repoDir$, showClean.i, showIgnored.i, List rows.Fi ClearList(rows()) - ; Charger exclusions permanentes locales + ; Load permanent local exclusions / Charger exclusions permanentes locales NewList excl.s() LoadLocalExcludes(repoDir$, excl()) @@ -1671,7 +1823,7 @@ Procedure.i BuildFullFileList(repoDir$, showClean.i, showIgnored.i, List rows.Fi statusMap() = code$ Next - ; 2) ls-files → suivis + ; 2) ls-files → tracked / Suivis NewMap trackedMap.i() gc\args = "ls-files" If RunGit(@gc) = 0 @@ -1686,7 +1838,7 @@ Procedure.i BuildFullFileList(repoDir$, showClean.i, showIgnored.i, List rows.Fi Next EndIf - ; 3) Ajout suivis (propres/modifiés) + ; 3) Add tracked files (clean/modified) / Ajout suivis (propres/modifiés) ForEach trackedMap() file$ = MapKey(trackedMap()) If FindMapElement(statusMap(), file$) @@ -1717,7 +1869,7 @@ Procedure.i BuildFullFileList(repoDir$, showClean.i, showIgnored.i, List rows.Fi EndIf Next - ; 4) Ajouter non suivis (??/!!) hors trackedMap + ; 4) Add untracked (??/!!) outside trackedMap / Ajouter non suivis (??/!!) hors trackedMap ForEach statusMap() file$ = MapKey(statusMap()) code$ = statusMap() @@ -1745,7 +1897,7 @@ Procedure.i BuildFullFileList(repoDir$, showClean.i, showIgnored.i, List rows.Fi ProcedureReturn ListSize(rows()) EndProcedure -; Récupère le nom de fichier de la ligne 'index' depuis la liste interne +; Get filename from line 'index' from internal list / Récupère le nom de fichier de la ligne 'index' depuis la liste interne Procedure.s FileFromRowsByIndex(index.i, List rows.FileRow()) Protected j.i = 0 ForEach rows() @@ -1757,25 +1909,31 @@ Procedure.s FileFromRowsByIndex(index.i, List rows.FileRow()) ProcedureReturn "" EndProcedure -; Trie la liste rows() par "intérêt" (cf. ordre ci-dessus) puis par chemin. +; ============================================================================= +; SORTING BY INTEREST / TRI PAR INTÉRÊT +; ============================================================================= + +; Sort rows() list by "interest" (see order above) then by path +; Trie la liste rows() par "intérêt" (cf. ordre ci-dessus) puis par chemin +; Uses buckets then reassembles. No underscore, no IIf. ; Utilise des seaux (buckets) puis recolle le tout. Pas de underscore, pas de IIf. Procedure.i SortRowsByInterest(List rows.FileRow()) - ; Catégorisation locale + ; Local categorization / Catégorisation locale Protected ProcedureReturnValue.i = 0 Protected cat.i Protected x$, y$ - ; Seaux - NewList b0.FileRow() ; conflits (U) - NewList b1.FileRow() ; changements indexés (X) - NewList b2.FileRow() ; changements non indexés (Y) - NewList b3.FileRow() ; nouveaux (??) + ; Buckets / Seaux + NewList b0.FileRow() ; Conflicts (U) / Conflits (U) + NewList b1.FileRow() ; Indexed changes (X) / Changements indexés (X) + NewList b2.FileRow() ; Non-indexed changes (Y) / Changements non indexés (Y) + NewList b3.FileRow() ; New (??) / Nouveaux (??) NewList b4.FileRow() ; OK - NewList b5.FileRow() ; ignorés (!!) - NewList b6.FileRow() ; exclus permanents (EX) - NewList b7.FileRow() ; introuvables (NF) + NewList b5.FileRow() ; Ignored (!!) / Ignorés (!!) + NewList b6.FileRow() ; Permanently excluded (EX) / Exclus permanents (EX) + NewList b7.FileRow() ; Not found (NF) / Introuvables (NF) - ; Helper: pousse un FileRow dans un seau + ; Helper: push a FileRow into a bucket / Helper: pousse un FileRow dans un seau Macro PushTo(bucket) AddElement(bucket()) bucket()\file = rows()\file @@ -1783,32 +1941,32 @@ Procedure.i SortRowsByInterest(List rows.FileRow()) bucket()\include = rows()\include EndMacro - ; 1) Répartition dans les seaux + ; 1) Distribution into buckets / Répartition dans les seaux ForEach rows() cat = -1 x$ = Left(rows()\stat, 1) y$ = Right(rows()\stat, 1) If FindString(rows()\stat, "U", 1) > 0 - cat = 0 + cat = 0 ; Conflicts / Conflits ElseIf rows()\stat = "??" - cat = 3 + cat = 3 ; New files / Nouveaux fichiers ElseIf rows()\stat = "OK" - cat = 4 + cat = 4 ; Up to date / À jour ElseIf rows()\stat = "!!" - cat = 5 + cat = 5 ; Ignored / Ignorés ElseIf rows()\stat = "EX" - cat = 6 + cat = 6 ; Permanently excluded / Exclus permanents ElseIf rows()\stat = "NF" - cat = 7 + cat = 7 ; Not found / Introuvables Else - ; autres codes porcelain XY + ; Other porcelain XY codes / Autres codes porcelain XY If x$ <> " " - cat = 1 + cat = 1 ; Indexed changes / Changements indexés ElseIf y$ <> " " - cat = 2 + cat = 2 ; Working tree changes / Changements répertoire de travail Else - ; par sécurité, considère comme non indexé + ; Safety, consider as non-indexed / Par sécurité, considère comme non indexé cat = 2 EndIf EndIf @@ -1825,7 +1983,7 @@ Procedure.i SortRowsByInterest(List rows.FileRow()) EndSelect Next - ; 2) Tri alphabétique par chemin dans chaque seau + ; 2) Alphabetical sort by path in each bucket / Tri alphabétique par chemin dans chaque seau SortStructuredList(b0(), #PB_Sort_Ascending, OffsetOf(FileRow\file), #PB_String) SortStructuredList(b1(), #PB_Sort_Ascending, OffsetOf(FileRow\file), #PB_String) SortStructuredList(b2(), #PB_Sort_Ascending, OffsetOf(FileRow\file), #PB_String) @@ -1835,7 +1993,7 @@ Procedure.i SortRowsByInterest(List rows.FileRow()) SortStructuredList(b6(), #PB_Sort_Ascending, OffsetOf(FileRow\file), #PB_String) SortStructuredList(b7(), #PB_Sort_Ascending, OffsetOf(FileRow\file), #PB_String) - ; 3) Recomposition de rows() dans l'ordre des seaux + ; 3) Recomposition of rows() in bucket order / Recomposition de rows() dans l'ordre des seaux ClearList(rows()) Macro AppendBucket(bucket2) @@ -1864,43 +2022,48 @@ Procedure.i SortRowsByInterest(List rows.FileRow()) ProcedureReturn ProcedureReturnValue EndProcedure -; ------------------------------------------------------------------ -; Ouvre l'interface principale (Push manuel, guide scrollable, -; prefs dépôt correctement rechargées & auto-sauvegardées) -; ------------------------------------------------------------------ -; ------------------------------------------------------------------ -; Ouvre l'interface principale (tri par intérêt avant affichage) -; ------------------------------------------------------------------ +; ============================================================================= +; MAIN GUI / INTERFACE PRINCIPALE +; ============================================================================= + +; Open main interface / Ouvre l'interface principale +; Features / Fonctionnalités : +; - Manual push, scrollable guide +; - Repository prefs correctly reloaded & auto-saved +; - Sort by interest before display +; - Push manuel, guide scrollable +; - Prefs dépôt correctement rechargées & auto-sauvegardées +; - Tri par intérêt avant affichage Procedure.i OpenGUI(initialDir$, prefsPath$) Protected repoDir$ = DetectRepoRoot(initialDir$) - ; Défauts globaux + ; Global defaults / Défauts globaux Protected defRemote$ = "" Protected defBranch$ = "" LoadPrefs(prefsPath$, defRemote$, defBranch$) - ; Prefs du dépôt (via structure) + ; Repository prefs (via structure) / Prefs du dépôt (via structure) Protected rp.RepoPrefs rp\remote = defRemote$ rp\branch = defBranch$ LoadRepoPrefs(repoDir$, @rp) - ; Sélection persistante + ; Persistent selection / Sélection persistante Protected keepIdx.i Protected keepFile$ - ; Ignorer automatiquement notre prefs locale + ; Automatically ignore our local prefs / Ignorer automatiquement notre prefs locale EnsureToolFilesExcluded(repoDir$) If OpenWindow(#GWindow, 0, 0, 920, 720, "PBIDE-GitTool — Git (mode simplifié)", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) - ; --- En-tête dépôt --- + ; --- Repository header / En-tête dépôt --- TextGadget(#GLabelRepo, 10, 12, 60, 22, "Dépôt :") StringGadget(#GStringRepo, 80, 10, 740, 24, repoDir$) ButtonGadget(#GButtonBrowse, 830, 10, 80, 24, "Parcourir…") - ; --- Options d'affichage --- - CheckBoxGadget(#GShowClean, 10, 40, 220, 20, "Afficher suivis à jour") + ; --- Display options / Options d'affichage --- + CheckBoxGadget(#GShowClean, 10, 40, 220, 20, "Afficher suivis à jour") SetGadgetState(#GShowClean, #True) CheckBoxGadget(#GShowIgnored, 240, 40, 240, 20, "Afficher ignorés (.gitignore)") SetGadgetState(#GShowIgnored, #False) @@ -1908,21 +2071,21 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) SetGadgetState(#GShowPermanent, #True) GadgetToolTip(#GShowIgnored, "Montre les fichiers ignorés par règles (.gitignore, exclude global) — statut '!!'.") - GadgetToolTip(#GShowPermanent, "Montre les fichiers exclus via l’outil (.git/info/exclude) — statut 'EX'.") + GadgetToolTip(#GShowPermanent, "Montre les fichiers exclus via l'outil (.git/info/exclude) — statut 'EX'.") - ; --- Liste des fichiers --- + ; --- File list / Liste des fichiers --- ListIconGadget(#GListStatus, 10, 66, 900, 320, "État", 240, #PB_ListIcon_CheckBoxes | #PB_ListIcon_FullRowSelect) AddGadgetColumn(#GListStatus, 1, "Fichier", 640) - ; --- Bande d’actions sur la liste --- - ButtonGadget(#GRefresh, 10, 392, 100, 26, "Rafraîchir") - ButtonGadget(#GInit, 120, 392, 100, 26, "Init repo") - ButtonGadget(#GExcludeForever, 230, 392, 150, 26, "Exclure (permanent)") + ; --- List action buttons / Bande d'actions sur la liste --- + ButtonGadget(#GRefresh, 10, 392, 100, 26, "Rafraîchir") + ButtonGadget(#GInit, 120, 392, 100, 26, "Init repo") + ButtonGadget(#GExcludeForever, 230, 392, 150, 26, "Exclure (permanent)") ButtonGadget(#GReincludeForever, 390, 392, 170, 26, "Ré-inclure (permanent)") - ButtonGadget(#GDiff, 570, 392, 90, 26, "Diff…") - ButtonGadget(#GRestoreFile, 670, 392, 130, 26, "Restaurer…") + ButtonGadget(#GDiff, 570, 392, 90, 26, "Diff…") + ButtonGadget(#GRestoreFile, 670, 392, 130, 26, "Restaurer…") - ; --- Zone message / remote / branche --- + ; --- Message / remote / branch area / Zone message / remote / branche --- TextGadget(#GLabelMsg, 10, 428, 100, 22, "Message :") StringGadget(#GStringMsg, 110, 426, 620, 24, "") @@ -1936,7 +2099,7 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) ButtonGadget(#GAddBranch, 670, 454, 110, 24, "Ajouter br.…") ButtonGadget(#GReloadBranches, 790, 454, 120, 24, "Actualiser br.") - ; --- Actions principales --- + ; --- Main actions / Actions principales --- ButtonGadget(#GCommit, 10, 490, 160, 30, "Add + Commit (cochés)") ButtonGadget(#GPush, 180, 490, 120, 30, "Push") ButtonGadget(#GPull, 310, 490, 120, 30, "Pull") @@ -1948,11 +2111,11 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) GadgetToolTip(#GRestoreFile, "Restaurer le fichier sélectionné à un commit précis.") GadgetToolTip(#GDiff, "Afficher les différences du fichier sélectionné.") - ; --- Guide (scrollable en lecture seule) --- + ; --- Guide (scrollable read-only) / Guide (scrollable en lecture seule) --- EditorGadget(#GGuide, 10, 530, 900, 170) SetGadgetAttribute(#GGuide, #PB_Editor_ReadOnly, 1) - ; --- Branches locales --- + ; --- Local branches / Branches locales --- NewList branchItems.s() ListBranches(repoDir$, branchItems()) ClearGadgetItems(#GComboBranch) @@ -1961,7 +2124,7 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) Next If rp\branch <> "" : SetGadgetText(#GComboBranch, rp\branch) : EndIf - ; --- Fichiers : construction, tri, affichage --- + ; --- Files: build, sort, display / Fichiers : construction, tri, affichage --- NewList rows.FileRow() BuildFullFileList(repoDir$, GetGadgetState(#GShowClean), GetGadgetState(#GShowIgnored), rows()) If GetGadgetState(#GShowPermanent) = 0 @@ -1971,7 +2134,7 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) FillStatusList(rows()) UpdateGuide(repoDir$, rows(), GetGadgetText(#GComboBranch), GetGadgetText(#GStringRemote)) - ; =================== Boucle événements =================== + ; =================== EVENT LOOP / BOUCLE ÉVÉNEMENTS =================== Repeat Protected ev.i = WaitWindowEvent() Select ev @@ -1979,7 +2142,7 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) Case #PB_Event_Gadget Select EventGadget() - ; ---- Changement de dépôt ---- + ; ---- Repository change / Changement de dépôt ---- Case #GButtonBrowse Protected newDir$ = PathRequester("Choisir le répertoire du dépôt", repoDir$) If newDir$ <> "" @@ -1987,17 +2150,20 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) SetGadgetText(#GStringRepo, repoDir$) EnsureToolFilesExcluded(repoDir$) + ; Reload repository preferences / Recharger les préférences du dépôt rp\remote = defRemote$ rp\branch = defBranch$ LoadRepoPrefs(repoDir$, @rp) SetGadgetText(#GStringRemote, rp\remote) + ; Refresh branch list / Actualiser la liste des branches ClearList(branchItems()) ListBranches(repoDir$, branchItems()) ClearGadgetItems(#GComboBranch) ForEach branchItems() : AddGadgetItem(#GComboBranch, -1, branchItems()) : Next If rp\branch <> "" : SetGadgetText(#GComboBranch, rp\branch) : EndIf + ; Refresh file list / Actualiser la liste des fichiers RememberSel() ClearList(rows()) BuildFullFileList(repoDir$, GetGadgetState(#GShowClean), GetGadgetState(#GShowIgnored), rows()) @@ -2010,7 +2176,7 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) UpdateGuide(repoDir$, rows(), GetGadgetText(#GComboBranch), GetGadgetText(#GStringRemote)) EndIf - ; ---- Filtres d'affichage ---- + ; ---- Display filters / Filtres d'affichage ---- Case #GShowClean, #GShowIgnored, #GShowPermanent, #GRefresh RememberSel() ClearList(rows()) @@ -2023,7 +2189,7 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) RestoreSel() UpdateGuide(repoDir$, rows(), GetGadgetText(#GComboBranch), GetGadgetText(#GStringRemote)) - ; ---- Init dépôt ---- + ; ---- Initialize repository / Init dépôt ---- Case #GInit DoInitRepo(repoDir$) RememberSel() @@ -2037,7 +2203,7 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) RestoreSel() UpdateGuide(repoDir$, rows(), GetGadgetText(#GComboBranch), GetGadgetText(#GStringRemote)) - ; ---- Exclusion permanente / ré-inclusion ---- + ; ---- Permanent exclusion / re-inclusion / Exclusion permanente / ré-inclusion ---- Case #GExcludeForever Protected idx.i = GetGadgetState(#GListStatus) If idx >= 0 @@ -2076,7 +2242,7 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) EndIf EndIf - ; ---- Diff / Restaurer ---- + ; ---- Diff / Restore / Diff / Restaurer ---- Case #GDiff OpenDiffWindow(repoDir$, rows()) @@ -2094,7 +2260,7 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) UpdateGuide(repoDir$, rows(), GetGadgetText(#GComboBranch), GetGadgetText(#GStringRemote)) EndIf - ; ---- Branches : maj / ajout ---- + ; ---- Branches: update / add / Branches : maj / ajout ---- Case #GReloadBranches ClearList(branchItems()) ListBranches(repoDir$, branchItems()) @@ -2116,7 +2282,7 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) EndIf EndIf - ; ---- Sauver défauts globaux (optionnel) ---- + ; ---- Save global defaults (optional) / Sauver défauts globaux (optionnel) ---- Case #GSavePrefs defRemote$ = GetGadgetText(#GStringRemote) defBranch$ = GetGadgetText(#GComboBranch) @@ -2126,6 +2292,7 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) MessageRequester("Préférences", "Échec d'enregistrement.", #PB_MessageRequester_Error) EndIf + ; ---- Auto-save repository: remote and branch when they change ---- ; ---- Auto-save dépôt : remote et branche quand ils changent ---- Case #GStringRemote If EventType() = #PB_EventType_Change @@ -2145,7 +2312,7 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) Else Protected remote$ = GetGadgetText(#GStringRemote) Protected branch$ = GetGadgetText(#GComboBranch) - Protected pushAfter.i = 0 ; push manuel + Protected pushAfter.i = 0 ; Manual push / Push manuel NewList files.s() CollectIncludedFiles(rows(), files()) If ListSize(files()) > 0 @@ -2154,6 +2321,7 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) DoCommit(repoDir$, msg$, pushAfter, remote$, branch$) EndIf + ; Refresh after commit / Actualiser après commit RememberSel() ClearList(rows()) BuildFullFileList(repoDir$, GetGadgetState(#GShowClean), GetGadgetState(#GShowIgnored), rows()) @@ -2172,20 +2340,29 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) Case #GPull DoPull(repoDir$, GetGadgetText(#GStringRemote), GetGadgetText(#GComboBranch)) - ; ---- Clic sur la liste (cocher/décocher = inclure/exclure) ---- + ; ---- Advanced window / Fenêtre avancée ---- + Case #GAdvanced + OpenAdvancedWindow(repoDir$) + + ; ---- Identity configuration / Configuration d'identité ---- + Case #GConfig + ConfigIdentityWizard(repoDir$) + + ; ---- List click (check/uncheck = include/exclude) / Clic sur la liste (cocher/décocher = inclure/exclure) ---- Case #GListStatus Protected idx2.i = GetGadgetState(#GListStatus) If idx2 >= 0 RememberSel() ToggleIncludeAt(idx2, rows()) SortRowsByInterest(rows()) - FillStatusList(rows()) ; garde la sélection par la suite + FillStatusList(rows()) ; Keep selection afterwards / Garde la sélection par la suite RestoreSel() EndIf EndSelect Case #PB_Event_CloseWindow + ; Save repository preferences on exit / Sauvegarder les préférences du dépôt à la sortie SaveRepoPrefs(repoDir$, GetGadgetText(#GStringRemote), GetGadgetText(#GComboBranch)) EndSelect @@ -2197,34 +2374,24 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) ProcedureReturn 0 EndProcedure -; ====== Installation IDE ====== -; Fenêtre assistant -#WInstall = 100 -#GLabelIde = 110 -#GStringIde = 111 -#GButtonIde = 112 -#GLabelTools = 113 -#GStringTools = 114 -#GButtonTools = 115 -#GLabelTheme = 116 -#GStringTheme = 117 -#GButtonTheme = 118 -#GInstallGo = 119 -#GInstallCancel = 120 -#GInstallNote = 121 +; ============================================================================= +; IDE INSTALLATION / INSTALLATION IDE +; ============================================================================= +; Install PBGit integration in IDE / Installe l'intégration PBGit dans l'IDE Procedure.i InstallPBGitInIDE(ideExe$, toolsPrefs$, themeZip$) Protected ok.i = 1, ideHome$, themesDir$, destZip$, args$, prg.i, copyok.i If FileSize(ideExe$) <= 0 Or FileSize(toolsPrefs$) <= 0 Or FileSize(themeZip$) <= 0 - MessageRequester("Installation", "Chemins invalides. Sélectionnez PureBasic.exe, le fichier d’outils et le thème.", #PB_MessageRequester_Error) + MessageRequester("Installation", "Chemins invalides. Sélectionnez PureBasic.exe, le fichier d'outils et le thème.", #PB_MessageRequester_Error) ProcedureReturn 0 EndIf - ideHome$ = GetPathPart(ideExe$) + ideHome$ = GetPathPart(ideExe$) themesDir$ = ideHome$ + "Themes" + #PathSep$ - destZip$ = themesDir$ + GetFilePart(themeZip$) + destZip$ = themesDir$ + GetFilePart(themeZip$) + ; Create Themes directory if needed / Créer le dossier Themes si nécessaire If FileSize(themesDir$) <> -2 If CreateDirectory(themesDir$) = 0 MessageRequester("Installation", "Impossible de créer le dossier Themes : " + themesDir$, #PB_MessageRequester_Error) @@ -2232,17 +2399,19 @@ Procedure.i InstallPBGitInIDE(ideExe$, toolsPrefs$, themeZip$) EndIf EndIf + ; Copy theme / Copier le thème copyok = CopyFile(themeZip$, destZip$) If copyok = 0 MessageRequester("Installation", "Échec de copie du thème vers : " + destZip$, #PB_MessageRequester_Error) ok = 0 EndIf + ; Import tools via /A switch / Importer les outils via le switch /A If ok args$ = "/A " + Chr(34) + toolsPrefs$ + Chr(34) prg = RunProgram(ideExe$, args$, "", #PB_Program_Hide | #PB_Program_Wait) If prg = 0 - MessageRequester("Installation", "Échec de l’import des outils (/A).", #PB_MessageRequester_Error) + MessageRequester("Installation", "Échec de l'import des outils (/A).", #PB_MessageRequester_Error) ok = 0 EndIf EndIf @@ -2255,40 +2424,42 @@ Procedure.i InstallPBGitInIDE(ideExe$, toolsPrefs$, themeZip$) ProcedureReturn ok EndProcedure +; Open installation wizard / Ouvre l'assistant d'installation Procedure.i OpenInstallWizard() Protected exeDefault$ = "" Protected dirExe$ = GetPathPart(ProgramFilename()) Protected prefsDefault$ = dirExe$ + "git_tools.prefs" Protected themeDefault$ = dirExe$ + "PBGitTheme.zip" - If OpenWindow(#WInstall, 0, 0, 680, 240, "PBIDE-GitTool — Assistant d’installation IDE", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) - TextGadget(#GLabelIde, 10, 16, 140, 22, "PureBasic.exe :") - StringGadget(#GStringIde,160, 14, 420, 24, exeDefault$) - ButtonGadget(#GButtonIde,590, 14, 80, 24, "Parcourir…") + If OpenWindow(#WInstall, 0, 0, 680, 240, "PBIDE-GitTool — Assistant d'installation IDE", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) + TextGadget(#GLabelIde, 10, 16, 140, 22, "PureBasic.exe :") + StringGadget(#GStringIde, 160, 14, 420, 24, exeDefault$) + ButtonGadget(#GButtonIde, 590, 14, 80, 24, "Parcourir…") TextGadget(#GLabelTools, 10, 56, 140, 22, "Fichier outils :") - StringGadget(#GStringTools,160, 54, 420, 24, prefsDefault$) - ButtonGadget(#GButtonTools,590, 54, 80, 24, "Parcourir…") + StringGadget(#GStringTools, 160, 54, 420, 24, prefsDefault$) + ButtonGadget(#GButtonTools, 590, 54, 80, 24, "Parcourir…") TextGadget(#GLabelTheme, 10, 96, 140, 22, "Thème icônes :") - StringGadget(#GStringTheme,160, 94, 420, 24, themeDefault$) - ButtonGadget(#GButtonTheme,590, 94, 80, 24, "Parcourir…") + StringGadget(#GStringTheme, 160, 94, 420, 24, themeDefault$) + ButtonGadget(#GButtonTheme, 590, 94, 80, 24, "Parcourir…") - ButtonGadget(#GInstallGo, 380, 150, 140, 30, "Installer") + ButtonGadget(#GInstallGo, 380, 150, 140, 30, "Installer") ButtonGadget(#GInstallCancel, 530, 150, 140, 30, "Annuler") TextGadget(#GInstallNote, 10, 190, 660, 40, "Astuce : placez 'git_tools.prefs' et 'PBGitTheme.zip' à côté de PBIDE-GitTool.exe pour auto-renseignement." + #LF$ + "Ensuite choisissez le thème (Préférences → Themes) et ajoutez les boutons 'Run tool' dans la Toolbar.") + ; Tooltips / Info-bulles GadgetToolTip(#GStringIde, "Chemin de PureBasic.exe (IDE).") - GadgetToolTip(#GButtonIde, "Parcourir pour sélectionner l’exécutable de l’IDE PureBasic.") - GadgetToolTip(#GStringTools, "Fichier d’outils externes à importer (/A).") + GadgetToolTip(#GButtonIde, "Parcourir pour sélectionner l'exécutable de l'IDE PureBasic.") + GadgetToolTip(#GStringTools, "Fichier d'outils externes à importer (/A).") GadgetToolTip(#GButtonTools, "Parcourir le fichier 'git_tools.prefs'.") - GadgetToolTip(#GStringTheme, "Archive ZIP du thème d’icônes pour la Toolbar.") + GadgetToolTip(#GStringTheme, "Archive ZIP du thème d'icônes pour la Toolbar.") GadgetToolTip(#GButtonTheme, "Parcourir le fichier 'PBGitTheme.zip'.") - GadgetToolTip(#GInstallGo, "Lancer l’installation (copie du thème + import des outils).") - GadgetToolTip(#GInstallCancel, "Fermer l’assistant sans rien modifier.") + GadgetToolTip(#GInstallGo, "Lancer l'installation (copie du thème + import des outils).") + GadgetToolTip(#GInstallCancel, "Fermer l'assistant sans rien modifier.") Repeat Protected ev.i = WaitWindowEvent() @@ -2322,39 +2493,44 @@ Procedure.i OpenInstallWizard() ProcedureReturn 0 EndProcedure -; ====== Entrée ====== -; On ne coupe pas si Git manque, pour permettre l’assistant d’installation +; ============================================================================= +; MAIN ENTRY POINT / POINT D'ENTRÉE PRINCIPAL +; ============================================================================= + +; Don't exit if Git is missing, to allow installation wizard +; On ne coupe pas si Git manque, pour permettre l'assistant d'installation If EnsureGitAvailable() = 0 - ; Averti, mais continue. + ; Warned, but continue / Averti, mais continue EndIf -; Préférences locales de l’outil +; Local tool preferences / Préférences locales de l'outil Define baseDoc$ = GetUserDirectory(#PB_Directory_Documents) If Right(baseDoc$, 1) <> #PathSep$ : baseDoc$ + #PathSep$ : EndIf Define prefsDir$ = baseDoc$ + "PBIDE-GitTool" + #PathSep$ If FileSize(prefsDir$) <> -2 : CreateDirectory(prefsDir$) : EndIf Define prefsPath$ = prefsDir$ + "settings.prefs" -; Paramètres CLI +; CLI parameters / Paramètres CLI Define argCount.i = CountProgramParameters() Define useGui.i = 1 Define repoArg$ = "", msgArg$ = "", remoteArg$ = "", branchArg$ = "" Define wantStatus.i = 0, wantInit.i = 0, wantCommit.i = 0, wantPush.i = 0, wantPull.i = 0 Define w.i -; Si aucun paramètre : proposer l’installation IDE +; If no parameters: offer IDE installation / Si aucun paramètre : proposer l'installation IDE If argCount = 0 - If MessageRequester("PBIDE-GitTool", "Aucun paramètre détecté." + #LF$ + "Souhaitez-vous installer l’intégration IDE (outils + thème) maintenant ?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes + If MessageRequester("PBIDE-GitTool", "Aucun paramètre détecté." + #LF$ + "Souhaitez-vous installer l'intégration IDE (outils + thème) maintenant ?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes OpenInstallWizard() End EndIf EndIf +; Parse command line arguments / Analyser les arguments de ligne de commande For w = 0 To argCount - 1 Define a$ = ProgramParameter(w) Select LCase(a$) - Case "--project" : If w + 1 < argCount : repoArg$ = ProgramParameter(w + 1) : EndIf - Case "--repo" : If w + 1 < argCount : repoArg$ = ProgramParameter(w + 1) : EndIf + Case "--project" : If w + 1 < argCount : repoArg$ = ProgramParameter(w + 1) : EndIf + Case "--repo" : If w + 1 < argCount : repoArg$ = ProgramParameter(w + 1) : EndIf Case "--status" : wantStatus = 1 : useGui = 0 Case "--init" : wantInit = 1 : useGui = 0 Case "--commit" : wantCommit = 1 : useGui = 0 : If w + 1 < argCount : msgArg$ = ProgramParameter(w + 1) : EndIf @@ -2366,11 +2542,12 @@ For w = 0 To argCount - 1 Next w If useGui + ; GUI mode / Mode interface graphique Define dir$ = repoArg$ If dir$ = "" : dir$ = DirFromArgOrFallback() : EndIf OpenGUI(dir$, prefsPath$) Else - ; Mode non interactif + ; Non-interactive mode / Mode non interactif Define remote$ = "", branch$ = "" LoadPrefs(prefsPath$, remote$, branch$) If remoteArg$ <> "" : remote$ = remoteArg$ : EndIf @@ -2380,6 +2557,7 @@ Else If baseDir$ = "" : baseDir$ = DirFromArgOrFallback() : EndIf baseDir$ = DetectRepoRoot(baseDir$) + ; Execute requested operation / Exécuter l'opération demandée If wantStatus Define st$, okstat.i = DoStatus(baseDir$, st$) If okstat : PrintN(st$) : EndIf @@ -2395,9 +2573,12 @@ Else EndIf EndIf +; ============================================================================= +; END OF FILE / FIN DU FICHIER +; ============================================================================= ; IDE Options = PureBasic 6.21 (Windows - x64) -; CursorPosition = 1835 -; FirstLine = 1824 +; CursorPosition = 422 +; FirstLine = 418 ; Folding = ----------- ; EnableXP ; DPIAware