git

package
v0.3.0-20260819153324-... Latest Latest
Warning

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

Go to latest
Published: Aug 19, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

README

Git change provider

Reads change metadata — files, line counts, author — out of a git repository, for a remote that offers no API to ask.

The GitHub and Phabricator providers query a service that already knows what a change contains. This one derives it. It keeps its own copy of the remote and computes each change from the commits themselves, which makes a plain git remote a first-class source of change metadata with nothing in front of it: an internal host, a mirror, or a bare repository on disk.

What a change is measured against

A git:// change URI names a commit and the ref it lives on, and nothing else. A pull request carries a base; this does not, so the baseline has to be derived — and for a stack it cannot be the target branch.

A stack's changes are cut one from the next. Measuring every change against the target would report the second as containing the first, and any consumer that sums line counts across a batch would count them twice. So the first change in a request is measured from where it diverged from the target, and each one after it from where it diverged from the change before it. The order of the URIs is the stack order, and it is load-bearing.

A change that shares no history with what it claims to land on is an error, not a change that touches nothing.

Its own copy

Each service keeps its own copy of a queue's repository and configures its own remote for it, so this provider's copy is independent of anything a merger keeps. Where that copy fetches from is configuration: a bind-mounted bare repository and a remote host are the same code path, differing only in the URL.

The copy is bare. Nothing here checks anything out — the provider answers questions about commits and never produces one — so there is no working tree to leave dirty and no index to corrupt.

Git commands against one repository cannot safely interleave, so every provider sharing a copy shares its lock.

Provisioning happens once, at wiring time, rather than on first use: resolving a provider happens per message on the validate path, so a copy created there would put a clone inside a retry loop and hide an unreachable remote behind queue processing instead of failing the service that owns the configuration.

Authentication

The provider does not decide what a credential is. It takes an Auth implementation and calls it before each fetch; an integrator supplies one that reads an environment variable, calls a secrets manager, or mints a short-lived token, and only that implementation changes when the answer does. Auth is called per fetch rather than once so an expiring credential can be refreshed.

A nil Auth means the remote needs none. That covers a local path, and an SSH remote served by the host's own SSH configuration and agent — the environment a fetch needs to reach a remote is passed through, while the configuration that could change what a diff says is not.

Tests

Hermetic, against throwaway repositories, driving the Bazel-pinned git rather than the host's. The test that matters most is the three-step stack: reporting a stack cumulatively is wrong by default, never fails loudly, and shows up only as odd-looking scores.

Documentation

Overview

Package git provides a changeprovider.ChangeProvider that reads change metadata out of a git repository, for a remote that offers no API to ask.

Where the GitHub and Phabricator providers query a service that already knows what a change contains, this one derives it: it keeps its own copy of the remote and computes each change's files, line counts and author from the commits themselves. That makes a plain git remote — an internal host, a mirror, a bare repository on disk — a first-class source of change metadata with no service in front of it.

What a change is measured against

A git:// change URI names a commit and the ref it lives on, and nothing else. Unlike a pull request it carries no base, so the baseline has to be derived, and for a stack it cannot be the target branch: a stack's changes are cut one from the next, so measuring each against the target would report the second change as containing the first as well. Each change is therefore measured from where it diverged from the change before it, and only the first from the target. Callers get per-change numbers that sum, which is what any consumer aggregating over a batch depends on.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func New

New returns a changeprovider.ChangeProvider reading from repo.

func SetConfig

func SetConfig(ctx context.Context, path, key, value string) error

SetConfig writes one local configuration value into the repository at path.

Exported for an Auth implementation, which configures a repository from outside this package and would otherwise have to find and run git itself.

Types

type Auth

type Auth interface {
	// Apply configures repoPath so that git commands run against remoteURL from
	// inside it can authenticate.
	Apply(ctx context.Context, repoPath, remoteURL string) error
}

Auth prepares a local repository to authenticate to its remote.

This provider never decides what a credential is, where it comes from, or how long it lives. An integrator wires an implementation in — reading an environment variable, calling a secrets manager, minting a short-lived token — and only that implementation changes when the answer does.

Apply runs immediately before every fetch rather than once at provisioning, so an implementation backed by an expiring credential can refresh it. It must therefore be cheap and idempotent.

A nil Auth means the remote needs none, which covers a local path and an SSH remote served by the host's own SSH configuration and agent.

type Params

type Params struct {
	Config       changeprovider.Config
	Repo         *Repo
	Logger       *zap.SugaredLogger
	MetricsScope tally.Scope
}

Params carries what a provider needs. The Repo is built once per repository and shared by every queue reading it.

type Repo

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

Repo is one local copy of a remote, shared by every provider built over it.

Bare, because nothing here checks anything out: the copy answers questions about commits and never produces one. That also means no index and no working tree to leave dirty between operations.

func NewRepo

func NewRepo(cfg RepoConfig) (*Repo, error)

NewRepo returns a Repo for cfg, resolving the git binary. It touches no disk; Provision does that.

func (*Repo) Provision

func (r *Repo) Provision(ctx context.Context) error

Provision creates the copy if it is not already there and points it at the remote, leaving an existing copy's objects alone.

Callers run this at wiring time rather than on first use: resolving a provider happens once per message on the validate path, so a copy created there would put a clone inside a retry loop and hide a bad remote behind queue processing rather than failing the service that owns it.

type RepoConfig

type RepoConfig struct {
	// Git is the path to the git binary. Empty resolves through GIT_EXECUTABLE
	// and then PATH.
	Git string
	// Path is where this service keeps its own copy. It belongs to this service
	// alone: another service reading the same remote keeps its own.
	Path string
	// RemoteURL is where the copy fetches from — a URL or a local path.
	RemoteURL string
	// Remote is the name the copy records RemoteURL under.
	Remote string
	// Target is the branch a change's diff is measured against.
	Target string
	// Auth prepares the copy to reach RemoteURL. Nil when it needs nothing.
	Auth Auth
}

RepoConfig describes one local copy of a remote.

Jump to

Keyboard shortcuts

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