From 7c0cd26da1662502198564a9e18c52a6d615e6ec Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Mon, 20 Apr 2026 09:24:51 -0600 Subject: [PATCH] refactor: GCInterval replaces LightGC; Sync/Init drop lightGC param gitshallow.Repo.GCInterval int: 0 (default) = git auto gc (no explicit call) N = aggressive gc + prune every Nth successful pull GC() simplified to always aggressive+prune (the only mode we use). Sync(), Init(), Fetch() all parameter-free; GCInterval baked into Repo. --- net/gitshallow/cmd/git-shallow-sync/main.go | 2 +- net/gitshallow/gitshallow.go | 74 ++++++++++----------- net/ipcohort/cmd/check-ip/blacklist.go | 2 +- 3 files changed, 36 insertions(+), 42 deletions(-) diff --git a/net/gitshallow/cmd/git-shallow-sync/main.go b/net/gitshallow/cmd/git-shallow-sync/main.go index f7389ef..511635a 100644 --- a/net/gitshallow/cmd/git-shallow-sync/main.go +++ b/net/gitshallow/cmd/git-shallow-sync/main.go @@ -57,7 +57,7 @@ func main() { repo := gitshallow.New(url, absPath, defaultDepth, defaultBranch) - updated, err := repo.Sync(false) + updated, err := repo.Sync() if err != nil { fmt.Fprintf(os.Stderr, "Sync failed: %v\n", err) os.Exit(1) diff --git a/net/gitshallow/gitshallow.go b/net/gitshallow/gitshallow.go index 3a68628..655ab45 100644 --- a/net/gitshallow/gitshallow.go +++ b/net/gitshallow/gitshallow.go @@ -11,13 +11,19 @@ import ( // Repo manages a shallow git clone used as a periodically-updated data source. type Repo struct { - URL string - Path string - Depth int // 0 defaults to 1, -1 for all - Branch string // Optional: specific branch to clone/pull - LightGC bool // true = skip aggressive GC; false (default) = aggressive+prune + URL string + Path string + Depth int // 0 defaults to 1, -1 for all + Branch string // Optional: specific branch to clone/pull - mu sync.Mutex + // GCInterval controls explicit aggressive GC after pulls. + // 0 (default) — no explicit gc; git runs gc.auto on its own schedule + // 1 — aggressive gc after every pull + // N — aggressive gc after every Nth pull + GCInterval int + + mu sync.Mutex + pullCount int } // New creates a new Repo instance. @@ -35,23 +41,20 @@ func New(url, path string, depth int, branch string) *Repo { // Init clones the repo if missing, then syncs once. // Returns whether anything new was fetched. -func (r *Repo) Init(lightGC bool) (bool, error) { +func (r *Repo) Init() (bool, error) { gitDir := filepath.Join(r.Path, ".git") if _, err := os.Stat(gitDir); err != nil { if _, err := r.Clone(); err != nil { return false, err } } - - updated, err := r.syncGit(lightGC) - return updated, err + return r.syncGit() } // Clone performs a shallow clone (--depth N --single-branch --no-tags). func (r *Repo) Clone() (bool, error) { r.mu.Lock() defer r.mu.Unlock() - return r.clone() } @@ -59,7 +62,6 @@ func (r *Repo) clone() (bool, error) { if r.exists() { return false, nil } - if r.URL == "" { return false, fmt.Errorf("repository URL is required") } @@ -93,18 +95,15 @@ func (r *Repo) exists() bool { // runGit executes a git command in the repo directory (or parent for clone). func (r *Repo) runGit(args ...string) (string, error) { cmd := exec.Command("git", args...) - if _, err := os.Stat(r.Path); err == nil && r.exists() { cmd.Dir = r.Path } else { cmd.Dir = filepath.Dir(r.Path) } - output, err := cmd.CombinedOutput() if err != nil { return "", fmt.Errorf("git %s failed: %v\n%s", strings.Join(args, " "), err, output) } - return strings.TrimSpace(string(output)), nil } @@ -112,7 +111,6 @@ func (r *Repo) runGit(args ...string) (string, error) { func (r *Repo) Pull() (updated bool, err error) { r.mu.Lock() defer r.mu.Unlock() - return r.pull() } @@ -144,47 +142,36 @@ func (r *Repo) pull() (updated bool, err error) { if err != nil { return false, err } - return oldHead != newHead, nil } -// GC runs git gc. aggressiveGC adds --aggressive; pruneNow adds --prune=now. -func (r *Repo) GC(aggressiveGC, pruneNow bool) error { +// GC runs git gc --aggressive --prune=now. +func (r *Repo) GC() error { r.mu.Lock() defer r.mu.Unlock() - - return r.gc(aggressiveGC, pruneNow) + return r.gc() } -func (r *Repo) gc(aggressiveGC, pruneNow bool) error { +func (r *Repo) gc() error { if !r.exists() { return fmt.Errorf("repository does not exist at %s", r.Path) } - - args := []string{"gc"} - if aggressiveGC { - args = append(args, "--aggressive") - } - if pruneNow { - args = append(args, "--prune=now") - } - - _, err := r.runGit(args...) + _, err := r.runGit("gc", "--aggressive", "--prune=now") return err } -// Sync clones if missing, pulls, and runs GC. Returns whether HEAD changed. -// lightGC=false runs aggressive GC with --prune=now to minimize disk use. -func (r *Repo) Sync(lightGC bool) (bool, error) { - return r.syncGit(lightGC) +// Sync clones if missing, pulls, and conditionally runs GC based on GCEvery. +// Returns whether HEAD changed. +func (r *Repo) Sync() (bool, error) { + return r.syncGit() } -// Fetch satisfies httpcache.Syncer using the Repo's LightGC setting. +// Fetch satisfies httpcache.Syncer. func (r *Repo) Fetch() (bool, error) { - return r.syncGit(r.LightGC) + return r.syncGit() } -func (r *Repo) syncGit(lightGC bool) (updated bool, err error) { +func (r *Repo) syncGit() (updated bool, err error) { r.mu.Lock() defer r.mu.Unlock() @@ -199,5 +186,12 @@ func (r *Repo) syncGit(lightGC bool) (updated bool, err error) { return updated, err } - return true, r.gc(!lightGC, !lightGC) + if r.GCInterval > 0 { + r.pullCount++ + if r.pullCount%r.GCInterval == 0 { + return true, r.gc() + } + } + + return true, nil } diff --git a/net/ipcohort/cmd/check-ip/blacklist.go b/net/ipcohort/cmd/check-ip/blacklist.go index 6485470..c62c1b2 100644 --- a/net/ipcohort/cmd/check-ip/blacklist.go +++ b/net/ipcohort/cmd/check-ip/blacklist.go @@ -82,7 +82,7 @@ func (s *Sources) Fetch() (bool, error) { // For HTTP: fetches each cacher unconditionally on first run. func (s *Sources) Init() error { if s.gitRepo != nil { - _, err := s.gitRepo.Init(s.gitRepo.LightGC) + _, err := s.gitRepo.Init() return err } for _, syn := range s.syncs {