Documentation
¶
Overview ¶
Package git is commons-db's git client: remote listing, checkouts into a shared cache, commits, tags and pushes, and the provider REST APIs (GitHub, GitLab, Azure DevOps) that open pull requests.
Layers ¶
- url.go parses a repository URL into provider coordinates (RepoURL).
- remote.go lists remote refs with go-git (LsRemote) and resolves a ref name to a commit (RemoteRefs.Resolve).
- clone.go, cache.go and worktree.go put a ref on disk with the git binary: a plain clone for shell checkouts, or a mirror plus leased worktrees.
- change.go composes a worktree into a branch, commit, push and pull request (Cache.Apply), optionally cherry-picking commits onto the base first (cherrypick.go).
- log.go reads the commits between two refs (Cache.Log, Cache.Compare) from the mirror; pullrequest.go opens a pull request between two existing branches (Repository.OpenPullRequest).
- provider*.go and rest.go talk to the hosting provider's REST API.
- factory.go builds a Client from a hydrated models.Connection.
Why disk operations use the git binary ¶
go-git is used for LsRemote only, against in-memory storage. Fetching with go-git breaks on servers that need multi_ack negotiation (Azure DevOps; see go-git plumbing/protocol/packp/srvresp.go, the multi_ack TODO), which the old client papered over by mutating the process-global transport.UnsupportedCapabilities. go-git v5 also has no linked worktrees. The git binary (>= 2.31, for GIT_CONFIG_COUNT) handles both, and nothing in this package mutates go-git globals: TLS for LsRemote is passed per call.
Credential policy ¶
Credentials reach the git binary only through its environment, never argv and never a config file on disk:
- HTTP(S): GIT_CONFIG_COUNT sets a URL-scoped http.<url>.extraHeader with the Basic Authorization header, plus credential.helper="" so no helper is consulted or written to.
- SSH: GIT_SSH_COMMAND points at a 0600 temporary key with strict known_hosts checking (disabled only by TLS.InsecureSkipVerify).
- GIT_TERMINAL_PROMPT=0 so a rejected credential fails instead of prompting.
Repository URLs must not embed a password. Every revision argument is preceded by --end-of-options, and branch, tag and revision names are validated before they reach a command line.
Config isolation ¶
Every command runs in an environment built by baseEnv, so the calling process's own git setup cannot steer it:
- GIT_CONFIG_NOSYSTEM=1 and GIT_CONFIG_GLOBAL=/dev/null: neither the system nor the user's global config is read, so no url.insteadOf, credential helper or default identity leaks in.
- core.hooksPath=/dev/null: no hook (pre-commit, pre-push, …) runs, from the user's config or from a repository template.
- commit.gpgSign=false and tag.gpgSign=false: a commit or tag never waits on a signing key or agent the server does not have.
- inherited GIT_DIR, GIT_WORK_TREE, GIT_CONFIG_*, GIT_SSH*, GIT_ASKPASS, GIT_AUTHOR_*, GIT_COMMITTER_* and GIT_TRACE* are dropped; the last keeps a traced Authorization header out of the stderr an error carries.
Commit and tag authors are therefore always explicit (Signature). DefaultIdentity is the one deliberate read of the global config: it returns the process's own user.name and user.email, for a caller that authors a commit as the server itself.
Index ¶
- Variables
- func Redact(text string) string
- func ValidateBranchName(name string) error
- func ValidateRevision(rev string) error
- func ValidateTagName(name string) error
- type APIError
- type Auth
- type Cache
- func (c *Cache) Apply(ctx context.Context, repo *Repository, request ChangeRequest) (ChangeResult, error)
- func (c *Cache) Checkout(ctx context.Context, repo *Repository, options CheckoutOptions) (*Worktree, error)
- func (c *Cache) Compare(ctx context.Context, repo *Repository, base, head string) (Comparison, error)
- func (c *Cache) Log(ctx context.Context, repo *Repository, base, head string, options LogOptions) ([]Commit, error)
- type ChangeRequest
- type ChangeResult
- type CheckoutOptions
- type CherryPickConflictError
- type CherryPickEmptyError
- type Client
- type CloneOptions
- type Commit
- type CommitOptions
- type Comparison
- type DirtyStats
- type FileChange
- type InspectOptions
- type LastCommit
- type ListRepositoriesOptions
- type LogOptions
- type Metadata
- type Provider
- type PullRequest
- type PullRequestRequest
- type PullRequestState
- type PushOptions
- type Ref
- type RefKind
- type RemoteRefs
- type RepoURL
- type Repository
- type RepositoryInfo
- type RepositoryOptions
- type Signature
- type TLS
- type TagOptions
- type Worktree
- func (w *Worktree) Commit(ctx context.Context, options CommitOptions) (string, error)
- func (w *Worktree) CreateBranch(ctx context.Context, name string) error
- func (w *Worktree) CreateTag(ctx context.Context, options TagOptions) error
- func (w *Worktree) DeleteFile(path string) error
- func (w *Worktree) Push(ctx context.Context, options PushOptions) error
- func (w *Worktree) Release() error
- func (w *Worktree) WriteFile(path string, content []byte) error
Constants ¶
This section is empty.
Variables ¶
var ( // ErrPullRequestsUnsupported is returned when the repository's provider // cannot open pull requests (plain git, or no provider). ErrPullRequestsUnsupported = errors.New("pull requests are not supported by this git provider") // ErrBranchExists is returned when a change would push to a branch that // already exists on the remote. ErrBranchExists = errors.New("branch already exists on the remote") // ErrTagExists is returned when a change would push a tag that already // exists on the remote. ErrTagExists = errors.New("tag already exists on the remote") // ErrBaseMoved is returned when a change pinned to a base commit // (ChangeRequest.BaseSHA) finds the base branch at a different commit. ErrBaseMoved = errors.New("change base moved off the expected commit") )
var ErrNoIdentity = errors.New("no git user.name/user.email configured")
ErrNoIdentity marks a process whose global git config lacks user.name or user.email.
var ErrNotRepository = errors.New("not a git repository")
Functions ¶
func ValidateBranchName ¶
ValidateBranchName applies the rules of `git check-ref-format --branch` without spawning git, so a branch name from a request is refused before it reaches `git branch`, `git push` or a refspec.
func ValidateRevision ¶
ValidateRevision rejects a revision that git could read as something other than a revision before it is ever placed on a git command line. A leading "-" would be parsed as an option (`--upload-pack=…` runs a program), and whitespace or a control character means the value was not one revision to begin with. Revision syntax itself is left to git. Callers still put `--end-of-options` ahead of the value.
func ValidateTagName ¶
ValidateTagName applies the same ref-format rules to a tag name.
Types ¶
type Auth ¶
Auth carries a repository's credentials. HTTP(S) uses Username/Password or Token (sent as Basic auth with the provider's token user); SSH uses PrivateKey, with Password as its passphrase.
type Cache ¶
type Cache struct {
Root string
}
Cache keeps one bare mirror per repository and worktrees per ref:
<Root>/<RepoURL.CacheKey>/mirror.git <Root>/<RepoURL.CacheKey>/mirror.lock <Root>/<RepoURL.CacheKey>/worktrees/<ref dir>[.lock]
mirror.lock serializes fetches and worktree bookkeeping; each worktree is leased exclusively until Release, so concurrent checkouts of different refs proceed while a second checkout of the same ref waits.
func (*Cache) Apply ¶
func (c *Cache) Apply(ctx context.Context, repo *Repository, request ChangeRequest) (ChangeResult, error)
Apply runs a ChangeRequest. Every check that can fail without touching the remote runs first: a provider without pull requests returns ErrPullRequestsUnsupported and an existing remote branch ErrBranchExists, before anything is pushed.
func (*Cache) Checkout ¶
func (c *Cache) Checkout(ctx context.Context, repo *Repository, options CheckoutOptions) (*Worktree, error)
Checkout leases a worktree at ref. A commit already in the mirror is not fetched again.
func (*Cache) Compare ¶
func (c *Cache) Compare(ctx context.Context, repo *Repository, base, head string) (Comparison, error)
Compare counts the commits head is ahead of and behind base and finds their merge base; refs without a common ancestor are an error.
func (*Cache) Log ¶
func (c *Cache) Log(ctx context.Context, repo *Repository, base, head string, options LogOptions) ([]Commit, error)
Log returns the commits reachable from head and not from base, oldest first: parents before children (--topo-order), so a merge commit follows every commit it merges. A commit whose patch base already carries (a cherry-pick of it, by patch-id) is omitted: git log --cherry-pick --right-only base...head. Merge commits are kept. base and head resolve like Checkout refs; both are fetched into the mirror first.
type ChangeRequest ¶
type ChangeRequest struct {
Base string
BaseSHA string
Branch string
Message string
Author Signature
CherryPick []string
CherryPickMainline int
AllowEmpty bool
Trailers []string
Files []FileChange
PullRequest *PullRequestRequest
Tag *TagOptions
}
ChangeRequest commits Files on a new Branch cut from Base, pushes it and, optionally, tags the commit and opens a pull request into Base.
BaseSHA, when set, pins the change to the commit the caller verified Files against: the branch is cut from exactly that commit, and a Base whose head is anywhere else fails with ErrBaseMoved before anything is pushed.
CherryPick replays full commit SHAs in order onto the branch (git cherry-pick -x, Author as committer, the original author kept) before Files are committed; Files may then be empty. A conflict aborts with *CherryPickConflictError, a merge commit needs CherryPickMainline (the 1-based parent to replay it against), and a commit already on the base is *CherryPickEmptyError unless AllowEmpty skips it. Trailers ("Key: value" lines) are appended to every replayed commit's message, after its origin line; Message is the message of the Files commit only. Nothing is pushed on any failure.
type ChangeResult ¶
type ChangeResult struct {
Branch string
CommitSHA string
PullRequest *PullRequest
Tag string
SkippedCherryPicks []string
}
ChangeResult is what Apply pushed and opened. SkippedCherryPicks are the CherryPick commits AllowEmpty skipped as already applied.
type CheckoutOptions ¶
CheckoutOptions configures Cache.Checkout. Ref is a branch, tag, full ref or 40-char SHA (empty is the remote HEAD). An Ephemeral worktree gets a unique directory removed on Release.
type CherryPickConflictError ¶
CherryPickConflictError is a ChangeRequest.CherryPick commit that does not apply cleanly onto the base: the cherry-pick was aborted and nothing was pushed. Files are the paths left unmerged.
func (*CherryPickConflictError) Error ¶
func (e *CherryPickConflictError) Error() string
type CherryPickEmptyError ¶
CherryPickEmptyError is a ChangeRequest.CherryPick commit whose changes are already on Base (its patch was applied there before): the cherry-pick was aborted and nothing was pushed.
func (*CherryPickEmptyError) Error ¶
func (e *CherryPickEmptyError) Error() string
type Client ¶
type Client struct {
Type string
Provider Provider
Auth Auth
// contains filtered or unexported fields
}
Client is a git connection: its provider API and the credentials for its repositories.
func FromConnection ¶
FromConnection builds a Client from a hydrated connection (URL, username, password and certificate already resolved). Type git uses Username/Password, with Certificate as the SSH private key, and its URL is the repository. github, gitlab and azure_devops carry a PAT in Password and the API URL in URL; the namespace comes from Properties organization, group or project respectively.
func (*Client) Repository ¶
func (c *Client) Repository(rawURL string) (*Repository, error)
Repository opens rawURL with the connection's credentials and provider, refusing a repository outside the connection's host and organization.
type CloneOptions ¶
CloneOptions configures Clone. An empty Ref is the remote HEAD; Depth 0 fetches full history.
type Commit ¶
type Commit struct {
SHA string
ShortSHA string
Subject string
Body string
Author Signature
Date time.Time
Parents []string
Files []string
}
Commit is one commit as Cache.Log reads it. Files are the paths it changed against its first parent; a merge commit (len(Parents) > 1) lists none.
type CommitOptions ¶
CommitOptions configures Worktree.Commit.
type Comparison ¶
Comparison is how head relates to base: Ahead commits on head not on base, Behind commits on base not on head, and their merge base.
type DirtyStats ¶
func (DirtyStats) Clean ¶
func (s DirtyStats) Clean() bool
type FileChange ¶
FileChange writes Content to Path, or deletes Path when Delete is set.
type InspectOptions ¶
InspectOptions carries the requested URL and ref, which a detached cache worktree cannot report itself.
type ListRepositoriesOptions ¶
type ListRepositoriesOptions struct {
Namespace string
}
ListRepositoriesOptions scopes ListRepositories. Namespace is a GitHub owner, GitLab group or Azure DevOps project; empty uses the connection's configured namespace, else every repository the credentials can see.
type LogOptions ¶
LogOptions configures Cache.Log. Limit > 0 keeps only the newest Limit commits; Paths keeps only the commits that touch them.
type Metadata ¶
type Metadata struct {
Repository string
Branch string
SHA string
Dirty DirtyStats
LastCommit LastCommit
}
type Provider ¶
type Provider interface {
// Type is the models.ConnectionType* of the provider.
Type() string
// Verify checks the credentials against the provider.
Verify(ctx context.Context) error
ListRepositories(ctx context.Context, options ListRepositoriesOptions) ([]RepositoryInfo, error)
// SupportsPullRequests reports whether OpenPullRequest can succeed, so a
// change can be refused before anything is pushed.
SupportsPullRequests() bool
OpenPullRequest(ctx context.Context, repo RepoURL, request PullRequestRequest) (PullRequest, error)
GetPullRequest(ctx context.Context, repo RepoURL, number int) (PullRequest, error)
ClosePullRequest(ctx context.Context, repo RepoURL, number int) error
}
Provider is a git hosting provider's REST API.
type PullRequest ¶
type PullRequest struct {
Number int
URL string
State PullRequestState
Head string
Base string
Title string
}
PullRequest is an opened pull (or merge) request.
type PullRequestRequest ¶
type PullRequestRequest struct {
Title string
Body string
Head string
Base string
Draft bool
Labels []string
Assignees []string
Reviewers []string
AutoMerge bool
}
PullRequestRequest opens a pull request from Head into Base.
type PullRequestState ¶
type PullRequestState string
PullRequestState is a provider-neutral pull request state.
const ( PullRequestOpen PullRequestState = "open" PullRequestClosed PullRequestState = "closed" PullRequestMerged PullRequestState = "merged" )
type PushOptions ¶
type PushOptions struct {
Branch string
RemoteBranch string
Tags []string
Force bool
ExpectedSHA string
ServerOptions []string
}
PushOptions configures Worktree.Push. Branch is a local branch pushed to RemoteBranch (default: the same name). A push never overwrites history unless Force is set, and then only with --force-with-lease against ExpectedSHA. ServerOptions are sent as git push options (-o).
type Ref ¶
Ref is a remote ref resolved to a commit. For an annotated tag SHA is the peeled commit and Annotated is set.
func Clone ¶
func Clone(ctx context.Context, repo *Repository, options CloneOptions) (Ref, error)
Clone puts ref of repo into Dir: init (or reuse an existing checkout), fetch exactly that ref, and check it out (on a local branch of the same name for branches, detached otherwise). It returns the ref with SHA set to the commit checked out.
type RemoteRefs ¶
RemoteRefs are the branches and tags a remote advertises, plus its HEAD.
func (RemoteRefs) Resolve ¶
func (r RemoteRefs) Resolve(name string) (Ref, error)
Resolve finds name as, in order: a full ref, HEAD, a branch, a tag (a name that is both a branch and a tag is ambiguous), then a 40-char SHA. An empty name is HEAD. Short SHAs are refused: they cannot be fetched by name.
type RepoURL ¶
type RepoURL struct {
Clone string
Scheme string
Host string
Path string
Provider string
// Owner is the GitHub owner, the GitLab group path (nested groups joined
// with "/"), or the Azure DevOps organization.
Owner string
// Project is the Azure DevOps project; empty elsewhere.
Project string
Name string
// Ref and Depth come from the ?ref= and ?depth= query parameters.
Ref string
Depth *int
User string
}
RepoURL is a parsed repository URL. Clone is what git is given: the URL without query, fragment or password.
func ParseRepoURL ¶
ParseRepoURL parses an HTTP(S), SSH, scp-style, git://, file:// URL or a local path. providerHint (a models.ConnectionType* git provider) overrides host-based provider detection, e.g. for a self-hosted GitLab.
type Repository ¶
type Repository struct {
URL RepoURL
Auth Auth
// Provider opens pull requests; nil or a generic git provider has none.
Provider Provider
}
Repository is one remote repository with the credentials to reach it and, when it is hosted on a supported provider, that provider's API.
func NewRepository ¶
func NewRepository(options RepositoryOptions) (*Repository, error)
NewRepository parses the URL and validates the credentials.
func (*Repository) LsRemote ¶
func (r *Repository) LsRemote(ctx context.Context) (RemoteRefs, error)
LsRemote lists the remote's refs with go-git against in-memory storage; TLS settings are passed per call.
func (*Repository) OpenPullRequest ¶
func (r *Repository) OpenPullRequest(ctx context.Context, request PullRequestRequest) (PullRequest, error)
OpenPullRequest opens a pull request from the existing remote branch Head into Base, pushing nothing: a whole-branch promotion. Both must be branches on the remote. A provider without pull requests returns ErrPullRequestsUnsupported before the remote is listed.
type RepositoryInfo ¶
type RepositoryInfo struct {
Name string
FullName string
Namespace string
CloneURL string
SSHURL string
WebURL string
DefaultBranch string
Private bool
Archived bool
}
RepositoryInfo is one repository as the provider lists it.
type RepositoryOptions ¶
type RepositoryOptions struct {
URL string
Auth Auth
Provider Provider
// ProviderType is the URL parsing hint (models.ConnectionType*); it
// defaults to Provider.Type().
ProviderType string
}
RepositoryOptions configures NewRepository.
type Signature ¶
Signature is a commit author or tag tagger.
func DefaultIdentity ¶
DefaultIdentity reads user.name and user.email from the calling process's own global git config, as the git binary resolves it from the process environment (HOME, XDG_CONFIG_HOME, GIT_CONFIG_GLOBAL). Every other command in this package runs with the global config isolated (baseEnv); this is the one read of it, so a server can author a commit as itself. A missing name or email is ErrNoIdentity.
type TLS ¶
TLS customizes HTTPS verification: an extra CA bundle (added to the system pool), a client certificate for mutual TLS, or no verification at all. InsecureSkipVerify also disables SSH host-key checking.
type TagOptions ¶
TagOptions configures Worktree.CreateTag; an empty Message makes a lightweight tag.
type Worktree ¶
Worktree is a leased checkout from a Cache. Release it when done.
func (*Worktree) CreateBranch ¶
CreateBranch creates name at the current commit and switches to it.
func (*Worktree) CreateTag ¶
func (w *Worktree) CreateTag(ctx context.Context, options TagOptions) error
CreateTag tags the current commit, annotated when Message is set.
func (*Worktree) DeleteFile ¶
DeleteFile removes a worktree-relative file; a missing file is an error.
func (*Worktree) Push ¶
func (w *Worktree) Push(ctx context.Context, options PushOptions) error
Push pushes the branch and tags to the worktree's repository.