From 6940055618b8f763912e82e81d4b13c2fa8b1ab4 Mon Sep 17 00:00:00 2001 From: Yann Lebrun Date: Fri, 15 Aug 2025 11:26:13 +0200 Subject: [PATCH] =?UTF-8?q?pr=C3=AAt=20pour=20la=20BETa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PBIDE-GitTool.pb | 619 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 453 insertions(+), 166 deletions(-) diff --git a/PBIDE-GitTool.pb b/PBIDE-GitTool.pb index 6312ac5..f8eaeb2 100644 --- a/PBIDE-GitTool.pb +++ b/PBIDE-GitTool.pb @@ -46,6 +46,10 @@ 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) + ; Fenêtre avancée (branches) #WAdv = 200 @@ -148,8 +152,73 @@ 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.s PorcelainToLabel(code$) ; (remplacer par la version complète ci-dessous) ; ====== Utils ====== +; Convertit le chemin pour Git (Windows ok) : +; - remplace "\" par "/" +; - retire un éventuel préfixe "./" +Procedure.s GitPath(file$) + Protected p$ = file$ + p$ = ReplaceString(p$, "\", "/") + If Left(p$, 2) = "./" + p$ = Mid(p$, 3) + EndIf + 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 +Procedure.s NormalizeGitFilePath(repoDir$, raw$) + Protected p$ = Trim(raw$) + ; retirer quotes simples/doubles éventuelles autour + If Left(p$, 1) = Chr(34) And Right(p$, 1) = Chr(34) + p$ = Mid(p$, 2, Len(p$)-2) + EndIf + If Left(p$, 1) = "'" And Right(p$, 1) = "'" + p$ = Mid(p$, 2, Len(p$)-2) + EndIf + + ; cas "old -> new" : on garde "new" + Protected pos.i = FindString(p$, "->", 1) + If pos > 0 + p$ = Trim(Mid(p$, pos + 2)) + EndIf + + ; retirer "./" + If Left(p$, 2) = "./" + p$ = Mid(p$, 3) + EndIf + + ; 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 +Procedure.i RepoFileExists(repoDir$, rel$) + Protected base$ = repoDir$ + If Right(base$, 1) <> #PathSep$ : base$ + #PathSep$ : EndIf + Protected abs$ = base$ + rel$ + Protected s.q = FileSize(abs$) + If s >= 0 Or s = -2 + ProcedureReturn 1 + EndIf + ProcedureReturn 0 +EndProcedure + + Procedure.s TrimNewlines(text$) Protected s$ = text$ While Right(s$, 1) = #LF$ Or Right(s$, 1) = #CR$ @@ -506,6 +575,65 @@ Procedure.i DoPull(repoDir$, remote$, branch$) ProcedureReturn 0 EndProcedure +; ------------------------------------------------------------------ +; 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 + MessageRequester("Diff", "Sélectionnez un fichier dans la liste.", #PB_MessageRequester_Info) + ProcedureReturn 0 + EndIf + + ; Retrouver le chemin du fichier depuis la liste rows() + Protected j.i = 0, target$ + ForEach rows() + If j = idx + target$ = rows()\file + Break + EndIf + j + 1 + Next + + If target$ = "" + MessageRequester("Diff", "Aucun fichier sélectionné.", #PB_MessageRequester_Info) + ProcedureReturn 0 + EndIf + + ; Exécuter 'git diff -- ""' + Protected gc.GitCall + gc\workdir = repoDir$ + gc\args = "diff -- " + Chr(34) + target$ + Chr(34) + If RunGit(@gc) <> 0 + MessageRequester("Diff", "Échec: " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error) + ProcedureReturn 0 + EndIf + + Protected diff$ = gc\output + If Trim(diff$) = "" + diff$ = "Aucune différence détectée pour ce fichier." + EndIf + + ; 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) + ButtonGadget(#GDiffClose, 690, 460, 100, 28, "Fermer") + SetGadgetText(#GDiffText, diff$) + + Repeat + Protected ev.i = WaitWindowEvent() + If ev = #PB_Event_Gadget And EventGadget() = #GDiffClose + CloseWindow(#WDiff) + Break + EndIf + Until ev = #PB_Event_CloseWindow + EndIf + + ProcedureReturn 1 +EndProcedure + + Procedure.i ListBranches(repoDir$, List branchesList.s()) ClearList(branchesList()) Protected gc.GitCall @@ -531,8 +659,13 @@ Procedure.i ListBranches(repoDir$, List branchesList.s()) EndProcedure ; ====== Status → lignes et gestion des coches ====== +; 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). Procedure.i LoadStatusRows(repoDir$, List rows.FileRow()) - Protected gc.GitCall, text$, line$, n.i, i.i + Protected gc.GitCall, text$, line$, code$, file$ + Protected i.i, n.i, start.i, ch$, pos.i + gc\args = "status --porcelain" gc\workdir = repoDir$ If RunGit(@gc) <> 0 @@ -542,36 +675,83 @@ Procedure.i LoadStatusRows(repoDir$, List rows.FileRow()) text$ = gc\output n = CountString(text$, #LF$) + 1 + ClearList(rows()) + For i = 1 To n - line$ = Trim(StringField(text$, i, #LF$)) - If line$ <> "" - AddElement(rows()) - rows()\stat = Left(line$, 2) - rows()\file = Trim(Mid(line$, 4)) - rows()\include = 0 + line$ = StringField(text$, i, #LF$) + line$ = Trim(line$) + If line$ = "" : Continue : EndIf + + ; 2 premières colonnes = statut (XY) + code$ = Left(line$, 2) + + ; Cherche le début du chemin : après les espaces/tabs suivant la colonne 3 + start = 3 + While start <= Len(line$) + ch$ = Mid(line$, start, 1) + If ch$ <> " " And ch$ <> #TAB$ + Break + EndIf + start + 1 + Wend + + file$ = Mid(line$, start) + ; Renommage "old -> new" : garder la destination + pos = FindString(file$, "->", 1) + If pos > 0 + file$ = Trim(Mid(file$, pos + 2)) EndIf + + ; (Optionnel) normalisation simple + If Left(file$, 2) = "./" : file$ = Mid(file$, 3) : EndIf + file$ = ReplaceString(file$, "/", #PathSep$) + + AddElement(rows()) + rows()\stat = code$ + rows()\file = file$ + rows()\include = 0 Next i + ProcedureReturn ListSize(rows()) EndProcedure + +; Remplit #GListStatus sans #LF$ (évite la perte du 1er caractère en col. 2) Procedure.i FillStatusList(List rows.FileRow()) ClearGadgetItems(#GListStatus) Protected idx.i = 0 - Protected label$ + Protected label$, file$ + ForEach rows() + ; 1) Texte de la 1re colonne (état lisible) label$ = PorcelainToLabel(rows()\stat) - AddGadgetItem(#GListStatus, -1, label$ + #LF$ + rows()\file) + AddGadgetItem(#GListStatus, -1, label$) + + ; 2) Texte de la 2e colonne (chemin) + + file$ = rows()\file + SetGadgetItemText(#GListStatus, idx, file$, 1) + + ; 3) Case à cocher selon include If rows()\include SetGadgetItemState(#GListStatus, idx, #PB_ListIcon_Checked) Else SetGadgetItemState(#GListStatus, idx, 0) EndIf + + ; 4) Debug (utile pour vérifier ce qui est réellement affiché) + If #EnableDebug + Debug "[Fill] idx=" + Str(idx) + " file='" + file$ + "' readback='" + GetGadgetItemText(#GListStatus, idx, 1) + "'" + EndIf + idx + 1 Next + ProcedureReturn CountGadgetItems(#GListStatus) EndProcedure + Procedure.i ToggleIncludeAt(index.i, List rows.FileRow()) If index < 0 : ProcedureReturn 0 : EndIf Protected c.i = CountGadgetItems(#GListStatus) @@ -724,11 +904,16 @@ Procedure.s PorcelainToLabel(code$) Protected x$ = Left(code$, 1) Protected y$ = Right(code$, 1) + If code$ = "OK" + ProcedureReturn "À jour (suivi)" + EndIf + If code$ = "??" ProcedureReturn "Nouveau (non suivi)" EndIf + If code$ = "!!" - ProcedureReturn "Ignoré" + ProcedureReturn "Ignoré (.gitignore)" EndIf ; Colonne X = indexé (staged) @@ -746,50 +931,6 @@ Procedure.s PorcelainToLabel(code$) ProcedureReturn "Changement" EndProcedure -Procedure.i OpenDiffWindow(repoDir$, List rows.FileRow()) - Protected idx.i = GetGadgetState(#GListStatus) - If idx < 0 - MessageRequester("Diff", "Sélectionnez un fichier dans la liste.", #PB_MessageRequester_Info) - ProcedureReturn 0 - EndIf - - Protected j.i = 0, target$ - ForEach rows() - If j = idx - target$ = rows()\file - Break - EndIf - j + 1 - Next - - If target$ = "" - MessageRequester("Diff", "Aucun fichier sélectionné.", #PB_MessageRequester_Info) - ProcedureReturn 0 - EndIf - - Protected gc.GitCall - gc\workdir = repoDir$ - gc\args = "diff -- " + Chr(34) + target$ + Chr(34) - If RunGit(@gc) <> 0 - MessageRequester("Diff", "Échec: " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error) - ProcedureReturn 0 - EndIf - - If OpenWindow(#WDiff, 0, 0, 800, 500, "Diff — " + target$, #PB_Window_SystemMenu | #PB_Window_ScreenCentered) - EditorGadget(#GDiffText, 10, 10, 780, 440) - ButtonGadget(#GDiffClose, 690, 460, 100, 28, "Fermer") - SetGadgetText(#GDiffText, gc\output) - Repeat - Protected ev.i = WaitWindowEvent() - If ev = #PB_Event_Gadget And EventGadget() = #GDiffClose - CloseWindow(#WDiff) - Break - EndIf - Until ev = #PB_Event_CloseWindow - EndIf - - ProcedureReturn 1 -EndProcedure Procedure.s GetLocalConfig(repoDir$, key$) Protected gc.GitCall @@ -917,22 +1058,28 @@ EndProcedure ; Restaure un fichier à l’état d’un commit précis ; - 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 - gc\workdir = repoDir$ - gc\args = "restore --source " + Chr(34) + commit$ + Chr(34) + " -- " + Chr(34) + file$ + Chr(34) + Protected gc.GitCall, q$ = Chr(34) + Protected path$ = GitPath(file$) + gc\workdir = repoDir$ + gc\args = "restore --source " + q$ + commit$ + q$ + " -- " + q$ + path$ + q$ If #EnableDebug Debug "[RestoreFile] " + gc\args + " (wd=" + repoDir$ + ")" EndIf - If RunGit(@gc) = 0 MessageRequester("Restaurer", "Le fichier a été restauré depuis le commit " + commit$ + ".", #PB_MessageRequester_Info) ProcedureReturn 1 EndIf - ; Fallback pour compatibilité - gc\args = "checkout " + Chr(34) + commit$ + Chr(34) + " -- " + Chr(34) + file$ + Chr(34) + ; Fallback (compat versions Git) + gc\args = "checkout " + q$ + commit$ + q$ + " -- " + q$ + path$ + q$ + If #EnableDebug + Debug "[RestoreFile fallback] " + gc\args + EndIf If RunGit(@gc) = 0 MessageRequester("Restaurer", "Le fichier a été restauré (fallback checkout) depuis " + commit$ + ".", #PB_MessageRequester_Info) ProcedureReturn 1 @@ -944,6 +1091,17 @@ 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 Procedure.i OpenRestoreFileWindow(repoDir$, List rows.FileRow()) Protected idx.i = GetGadgetState(#GListStatus) If idx < 0 @@ -951,41 +1109,67 @@ Procedure.i OpenRestoreFileWindow(repoDir$, List rows.FileRow()) ProcedureReturn 0 EndIf - ; Récupère le chemin du fichier choisi dans rows() - Protected j.i = 0, target$ - ForEach rows() - If j = idx - target$ = rows()\file - Break + ; 1) Deux sources : gadget (colonne 2) et liste interne rows() + Protected fromGadget$ = GetGadgetItemText(#GListStatus, idx, 1) + Protected fromRows$ = FileFromRowsByIndex(idx, rows()) + + ; 2) Choisir la version la plus fiable + Protected target$ = fromGadget$ + If fromRows$ <> "" + ; Si la version rows() est plus longue OU si fromRows$ commence par un caractère + ; qui, en retirant le 1er, donne exactement fromGadget$, on privilégie rows(). + If Len(fromRows$) > Len(fromGadget$) + target$ = fromRows$ + ElseIf Len(fromRows$) > 1 And Mid(fromRows$, 2) = fromGadget$ + target$ = fromRows$ EndIf - j + 1 - Next + EndIf + + ; Dernier filet de sécurité + If target$ = "" : target$ = fromRows$ : EndIf + If target$ = "" : target$ = fromGadget$ : EndIf + + If #EnableDebug + Debug "[Restore select] gadget='" + fromGadget$ + "' rows='" + fromRows$ + "' chosen='" + target$ + "'" + EndIf If target$ = "" MessageRequester("Restaurer", "Aucun fichier sélectionné.", #PB_MessageRequester_Info) ProcedureReturn 0 EndIf - ; Récupère l’historique des commits de ce fichier (50 derniers) - Protected gc.GitCall, out$, line$, n.i, i.i - gc\workdir = repoDir$ - gc\args = "log --date=short --pretty=format:%h%x09%ad%x09%s -n 50 -- " + Chr(34) + target$ + Chr(34) + ; Normaliser le chemin pour Git (slashs, ./) + Protected fileArg$ = GitPath(target$) + Protected gc.GitCall, out$, line$, n.i, i.i, q$ = Chr(34) + ; 3) Historique avec suivi des renommages + gc\workdir = repoDir$ + gc\args = "log --follow --date=short --pretty=format:%h%x09%ad%x09%s -n 200 -- " + q$ + fileArg$ + q$ + If #EnableDebug : Debug "[Restore log] " + gc\args : EndIf If RunGit(@gc) <> 0 MessageRequester("Restaurer", "Échec du log : " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error) ProcedureReturn 0 EndIf - out$ = gc\output + + ; 4) Fallback : toutes les refs (utile si l’historique du fichier est sur une autre branche) If Trim(out$) = "" - MessageRequester("Restaurer", "Aucun commit trouvé pour ce fichier.", #PB_MessageRequester_Info) + gc\args = "log --all --follow --date=short --pretty=format:%h%x09%ad%x09%s -n 200 -- " + q$ + fileArg$ + q$ + If #EnableDebug : Debug "[Restore log fallback --all] " + gc\args : EndIf + If RunGit(@gc) = 0 + out$ = gc\output + EndIf + EndIf + + 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é.", + #PB_MessageRequester_Info) ProcedureReturn 0 EndIf - ; Prépare une liste parallèle des hash pour retrouver le commit sélectionné + ; Liste des commits → UI NewList hashes.s() - - ; Fenêtre de sélection If OpenWindow(#WRestore, 0, 0, 760, 420, "Restaurer : " + target$, #PB_Window_SystemMenu | #PB_Window_ScreenCentered) TextGadget(#GRestInfo, 10, 10, 740, 22, "Choisissez le commit vers lequel restaurer le fichier.") ListIconGadget(#GRestList, 10, 40, 740, 330, "Commit", 100, #PB_ListIcon_FullRowSelect) @@ -994,12 +1178,10 @@ Procedure.i OpenRestoreFileWindow(repoDir$, List rows.FileRow()) ButtonGadget(#GRestOK, 540, 380, 100, 28, "Restaurer") ButtonGadget(#GRestCancel, 650, 380, 100, 28, "Annuler") - ; Remplit la liste n = CountString(out$, #LF$) + 1 For i = 1 To n line$ = StringField(out$, i, #LF$) If Trim(line$) <> "" - ; Format: %h%ad%s Protected h$ = StringField(line$, 1, #TAB$) Protected d$ = StringField(line$, 2, #TAB$) Protected s$ = StringField(line$, 3, #TAB$) @@ -1008,7 +1190,6 @@ Procedure.i OpenRestoreFileWindow(repoDir$, List rows.FileRow()) EndIf Next - ; Boucle fenêtre Repeat Protected ev.i = WaitWindowEvent() If ev = #PB_Event_Gadget @@ -1018,15 +1199,13 @@ Procedure.i OpenRestoreFileWindow(repoDir$, List rows.FileRow()) If idx < 0 MessageRequester("Restaurer", "Sélectionnez un commit.", #PB_MessageRequester_Warning) Else - ; Récupère le hash à l’index idx - j = 0 - Protected chosen$ + Protected k.i = 0, chosen$ ForEach hashes() - If j = idx : chosen$ = hashes() : Break : EndIf - j + 1 + If k = idx : chosen$ = hashes() : Break : EndIf + k + 1 Next If chosen$ <> "" - If RestoreFileFromCommit(repoDir$, target$, chosen$) + If RestoreFileFromCommit(repoDir$, fileArg$, chosen$) CloseWindow(#WRestore) ProcedureReturn 1 EndIf @@ -1046,8 +1225,6 @@ Procedure.i OpenRestoreFileWindow(repoDir$, List rows.FileRow()) ProcedureReturn 0 EndProcedure - - ; 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 ; - Rappelle la différence Commit / Push pour débutants @@ -1118,85 +1295,198 @@ EndProcedure ; Affiche stdout + stderr dans une fenêtre (évite le texte tronqué) +; Construit la liste rows() avec tous les fichiers (selon options), SANS correction de nom +; - 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) +; - rows()\include = 1 pour les éléments potentiellement à committer (≠ OK/!!) +Procedure.i BuildFullFileList(repoDir$, showClean.i, showIgnored.i, List rows.FileRow()) + Protected gc.GitCall + Protected text$, line$, code$, file$ + Protected i.i, n.i, start.i, ch$, pos.i, exists.i, isDelete.i + ClearList(rows()) -; ====== UI principale ====== -; Gadgets IDs (inclut les NOUVEAUX boutons) -#GWindow = 1 -#GLabelRepo = 10 -#GStringRepo = 11 -#GButtonBrowse = 12 -#GListStatus = 13 -#GRefresh = 14 -#GLabelMsg = 15 -#GStringMsg = 16 -#GCheckPush = 17 -#GLabelRemote = 18 -#GStringRemote = 19 -#GLabelBranch = 20 -#GComboBranch = 21 -#GSavePrefs = 22 -#GInit = 23 -#GCommit = 24 -#GPull = 25 -#GPush = 26 -#GInclude = 27 -#GExclude = 28 -#GIncludeAll = 29 -#GExcludeAll = 30 -#GAdvanced = 31 + ; --- 1) status --porcelain --ignored → map fichier -> code (??, !!, " M", etc.) + NewMap statusMap.s() + gc\workdir = repoDir$ + gc\args = "status --porcelain --ignored" + If RunGit(@gc) <> 0 + MessageRequester("Git status", "Échec: " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error) + ProcedureReturn 0 + EndIf + + text$ = gc\output + n = CountString(text$, #LF$) + 1 + For i = 1 To n + line$ = Trim(StringField(text$, i, #LF$)) + If line$ = "" : Continue : EndIf + + code$ = Left(line$, 2) + + ; --- parsing robuste du chemin (tout ce qui suit les espaces/tabs après col.2) --- + start = 3 + While start <= Len(line$) + ch$ = Mid(line$, start, 1) + If ch$ <> " " And ch$ <> #TAB$ + Break + EndIf + start + 1 + Wend + file$ = Mid(line$, start) + ; renommage "old -> new" : garder la destination + pos = FindString(file$, "->", 1) + If pos > 0 : file$ = Trim(Mid(file$, pos + 2)) : EndIf + ; normalisation simple + If Left(file$, 2) = "./" : file$ = Mid(file$, 3) : EndIf + file$ = ReplaceString(file$, "/", #PathSep$) + ; ------------------------------------------------------------------------------- + + AddMapElement(statusMap(), file$) + statusMap() = code$ + Next + + ; --- 2) ls-files → tous les suivis + NewMap trackedMap.i() + gc\args = "ls-files" + If RunGit(@gc) = 0 + text$ = gc\output + n = CountString(text$, #LF$) + 1 + For i = 1 To n + file$ = Trim(StringField(text$, i, #LF$)) + If file$ = "" : Continue : EndIf + If Left(file$, 2) = "./" : file$ = Mid(file$, 3) : EndIf + file$ = ReplaceString(file$, "/", #PathSep$) + AddMapElement(trackedMap(), file$) : trackedMap() = 1 + Next + EndIf + + ; --- 3) Ajouter les suivis (propres/modifiés) + ForEach trackedMap() + file$ = MapKey(trackedMap()) + If FindMapElement(statusMap(), file$) + code$ = statusMap() + Else + code$ = "OK" + EndIf + + If code$ = "OK" And showClean = 0 : Continue : EndIf + If code$ = "!!" And showIgnored = 0 : Continue : EndIf + + exists = RepoFileExists(repoDir$, file$) + isDelete = 0 + If Left(code$, 1) = "D" Or Right(code$, 1) = "D" : isDelete = 1 : EndIf + + AddElement(rows()) + rows()\file = file$ + If exists = 0 And isDelete = 0 + rows()\stat = "NF" ; Introuvable (FS) + rows()\include = 0 + Else + rows()\stat = code$ + rows()\include = Bool(code$ <> "OK" And code$ <> "!!") + EndIf + Next + + ; --- 4) Ajouter non suivis (!!/??) qui ne sont pas dans trackedMap + ForEach statusMap() + file$ = MapKey(statusMap()) + code$ = statusMap() + If FindMapElement(trackedMap(), file$) = 0 + If code$ = "!!" And showIgnored = 0 : Continue : EndIf + + exists = RepoFileExists(repoDir$, file$) + + AddElement(rows()) + rows()\file = file$ + If exists = 0 And Left(code$, 1) <> "D" And Right(code$, 1) <> "D" + rows()\stat = "NF" + rows()\include = 0 + Else + rows()\stat = code$ + rows()\include = 1 + EndIf + EndIf + Next + + ProcedureReturn ListSize(rows()) +EndProcedure + +; 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() + If j = index + ProcedureReturn rows()\file + EndIf + j + 1 + Next + ProcedureReturn "" +EndProcedure + +; 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 Procedure.i OpenGUI(initialDir$, prefsPath$) Protected repoDir$ = DetectRepoRoot(initialDir$) Protected remote$ = "", branch$ = "" LoadPrefs(prefsPath$, remote$, branch$) - ; On élargit légèrement pour caser le nouveau bouton - If OpenWindow(#GWindow, 0, 0, 900, 640, "PBIDE-GitTool — Git (mode simplifié)", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) + If OpenWindow(#GWindow, 0, 0, 900, 660, "PBIDE-GitTool — Git (mode simplifié)", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) TextGadget(#GLabelRepo, 10, 12, 60, 22, "Dépôt :") StringGadget(#GStringRepo, 80, 10, 720, 24, repoDir$) ButtonGadget(#GButtonBrowse, 810, 10, 80, 24, "Parcourir…") + ; Options d’affichage + CheckBoxGadget(#GShowClean, 10, 40, 180, 20, "Afficher suivis à jour") + SetGadgetState(#GShowClean, #True) ; par défaut : on veut tout voir + CheckBoxGadget(#GShowIgnored, 200, 40, 200, 20, "Inclure ignorés (.gitignore)") + SetGadgetState(#GShowIgnored, #False) + ; Liste avec cases à cocher - ListIconGadget(#GListStatus, 10, 46, 880, 300, "Statut", 180, #PB_ListIcon_CheckBoxes | #PB_ListIcon_FullRowSelect) + ListIconGadget(#GListStatus, 10, 66, 880, 300, "État", 180, #PB_ListIcon_CheckBoxes | #PB_ListIcon_FullRowSelect) AddGadgetColumn(#GListStatus, 1, "Fichier", 680) ; Ligne actions liste - ButtonGadget(#GRefresh, 10, 352, 90, 26, "Rafraîchir") - ButtonGadget(#GInit, 110, 352, 90, 26, "Init repo") - ButtonGadget(#GInclude, 210, 352, 90, 26, "Inclure") - ButtonGadget(#GExclude, 310, 352, 90, 26, "Exclure") - ButtonGadget(#GIncludeAll, 410, 352, 110, 26, "Tout inclure") - ButtonGadget(#GExcludeAll, 530, 352, 110, 26, "Tout exclure") - ButtonGadget(#GDiff, 650, 352, 80, 26, "Diff…") - ButtonGadget(#GRestoreFile,740, 352, 150, 26, "Restaurer fichier…") + ButtonGadget(#GRefresh, 10, 372, 90, 26, "Rafraîchir") + ButtonGadget(#GInit, 110, 372, 90, 26, "Init repo") + ButtonGadget(#GInclude, 210, 372, 90, 26, "Inclure") + ButtonGadget(#GExclude, 310, 372, 90, 26, "Exclure") + ButtonGadget(#GIncludeAll, 410, 372, 110, 26, "Tout inclure") + ButtonGadget(#GExcludeAll, 530, 372, 110, 26, "Tout exclure") + ButtonGadget(#GDiff, 650, 372, 80, 26, "Diff…") + ButtonGadget(#GRestoreFile,740, 372, 150, 26, "Restaurer fichier…") ; Zone commit / push - TextGadget(#GLabelMsg, 10, 388, 100, 22, "Message :") - StringGadget(#GStringMsg, 110, 386, 620, 24, "") - CheckBoxGadget(#GCheckPush, 10, 418, 140, 22, "Pousser après") + TextGadget(#GLabelMsg, 10, 408, 100, 22, "Message :") + StringGadget(#GStringMsg, 110, 406, 620, 24, "") + CheckBoxGadget(#GCheckPush, 10, 438, 140, 22, "Pousser après") SetGadgetState(#GCheckPush, #True) - TextGadget(#GLabelRemote, 160, 418, 60, 22, "Remote :") - StringGadget(#GStringRemote, 220, 416, 120, 24, remote$) - TextGadget(#GLabelBranch, 350, 418, 60, 22, "Branche :") - ComboBoxGadget(#GComboBranch, 410, 416, 170, 24) - ButtonGadget(#GSavePrefs, 590, 416, 220, 24, "Sauver défauts") - ButtonGadget(#GAdvanced, 820, 416, 70, 24, "Avancé…") + TextGadget(#GLabelRemote, 160, 438, 60, 22, "Remote :") + StringGadget(#GStringRemote, 220, 436, 120, 24, remote$) + TextGadget(#GLabelBranch, 350, 438, 60, 22, "Branche :") + ComboBoxGadget(#GComboBranch, 410, 436, 170, 24) + ButtonGadget(#GSavePrefs, 590, 436, 220, 24, "Sauver défauts") + ButtonGadget(#GAdvanced, 820, 436, 70, 24, "Avancé…") - ButtonGadget(#GCommit, 10, 450, 120, 30, "Add + Commit") - ButtonGadget(#GPush, 140, 450, 120, 30, "Push") - ButtonGadget(#GPull, 270, 450, 120, 30, "Pull") - ButtonGadget(#GConfig, 400, 450, 170, 30, "Configurer identité…") - ButtonGadget(#GMakeIgnore, 580, 450, 230, 30, "Créer .gitignore (recommandé)") + ButtonGadget(#GCommit, 10, 470, 120, 30, "Add + Commit") + ButtonGadget(#GPush, 140, 470, 120, 30, "Push") + ButtonGadget(#GPull, 270, 470, 120, 30, "Pull") + ButtonGadget(#GConfig, 400, 470, 170, 30, "Configurer identité…") + ButtonGadget(#GMakeIgnore, 580, 470, 230, 30, "Créer .gitignore (recommandé)") ; Panneau Guide - EditorGadget(#GGuide, 10, 490, 880, 140) + EditorGadget(#GGuide, 10, 510, 880, 140) DisableGadget(#GGuide, 1) - ; Infobulles utiles - GadgetToolTip(#GRestoreFile, "Restaurer le fichier sélectionné à un commit précis (historique).") - GadgetToolTip(#GDiff, "Afficher les différences du fichier sélectionné.") + ; Infobulles clés + GadgetToolTip(#GShowClean, "Afficher aussi les fichiers déjà suivis et sans changement (pratique pour restaurer).") + GadgetToolTip(#GShowIgnored, "Inclure les fichiers ignorés par .gitignore (lecture seule pour info).") + GadgetToolTip(#GListStatus, "Cochez pour inclure un fichier au commit. 'Diff…' pour voir le détail. 'Restaurer fichier…' pour revenir à un commit.") ; Branches NewList branchItems.s() @@ -1205,9 +1495,9 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) ForEach branchItems() : AddGadgetItem(#GComboBranch, -1, branchItems()) : Next If branch$ <> "" : SetGadgetText(#GComboBranch, branch$) : EndIf - ; Status + ; Fichiers (liste complète) NewList rows.FileRow() - LoadStatusRows(repoDir$, rows()) + BuildFullFileList(repoDir$, GetGadgetState(#GShowClean), GetGadgetState(#GShowIgnored), rows()) FillStatusList(rows()) UpdateGuide(repoDir$, rows(), GetGadgetText(#GComboBranch), GetGadgetText(#GStringRemote)) @@ -1228,23 +1518,28 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) ClearGadgetItems(#GComboBranch) ForEach branchItems() : AddGadgetItem(#GComboBranch, -1, branchItems()) : Next ClearList(rows()) - LoadStatusRows(repoDir$, rows()) + BuildFullFileList(repoDir$, GetGadgetState(#GShowClean), GetGadgetState(#GShowIgnored), rows()) FillStatusList(rows()) UpdateGuide(repoDir$, rows(), GetGadgetText(#GComboBranch), GetGadgetText(#GStringRemote)) EndIf - Case #GRefresh - repoDir$ = GetGadgetText(#GStringRepo) + Case #GShowClean, #GShowIgnored + ; Refiltrer l’affichage à la volée ClearList(rows()) - LoadStatusRows(repoDir$, rows()) + BuildFullFileList(repoDir$, GetGadgetState(#GShowClean), GetGadgetState(#GShowIgnored), rows()) + FillStatusList(rows()) + UpdateGuide(repoDir$, rows(), GetGadgetText(#GComboBranch), GetGadgetText(#GStringRemote)) + + Case #GRefresh + ClearList(rows()) + BuildFullFileList(repoDir$, GetGadgetState(#GShowClean), GetGadgetState(#GShowIgnored), rows()) FillStatusList(rows()) UpdateGuide(repoDir$, rows(), GetGadgetText(#GComboBranch), GetGadgetText(#GStringRemote)) Case #GInit - repoDir$ = GetGadgetText(#GStringRepo) DoInitRepo(repoDir$) ClearList(rows()) - LoadStatusRows(repoDir$, rows()) + BuildFullFileList(repoDir$, GetGadgetState(#GShowClean), GetGadgetState(#GShowIgnored), rows()) FillStatusList(rows()) UpdateGuide(repoDir$, rows(), GetGadgetText(#GComboBranch), GetGadgetText(#GStringRemote)) @@ -1277,24 +1572,20 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) UpdateGuide(repoDir$, rows(), GetGadgetText(#GComboBranch), GetGadgetText(#GStringRemote)) Case #GDiff - repoDir$ = GetGadgetText(#GStringRepo) OpenDiffWindow(repoDir$, rows()) Case #GRestoreFile - repoDir$ = GetGadgetText(#GStringRepo) If OpenRestoreFileWindow(repoDir$, rows()) - ; Après restauration : rafraîchir l’état ClearList(rows()) - LoadStatusRows(repoDir$, rows()) + BuildFullFileList(repoDir$, GetGadgetState(#GShowClean), GetGadgetState(#GShowIgnored), rows()) FillStatusList(rows()) UpdateGuide(repoDir$, rows(), GetGadgetText(#GComboBranch), GetGadgetText(#GStringRemote)) EndIf Case #GAdvanced - repoDir$ = GetGadgetText(#GStringRepo) OpenAdvancedWindow(repoDir$) ClearList(rows()) - LoadStatusRows(repoDir$, rows()) + BuildFullFileList(repoDir$, GetGadgetState(#GShowClean), GetGadgetState(#GShowIgnored), rows()) FillStatusList(rows()) UpdateGuide(repoDir$, rows(), GetGadgetText(#GComboBranch), GetGadgetText(#GStringRemote)) @@ -1309,21 +1600,18 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) UpdateGuide(repoDir$, rows(), branch$, remote$) Case #GConfig - repoDir$ = GetGadgetText(#GStringRepo) If ConfigIdentityWizard(repoDir$) UpdateGuide(repoDir$, rows(), GetGadgetText(#GComboBranch), GetGadgetText(#GStringRemote)) EndIf Case #GMakeIgnore - repoDir$ = GetGadgetText(#GStringRepo) If MakeDefaultGitignore(repoDir$) ClearList(rows()) - LoadStatusRows(repoDir$, rows()) + BuildFullFileList(repoDir$, GetGadgetState(#GShowClean), GetGadgetState(#GShowIgnored), rows()) FillStatusList(rows()) EndIf Case #GCommit - repoDir$ = GetGadgetText(#GStringRepo) remote$ = GetGadgetText(#GStringRemote) branch$ = GetGadgetText(#GComboBranch) Protected msg$ = GetGadgetText(#GStringMsg) @@ -1338,19 +1626,17 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) DoCommit(repoDir$, msg$, GetGadgetState(#GCheckPush), remote$, branch$) EndIf ClearList(rows()) - LoadStatusRows(repoDir$, rows()) + BuildFullFileList(repoDir$, GetGadgetState(#GShowClean), GetGadgetState(#GShowIgnored), rows()) FillStatusList(rows()) UpdateGuide(repoDir$, rows(), GetGadgetText(#GComboBranch), GetGadgetText(#GStringRemote)) EndIf Case #GPush - repoDir$ = GetGadgetText(#GStringRepo) remote$ = GetGadgetText(#GStringRemote) branch$ = GetGadgetText(#GComboBranch) DoPush(repoDir$, remote$, branch$) Case #GPull - repoDir$ = GetGadgetText(#GStringRepo) remote$ = GetGadgetText(#GStringRemote) branch$ = GetGadgetText(#GComboBranch) DoPull(repoDir$, remote$, branch$) @@ -1369,6 +1655,7 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) ProcedureReturn 0 EndProcedure + ; ====== Installation IDE ====== ; Fenêtre assistant #WInstall = 100 @@ -1568,9 +1855,9 @@ Else EndIf ; IDE Options = PureBasic 6.21 (Windows - x64) -; CursorPosition = 1370 -; FirstLine = 1329 -; Folding = ------- +; CursorPosition = 1413 +; FirstLine = 1375 +; Folding = -------- ; EnableXP ; DPIAware ; Executable = ..\PBIDE-GitTool.exe