Documentation
¶
Overview ¶
Package gitcli implements the Git ports over the git binary. Every invocation runs in an isolated environment that is built here and never inherits the caller's.
Index ¶
- Variables
- func HooksDir(dataDir string) string
- func RepoDir(dataDir string, name domain.RepoName) string
- type Config
- type Git
- func (g *Git) BranchHead(ctx context.Context, repo domain.RepoName, branch string) (domain.ObjectID, error)
- func (g *Git) ChangedPaths(ctx context.Context, repo domain.RepoName, base, head domain.ObjectID, ...) ([]string, bool, error)
- func (g *Git) CheckVersion(ctx context.Context) (Version, error)
- func (g *Git) Close(ctx context.Context) error
- func (g *Git) CommitTree(ctx context.Context, repo domain.RepoName, id domain.ObjectID) (domain.ObjectID, error)
- func (g *Git) DefaultBranch(ctx context.Context, name domain.RepoName) (string, bool, error)
- func (g *Git) DeleteBranch(ctx context.Context, repo domain.RepoName, branch string, old domain.ObjectID) error
- func (g *Git) DiffCommits(ctx context.Context, repo domain.RepoName, base, head domain.ObjectID, ...) (domain.DiffStats, domain.Diff, error)
- func (g *Git) Discard(_ context.Context, id ports.StagedID) error
- func (g *Git) GetCommit(ctx context.Context, repo domain.RepoName, rev domain.Revision, ...) (domain.CommitDetail, error)
- func (g *Git) GetTree(ctx context.Context, repo domain.RepoName, rev domain.Revision, ...) (domain.Tree, error)
- func (g *Git) HTTPBackend(ctx context.Context, push bool) (ports.HTTPBackend, error)
- func (g *Git) HasBranch(ctx context.Context, name domain.RepoName, branch string) (bool, error)
- func (g *Git) HasCommit(ctx context.Context, repo domain.RepoName, id domain.ObjectID) (bool, error)
- func (g *Git) HeadBranch(_ context.Context, name domain.RepoName) (string, error)
- func (g *Git) IsAncestor(ctx context.Context, repo domain.RepoName, ...) (bool, error)
- func (g *Git) List(_ context.Context) (ports.RepoDirs, error)
- func (g *Git) ListCommits(ctx context.Context, repo domain.RepoName, q ports.CommitQuery) (ports.CommitPage, error)
- func (g *Git) ListPaths(ctx context.Context, repo domain.RepoName, rev domain.Revision, ...) (domain.PathList, error)
- func (g *Git) ListRange(ctx context.Context, repo domain.RepoName, exclude, head domain.ObjectID, ...) ([]domain.Commit, bool, error)
- func (g *Git) ListRefs(ctx context.Context, repo domain.RepoName) ([]domain.Ref, error)
- func (g *Git) Maintain(ctx context.Context, name domain.RepoName) error
- func (g *Git) MergeBase(ctx context.Context, repo domain.RepoName, a, b domain.ObjectID) (domain.ObjectID, error)
- func (g *Git) OnTrash(fn func(dir string))
- func (g *Git) OpenBlob(ctx context.Context, repo domain.RepoName, rev domain.Revision, ...) (domain.BlobInfo, io.ReadCloser, error)
- func (g *Git) Publish(ctx context.Context, id ports.StagedID, name domain.RepoName) error
- func (g *Git) PurgeStaging(ctx context.Context) error
- func (g *Git) ResolveRefPath(ctx context.Context, repo domain.RepoName, path string) (domain.RefPath, error)
- func (g *Git) ResolveRevision(ctx context.Context, repo domain.RepoName, rev domain.Revision) (domain.ResolvedRevision, error)
- func (g *Git) SetDefaultBranch(ctx context.Context, name domain.RepoName, branch string) error
- func (g *Git) Stage(ctx context.Context) (ports.StagedID, error)
- func (g *Git) Trash(ctx context.Context, name domain.RepoName) error
- func (g *Git) UpdateBranch(ctx context.Context, repo domain.RepoName, branch string, ...) error
- func (g *Git) WriteCommit(ctx context.Context, repo domain.RepoName, object []byte) (domain.ObjectID, error)
- type Version
Constants ¶
This section is empty.
Variables ¶
var ErrClosed = errors.New("gitcli: reader is closed")
ErrClosed is returned by reads after Close.
var MinVersion = Version{Major: 2, Minor: 45}
MinVersion is the oldest supported git.
Functions ¶
Types ¶
type Config ¶
type Config struct {
// Bin is the git executable: a name looked up in the process PATH, or a path.
// It is required; the default belongs to the configuration.
Bin string
// DataDir is the data root (absolute). repos/, repos/.staging/ and
// trash/ below it must exist; run/git-home/ is the HOME of git.
DataDir string
// Path is the PATH given to git, which it uses to find its helpers.
Path string
// MaxReceiveBytes is the largest pack a push may send
// (receive.maxInputSize); zero leaves Git unlimited.
MaxReceiveBytes int64
// CatFileIdle is how long a repository's long-lived `git cat-file`
// process may stay unused before it is stopped. Zero means 60 seconds.
CatFileIdle time.Duration
// MaxCatFiles bounds the long-lived cat-file processes. Zero means 16.
MaxCatFiles int
// MaxExec bounds the one-shot git processes that run at once (every
// command but the long-lived cat-file processes and the blob streams);
// callers beyond it wait for a slot until their context ends. Zero means
// max(4, 2*GOMAXPROCS).
MaxExec int
// MaxBlobStreams bounds the open blob streams, each of which holds a git
// process until its reader is closed. Streams beyond it are refused with
// domain.ErrResourceExhausted rather than queued: a slow reader could hold
// a slot for long. Zero means 32.
MaxBlobStreams int
}
Config configures a Git adapter. The adapter never reads the process environment: the caller passes what it needs.
type Git ¶
type Git struct {
// contains filtered or unexported fields
}
Git is the git CLI adapter for ports.GitRepos.
func (*Git) BranchHead ¶ added in v0.2.0
func (g *Git) BranchHead(ctx context.Context, repo domain.RepoName, branch string) (domain.ObjectID, error)
BranchHead resolves refs/heads/<branch>^{commit} with `cat-file`.
func (*Git) ChangedPaths ¶ added in v0.7.0
func (g *Git) ChangedPaths(ctx context.Context, repo domain.RepoName, base, head domain.ObjectID, limit int) ([]string, bool, error)
ChangedPaths runs `git diff-tree -r --name-only -z` from base to head, with renames not detected (a rename lists both paths, as GitHub's push filters see it), and stops reading after max paths: truncated tells there were more. A zero base diffs against the empty tree.
func (*Git) CheckVersion ¶
CheckVersion runs `git version` and fails if git is missing, unreadable or older than MinVersion. It returns the version found.
func (*Git) Close ¶
Close stops the long-lived git processes of the read side and waits for them, until ctx ends. Reads after Close fail with ErrClosed. Close is idempotent; the adapter's write side keeps working.
func (*Git) CommitTree ¶ added in v0.2.0
func (g *Git) CommitTree(ctx context.Context, repo domain.RepoName, id domain.ObjectID) (domain.ObjectID, error)
CommitTree runs `git rev-parse <commit>^{tree}`.
func (*Git) DefaultBranch ¶
DefaultBranch reads HEAD and checks the branch with `git for-each-ref`.
func (*Git) DeleteBranch ¶ added in v0.4.0
func (g *Git) DeleteBranch(ctx context.Context, repo domain.RepoName, branch string, old domain.ObjectID) error
DeleteBranch runs `git update-ref -d refs/heads/<branch> <old>`, which takes the ref lock and refuses when the ref is not at old. A failure is told from a moved or deleted branch by reading the branch again.
func (*Git) DiffCommits ¶ added in v0.2.0
func (g *Git) DiffCommits(ctx context.Context, repo domain.RepoName, base, head domain.ObjectID, limits domain.DiffLimits) (domain.DiffStats, domain.Diff, error)
DiffCommits reads the diff between two commits like GetCommit does.
func (*Git) GetCommit ¶
func (g *Git) GetCommit(ctx context.Context, repo domain.RepoName, rev domain.Revision, limits domain.DiffLimits) (domain.CommitDetail, error)
GetCommit returns a commit with its diff against its first parent (the empty tree for a root commit), read in one pass of
git diff-tree -r -M -z --raw --numstat -p <parent> <commit>
which prints the changed files, then their line counts, then the patch. Statistics come from the line counts, binary files from their "-" counts. The patch is parsed as it streams: nothing beyond the limits is kept, and git is stopped once nothing more is needed.
func (*Git) GetTree ¶
func (g *Git) GetTree(ctx context.Context, repo domain.RepoName, rev domain.Revision, path domain.TreePath, includeLast bool) (domain.Tree, error)
GetTree lists a directory with `git ls-tree -z -l`.
func (*Git) HTTPBackend ¶
HTTPBackend resolves git-http-backend through `git --exec-path` of the configured git (once), and builds its environment: the isolated one, GIT_PROJECT_ROOT=<data>/repos, GIT_HTTP_EXPORT_ALL and the configuration passed by GIT_CONFIG_COUNT/KEY/VALUE, so that nothing is written to the repositories:
core.hooksPath=<data>/run/hooks receive.fsckObjects=true uploadpack.allowFilter=true pack.threads=1 pack.windowMemory=256m http.receivepack=true (push only) receive.maxInputSize=<max> (push only, when configured)
A push also gets REMOTE_USER=owner.
func (*Git) HasBranch ¶
HasBranch checks refs/heads/<branch> with `git for-each-ref` after `git check-ref-format`, which rejects the glob characters for-each-ref would otherwise interpret.
func (*Git) HasCommit ¶ added in v0.2.0
func (g *Git) HasCommit(ctx context.Context, repo domain.RepoName, id domain.ObjectID) (bool, error)
HasCommit asks `cat-file` for the type of the object.
func (*Git) HeadBranch ¶
HeadBranch reads the HEAD file of the bare repository (files ref backend). It is empty when HEAD is detached.
func (*Git) IsAncestor ¶ added in v0.2.0
func (g *Git) IsAncestor(ctx context.Context, repo domain.RepoName, ancestor, descendant domain.ObjectID) (bool, error)
IsAncestor runs `git merge-base --is-ancestor`, which exits 1 for "no".
func (*Git) List ¶
List returns the repository directories. Entries that are not "<valid name>.git" directories are reported as invalid; .staging is not an entry.
func (*Git) ListCommits ¶
func (g *Git) ListCommits(ctx context.Context, repo domain.RepoName, q ports.CommitQuery) (ports.CommitPage, error)
ListCommits pages through `git log`, which stops after Skip+Limit+1 commits.
func (*Git) ListPaths ¶ added in v0.6.0
func (g *Git) ListPaths(ctx context.Context, repo domain.RepoName, rev domain.Revision, maxPaths, maxBytes int) (domain.PathList, error)
ListPaths lists the files and symbolic links of a revision with `git ls-tree -r -z`, in its order, up to the bounds of the port.
func (*Git) ListRange ¶ added in v0.2.0
func (g *Git) ListRange(ctx context.Context, repo domain.RepoName, exclude, head domain.ObjectID, limit int) ([]domain.Commit, bool, error)
ListRange runs `git log <head> ^<exclude>`.
func (*Git) ListRefs ¶
ListRefs lists HEAD, the branches and the tags with `git for-each-ref`. An annotated tag is followed to its commit; a tag of a tag is followed with one `cat-file` request each.
func (*Git) MergeBase ¶ added in v0.2.0
func (g *Git) MergeBase(ctx context.Context, repo domain.RepoName, a, b domain.ObjectID) (domain.ObjectID, error)
MergeBase runs `git merge-base`, which exits 1 when there is none.
func (*Git) OnTrash ¶ added in v0.3.0
OnTrash registers fn, which Trash calls with the directory the repository had, after it moved it away. Another reader of the repository (the ermitegit adapter) uses it to drop what it holds open. Call it once while wiring, before the adapter serves.
func (*Git) OpenBlob ¶
func (g *Git) OpenBlob(ctx context.Context, repo domain.RepoName, rev domain.Revision, p domain.TreePath) (domain.BlobInfo, io.ReadCloser, error)
OpenBlob describes the file at path and returns its content as a stream.
A blob up to 256 KiB is read through the repository's long-lived cat-file process. A larger one gets a `git cat-file blob` process that lives until the reader is closed, or ctx ends, or the adapter is closed; the memory used is a few buffers, whatever the size. The reader fails with io.ErrUnexpectedEOF if git stops before the announced size.
func (*Git) Publish ¶
Publish renames the staged repository to repos/<name>.git without replacing anything.
func (*Git) PurgeStaging ¶
PurgeStaging removes everything under repos/.staging/.
func (*Git) ResolveRefPath ¶
func (g *Git) ResolveRefPath(ctx context.Context, repo domain.RepoName, path string) (domain.RefPath, error)
ResolveRefPath tries the leading segments of path as a revision (branch, tag, full ref name, HEAD; a single segment may also be a commit id), longest first, with up to one `cat-file` request per candidate on the repository's process. At most domain.MaxRefPathTries segments are tried.
func (*Git) ResolveRevision ¶
func (g *Git) ResolveRevision(ctx context.Context, repo domain.RepoName, rev domain.Revision) (domain.ResolvedRevision, error)
ResolveRevision resolves a revision with `cat-file`.
func (*Git) SetDefaultBranch ¶
SetDefaultBranch runs `git symbolic-ref HEAD refs/heads/<branch>`.
func (*Git) Stage ¶
Stage runs `git init --bare` in a new directory under repos/.staging/ and sets the repository defaults. The initial branch is main.
func (*Git) UpdateBranch ¶ added in v0.2.0
func (g *Git) UpdateBranch(ctx context.Context, repo domain.RepoName, branch string, old, newID domain.ObjectID) error
UpdateBranch runs `git update-ref refs/heads/<branch> <new> <old>`, which takes the ref lock and refuses when the ref is not at old. A failure is told from a moved branch by reading the branch again.
type Version ¶
type Version struct{ Major, Minor, Patch int }
Version is a git release number.
func ParseVersion ¶
ParseVersion parses the output of `git version`, such as "git version 2.45.0", "git version 2.39.5 (Apple Git-154)" or "git version 2.46.0.rc1".