gitcli

package
v0.4.7 Latest Latest
Warning

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

Go to latest
Published: Jun 12, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package gitcli wraps the system git binary: clone, log --numstat --name-status, and per-path log.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client struct {
	Bin         string
	Log         *slog.Logger
	GitHubToken string
}

Client is a thin wrapper around the system git binary. If GitHubToken is set, Clone and LsRemote authenticate to github.com using that token via an inline credential helper: the token value is passed in the subprocess env as XRAY_GIT_TOKEN and read by the helper at credential-fill time, never appearing in argv or in xray's debug log. Other methods operate on a local clone and do not consult the token. When GitHubToken is empty the client degrades to ambient git auth (SSH, credential helper, gh CLI, etc.) — the historical behaviour.

func (*Client) CatFileBatch

func (c *Client) CatFileBatch(ctx context.Context, clonePath string, refs []string, fn func(ref string, content []byte)) error

CatFileBatch calls git cat-file --batch for all refs (each "<sha>:<path>") and invokes fn once per ref in input order. fn receives the raw blob content, or nil when the object is absent or not a blob. Content per object is capped at maxShowFileBytes. One subprocess handles all refs.

func (*Client) CheckAncestors

func (c *Client) CheckAncestors(ctx context.Context, clonePath string, candidates []string, descendant string) (map[string]bool, error)

CheckAncestors reports for each candidate OID whether it is an ancestor of (or equal to) descendant. One `git log` call replaces N separate `git merge-base --is-ancestor` subprocess spawns — the key saving for repos with many single-parent-merge PRs (ADR-021 rebase/squash detection).

`git log --pretty=%H <cand1> <cand2> ... ^<descendant>` outputs every commit reachable from any candidate but NOT from descendant. A candidate that appears in the output is therefore NOT an ancestor of descendant; one that is absent IS an ancestor (all its reachable commits are already reachable from descendant).

func (*Client) CheckMailmap

func (c *Client) CheckMailmap(ctx context.Context, clonePath, name, email string) (string, string, error)

CheckMailmap shells to `git check-mailmap` to resolve a single identity. Used by the test fixture to validate parser output against git itself; not called on the production hot path because parsing per repo and resolving in memory is orders of magnitude cheaper than forking per commit.

func (*Client) Clone

func (c *Client) Clone(ctx context.Context, slug, dest string, shallowSince time.Time) error

Clone shallow-clones slug ("owner/repo") into dest. dest must not exist. The shallow window starts at shallowSince - 30d to keep rename history (commit_files prev_path tracking) coherent at the window boundary.

func (*Client) DefaultBranch

func (c *Client) DefaultBranch(ctx context.Context, clonePath string) (string, error)

DefaultBranch resolves the default branch of clonePath from origin/HEAD's symbolic ref. Falls back to "main" on failure.

func (*Client) HeadSHA

func (c *Client) HeadSHA(ctx context.Context, clonePath string) (string, error)

HeadSHA returns the SHA of HEAD in clonePath.

func (*Client) IsAncestor

func (c *Client) IsAncestor(ctx context.Context, clonePath, ancestor, descendant string) (bool, error)

IsAncestor reports whether `ancestor` is an ancestor of (or equal to) `descendant` in clonePath. Implemented via `git merge-base --is-ancestor`, which exits 0 when true and 1 when false; any other exit code is surfaced as an error. Added to support ADR-021's merge-method derivation in the github connector: rebase vs squash is the reachability of the PR's head commits from the merge commit.

func (*Client) LogNumstat

func (c *Client) LogNumstat(ctx context.Context, clonePath string, since, until time.Time, branch string) ([]CommitRecord, error)

LogNumstat streams parsed commits + per-file numstat in the window. branch is the ref to walk; if empty, HEAD is used.

func (*Client) LogPath

func (c *Client) LogPath(ctx context.Context, clonePath, path string) (string, time.Time, time.Time, error)

LogPath returns the first-seen commit/time and last-modified time for a single path. The clone must contain enough history to reach the path's introducing commit; for working-tree artifacts this is satisfied by the shallowSince - 30d clone window.

func (*Client) LsFiles

func (c *Client) LsFiles(ctx context.Context, clonePath string) ([]string, error)

LsFiles returns the list of files tracked at HEAD in the cloned repo (git ls-files --cached). Paths are relative to the repo root and use forward slashes. .gitignore is honoured naturally by git's index.

func (*Client) LsRemote

func (c *Client) LsRemote(ctx context.Context, slug string) error

LsRemote verifies clone access without cloning.

func (*Client) ReadMailmap

func (c *Client) ReadMailmap(_ context.Context, clonePath string) (*Mailmap, error)

ReadMailmap reads the repo's top-level .mailmap (the only location git supports without explicit config), returning ParseMailmap'd state. Missing file returns a zero-value Mailmap with Applied() == false and nil error; any other read or parse error is returned.

func (*Client) RemoteBranches

func (c *Client) RemoteBranches(ctx context.Context, clonePath string) ([]RemoteBranch, error)

RemoteBranches enumerates origin's branches from the local clone, returning name, tip SHA, and committer date for each. `origin/HEAD` is filtered out. Replaces a REST ListBranches round-trip; the data is already in the clone after fetch.

func (*Client) ShowFile

func (c *Client) ShowFile(ctx context.Context, clonePath, sha, path string) ([]byte, error)

ShowFile streams the contents of path at sha from clonePath, equivalent to `git show <sha>:<path>`. Returns os.ErrNotExist if the path is absent at that revision (typical for delete entries). Output is capped at maxShowFileBytes so a runaway blob can't OOM the extractor.

type CommitRecord

type CommitRecord struct {
	SHA             string
	AuthorHandle    string
	AuthorEmail     string
	CommitterHandle string
	CommitterEmail  string
	AuthoredAt      time.Time
	CommittedAt     time.Time
	Subject         string
	Body            string
	ParentSHAs      []string
	Files           []FileChange
}

CommitRecord is a parsed git log entry covering everything the github connector needs to populate commits, commit_files, and commit_coauthors. Body is exposed so the connector can parse trailers and structured signals; the connector discards it afterwards.

type FileChange

type FileChange struct {
	Path       string
	PrevPath   string
	ChangeType string // A | M | D | R | C
	Additions  int
	Deletions  int
}

FileChange is a single per-file numstat row attached to a commit.

type Mailmap

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

Mailmap resolves alias (name, email) pairs to canonical (name, email). It mirrors git's resolution semantics as implemented by `git check-mailmap`: the four supported line shapes are

Proper Name <commit@email.xx>
<proper@email.xx> <commit@email.xx>
Proper Name <proper@email.xx> <commit@email.xx>
Proper Name <proper@email.xx> Commit Name <commit@email.xx>

Comment ('#') and blank lines are skipped. Lookup keys are the (name, email) pair from the commit side; entries that didn't supply a commit-name match any commit-name with the same commit-email.

A zero-value Mailmap returns inputs unchanged from Resolve and reports Applied() == false — the run.go aggregator uses Applied() to populate manifest.mailmap_applied.

func ParseMailmap

func ParseMailmap(raw []byte) (*Mailmap, error)

ParseMailmap parses raw .mailmap bytes into a Mailmap. A zero-entry file (only comments / blanks) returns an empty, non-applied Mailmap.

func (*Mailmap) Applied

func (m *Mailmap) Applied() bool

Applied reports whether a non-empty .mailmap was parsed into this Mailmap. An empty file (no entries) reads as false: there's nothing to canonicalise and assay's mailmap_applied flag should reflect that.

func (*Mailmap) Resolve

func (m *Mailmap) Resolve(name, email string) (string, string)

Resolve returns the canonical (name, email) for a commit identity. The match preference matches git: pair match (commit-name + commit-email) beats email-only match.

type RemoteBranch

type RemoteBranch struct {
	Name          string
	LastCommitSHA string
	LastCommitAt  time.Time
}

RemoteBranch is one row of `git for-each-ref refs/remotes/origin/`. Name is the short ref with the `origin/` prefix stripped.

Jump to

Keyboard shortcuts

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