gitlocal

package
v0.2.0-rc.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 11, 2026 License: MIT Imports: 30 Imported by: 0

Documentation

Overview

Package gitlocal reads a pull request out of a local clone with git, for a run on the user's machine. It resolves the pull request's commits, computes the comparison and per-file patches that bundle.BuildDiffIndex reads, reads single files at a revision, and exports a commit's tree, or one path of it, as a tar archive.

The git invocations (diff flags, rename detection, status mapping, patch formats and size limits) are the ones the hosted service's git mirror uses, and they run in the same kind of repository: a private temporary bare repository with default configuration and no refs, which borrows the clone's objects through objects/info/alternates. So the clone's own configuration (diff order files, submodule display, big-file threshold), attributes (working tree, info/attributes), .gitmodules, replace refs, grafts and promisor remote do not reach a read, nor do the user's global and system configuration or GIT_ environment variables, and the same pull request produces the same comparison, patches and blobs locally and hosted. A partial clone missing objects a read needs is reported as ErrPartialClone; nothing is fetched on demand.

Only two operations run in the clone itself, with the user's configuration so credentials, URL rewrites and safe.directory work: finding the repository, and fetching a missing pull request commit, which lands under refs/baum/ and runs no hooks. Besides those, the refs that keep saved review versions' commits (RetainPrefix) are listed and written in the clone's common directory with the private repository's environment, no hooks and no reflog (UpdateRetained). The clone's branches, HEAD, index, working tree, configuration and worktree registrations are never changed, except by a Fix: the temporary worktree, refs and push of a fix a person confirmed (see Fix). An export is the hosted mirror's own git archive command run in the private repository, so it is byte for byte the archive the hosted service produces, including whatever attributes that archive applies: it still applies working-tree-encoding from the tree.

Every git process runs without a shell, under a timeout, with its output capped, and with prompts, pagers and editors disabled. Errors carry a short sanitised stderr snippet and never the command line.

It needs git 2.29 or newer (fetch --no-write-fetch-head and --no-auto-maintenance); Open refuses an older one.

Index

Constants

View Source
const (
	DefaultRemote          = "origin"
	DefaultMaxComparison   = 20 << 20
	DefaultMaxPatchBytes   = 20 << 20
	DefaultMaxBlobBytes    = 10 << 20
	DefaultMaxArchiveBytes = 1 << 30
	DefaultArchiveTimeout  = 10 * time.Minute
	DefaultCommandTimeout  = 2 * time.Minute
	DefaultFetchTimeout    = 10 * time.Minute
)

Defaults match the hosted git mirror's limits.

View Source
const DefaultCloneTimeout = 30 * time.Minute

DefaultCloneTimeout bounds one clone.

View Source
const DefaultRef = "refs/baum/default"

DefaultRef is the ref FetchBranch fetches a branch into.

View Source
const MaxCommits = 2000

MaxCommits is the most commits Commits lists. A pull request with more is reported as bundle.ErrOutputTooLarge rather than listed in part.

View Source
const MinGitVersion = "2.29"

MinGitVersion is the oldest git this package supports, as shown to users.

View Source
const RetainPrefix = "refs/baum/reviews/"

RetainPrefix is the namespace of the refs that keep the commits of saved review versions in the clone, so git gc never prunes them while their versions exist: RetainPrefix + <repository>/<pr>/<version>/<role>. <repository> is the store's lowercased repository key (owner/name, or host/owner/name) as one component, every byte but a-z, 0-9, '-' and '_' written as %XX; <role> is head, base or merge-base. Nothing else under refs/baum/ is ever written or removed through it.

Variables

View Source
var (

	// ErrInvalidCloneURL reports a clone URL that is not an HTTPS GitHub
	// repository URL built by GitHubCloneURL.
	ErrInvalidCloneURL = errors.New("not a github.com repository URL")
	// ErrCloneTargetExists reports a target directory that is not empty.
	ErrCloneTargetExists = errors.New("the target directory exists and is not empty")
)
View Source
var (

	// ErrInvalidInput reports an argument that is not a full commit ID, a
	// plain repository path or a usable ref name.
	ErrInvalidInput = errors.New("invalid git input")
	// ErrObjectNotFound reports a commit, merge base or file that does not
	// exist.
	// It satisfies errors.Is(err, bundle.ErrNotFound), the contract of
	// bundle.PinnedExport and bundle.BlobFetcher.
	ErrObjectNotFound error = objectNotFoundError{}
	// ErrPartialClone reports a partial clone that lacks objects a read
	// needs. They are not fetched on demand.
	// ErrClosed reports an operation on a Repo after Close.
	ErrClosed       = errors.New("git repository is closed")
	ErrPartialClone = errors.New("the partial clone is missing objects this pull request needs; use a full clone, or let git fetch them first, for example with git diff --stat <base> <head> in the clone")
)
View Source
var ErrAmbiguous = errors.New("ambiguous abbreviated commit id")

ErrAmbiguous reports an abbreviated commit id that matches more than one object.

View Source
var ErrHeadMoved = errors.New("the pull request's branch has moved on since the fix was started")

ErrHeadMoved reports a pull request branch that no longer points at the commit the fix was confirmed for.

View Source
var ErrMergeTreeUnsupported = errors.New("this git is too old to find merge conflicts: it needs git 2.38 or newer")

ErrMergeTreeUnsupported: this git cannot merge two commits without a working tree (git 2.38 added merge-tree --write-tree).

View Source
var ErrPushRejected = errors.New("the remote refused the push")

ErrPushRejected reports a push the remote refused, typically because the branch moved on while the fix ran.

View Source
var ErrURLRewritten = errors.New("your Git configuration changes the requested GitHub clone URL; remove the rewrite or choose a folder with an existing clone")

ErrURLRewritten reports a Git configuration (url.<base>.insteadOf) that changes the requested clone URL, including to another GitHub repository.

View Source
var RetainRoles = []string{"head", "base", "merge-base"}

RetainRoles are the commits of a version a retained ref can name.

Functions

func AddRenamed

func AddRenamed(from, to string)

AddRenamed records that GitHub forwards the repository "owner/name" from to "owner/name" to. Names forwarded to from now forward to to, and to itself is current again: renaming back undoes the earlier rename.

func CheckoutStamp added in v0.2.0

func CheckoutStamp(dir string) (stamp, root string, ok bool)

CheckoutStamp describes, without running git, what the checkout containing dir has checked out and how it is configured: what HEAD names, the commit of the branch it names, and the size and modification time of the common directory's config, where remotes and upstreams live. It changes with a checkout, a commit, a reset or a remote or upstream edit; "git status", which editors run all the time and which rewrites the index, leaves it alone. root is the checkout's top directory. ok is false when dir is not in a checkout this can read; callers then ask git.

func Clone

func Clone(ctx context.Context, opts CloneOptions) (err error)

Clone clones a GitHub repository into opts.Dir. The token travels only in git's private environment, as for a fetch (see withCredential), never in arguments, files or the error. No hook, template, fsmonitor, submodule or protocol other than HTTPS runs, and with a token certificate verification is forced on. It creates missing parent directories (0755) and the target itself atomically. A failed clone removes only what this call created: the target when it is empty or holds a .git git left, and the parents it made as far as they are empty. A target that already existed (empty) is never removed or emptied by baum; git cleans up its own partial work there.

func CommonDir added in v0.2.0

func CommonDir(ctx context.Context, dir string) (string, error)

CommonDir is the absolute, cleaned git directory shared by every working tree of the clone containing dir: the one holding the branch reflogs and the linked worktrees' bookkeeping.

func ConfigRemoteURLs added in v0.2.0

func ConfigRemoteURLs(commonDir string) []string

ConfigRemoteURLs lists the remote URLs written in the "config" file of a clone's common git directory, exactly as written. It reads that one file directly, without running git, so it follows no "include" and no "insteadOf" rewrite, and returns nothing when the file cannot be read.

func EarlierNames

func EarlierNames(repository string) []string

EarlierNames lists the recorded earlier names of the repository "owner/name", sorted.

func GitHubCloneURL

func GitHubCloneURL(repository string) (string, error)

GitHubCloneURL is the only form of URL Clone accepts: the HTTPS URL of a github.com repository named by a validated owner/name. The ".git" suffix makes the credential's scope a prefix of every request git sends.

func NormalizeRepository

func NormalizeRepository(remote string) (string, bool)

NormalizeRepository returns the lower-case "owner/name" a git remote URL names, ignoring its host (see RemoteRepository), and false for a URL that does not name one.

func ReadEnv

func ReadEnv(dir string) []string

ReadEnv is the environment for a read-only command run in the private repository at dir: no variable of the caller's that names a repository or changes how it is read, no system or global configuration or attributes, no replace refs, no lazy fetching, and a home directory with nothing in it.

func RemoteEarlierNames added in v0.2.0

func RemoteEarlierNames(commonDir string) []string

RemoteEarlierNames lists the recorded earlier names (see AddRenamed), as lower-case "owner/name"s, that remote URLs of the clone with the common git directory still use, sorted. Unlike RemoteRepository it does not map them to the current name.

func RemoteRepository

func RemoteRepository(remote string) (host, repository string, ok bool)

RemoteRepository returns the host and the lower-case "owner/name" a git remote URL names, and false for a URL that does not name one. It reads the https, http, ssh and git forms, the scp-like "user@host:owner/name" form, a ".git" suffix and trailing slashes, and ignores credentials and port. A local path or file URL names no hosted repository. The host is lower-case and may be an SSH alias rather than a real host name. On github.com, an earlier name GitHub forwards reads as the current one (RenamedTo).

func RemoveRenamed

func RemoveRenamed(from string)

RemoveRenamed forgets the earlier name from: remotes using it read as it again.

func RenamedTo

func RenamedTo(repository string) string

RenamedTo is the current lower-case "owner/name" of repository: the one GitHub forwards it to when it is a recorded earlier name, else repository.

func RetainVersionPrefix

func RetainVersionPrefix(repository string, number, version int) string

RetainVersionPrefix is the directory of a version's retained refs, ending in a slash.

func SameRepository

func SameRepository(remote, repository string) bool

SameRepository reports whether the remote URL names the repository "owner/name", ignoring case.

func SetBackground

func SetBackground(b *Background)

SetBackground applies b to every later fetch in this process; nil removes it.

func SupportedGitVersion

func SupportedGitVersion(version string) error

SupportedGitVersion fails when the output of "git version" names a git older than this package supports. An unrecognised string is let through.

Types

type AuthError

type AuthError struct {
	// Host is the remote's host, "" when its URL names none.
	Host string
	// SSH is true for an SSH remote, false for HTTPS.
	SSH bool
	Err error
}

AuthError is a fetch the remote refused because signing in needed an answer nobody can give: git runs with GIT_TERMINAL_PROMPT=0 and, in the daemon, SSH in batch mode, so a key passphrase or a password is never asked for. The user has to make the credential available without one.

func (*AuthError) Error

func (e *AuthError) Error() string

func (*AuthError) Unwrap

func (e *AuthError) Unwrap() error

type Background

type Background struct {
	// SSHAgent returns the SSH agent socket a fetch may use, or "" for none.
	// A socket that no longer exists must not be returned. It is asked only
	// for fetches from SSH remotes.
	SSHAgent func(ctx context.Context) string
}

Background makes fetches safe for a process nobody watches (the baum daemon): no command may wait for an answer. Git already runs with GIT_TERMINAL_PROMPT=0 and a timeout; with a Background, SSH never asks through SSH_ASKPASS, and when the user configured no ssh command (GIT_SSH_COMMAND, core.sshCommand or GIT_SSH) OpenSSH runs with BatchMode=yes, so a missing key or unknown host is a clear git error rather than a hang. A configured command is left alone: it may be plink or a wrapper that does not take OpenSSH options.

type Blob

type Blob struct {
	OID     string
	Mode    string
	Content []byte
}

Blob is a file read at a revision.

type ChangedFile

type ChangedFile struct {
	// Path is the file's path at the head (at the base for a deletion).
	Path string
	// OldPath is the path the file had at the base, when the range renamed it.
	OldPath string
	// Submodule is a path that is a submodule (a gitlink) on either side.
	Submodule bool
	// TypeChanged is a path whose type changed, a file to a symlink say.
	TypeChanged bool
}

ChangedFile is one file a range changes, as git lists it with rename detection.

type CloneOptions

type CloneOptions struct {
	// URL comes from GitHubCloneURL.
	URL string
	// Dir is the absolute target directory. It must not exist or be empty.
	Dir string
	// Token is the GitHub credential; empty clones anonymously.
	Token string
	// Timeout overrides DefaultCloneTimeout.
	Timeout time.Duration
}

CloneOptions describe one clone.

type Commit

type Commit struct {
	OID     string
	Parents []string
	// AuthorName and AuthorEmail are the author's identity as recorded.
	AuthorName  string
	AuthorEmail string
	// CommittedAt is the committer date in RFC 3339, as git prints %cI.
	CommittedAt string
	Subject     string
	// Trailers are the "Key: value" lines of the message's trailer block,
	// continuation lines unfolded.
	Trailers []string
	// Message is the whole commit message, as git prints %B without its final
	// newline. Entire-Checkpoint lines are read from all of it, not from the
	// trailer block alone (entirecheckpoint.ParseMessage).
	Message string
	// Files are the repository-relative paths the commit changed, both sides
	// of a rename or copy. A merge commit lists none.
	Files []string
}

Commit is one commit in a range, as the matcher and a reader need it.

type CommitChanges

type CommitChanges struct {
	OID string
	// Parent is the commit's first parent; "" for a root commit.
	Parent string
	// Message is the whole commit message, as git prints %B without its final
	// newline.
	Message string
	// Files are the changed files with rename detection (-M, -l1000), each with
	// its added and deleted line counts (nil for a binary file).
	Files []bundle.FileChange
}

CommitChanges is one commit's message and the files it changed against its first parent, or against the empty tree for a root commit.

type Error

type Error struct {
	Command string
	Stderr  string
	Err     error
}

Error is a failed git command. It names the subcommand and carries a bounded, sanitised stderr snippet, never the arguments or full output.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Unwrap

func (e *Error) Unwrap() error

type Fix

type Fix struct {

	// Head is the pull request's head commit the fix starts from; Base the
	// base branch's commit, empty when the base was not fetched.
	Head, Base string
	// contains filtered or unexported fields
}

A Fix is a temporary linked worktree of the clone in which an agent fixes a pull request, and the git around it: fetching the pull request's branch, starting a merge of its base, committing what the agent left, checking the result descends from the head it started from, and pushing it. It is the one place this package changes the clone: it adds a worktree registration and temporary refs under refs/baum/fix/, and Close removes both.

Every command runs with the user's configuration, as a fetch does, so identity, signing, credentials and URL rewrites work as they do for the user, but with core.hooksPath pointing at an empty directory outside the checkout, so no hook runs, from the clone or from the pull request. After the agent has run, commands name the worktree's git directory explicitly (GIT_DIR and GIT_WORK_TREE), so a .git file the agent rewrote is ignored.

func (*Fix) Changed

func (f *Fix) Changed(ctx context.Context, commit string) ([]string, error)

Changed lists the paths commit changes against the fix's starting head, a rename as its old and new paths.

func (*Fix) CheckSigning

func (f *Fix) CheckSigning(ctx context.Context) error

CheckSigning makes sure a commit in the checkout can be made at all when the user's configuration signs commits (commit.gpgSign): it creates a signed commit object of HEAD's tree that nothing refers to, so a key that needs a passphrase prompt or an agent that is not running fails now, before the agent has run, instead of at the commit.

func (*Fix) Close

func (f *Fix) Close() error

Close removes the checkout, its registration and the temporary refs, and then the fix's directory. It runs even after the context of the fix ended, and is safe to call more than once.

func (*Fix) Commit

func (f *Fix) Commit(ctx context.Context, message string) (string, error)

Commit stages everything the agent left in the checkout, ignored files excepted, and commits it with message, completing a merge in progress. With nothing to commit it changes nothing. It returns the checkout's HEAD.

func (*Fix) Descends

func (f *Fix) Descends(ctx context.Context, commit string) (bool, error)

Descends reports whether commit has the fix's starting head among its ancestors (or is it): a fix may only add commits on top of it.

func (*Fix) Dir

func (f *Fix) Dir() string

Dir is the checkout the agent works in.

func (*Fix) HEAD

func (f *Fix) HEAD(ctx context.Context) (string, error)

HEAD is the commit the checkout is at.

func (*Fix) Merge

func (f *Fix) Merge(ctx context.Context, message string) ([]string, error)

Merge starts merging the base branch into the checkout with message. It returns the conflicted paths when git stopped at conflicts, leaving the merge in progress for the agent to resolve, and none when the merge committed or there was nothing to merge.

func (*Fix) Push

func (f *Fix) Push(ctx context.Context, commit string) error

Push updates the pull request's branch to commit with a plain push to the Repo's remote, never forced: the remote refuses it (ErrPushRejected) unless the branch still points at an ancestor of commit. It uses the user's own git credentials (credential helper, SSH agent), never baum's GitHub token, and pushes nothing else: no tags, no submodules.

type FixOptions

type FixOptions struct {
	// Dir is a new directory outside the clone that holds the fix: its
	// checkout and its empty hooks directory. Close removes it.
	Dir string
	// ID names the temporary refs, refs/baum/fix/<ID>/head and .../base.
	ID string
	// HeadRef is the pull request's branch and HeadSHA the commit it must
	// still point at.
	HeadRef, HeadSHA string
	// BaseRef, when set, is the base branch, fetched for a merge.
	BaseRef string
	// Env are the user's agent variables ("NAME=value", config.Config.AgentEnv)
	// for every command in the checkout and the push, set over the user's
	// environment; one that would redirect git to another repository is
	// left out.
	Env []string
}

FixOptions say where a Fix starts.

type Light

type Light struct {
	// Top is the working tree's top-level directory and CommonDir the
	// repository directory shared by its linked worktrees, both absolute and
	// cleaned.
	Top, CommonDir string
	// Remotes are the configured remote URLs, as written, in configuration
	// order.
	Remotes []Remote
}

Light is what Probe learns about a directory: the working tree it belongs to and the remote URLs configured there.

func Probe

func Probe(ctx context.Context, dir string, timeout time.Duration) (Light, error)

Probe finds the working tree containing dir and reads its remote URLs with two git commands, each bounded by timeout. Unlike Open it creates no private repository, so a scan can afford it for many directories. It reads configuration only and never contacts a remote.

type Options

type Options struct {
	// Remote is fetched from when a pull request commit is missing.
	Remote string
	// TempDir holds the private repository; empty uses the
	// system temporary directory. It must be outside the clone.
	TempDir string
	// MaxComparisonBytes caps the output of each comparison command.
	MaxComparisonBytes int64
	// MaxPatchBytes caps one file's patch.
	MaxPatchBytes int64
	// MaxBlobBytes caps one file read by Blob.
	MaxBlobBytes int64
	// MaxArchiveBytes caps the tar archive of one export.
	MaxArchiveBytes int64
	// ArchiveTimeout bounds one export from start to Close, however slowly
	// the archive is read.
	ArchiveTimeout time.Duration
	// CommandTimeout bounds each local git command; FetchTimeout a fetch.
	CommandTimeout time.Duration
	FetchTimeout   time.Duration
}

Options configure a Repo. The zero value uses the defaults.

type Pull

type Pull struct {
	BaseOID      string
	HeadOID      string
	MergeBaseOID string
}

Pull is a pull request whose commits all exist locally.

type PullRequest

type PullRequest struct {
	Number  int
	BaseSHA string
	HeadSHA string
	// BaseRef is the base branch name (for example "main"); it is fetched
	// only when the base commit is missing locally.
	BaseRef string
	// Remote is the remote a missing commit is fetched from; "" is the
	// Repo's Options.Remote.
	Remote string
}

PullRequest identifies a pull request and its commits as GitHub reports them.

type RefUpdate

type RefUpdate struct {
	Name, Old, New string
}

RefUpdate is one change of a retained ref. An empty Old creates the ref, failing if it exists; an empty New deletes it; both set move it, and the change fails unless the ref still names Old.

type Remote

type Remote struct {
	Name string
	URL  string
}

Remote is a remote of the clone and one URL it is configured with.

type Repo

type Repo struct {
	// contains filtered or unexported fields
}

Repo is a local clone. Close removes its private repository.

func Open

func Open(ctx context.Context, dir string, opts Options) (*Repo, error)

Open checks that dir is inside a git repository with a working tree and sets up the private repository reads run in. The caller must Close the result.

func (*Repo) AuthoredCommits

func (r *Repo) AuthoredCommits(ctx context.Context, since time.Time, email string) ([]Commit, error)

AuthoredCommits lists the commits on the clone's local branches that email authored and committed at or after since, newest first and each once, without their files. Merge commits are left out: they record a merge, not work. It reads only what the clone holds and lists at most MaxCommits; more is errOutputTooLarge, as Commits reports it.

func (*Repo) Blob

func (r *Repo) Blob(ctx context.Context, commitOID, path string) ([]byte, error)

Blob is ReadBlob's content, for bundle.BlobFetcher (LoadReviewConfig): a missing file is an error that satisfies errors.Is(err, bundle.ErrNotFound).

func (*Repo) BranchUpstream

func (r *Repo) BranchUpstream(ctx context.Context, branch string) (remote, merge string, err error)

BranchUpstream is the upstream configured for a local branch: the branch.<name>.remote (a remote name, a URL, or "." for the clone itself) and branch.<name>.merge (the ref fetched, such as refs/heads/main), each "" when unset.

func (*Repo) ChangedFiles

func (r *Repo) ChangedFiles(ctx context.Context, baseOID, headOID string) (map[string]ChangedFile, error)

ChangedFiles lists the files that differ between two commits, with the rename detection a pull request page uses (-M, with -l1000 bounding the work), by the new path, or by the path a deleted file had. It reads only objects the clone holds and never fetches: objects it lacks are ErrObjectNotFound, or ErrPartialClone in a partial clone. A list over MaxComparisonBytes is bundle.ErrOutputTooLarge.

func (*Repo) Close

func (r *Repo) Close() error

Close cancels exports still open, waits for operations in flight, then removes the private repository. Later operations fail with ErrClosed. It is safe to call more than once.

func (*Repo) CommitChanges

func (r *Repo) CommitChanges(ctx context.Context, oid string) (CommitChanges, error)

CommitChanges reads commit oid's message and the files it changed, diffed against its first parent (a merge commit too) or, for a root commit, the empty tree. It reads only objects the clone holds and never fetches: objects it lacks are ErrObjectNotFound, or ErrPartialClone in a partial clone. A message over 1 MiB or a file list over MaxComparisonBytes is bundle.ErrOutputTooLarge.

func (*Repo) CommitFilePatch

func (r *Repo) CommitFilePatch(ctx context.Context, oid, parent string, maxBytes int64, paths ...string) ([]byte, error)

CommitFilePatch is the diff of one file, or of a renamed file's old and new path, in commit oid against parent (its first parent; "" for a root commit, which is diffed against the empty tree). It is read as FilePatch reads a range's file: three lines of context, the myers algorithm, -M and -l1000 with a literal pathspec for each path, no external diff driver, text conversion or colour, and git's file header lines included. The patch is at most maxBytes (MaxPatchBytes when zero), else bundle.ErrOutputTooLarge.

func (*Repo) Commits

func (r *Repo) Commits(ctx context.Context, base, head string) ([]Commit, error)

Commits lists the commits reachable from head and not from base, oldest first, the way a pull request lists its commits. It reads only objects already in the clone and never fetches: a base or head that is absent, including one a partial clone has not downloaded, is ErrObjectNotFound.

func (*Repo) CommonDir

func (r *Repo) CommonDir() string

CommonDir is the shared git directory, identical across linked worktrees.

func (*Repo) Compare

func (r *Repo) Compare(ctx context.Context, base, head string) (bundle.Comparison, error)

Compare lists the files that differ between the merge base of base and head and head, like the hosted comparison in its merge-base mode. Rename detection is on (-M, limit 1000). Each file carries added and deleted line counts (nil for binary files) and a patch hash that ignores line numbers, index lines and file names. The result's BaseOID is the merge base.

func (*Repo) Conflicts

func (r *Repo) Conflicts(ctx context.Context, remote string, number int, headSHA, baseRef string) (baseSHA string, paths []string, err error)

Conflicts merges the pull request's head into the tip of its base branch in memory, as GitHub's merge would, and lists the files that conflict. The head is fetched from refs/pull/<N>/head when missing, and the base branch tip always, as it moves. remote "" is Options.Remote. It returns the base commit the answer is for, and no paths when the merge is clean. Nothing is written to the clone's working tree or branches.

func (*Repo) CurrentBranch

func (r *Repo) CurrentBranch(ctx context.Context) (string, error)

CurrentBranch is the name of the branch HEAD is on, without "refs/heads/", or "" when HEAD is detached.

func (*Repo) Dir

func (r *Repo) Dir() string

Dir is the clone's top-level directory.

func (*Repo) ExportCommit

func (r *Repo) ExportCommit(ctx context.Context, commitOID string) (io.ReadCloser, error)

ExportCommit streams the full tree of commit commitOID as a tar archive, built exactly as the hosted git mirror builds its export:

git archive --format=tar --worktree-attributes <commitOID>

run in the private bare repository, so the clone's configuration, attributes and .gitmodules do not reach it, and the archive is byte for byte the hosted one, including the attributes it applies: it still applies working-tree-encoding from the tree. The archive streams from git's stdout; it is never buffered whole. Reading more than Options.MaxArchiveBytes kills git and fails with bundle.ErrOutputTooLarge, and Options.ArchiveTimeout bounds the whole export. A git that fails part way is an error from Read, never a silently truncated archive. An unknown commit is ErrObjectNotFound.

The caller must Close the result, which kills git if it is still running and waits for it. Repo.Close cancels and reaps exports still open.

func (*Repo) ExportCommitPath

func (r *Repo) ExportCommitPath(ctx context.Context, commitOID, path string) (io.ReadCloser, error)

ExportCommitPath is ExportCommit for one path of the commit, a file or a directory, as the hosted mirror does:

git archive --format=tar --worktree-attributes <commitOID> -- :(literal)<path>

A path the commit does not hold is ErrObjectNotFound.

func (*Repo) FetchBranch

func (r *Repo) FetchBranch(ctx context.Context, remote, branch string) (string, error)

FetchBranch fetches branch from remote into DefaultRef and returns the commit it points at.

func (*Repo) FetchPulls

func (r *Repo) FetchPulls(ctx context.Context, remote string, prs []PullRequest) error

FetchPulls fetches what the clone lacks of the pull requests' commits, as ResolvePull fetches them: a missing head from refs/pull/<N>/head into refs/baum/pull/<N>/head and a missing base by its id into refs/baum/pull/<N>/base of the first pull request it is the base of, up to 100 refspecs in one fetch. A clone with every commit makes no network call, and a pull request without a valid number and ids is skipped. When a fetch fails, the remote is asked for its HEAD: out of reach, the error is returned at once; answering, it lacks some of the refs, and those of the failed fetch are fetched one at a time so they keep out no other. A pull request whose commits are still missing is left for the caller to find so. A deadline or cancellation is returned at once.

func (*Repo) FilePatch

func (r *Repo) FilePatch(ctx context.Context, baseOID, headOID string, maxBytes int64, paths ...string) ([]byte, error)

FilePatch is the diff of one file, or of a renamed file's old and new path, between two commits, read the way a pull request page reads it: -M and -l1000 with a literal pathspec for each path, so a rename is detected when both names are given. It is git's own output, file header lines included, with three lines of context and the myers algorithm fixed rather than read from configuration, and no external diff driver, text conversion or colour. A binary file has no "@@" hunks. The patch is at most maxBytes (MaxPatchBytes when zero), else bundle.ErrOutputTooLarge. It reads only objects the clone holds and never fetches: objects it lacks are ErrObjectNotFound, or ErrPartialClone in a partial clone.

func (*Repo) HasCommits

func (r *Repo) HasCommits(ctx context.Context, oids []string) (map[string]bool, error)

HasCommits reports which of oids are commits the clone holds. Nothing is fetched: a commit a partial clone lacks is reported missing.

func (*Repo) HeadCommit

func (r *Repo) HeadCommit(ctx context.Context) (string, error)

HeadCommit is the id of the commit HEAD names, whether it is on a branch or detached; "" when HEAD is unborn.

func (*Repo) PatchFetcher

func (r *Repo) PatchFetcher(ctx context.Context, comparison bundle.Comparison) bundle.PatchFetcher

PatchFetcher reads one file's text patch for a comparison Compare returned. A binary change reads as git's "Binary files ... differ" line. A patch over MaxPatchBytes is bundle.ErrOutputTooLarge; a git failure on the file itself (exit status, not cancellation or timeout) is bundle.ErrPatchUnreadable, except that missing objects of a partial clone are ErrPartialClone.

func (*Repo) PrivateDir

func (r *Repo) PrivateDir() string

PrivateDir is the private repository reads run in: a bare repository with no remote and no refs that borrows the clone's objects. A caller that runs its own read-only git commands against objects points them here, so git has no remote to fetch a missing object from. It exists until Close.

func (*Repo) ReadBlob

func (r *Repo) ReadBlob(ctx context.Context, rev, path string) (Blob, error)

ReadBlob reads the blob at path in commit rev: a regular or executable file, or a symbolic link (its content is the link target), as the hosted mirror reads one. A missing path, or a directory or submodule, is ErrObjectNotFound; a file over MaxBlobBytes is bundle.ErrOutputTooLarge. A timeout or cancellation is returned as the context's error, where the hosted mirror reports ErrObjectNotFound, so a slow read is not mistaken for a missing file.

func (*Repo) RemoteFetchURL

func (r *Repo) RemoteFetchURL(ctx context.Context, name string) (string, error)

RemoteFetchURL is the URL git fetches the named remote from, with the user's url.<base>.insteadOf rewrites applied, or "" when the clone has no such remote. It reads configuration only and never contacts the remote.

func (*Repo) RemotePushURL

func (r *Repo) RemotePushURL(ctx context.Context, name string) (string, error)

RemotePushURL is the URL git pushes to the named remote with: its pushurl when it has one, else its fetch URL, with url.<base>.insteadOf and pushInsteadOf rewrites applied. It is "" when the clone has no such remote. It reads configuration only and never contacts the remote.

func (*Repo) RemotePushURLs

func (r *Repo) RemotePushURLs(ctx context.Context, name string) (configured, effective []string, err error)

RemotePushURLs lists every URL git pushes the named remote to, in the order git pushes them: as configured (its pushurl entries, else its url entries) and, at the same index, as git rewrites it with url.<base>.pushInsteadOf and insteadOf. Both are empty when the clone has no such remote. It reads configuration only and never contacts the remote.

func (*Repo) RemoteURL

func (r *Repo) RemoteURL(ctx context.Context) (string, error)

RemoteURL is the configured URL of the clone's remote exactly as written in its configuration, or "" when the remote is not configured. It reads configuration only and never contacts the remote.

func (*Repo) RemoteURLs

func (r *Repo) RemoteURLs(ctx context.Context) ([]Remote, error)

RemoteURLs lists every configured remote URL of the clone exactly as written in its configuration, in configuration order; a remote with several URLs appears once for each. It reads configuration only and never contacts a remote.

func (*Repo) ResolveCommit

func (r *Repo) ResolveCommit(ctx context.Context, id string) (string, error)

ResolveCommit expands a full or abbreviated commit id (7 to 64 lower-case hex digits) to the full id of a commit the clone holds. It never fetches: an id the clone lacks is ErrObjectNotFound, one that matches several objects is ErrAmbiguous, and anything that is not hex is ErrInvalidInput.

func (*Repo) ResolvePull

func (r *Repo) ResolvePull(ctx context.Context, pr PullRequest) (Pull, error)

ResolvePull makes sure the base and head commits exist locally and computes the merge base. A missing head is fetched from refs/pull/<N>/head and a missing base from the base branch, into refs/baum/pull/<N>/; a commit that is still missing (the pull request or branch has moved on since the commit was recorded) is then fetched by its ID, as the hosted mirror recovers one. A clone with both commits makes no network call.

func (*Repo) RetainedRefs

func (r *Repo) RetainedRefs(ctx context.Context, prefix string) (Retained, error)

RetainedRefs lists the clone's refs under prefix, which must be within RetainPrefix.

func (*Repo) StartFix

func (r *Repo) StartFix(ctx context.Context, opts FixOptions) (*Fix, error)

StartFix fetches the pull request's branch (and base branch) from the Repo's remote into refs/baum/fix/<ID>/, refuses a branch that moved on from HeadSHA, and checks HeadSHA out, detached, in a new linked worktree at <Dir>/checkout. The caller must Close the result, also on failure later on.

func (*Repo) TextPatch

func (r *Repo) TextPatch(ctx context.Context, baseOID, headOID string, paths ...string) ([]byte, error)

TextPatch diffs one file (or a rename's old and new path) between two commits exactly as given, without computing a merge base.

func (*Repo) UpdateRetained

func (r *Repo) UpdateRetained(ctx context.Context, updates []RefUpdate) error

UpdateRetained applies updates to the clone's retained refs as one transaction: all of them, or none when any fails. Every name must be one ParseRetainRef reads; git runs with no hooks and none of the user's global configuration, and acts on the refs themselves (--no-deref): a symbolic ref is never followed to the ref it names.

func (*Repo) UserEmail

func (r *Repo) UserEmail(ctx context.Context) (string, error)

UserEmail is the clone's configured user.email, as git would record it on a commit made there, or "" when none is configured. It reads configuration only.

func (*Repo) UserName

func (r *Repo) UserName(ctx context.Context) (string, error)

UserName is the clone's configured user.name, "" when none is configured. It reads configuration only.

func (*Repo) ValidationCommit

func (r *Repo) ValidationCommit(ctx context.Context, id string) (commitlabel.Commit, error)

ValidationCommit reads a unique commit and its tree without fetching. The object-prefix lookup avoids Git's ref-name DWIM rules for hex-looking refs.

type Retained

type Retained struct {
	// Refs are the plain refs, by name, with the commit each names.
	Refs map[string]string
	// Symbolic are the symbolic refs, which baum never writes: they are
	// foreign, and must be neither moved nor deleted.
	Symbolic map[string]bool
}

Retained are the clone's refs under a prefix of RetainPrefix.

type RetainedName

type RetainedName struct {
	Repository string
	Number     int
	Version    int
	Role       string
}

RetainedName is what a retained ref's name says.

func ParseRetainRef

func ParseRetainRef(name string) (RetainedName, bool)

ParseRetainRef reads a retained ref's name. Only a name RetainedName.Ref writes exactly parses; any other, under RetainPrefix or not, does not.

func (RetainedName) Ref

func (n RetainedName) Ref() string

Ref is the ref's full name.

type Worktree

type Worktree struct {
	// Path is the working tree's top-level directory, cleaned and in this
	// platform's form. It may no longer exist.
	Path string
	// Main is the clone's original working tree, the one that holds the
	// repository; the others are linked worktrees.
	Main bool
	// Prunable is a linked worktree whose directory is gone; git still
	// remembers it until the next prune.
	Prunable bool
}

Worktree is one working tree of a clone.

func Worktrees

func Worktrees(ctx context.Context, dir string) ([]Worktree, error)

Worktrees lists every working tree of the clone containing dir, the main one first, linked worktrees after it, including ones whose directory has been deleted. A bare main repository has no working tree and is left out.

It reads the clone's worktree bookkeeping and nothing else: finding the repository is the one command run with the user's configuration, as Open does, and git worktree list then runs against the repository directory with the read environment (no system or global configuration, no GIT_ variable of the caller's, a home directory with nothing in it). Listing worktrees reads no object, runs no hook, filter or helper, and never contacts a remote. It needs git 2.36 or newer, for -z.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL