diff --git a/main.pb b/main.pb index 4ed9d95..7e02e3d 100644 --- a/main.pb +++ b/main.pb @@ -227,6 +227,14 @@ Enumeration #GdtBtnPull #GdtBtnPush + #GdtLblRemoteStatus ; Label pour "Status :" + #GdtTxtRemoteStatus ; Texte du status (À jour, 3 en retard, etc.) + #GdtLblLastFetch ; Label pour "Dernière sync :" + #GdtTxtLastFetch ; Texte de la dernière synchronisation + #GdtLblAction ; Label pour "Action :" + #GdtTxtAction ; Texte de l'action recommandée + #GdtBtnCheckRemote + ; Liste + actions locales directement en dessous #GdtBtnRestore #GdtBtnRename @@ -404,7 +412,22 @@ Structure listFilesGit importance.i EndStructure +; Structure pour stocker les infos remote +Structure RemoteStatusInfo + hasRemote.b ; Remote configuré + isUpToDate.b ; À jour ou pas + ahead.i ; Commits en avance + behind.i ; Commits en retard + lastFetch.s ; Timestamp du dernier fetch + remoteUrl.s ; URL du remote + remoteBranch.s ; Branche remote + localBranch.s ; Branche locale + status.s ; Description textuelle du statut + needsAction.s ; Action recommandée +EndStructure + Structure GitStatus + branchInfo.GitBranchInfo List files.GitFileEntry() EndStructure @@ -429,6 +452,7 @@ Structure main info.info GitCall.RunProgramCall GitStatus.GitStatus + remoteStatus.RemoteStatusInfo ; Variable globale pour stocker le status currentPath.s CurrentBranch.s List GitHistory.GitCommitRow() @@ -717,6 +741,148 @@ EndProcedure ; ? = non suivi (Untracked) - uniquement en position Y ; ! = ignoré (Ignored) - uniquement en position Y ;-status + +; ============================================================================= +; Fonction pour récupérer le status remote +; ============================================================================= +Procedure.i GetRemoteStatusInfo() + ; Récupère les informations de status du remote + ; Met à jour la structure globale remoteStatus + ; Retourne : 1=succès, 0=échec + + ; Reset de la structure + main\remoteStatus\hasRemote = #False + main\remoteStatus\isUpToDate = #False + main\remoteStatus\ahead = 0 + main\remoteStatus\behind = 0 + main\remoteStatus\status = "Vérification..." + main\remoteStatus\needsAction = "" + + ; Vérifier que Git est dispo et repo initialisé + If Not main\info\isGit Or Not main\info\isInit + main\remoteStatus\status = "Repo non initialisé" + ProcedureReturn 0 + EndIf + + ; Récupérer la branche courante + main\GitCall\args = "branch --show-current" + If RunExe(@main\GitCall) = 0 + main\remoteStatus\localBranch = _SupTrim(main\GitCall\output) + Else + main\remoteStatus\localBranch = "unknown" + EndIf + + ; Vérifier qu'il y a un remote + main\GitCall\args = "remote get-url origin" + If RunExe(@main\GitCall) <> 0 + main\remoteStatus\status = "Aucun remote configuré" + ProcedureReturn 0 + EndIf + + main\remoteStatus\hasRemote = #True + main\remoteStatus\remoteUrl = _SupTrim(main\GitCall\output) + main\remoteStatus\remoteBranch = "origin/" + main\remoteStatus\localBranch + + ; Fetch silencieux pour avoir les dernières infos + main\GitCall\args = "fetch origin --quiet" + If RunExe(@main\GitCall) <> 0 + main\remoteStatus\status = "Erreur réseau" + main\remoteStatus\needsAction = "Vérifiez la connexion" + ProcedureReturn 0 + EndIf + + ; Timestamp du fetch + main\remoteStatus\lastFetch = FormatDate("%dd/%mm/%yyyy %hh:%ii", Date()) + + ; Vérifier si la branche remote existe + main\GitCall\args = "rev-parse --verify " + main\remoteStatus\remoteBranch + If RunExe(@main\GitCall) <> 0 + main\remoteStatus\status = "Branche remote inexistante" + main\remoteStatus\needsAction = "Push pour créer" + ProcedureReturn 1 + EndIf + + ; Comparer local vs remote + main\GitCall\args = "rev-list --left-right --count " + main\remoteStatus\localBranch + "..." + main\remoteStatus\remoteBranch + If RunExe(@main\GitCall) = 0 + Protected counts.s = _SupTrim(main\GitCall\output) + main\remoteStatus\ahead = Val(StringField(counts, 1, #TAB$)) + main\remoteStatus\behind = Val(StringField(counts, 2, #TAB$)) + + ; Construire le status et les actions + If main\remoteStatus\ahead = 0 And main\remoteStatus\behind = 0 + main\remoteStatus\isUpToDate = #True + main\remoteStatus\status = "À jour" + main\remoteStatus\needsAction = "" + + ElseIf main\remoteStatus\ahead > 0 And main\remoteStatus\behind = 0 + main\remoteStatus\status = Str(main\remoteStatus\ahead) + " en avance" + main\remoteStatus\needsAction = "Push recommandé" + + ElseIf main\remoteStatus\ahead = 0 And main\remoteStatus\behind > 0 + main\remoteStatus\status = Str(main\remoteStatus\behind) + " en retard" + main\remoteStatus\needsAction = "Pull nécessaire" + + Else + main\remoteStatus\status = Str(main\remoteStatus\ahead) + " en avance, " + Str(main\remoteStatus\behind) + " en retard" + main\remoteStatus\needsAction = "Branches divergées" + EndIf + Else + main\remoteStatus\status = "Erreur comparaison" + ProcedureReturn 0 + EndIf + + ProcedureReturn 1 +EndProcedure +; ============================================================================= +; Fonction pour mettre à jour l'affichage dans la GUI +; ============================================================================= +Procedure UpdateRemoteStatusDisplay() + ; Met à jour les gadgets d'affichage du status + + If main\remoteStatus\hasRemote + ; Couleur selon le statut + Protected statusText.s = main\remoteStatus\status + Protected color.i = RGB(60, 60, 60) ; Gris par défaut + + If main\remoteStatus\isUpToDate + color = RGB(0, 128, 0) ; Vert pour "à jour" + ElseIf main\remoteStatus\behind > 0 + color = RGB(255, 140, 0) ; Orange pour "en retard" + ElseIf main\remoteStatus\ahead > 0 + color = RGB(0, 100, 200) ; Bleu pour "en avance" + EndIf + + SetGadgetText(#GdtTxtRemoteStatus, statusText) + ; Note: SetGadgetColor() nécessite que le gadget supporte les couleurs + + ; Action recommandée dans un gadget séparé + If main\remoteStatus\needsAction <> "" + SetGadgetText(#GdtTxtAction, main\remoteStatus\needsAction) + Else + SetGadgetText(#GdtTxtAction, "Aucune action nécessaire") + EndIf + + ; Dernière synchronisation + If main\remoteStatus\lastFetch <> "" + SetGadgetText(#GdtTxtLastFetch, main\remoteStatus\lastFetch) + Else + SetGadgetText(#GdtTxtLastFetch, "Jamais") + EndIf + Else + SetGadgetText(#GdtTxtRemoteStatus, "Pas de remote") + SetGadgetText(#GdtTxtAction, "-") + SetGadgetText(#GdtTxtLastFetch, "-") + EndIf +EndProcedure + +Procedure CheckRemoteThread(null) + ; Thread pour vérifier le status remote en arrière-plan + GetRemoteStatusInfo() + UpdateRemoteStatusDisplay() +EndProcedure + + Procedure.s ExtractField(line.s, position.l) ; Extrait un champ à une position donnée (séparé par espaces) Protected count.l = CountString(line, " ") @@ -1301,6 +1467,205 @@ Procedure ReadGitIgnorefile() EndIf EndProcedure +; ============================================================================= +;-Fonction de vérification du statut remote +; ============================================================================= + +Procedure.i CheckRemoteStatus(remoteName.s = "origin", showDialog.b = #True) + ; Vérifie le statut du remote et affiche les informations + ; Retourne : 0=erreur, 1=à jour, 2=pull nécessaire, 3=push nécessaire, 4=divergé + + Protected result.i = 0 + Protected currentBranch.s, remoteBranch.s, ahead.i = 0, behind.i = 0 + Protected info.s, needAction.s, title.s + + ; Vérifier que Git est disponible et que c'est un repo + If Not main\info\isGit Or Not main\info\isInit + If showDialog + MessageRequester("Remote Status", "Git non détecté ou dépôt non initialisé.", #PB_MessageRequester_Warning) + EndIf + ProcedureReturn 0 + EndIf + + ; Vérifier qu'il y a un remote configuré + main\GitCall\args = "remote get-url " + remoteName + If RunExe(@main\GitCall) <> 0 + If showDialog + MessageRequester("Remote Status", "Aucun remote '" + remoteName + "' configuré.", #PB_MessageRequester_Info) + EndIf + ProcedureReturn 0 + EndIf + + Protected remoteUrl.s = _SupTrim(main\GitCall\output) + + ; Récupérer la branche courante + main\GitCall\args = "branch --show-current" + If RunExe(@main\GitCall) <> 0 + If showDialog + MessageRequester("Remote Status", "Impossible de déterminer la branche courante.", #PB_MessageRequester_Error) + EndIf + ProcedureReturn 0 + EndIf + + currentBranch = _SupTrim(main\GitCall\output) + If currentBranch = "" + currentBranch = "HEAD détaché" + EndIf + + ; Récupérer les informations du remote (fetch) + main\GitCall\args = "fetch " + remoteName + If RunExe(@main\GitCall) <> 0 + If showDialog + MessageRequester("Remote Status", + "Erreur lors de la récupération des informations du remote :" + #LF$ + + main\GitCall\errors, #PB_MessageRequester_Error) + EndIf + ProcedureReturn 0 + EndIf + + ; Construire le nom de la branche remote + remoteBranch = remoteName + "/" + currentBranch + + ; Vérifier si la branche remote existe + main\GitCall\args = "rev-parse --verify " + remoteBranch + If RunExe(@main\GitCall) <> 0 + ; La branche remote n'existe pas + info = "Remote : " + remoteUrl + #LF$ + #LF$ + + "Branche locale : " + currentBranch + #LF$ + + "Branche remote : n'existe pas" + #LF$ + #LF$ + + "La branche locale n'a pas d'équivalent sur le remote." + + needAction = "Action recommandée : PUSH pour créer la branche sur le remote" + title = "Remote Status - Push recommandé" + result = 3 + Else + ; Comparer les branches locale et remote + main\GitCall\args = "rev-list --left-right --count " + currentBranch + "..." + remoteBranch + If RunExe(@main\GitCall) = 0 + Protected counts.s = _SupTrim(main\GitCall\output) + ahead = Val(StringField(counts, 1, #TAB$)) + behind = Val(StringField(counts, 2, #TAB$)) + + ; Construire le message d'information + info = "Remote : " + remoteUrl + #LF$ + #LF$ + + "Branche locale : " + currentBranch + #LF$ + + "Branche remote : " + remoteBranch + #LF$ + #LF$ + + "Commits en avance : " + Str(ahead) + #LF$ + + "Commits en retard : " + Str(behind) + + ; Déterminer l'action nécessaire + If ahead = 0 And behind = 0 + needAction = "Statut : À JOUR - Aucune action nécessaire" + title = "Remote Status - À jour" + result = 1 + ElseIf ahead > 0 And behind = 0 + needAction = "Action recommandée : PUSH pour envoyer vos " + Str(ahead) + " commit(s)" + title = "Remote Status - Push recommandé" + result = 3 + ElseIf ahead = 0 And behind > 0 + needAction = "Action recommandée : PULL pour récupérer " + Str(behind) + " commit(s)" + title = "Remote Status - Pull nécessaire" + result = 2 + Else + needAction = "ATTENTION : Branches divergées" + #LF$ + + "Action recommandée : PULL puis résolution des conflits éventuels" + title = "Remote Status - Branches divergées" + result = 4 + EndIf + Else + info = "Erreur lors de la comparaison des branches" + needAction = "Vérifiez manuellement l'état du dépôt" + title = "Remote Status - Erreur" + result = 0 + EndIf + EndIf + + ; Afficher le dialog si demandé + If showDialog + Protected fullMessage.s = info + #LF$ + #LF$ + needAction + + ; Choisir l'icône selon le résultat + Protected flags.i + Select result + Case 1 : flags = #PB_MessageRequester_Info + Case 2 : flags = #PB_MessageRequester_Info + Case 3 : flags = #PB_MessageRequester_Info + Case 4 : flags = #PB_MessageRequester_Warning + Default : flags = #PB_MessageRequester_Error + EndSelect + + MessageRequester(title, fullMessage, flags) + EndIf + + ; Debug pour développement + If #EnableDebug + Debug "[CheckRemoteStatus] Result: " + Str(result) + " (ahead:" + Str(ahead) + ", behind:" + Str(behind) + ")" + EndIf + + ProcedureReturn result +EndProcedure + +; Fonction helper pour mettre à jour automatiquement les informations +Procedure.i UpdateRemoteInfo() + ; Met à jour les informations du remote et retourne le statut + ; Cette fonction peut être appelée périodiquement ou sur demande + + Protected status.i = CheckRemoteStatus("origin", #False) + + ; Mettre à jour l'interface selon le statut + Select status + Case 1 ; À jour + ; Peut changer la couleur du bouton Pull/Push ou ajouter une icône + + Case 2 ; Pull nécessaire + ; Peut mettre en évidence le bouton Pull + + Case 3 ; Push nécessaire + ; Peut mettre en évidence le bouton Push + + Case 4 ; Divergé + ; Peut afficher un avertissement + + EndSelect + + ProcedureReturn status +EndProcedure + +; ============================================================================= +; Informations supplémentaires intéressantes à ajouter +; ============================================================================= + +Procedure.s GetAdditionalRemoteInfo() + ; Récupère des infos bonus intéressantes + Protected info.s = "" + + If main\remoteStatus\hasRemote + ; Dernier commit sur la branche remote + main\GitCall\args = "log " + main\remoteStatus\remoteBranch + " -1 --pretty=format:" + Chr(34) + "%h - %an, %ar: %s" + Chr(34) + If RunExe(@main\GitCall) = 0 + info + "Dernier commit remote : " + _SupTrim(main\GitCall\output) + #LF$ + EndIf + + ; Taille du repo remote (approximative via objets) + main\GitCall\args = "count-objects -v" + If RunExe(@main\GitCall) = 0 + Protected sizeKb.s = StringField(main\GitCall\output, 1, #LF$) + If FindString(sizeKb, "size-pack") + sizeKb = StringField(sizeKb, 2, " ") + info + "Taille du dépôt : " + Str(Val(sizeKb) / 1024) + " MB" + #LF$ + EndIf + EndIf + + ; Nombre total de commits + main\GitCall\args = "rev-list --count HEAD" + If RunExe(@main\GitCall) = 0 + info + "Commits totaux : " + _SupTrim(main\GitCall\output) + #LF$ + EndIf + + EndIf + + ProcedureReturn info +EndProcedure ;-Suite @@ -1976,7 +2341,7 @@ Procedure RefreshFileList(null.i) ;DoLsFilesByType("O") ;DoLsFilesByType("I") ;DoStatus() - + CheckRemoteStatus() GetGitStatus() ForEach main\listFilesGit() @@ -2137,7 +2502,6 @@ Procedure OpenGUI() #WinW = 950 #WinH = 720 ; plus haut pour la zone d’aide #PanelW = 920 - #PanelH = 500 #InPad = 10 ; marge interne au Panel #InFrmY=30 #InMargin = 10 ; espace vertical entre frames @@ -2146,14 +2510,14 @@ Procedure OpenGUI() #BtnW = 95 If OpenWindow(#WinMain, 0, 0, #WinW, #WinH, "Git helpmate", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) - - PanelGadget(#GdtPnl, #InPad,#InPad, #PanelW, #PanelH) + Protected PanelH.l=WindowHeight(#WinMain)- 2*#InPad + PanelGadget(#GdtPnl, #InPad,#InPad, #PanelW, PanelH) ; ============================================================ ; Onglet 1 : Dépôt ; ============================================================ AddGadgetItem(#gdtPnl, -1, T("#gdtPnl-Repo","Dépôt")) - Protected PanelH.l=WindowHeight(#WinMain)- 2*#InPad + ; ---- Cadre "Dépôt local" ---- Protected Width.l=#PanelW - #InPad*2 @@ -2167,7 +2531,7 @@ Procedure OpenGUI() CloseGadgetList() ; ---- Cadre "Distant" ---- Define yRemote = DownGadget(#GdtFrmLocal)+ #InPad - FrameGadget(#GdtFrmRemote, #InPad, yRemote, #PanelW - 2*#InPad, #InFrmY+#BtnH*2+#InMargin*2, T("GdtFrmRemote","Distant (remote / branche)"),#PB_Frame_Container) + FrameGadget(#GdtFrmRemote, #InPad, yRemote, #PanelW - 2*#InPad, #InFrmY+#BtnH*4+#InMargin*4, T("GdtFrmRemote","Distant (remote / branche)"),#PB_Frame_Container) TextGadget(#GdtLblRemote, #InPad , #InFrmY, 60, #BtnH, T("GdtLblRemote","Remote :")) StringGadget(#GdtFieldRemote, RightGadget(#GdtLblRemote)+#InMargin, #InFrmY, 420, #BtnH, "") TextGadget(#GdtLblBranch, RightGadget(#GdtFieldRemote)+ #InMargin, #InFrmY, 70, #BtnH, T("GdtLblBranch","Branche :")) @@ -2176,10 +2540,20 @@ Procedure OpenGUI() ButtonGadget(#GdtBtnClone, #InPad,DownGadget(#GdtLblRemote)+#InMargin, #BtnW,#BtnH, T("GdtBtnClone","Clone")) ButtonGadget(#GdtBtnPull, RightGadget(#GdtBtnClone)+#InPad,GadgetY(#GdtBtnClone), #BtnW, #BtnH, T("GdtBtnPull","Pull")) ButtonGadget(#GdtBtnPush, RightGadget(#GdtBtnPull)+#InPad,GadgetY(#GdtBtnClone), #BtnW, #BtnH, T("GdtBtnPush","Push")) + + ; Informations de status (sous les boutons Clone/Pull/Push) + Define yStatus = DownGadget(#GdtBtnClone)+#InMargin ; Sous les boutons + TextGadget(#GdtLblRemoteStatus, #InPad , yStatus, 60, #BtnH, "Status :") + TextGadget(#GdtTxtRemoteStatus, RightGadget(#GdtLblRemoteStatus), yStatus, 200, #BtnH, "Vérification...", #PB_Text_Border) + TextGadget(#GdtLblLastFetch, #InPad + 285, yStatus, 80, #BtnH, "Dernière sync :") + TextGadget(#GdtTxtLastFetch, #InPad + 370, yStatus, 120, #BtnH, "-", #PB_Text_Border) + ButtonGadget(#GdtBtnCheckRemote, #InPad + 500, yStatus - 2, 80, #BtnH, "Vérifier") + TextGadget(#GdtLblAction, #InPad, DownGadget(#GdtLblRemoteStatus)+#InMargin, 50, 20, "Action :") + TextGadget(#GdtTxtAction, RightGadget(#GdtLblAction), GadgetY(#GdtLblAction), 300, 20, "-", #PB_Text_Border) CloseGadgetList() ; ---- Cadre "Fichiers & modifications" ---- Define yFiles = DownGadget(#GdtFrmRemote)+ #InPad - Define hFiles = #PanelH - yFiles - #InPad + Define hFiles = PanelH - yFiles - #InPad FrameGadget(#GdtFrmFiles, #InPad, yFiles, #PanelW - 2*#InPad, hFiles, T("GdtFrmFiles","Fichiers & modifications"),#PB_Frame_Container) ListIconGadget(#GdtListStatus, #InPad, #InFrmY, GadgetWidth(#GdtFrmFiles)-2*#InPad, 120, T("GdtListStatus-Path","Path"), 300, #PB_ListIcon_CheckBoxes | #PB_ListIcon_MultiSelect | #PB_ListIcon_FullRowSelect |#PB_ListIcon_AlwaysShowSelection) AddGadgetColumn(#GdtListStatus, 1, T("GdtListStatus-Status","Status"), 50) @@ -2209,7 +2583,7 @@ Procedure OpenGUI() ListIconGadget(#GdtListHistory, #InPad, #InPad, #PanelW - 2*#InPad, - #PanelH - 2*#InPad - #BtnH - 10 - 150 - 10, + PanelH - 2*#InPad - #BtnH - 10 - 150 - 10, T("GdtListHistory-Header","Header"), 220, #PB_ListIcon_FullRowSelect) AddGadgetColumn(#GdtListHistory, 1, T("GdtListHistory-Date","Date"), 150) AddGadgetColumn(#GdtListHistory, 2, T("GdtListHistory-Author","Auteur"), 180) @@ -2220,14 +2594,14 @@ Procedure OpenGUI() ; Bouton Restore This Commit (sous la liste) ButtonGadget(#GdtBtnRestoreCommit, #InPad, - #PanelH - #InPad - #BtnH - 150 - 10, + PanelH - #InPad - #BtnH - 150 - 10, 180, #BtnH, T("GdtBtnRestoreCommit","Restore This Commit")) ; Zone d’info du commit (Editor, lecture seule) EditorGadget(#GdtTxtCommitInfo, #InPad, - #PanelH - #InPad - 150, + PanelH - #InPad - 150, #PanelW - 2*#InPad, 150, #PB_Editor_ReadOnly) @@ -2236,7 +2610,7 @@ Procedure OpenGUI() ; Onglet 3 : .gitignore file ; ============================================================ AddGadgetItem(#gdtPnl, -1, T("#gdtPnl-gitignore",".gitignore file")) - EditorGadget(#GdtTxtGitIgnore,#InPad, #InPad,#PanelW - 2*#InPad,#PanelH - 4*#InPad-#BtnH) + EditorGadget(#GdtTxtGitIgnore,#InPad, #InPad,#PanelW - 2*#InPad,PanelH - 4*#InPad-#BtnH) ButtonGadget(#GdtBtnSaveGitIgnore,#InPad,GadgetY(#GdtTxtGitIgnore)+GadgetHeight(#GdtTxtGitIgnore)+#InPad,100,#BtnH,T("GdtBtnSaveGitIgnore","Save File")) ; ============================================================ ; Onglet 4 : Config @@ -2269,7 +2643,7 @@ Procedure OpenGUI() Define helpX = #InPad + #PanelW + helpGap ; à droite du panel Define helpY = #InPad Define helpW = WindowWidth(#WinMain)-GadgetWidth(#gdtPnl)- #InPad*4 ; reste de largeur jusqu'à la marge droite - Define helpH = #PanelH ; même hauteur que le panel + Define helpH = PanelH ; même hauteur que le panel ; (Optionnel) borne minimale si besoin If helpW < 100 : helpW = 100 : EndIf @@ -2338,10 +2712,16 @@ Procedure OpenGUI() CreateThread(@RefreshFileList(),0) EndIf Case #GdtFieldRemote - ;If EventType()=#PB_EventType_LostFocus - ; AddRemoteRepo(GetGadgetText(#GdtFieldRemote),"origin") - ; EndIf - + If EventType()=#PB_EventType_LostFocus + ; AddRemoteRepo(GetGadgetText(#GdtFieldRemote),"origin") + CreateThread(@CheckRemoteThread(), 0) + EndIf + Case #GdtBtnCheckRemote + Debug "Click>#GdtBtnCheckRemote" + DisableGadget(#GdtBtnCheckRemote, #True) + SetGadgetText(#GdtTxtRemoteStatus, "Vérification...") + CreateThread(@CheckRemoteThread(), 0) + DisableGadget(#GdtBtnCheckRemote, #False) Case #GdtBtnClone Debug "Click>#GdtBtnClone" DoClone() @@ -2350,7 +2730,7 @@ Procedure OpenGUI() DoPush() Case #GdtBtnPull Debug "Click>#GdtBtnPull" - DoPull() + SmartPull() Case #GdtListStatus Debug "Click>#GdtListStatus" Case #GdtBtnIgnore @@ -2401,9 +2781,9 @@ OpenGUI() ; IDE Options = PureBasic 6.21 (Windows - x64) -; CursorPosition = 1440 -; FirstLine = 1415 -; Folding = ---------- +; CursorPosition = 2645 +; FirstLine = 2641 +; Folding = ----------- ; Optimizer ; EnableThread ; EnableXP