Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

GitRepository `.spec.ref.commit` + `.spec.ref.branch` does not shallow clone, contrary to the docs

Aperta
#2,146 2 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
42/100
Tipo di issue
Bug
Chiarezza
Abbastanza chiara
Stato di attività
Attiva
Stack tecnologico
git, go
Ambito
devops

Direzione di ricerca

Inizia da pkg/git/gogit/clone.go, in particolare cloneCommit, e confrontalo con cloneBranch, cloneTag e cloneSemVer; poi esamina gitrepository_controller.go e docs/spec/v1/gitrepositories.md. Conferma il comportamento attuale e i test pertinenti prima di decidere se l'ambito si limita alla documentazione o include il cloning a profondità limitata e il commit fast path. Il lavoro è concluso quando il comportamento selezionato è implementato e documentato accuratamente, con copertura di regressione per il percorso interessato.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Summary

The v1 GitRepository docs state that combining .spec.ref.commit with .spec.ref.branch performs a shallow clone. It does not — the commit clone path never sets a depth. The combination gives you --single-branch, not --depth.

Either the docs should be corrected, or a real bounded-depth mechanism should be added. I'd argue for both, plus a third fix that is probably the biggest win (see "Skipped fast path" below).

What the docs say

docs/spec/v1/gitrepositories.md, under Commit example:

This field takes precedence over all other fields. It can be combined with .spec.ref.branch to perform a shallow clone of the branch, in which the commit must exist:

spec:
  ref:
    branch: <branch>
    commit: "<commit SHA within branch>"

The wording appears to date back to the v1beta2 API field comment from the gitImplementation: go-git | libgit2 era (quoted in #1017):

// Commit SHA to check out, takes precedence over all reference fields.
//
// When GitRepositorySpec.GitImplementation is set to 'go-git', this can be
// combined with Branch to shallow clone the branch, in which the commit is
// expected to exist.
What the code does

In fluxcd/pkg/git/gogit, cloneBranch, cloneTag and cloneSemVer all honour opts.ShallowClone:

var depth int
if opts.ShallowClone {
    depth = 1
}
cloneOpts := &extgogit.CloneOptions{ /* ... */ Depth: depth /* ... */ }

cloneCommit has no depth variable and no Depth field in its CloneOptions at all. The only effect of also setting a branch is:

if opts.Branch != "" {
    cloneOpts.SingleBranch = g.singleBranch          // true by default
    cloneOpts.ReferenceName = plumbing.NewBranchReferenceName(opts.Branch)
}

singleBranch defaults to true in NewClient ("Default to single branch as it is the most performant option"), and Client.clone dispatches on Commit before any other ref field, so .spec.ref.commit always routes here.

So branch + commit narrows the fetch from all branches, full history to one branch, full history. That is a real improvement over commit alone, but it is --single-branch, not a shallow clone. source-controller sets ShallowClone: true unconditionally in gitrepository_controller.go, and cloneCommit silently ignores it.

Skipped fast path

cloneCommit is also the only clone path without the LastObservedCommit short-circuit that the others open with:

if lastObserved := git.TransformRevision(opts.LastObservedCommit); lastObserved != "" {
    head, err := g.getRemoteHEAD(ctx, url, ref, authMethod)   // ls-remote, no clone
    // ...
    if head != "" && shortRef == lastObserved {
        return c, nil                                          // nothing fetched
    }
}

This is visible in the controller logs. A branch- or tag-pinned GitRepository logs no changes since last reconciliation; a commit-pinned one logs artifact up-to-date with remote revision — i.e. it cloned first and only then discovered the artifact was already current.

This seems like the most valuable thing to fix: a commit pin is immutable, so polling can never discover a new revision, yet every interval pays for a full-history fetch.

Impact

Observed on a production cluster, source-controller v1.9.4:

  • Three commit-pinned GitRepositories (branch + commit + sparseCheckout) against one large monorepo, interval: 1h.
  • Each reconcile takes 3–4 minutes and drives the container's Go heap to its 1Gi limit. Since GOMEMLIMIT is wired to limits.memory in the stock manifest, Go enters continuous GC at that ceiling, /healthz misses the Kubernetes default timeoutSeconds: 1, and the kubelet restarts the container.
  • 49 restarts in ~6 days, every one of them Killing: Container manager failed liveness probe. The 10s terminationGracePeriodSeconds is then too short for a clean shutdown, so the container is SIGKILLed (exit 137) without releasing its leader lease, costing another ~45s of lease expiry per restart, after which everything reconciles at once and can spike again.
  • A tag-pinned GitRepository against the same monorepo in the same cluster returns instantly via the fast path — useful as a control.

Not filing this as "source-controller crashes"; the resource limits are ours to tune. The point is that the documented mitigation (add branch to get a shallow clone) does not exist, so there is no way to reduce the fetch cost of a commit pin.

Proposals
  1. Docs: correct the Commit example — branch + commit restricts the fetch to a single branch, it does not make the clone shallow.
  2. Bounded, incremental deepening: give cloneCommit a real depth-limited fetch. Start at a modest depth, and deepen incrementally (git fetch --deepen-style) until the commit resolves, falling back to full history if it is never found. In a GitOps flow the pinned commit is usually close to the branch tip, so a first attempt at a small depth would hit the overwhelming majority of the time. A user-facing knob (e.g. .spec.ref.depth, or a controller flag for the initial depth) would let people tune it.
  3. Fast path for commit pins: move the artifact-up-to-date check ahead of the clone when .spec.ref.commit is set. If the pinned commit equals the last observed revision and the artifact is present in storage, no fetch is needed at all.

(2) and (3) are independent; (3) alone would eliminate the recurring cost for anyone pinning commits, while (2) helps the first fetch and any genuine revision change.

Versions
  • source-controller v1.9.4
  • github.com/fluxcd/pkg/git v0.52.0 (per source-controller's go.mod)
  • git/gogit/clone.go is blob b59744db03f544e46e090a1fdbd7cdd0737a731c on both git/v0.52.0 and main at time of writing, so this is current on main too.

Happy to open a PR for the docs fix, and to help with (2)/(3) if the approach sounds right.

Lingua principale
Go
Stelle
283
Fork
252
Merge medio
1h 6m
PR unite (30g)
12

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di fluxcd/source-controller

Tutte le issue di fluxcd/source-controller

Issue simili

Altre issue su Go

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.