Skip to content

Options reference

Two families of option, applied at different moments.

  • RepoOpt configures the repository object, and is passed to NewRepo / NewThreadSafeRepo.
  • CloneOption configures a single clone, and is passed to Clone, OpenInMemory and Open.

RepoOpt: constructor options

type RepoOpt func(*Repo) error

One is exported.

WithConfig

func WithConfig(cfg *config.Config) RepoOpt

Supplies a go-git *config.Config (from github.com/go-git/go-git/v5/config).

It applies only to OpenInMemory, where it is installed on the in-memory storage backend before the clone. Clone, OpenLocal and InitLocal never read it, because those backends take their configuration from the .git directory on disk.

Passing WithConfig and then calling Clone is silently a no-op. To configure an on-disk repository, reach through to go-git after opening it:

err := r.WithRepo(func(gr *git.Repository) error {
    return gr.SetConfig(cfg)
})

CloneOption: per-clone options

type CloneOptions struct {
    git.CloneOptions
    SparseCheckoutDirectories []string
}

type CloneOption func(*CloneOptions)

CloneOptions embeds go-git's struct, so every go-git clone field is reachable through it by promotion, and carries beside it what the module does after the clone.

Option Sets Default when the option is absent
WithShallowClone(depth int) CloneOptions.Depth 0, meaning full history
WithSingleBranch(branch string) CloneOptions.SingleBranch, and ReferenceName when branch is non-empty all branches, remote HEAD checked out
WithNoTags() CloneOptions.Tags = git.NoTags go-git's clone default, AllTags
WithRecurseSubmodules() CloneOptions.RecurseSubmodules = git.DefaultSubmoduleRecursionDepth submodules are not initialised
WithSparseCheckout(dirs ...string) CloneOptions.NoCheckout = true and SparseCheckoutDirectories, then a sparse checkout after the clone the whole tree is materialised

They compose, and are applied in the order given:

_, _, err := r.OpenInMemory(ctx, url, "main",
    repo.WithShallowClone(1),
    repo.WithSingleBranch("main"),
    repo.WithNoTags(),
)

Which methods honour clone options

Method Clone options
Clone(ctx, uri, targetPath, opts...) Applied
OpenInMemory(ctx, location, branch, opts...) Applied
Open(ctx, repo.InMemoryRepo, location, branch, opts...) Applied, forwarded to OpenInMemory
Open(ctx, repo.LocalRepo, location, branch, opts...) Ignored. The local path calls OpenLocal(ctx, location), which takes neither options nor a branch.
OpenLocal, InitLocal Not accepted

That last row is the trap worth knowing. Open has a uniform signature across both backends, so a program that selects its backend from configuration compiles either way. Switching RepoType from inmemory to local then silently drops both the clone options and the branch argument, and nothing warns.

WithShallowClone

depth is passed straight through as go-git's Depth. WithShallowClone(0) is equivalent to not calling it at all.

A shallow clone truncates history, which is what makes it cheap and what stops later operations that need ancestry (a merge base, a full git log) seeing past the cut.

It matters most for in-memory repositories, where everything fetched stays resident in RAM.

WithSingleBranch

Sets SingleBranch: true. With a non-empty branch it also sets ReferenceName to refs/heads/<branch>, so that branch is the one fetched and checked out.

With branch empty it limits the fetch to a single branch but leaves the choice to go-git, which resolves the remote's HEAD.

WithNoTags

Sets Tags: git.NoTags.

Worth knowing what you are turning off. go-git's clone default is AllTags: every tag in the remote, whether or not it is reachable from the branch you asked for. On a repository with a long release history that is a meaningful amount of extra transfer, and for an in-memory clone all of it stays resident.

WithRecurseSubmodules

Sets RecurseSubmodules to go-git's DefaultSubmoduleRecursionDepth, which is 10 levels. The depth is fixed: no exported option sets another value. Write your own if you need a different one; see writing a clone option by hand.

Nothing else in this module touches submodules. There is no update, sync or status operation for them.

WithSparseCheckout

Materialises only the paths under the given directory prefixes. The clone itself runs with NoCheckout, and the module then checks out the branch (the branch argument to OpenInMemory, or the clone's HEAD for Clone and an empty branch) limited to those prefixes, through go-git's CheckoutOptions.SparseCheckoutDirectories.

_, _, err := r.OpenInMemory(ctx, url, "main",
    repo.WithSingleBranch("main"),
    repo.WithNoTags(),
    repo.WithShallowClone(1),
    repo.WithSparseCheckout("data/releases/"),
)

What it saves is the working-tree copy, not the objects. Every object the clone fetches is held either way, so this composes with, rather than replaces, WithShallowClone and WithSingleBranch. Measured on 300 incompressible 20 KiB files with go-git v5.19.2: 12.6 MiB live heap after a full in-memory open, 5.4 MiB after a sparse one.

Matching is by string prefix, because that is what go-git does: data/releases also keeps data/releases-old/. End a prefix with / to ask for exactly that directory. A leading / is stripped, since it would match nothing.

Two mistakes are refused rather than applied:

  • No directories, "" or "." return ErrNoSparseDirectories. Each would be a full checkout spending what you asked to save.
  • A set that matches no path in the tree returns ErrNoSparseMatch. Without it the working tree is empty and the first symptom is cannot create empty commit from a later commit, which reads as "nothing to do".

Commits after a sparse checkout keep the full tree: the paths outside the sparse set stay in the index flagged skip-worktree, so AddAll does not stage them as deletions.

The working tree stays sparse for the life of the handle. See what this module does not do.

Writing a clone option by hand

CloneOption is a func(*repo.CloneOptions), and the go-git fields are promoted through the embedded struct, so an option for a field the module does not name is a literal:

depth3 := repo.CloneOption(func(o *repo.CloneOptions) {
    o.RecurseSubmodules = 3
})

Releases up to v0.4.0 took *git.CloneOptions here. The body of such an option is unchanged; only the parameter type moves, and the compiler names every site.

Where the repository is bound

Clone, Open, OpenLocal, OpenInMemory and InitLocal all bind the resulting *git.Repository and *git.Worktree to the receiver, so subsequent calls need no handle. They also return the raw pair for setup-time convenience.

Calling one of them a second time replaces the binding. There is no Close: an in-memory repository is released when the last reference to it is dropped, and an on-disk one holds no long-lived handle to release.