v0.7.5 - Miroir depot git et detection fichiers non suivis
Changements : - Detection des fichiers non suivis (untracked) dans chaque depot - Affichage "X fichier(s) en trop" dans le statut - Popup de confirmation listant les fichiers avant suppression (git clean -fd) - Suppression auto des fichiers en trop via "Tout mettre a jour" - Verification du depot distant via git ls-remote avant de proposer le clone - Affichage "Depot introuvable" si l'URL pointe vers un repo inexistant Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
41
git.go
41
git.go
@@ -16,16 +16,18 @@ import (
|
||||
)
|
||||
|
||||
type RepoResult struct {
|
||||
Name string
|
||||
Path string
|
||||
URL string
|
||||
Pending bool
|
||||
UpToDate bool
|
||||
Offline bool
|
||||
NeedsClone bool
|
||||
Error string
|
||||
NewCommits int
|
||||
LocalChanges int
|
||||
Name string
|
||||
Path string
|
||||
URL string
|
||||
Pending bool
|
||||
UpToDate bool
|
||||
Offline bool
|
||||
NeedsClone bool
|
||||
Error string
|
||||
NewCommits int
|
||||
LocalChanges int
|
||||
UntrackedFiles int
|
||||
UntrackedList []string // liste des fichiers non suivis
|
||||
}
|
||||
|
||||
func runGit(args []string, cwd string, timeout time.Duration) (code int, stdout string, stderr string) {
|
||||
@@ -115,10 +117,17 @@ func checkRepo(cfg RepoConfig) RepoResult {
|
||||
|
||||
_, status, _ := runGit([]string{"status", "--porcelain"}, local, 5*time.Second)
|
||||
if status != "" {
|
||||
res.LocalChanges = len(strings.Split(strings.TrimSpace(status), "\n"))
|
||||
for _, line := range strings.Split(strings.TrimSpace(status), "\n") {
|
||||
if strings.HasPrefix(line, "?? ") {
|
||||
res.UntrackedFiles++
|
||||
res.UntrackedList = append(res.UntrackedList, strings.TrimPrefix(line, "?? "))
|
||||
} else {
|
||||
res.LocalChanges++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.UpToDate = res.NewCommits == 0 && res.LocalChanges == 0
|
||||
res.UpToDate = res.NewCommits == 0 && res.LocalChanges == 0 && res.UntrackedFiles == 0
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -154,6 +163,14 @@ func doCheckout(res RepoResult) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func doClean(res RepoResult) error {
|
||||
code, _, stderr := runGit([]string{"clean", "-fd"}, res.Path, 60*time.Second)
|
||||
if code != 0 {
|
||||
return fmt.Errorf("%s", stderr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ── Progression Git ───────────────────────────────────────────────────────────
|
||||
|
||||
// ProgressInfo contient l'état de progression d'une opération git.
|
||||
|
||||
65
gui.go
65
gui.go
@@ -46,6 +46,12 @@ func (it *RepoItem) statusText() string {
|
||||
}
|
||||
msg += fmt.Sprintf("%d modif. locale(s)", r.LocalChanges)
|
||||
}
|
||||
if r.UntrackedFiles > 0 {
|
||||
if msg != "" {
|
||||
msg += ", "
|
||||
}
|
||||
msg += fmt.Sprintf("%d fichier(s) en trop", r.UntrackedFiles)
|
||||
}
|
||||
if msg == "" {
|
||||
return "À jour"
|
||||
}
|
||||
@@ -195,7 +201,7 @@ func (m *RepoModel) hasUpdates() bool {
|
||||
defer m.mu.RUnlock()
|
||||
for _, it := range m.items {
|
||||
r := it.result
|
||||
if !r.Pending && r.Error == "" && !r.Offline && (r.NewCommits > 0 || r.LocalChanges > 0 || r.NeedsClone) {
|
||||
if !r.Pending && r.Error == "" && !r.Offline && (r.NewCommits > 0 || r.LocalChanges > 0 || r.UntrackedFiles > 0 || r.NeedsClone) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -430,6 +436,12 @@ func logLineForResult(r RepoResult) string {
|
||||
}
|
||||
msg += fmt.Sprintf("%d modif. locale(s)", r.LocalChanges)
|
||||
}
|
||||
if r.UntrackedFiles > 0 {
|
||||
if msg != "" {
|
||||
msg += ", "
|
||||
}
|
||||
msg += fmt.Sprintf("%d fichier(s) en trop", r.UntrackedFiles)
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
@@ -450,6 +462,41 @@ func (a *App) recheckOne(idx int) {
|
||||
}()
|
||||
}
|
||||
|
||||
// proposeClean affiche un popup listant les fichiers non suivis et propose de les supprimer.
|
||||
func (a *App) proposeClean(idx int, res RepoResult) {
|
||||
// Construire la liste des fichiers (max 30 affichés)
|
||||
list := ""
|
||||
for i, f := range res.UntrackedList {
|
||||
if i >= 30 {
|
||||
list += fmt.Sprintf("\n... et %d autre(s)", len(res.UntrackedList)-30)
|
||||
break
|
||||
}
|
||||
list += "\n - " + f
|
||||
}
|
||||
msg := fmt.Sprintf("[%s] %d fichier(s) non suivi(s) détecté(s) :%s\n\nSupprimer ces fichiers ?",
|
||||
res.Name, res.UntrackedFiles, list)
|
||||
|
||||
ans := walk.MsgBox(a.mw, "Fichiers en trop", msg, walk.MsgBoxYesNo|walk.MsgBoxIconQuestion)
|
||||
if ans == walk.DlgCmdYes {
|
||||
a.appendLog(fmt.Sprintf("[%s] Nettoyage de %d fichier(s)...", res.Name, res.UntrackedFiles))
|
||||
go func() {
|
||||
err := doClean(res)
|
||||
a.mw.Synchronize(func() {
|
||||
if err != nil {
|
||||
a.appendLog(fmt.Sprintf("[%s] Erreur nettoyage: %v", res.Name, err))
|
||||
logError(fmt.Sprintf("[%s] clean: %v", res.Name, err))
|
||||
} else {
|
||||
a.appendLog(fmt.Sprintf("[%s] %d fichier(s) supprimé(s)", res.Name, res.UntrackedFiles))
|
||||
logInfo(fmt.Sprintf("[%s] %d fichier(s) supprimé(s)", res.Name, res.UntrackedFiles))
|
||||
}
|
||||
a.recheckOne(idx)
|
||||
})
|
||||
}()
|
||||
} else {
|
||||
a.recheckOne(idx)
|
||||
}
|
||||
}
|
||||
|
||||
// ── Progression ───────────────────────────────────────────────────────────────
|
||||
|
||||
// makeProgressCB crée un callback de progression pour la ligne row du tableau.
|
||||
@@ -487,7 +534,7 @@ func (a *App) onSelectionChanged() {
|
||||
if res.NeedsClone {
|
||||
a.btnAction.SetText("Cloner")
|
||||
a.btnAction.SetEnabled(true)
|
||||
} else if res.NewCommits > 0 || res.LocalChanges > 0 {
|
||||
} else if res.NewCommits > 0 || res.LocalChanges > 0 || res.UntrackedFiles > 0 {
|
||||
a.btnAction.SetText("Mettre à jour")
|
||||
a.btnAction.SetEnabled(true)
|
||||
} else {
|
||||
@@ -503,6 +550,12 @@ func (a *App) doAction() {
|
||||
}
|
||||
cfg := a.reposConfig[idx]
|
||||
|
||||
// Si uniquement des fichiers en trop, proposer directement le nettoyage
|
||||
if res.UntrackedFiles > 0 && res.NewCommits == 0 && res.LocalChanges == 0 && !res.NeedsClone {
|
||||
a.proposeClean(idx, res)
|
||||
return
|
||||
}
|
||||
|
||||
a.btnAction.SetEnabled(false)
|
||||
a.appendLog(fmt.Sprintf("[%s] Mise à jour en cours...", res.Name))
|
||||
|
||||
@@ -529,6 +582,11 @@ func (a *App) doAction() {
|
||||
a.appendLog(fmt.Sprintf("[%s] Mise à jour OK", res.Name))
|
||||
logInfo(fmt.Sprintf("[%s] Mise à jour OK", res.Name))
|
||||
}
|
||||
// Si des fichiers en trop après la mise à jour, proposer le nettoyage
|
||||
if res.UntrackedFiles > 0 {
|
||||
a.proposeClean(idx, res)
|
||||
return
|
||||
}
|
||||
// Re-vérifier uniquement ce dépôt, pas tous
|
||||
a.recheckOne(idx)
|
||||
})
|
||||
@@ -559,6 +617,9 @@ func (a *App) updateAll() {
|
||||
if err == nil && res.NewCommits > 0 {
|
||||
err = doPullWithProgress(res, cb)
|
||||
}
|
||||
if err == nil && res.UntrackedFiles > 0 {
|
||||
err = doClean(res)
|
||||
}
|
||||
}
|
||||
a.mw.Synchronize(func() {
|
||||
if err != nil {
|
||||
|
||||
2
main.go
2
main.go
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/lxn/walk"
|
||||
)
|
||||
|
||||
const VERSION = "0.7.4"
|
||||
const VERSION = "0.7.5"
|
||||
|
||||
func exeDir() string {
|
||||
exe, err := os.Executable()
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.7.4
|
||||
0.7.5
|
||||
|
||||
Reference in New Issue
Block a user