mirror of
https://github.com/therootcompany/golib.git
synced 2026-11-04 06:55:00 +00:00
fix(gitshallow): depth-3 windowed fetch with low-memory GC
- Default depth changed from 1 to 3: enough overlap for delta compression even if the remote pushes 2 commits between fetches - Keep --depth on fetch (not just clone) so shallow stays windowed - GCInterval default changed: 0 now means every fetch (was disabled) - GC sequence: reflog expire → prune → gc --prune=now Expire makes old shallow entries' objects unreachable, prune removes them cheaply (5MB RSS), then gc delta-compresses what remains (~10MB) - Only GC when HEAD actually changed (skip no-op fetches) - Dropped GCAggressive (no benefit in shallow clones)
This commit is contained in:
parent
1dacbdd8f7
commit
1db21be047
@ -19,7 +19,7 @@ import (
|
|||||||
type Repo struct {
|
type Repo struct {
|
||||||
URL string
|
URL string
|
||||||
Path string
|
Path string
|
||||||
Depth int // 0 defaults to 1, -1 for all
|
Depth int // 0 defaults to 3, -1 for all
|
||||||
Branch string // Optional: specific branch to clone/pull
|
Branch string // Optional: specific branch to clone/pull
|
||||||
|
|
||||||
// MaxAge skips the git fetch when .git/FETCH_HEAD is younger than this
|
// MaxAge skips the git fetch when .git/FETCH_HEAD is younger than this
|
||||||
@ -28,10 +28,12 @@ type Repo struct {
|
|||||||
// hammer the remote. 0 defaults to 1 minute; -1 disables.
|
// hammer the remote. 0 defaults to 1 minute; -1 disables.
|
||||||
MaxAge time.Duration
|
MaxAge time.Duration
|
||||||
|
|
||||||
// GCInterval controls explicit aggressive GC after pulls.
|
// GCInterval controls explicit GC after fetches. Shallow clones
|
||||||
// 0 (default) — no explicit gc; git runs gc.auto on its own schedule
|
// accumulate loose objects quickly, so the default is to gc after
|
||||||
// 1 — aggressive gc after every pull
|
// every fetch.
|
||||||
// N — aggressive gc after every Nth pull
|
// 0 (default) — gc after every fetch
|
||||||
|
// N — gc after every Nth fetch
|
||||||
|
// -1 — no explicit gc; git runs gc.auto on its own schedule
|
||||||
GCInterval int
|
GCInterval int
|
||||||
|
|
||||||
sf singleflight.Group
|
sf singleflight.Group
|
||||||
@ -67,11 +69,13 @@ func (r *Repo) validateArgs() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// effectiveDepth returns the depth to use for clone/pull.
|
// effectiveDepth returns the depth to use for clone and fetch.
|
||||||
// 0 means unset — defaults to 1. -1 means full history.
|
// 0 means unset — defaults to 3 (enough overlap that the server can
|
||||||
|
// delta-compress even if the remote pushes 2 commits between fetches).
|
||||||
|
// -1 means full history.
|
||||||
func (r *Repo) effectiveDepth() int {
|
func (r *Repo) effectiveDepth() int {
|
||||||
if r.Depth == 0 {
|
if r.Depth == 0 {
|
||||||
return 1
|
return 3
|
||||||
}
|
}
|
||||||
return r.Depth
|
return r.Depth
|
||||||
}
|
}
|
||||||
@ -187,7 +191,6 @@ func (r *Repo) pull(ctx context.Context) (updated bool, err error) {
|
|||||||
if depth := r.effectiveDepth(); depth >= 0 {
|
if depth := r.effectiveDepth(); depth >= 0 {
|
||||||
fetchArgs = append(fetchArgs, "--depth", fmt.Sprintf("%d", depth))
|
fetchArgs = append(fetchArgs, "--depth", fmt.Sprintf("%d", depth))
|
||||||
}
|
}
|
||||||
// `--` separates flags from positional remote/branch.
|
|
||||||
fetchArgs = append(fetchArgs, "--", "origin", branch)
|
fetchArgs = append(fetchArgs, "--", "origin", branch)
|
||||||
if _, err := r.runGit(ctx, fetchArgs...); err != nil {
|
if _, err := r.runGit(ctx, fetchArgs...); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -208,7 +211,13 @@ func (r *Repo) gc(ctx context.Context) error {
|
|||||||
if !r.exists() {
|
if !r.exists() {
|
||||||
return fmt.Errorf("repository does not exist at %s", r.Path)
|
return fmt.Errorf("repository does not exist at %s", r.Path)
|
||||||
}
|
}
|
||||||
_, err := r.runGit(ctx, "gc", "--aggressive", "--prune=now")
|
if _, err := r.runGit(ctx, "reflog", "expire", "--expire=all", "--all"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := r.runGit(ctx, "prune"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err := r.runGit(ctx, "gc", "--prune=now")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,17 +261,23 @@ func (r *Repo) fetch(ctx context.Context) (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.GCInterval > 0 {
|
if r.GCInterval >= 0 {
|
||||||
|
interval := r.GCInterval
|
||||||
|
if interval == 0 {
|
||||||
|
interval = 1
|
||||||
|
}
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
r.pullCount++
|
r.pullCount++
|
||||||
shouldGC := r.pullCount%r.GCInterval == 0
|
shouldGC := r.pullCount%interval == 0
|
||||||
r.mu.Unlock()
|
r.mu.Unlock()
|
||||||
if shouldGC {
|
if shouldGC {
|
||||||
return true, r.gc(ctx)
|
if gcErr := r.gc(ctx); gcErr != nil {
|
||||||
|
return updated, gcErr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, nil
|
return updated, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilePath returns the absolute path to relPath within this repo.
|
// FilePath returns the absolute path to relPath within this repo.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user