Skip to content

Context and cancellation

Every network-touching operation takes a context.Context as its first argument. Clone, in-memory clone, push, and the pull that CreateBranch performs all run against a remote, and a remote can hang. Without a context there is no way to bound how long a clone against an unresponsive server blocks. On a ThreadSafeRepo that is worse than a stall, because the mutex is held for the operation's full duration, so one hung clone wedges every other method behind it. The context is the cancellation handle that fixes both.

The thin-veneer stance

The role interfaces traffic in go-git's own concrete types: *git.Repository, *git.Worktree, *object.File, plumbing.ReferenceName, *git.PushOptions, and so on. That is deliberate. This package is a thin veneer over go-git: it adds binding, role decomposition, forge-aware auth, and concurrency safety, but it does not try to hide go-git behind an abstraction of its own. Threading a context through the network methods keeps faith with that stance. It wires each call to the …Context form go-git already provides (CloneContext, PlainCloneContext, PushContext, PullContext) rather than inventing a cancellation mechanism.

Which methods take a context

The network-touching methods of the Opener, Committer, and Brancher roles:

Open(ctx, repoType, location, branch, opts...)
OpenLocal(ctx, location)                 // branch parameter removed, see below
OpenInMemory(ctx, location, branch, opts...)
Clone(ctx, uri, targetPath, opts...)
Commit(ctx, message, opts)
Push(ctx, opts)
CreateBranch(ctx, branchName)

Commit and OpenLocal do no network I/O, but they belong to roles whose other methods do, so they take the context too for a uniform role surface; they ignore it.

Migration (pre-1.0 breaking change)

This is an API-breaking signature change. The module is pre-1.0, so it ships as a minor bump. To migrate:

  • Add a context as the first argument to Open, OpenLocal, OpenInMemory, Clone, Commit, Push, and CreateBranch. Pass the request/operation context you already have, or context.Background() at a top-level entry point.
  • Drop the branch argument from OpenLocal. It was silently unused on the open path. OpenLocal(ctx, location) opens an existing repository, and only when the path is genuinely not a repository (git.ErrRepositoryNotExists) does it initialise a fresh one on main. To initialise on a specific branch, use InitLocal(location, branch), which is the purpose-built init entry point.

OpenLocal no longer clobbers on a non-fatal open error

Previously OpenLocal fell into initialise-a-new-repository on any git.PlainOpen failure: a corrupt repository, a permission error, anything. So a transient fault on a real project directory got an empty repository initialised over it, destroying the evidence of the original problem. It now initialises only for git.ErrRepositoryNotExists and returns every other open error verbatim (wrapped as failed to open git repository at <path>). If you relied on the old init-on-any-error behaviour, call InitLocal explicitly.

Since v0.3.0 one case inside git.ErrRepositoryNotExists is carved back out. A .git file naming a git directory that has gone (a pruned linked worktree, a moved --separate-git-dir checkout, a submodule whose superproject was re-cloned) returns ErrMissingGitDir and writes nothing, because that directory holds real work rather than being empty.