gitmeta

package
v0.13.0 Latest Latest
Warning

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

Go to latest
Published: Sep 14, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

permalink.go derives the pieces GetPermalink (D-06) needs to construct a GitHub blob URL pinned to the indexed commit: the remote's owner/repo (D-08: GitHub only, everything else is an explicit no-link) and whether that commit is observably present on a remote-tracking branch (D-07: the check is sound in one direction only, so "could not check" and "checked, not there" must never collapse to the same value). D-09's line-range anchor is assembled by the caller (internal/uiserver/permalink.go) from values this file returns.

Every function here follows the package's existing degrade-to-a-value contract (see the package doc in worktree.go): no error is ever returned. But the VALUE degraded to RECORDS WHICH failure occurred — RemotePresenceUnknown rather than a bare false, and a populated GitHubRemote.Reason rather than an empty struct — because a caller that cannot distinguish "I checked and found nothing" from "I could not check" cannot honestly report either (cycle-1 review, Codex, HIGH).

Package gitmeta is the stdlib-only, best-effort git introspection layer that detects when a resolved CodeGraph index belongs to a different git working tree than the caller (WORK-01/02/03). It shells out to the local `git` binary via os/exec — no pure-Go git library, no CGo (D-03/D-04) — and is deliberately free of internal/query and internal/mcp concerns, so Phase 5's git sync hooks can reuse it unchanged.

Every function here degrades to a safe zero value on ANY failure: missing git, a non-repo path, a timeout, or a transient error all report "no signal" rather than an error. A read query must never fail or block on git being unavailable, slow, or absent (WORK-03).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CommonDir

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

CommonDir returns the absolute, symlink-resolved git COMMON directory for dir — the shared `.git` every worktree of one repository points at, or "" when dir isn't a repo. Linked worktrees of the SAME repository report the SAME common dir; a submodule or an embedded clone is a DIFFERENT repository and reports its own (e.g. `.git/modules/<name>`, or its own `.git`). That distinction is what separates a genuine borrowed worktree from a nested repo the parent index already covers (see DetectIndexMismatch gate 4).

func HooksDir

func HooksDir(ctx context.Context, projectRoot string) string

HooksDir returns the git hooks directory for projectRoot, resolved via `git rev-parse --git-path hooks` — the only correct way to honor core.hooksPath and linked worktrees (which share the main checkout's common hooks dir). A relative result is joined against projectRoot; an absolute result (the case for linked worktrees) is passed through unchanged. Unlike CommonDir, this deliberately does NOT call realpath: D-04 specifies resolve-or-passthrough only, not symlink resolution. Degrades to "" on any error, empty output, or non-repo projectRoot.

func IsGitRepo

func IsGitRepo(ctx context.Context, dir string) bool

IsGitRepo reports whether dir is inside a git working tree. It follows the same exec contract as WorktreeRoot/CommonDir (gitTimeout, cmd.Dir, cmd.Stdin nil) and degrades to false on any failure — missing git, a non-repo path, a timeout, or a transient error all report "no signal" rather than propagating an error (D-10).

func RemoteURL added in v0.12.0

func RemoteURL(ctx context.Context, dir string) string

RemoteURL returns dir's origin remote URL with any `url.<base>.insteadOf` rewrite already applied, or "" if dir has no origin remote, isn't a git repository, or git is unavailable. Uses `git ls-remote --get-url origin`, which git's own documentation states "exit[s] without talking to the remote" — this must never be swapped for a plain `git remote get-url` / config read, which returns the UNREWRITTEN value and would silently disagree with what git itself resolves for a developer with an insteadOf rule configured.

Note: `git ls-remote --get-url <name>` does not fail when <name> has no configured remote — it echoes the literal argument back unchanged (verified empirically). Callers must treat that echo, not a non-zero exit, as "no such remote".

func WorktreeRoot

func WorktreeRoot(ctx context.Context, dir string) string

WorktreeRoot returns the absolute, symlink-resolved toplevel of the git working tree that dir belongs to, or "" when dir isn't inside a git repo (or git is unavailable/slow). `git rev-parse --show-toplevel` reports the PER-WORKTREE root: the main checkout and each linked worktree resolve to their own distinct directory — exactly the distinction detection needs.

Types

type CachingDetector

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

CachingDetector memoizes DetectIndexMismatch verdicts — POSITIVE and NEGATIVE — keyed on the documented cache key (startPath + "\x00" + indexRoot, per D-13). Detection costs up to four git subprocesses (see DetectIndexMismatch's doc comment); on a long-lived MCP server that would otherwise re-pay that cost on every single tool call.

The cache deliberately lives HERE, not on internal/query.Engine: internal/mcp's openEngine builds a FRESH Engine on every single tool call by design, so an Engine-scoped cache would yield zero cross-call benefit on the exact long-lived surface the cache exists for. internal/mcp constructs one CachingDetector per server and closes over it in every handler; the CLI constructs one per invocation (free — it's one-shot); both surfaces share this identical type (D-13, corrected 2026-07-15).

func NewCachingDetector

func NewCachingDetector() *CachingDetector

NewCachingDetector returns a ready-to-use, empty CachingDetector.

func (*CachingDetector) Detect

func (d *CachingDetector) Detect(ctx context.Context, startPath, indexRoot string) *Mismatch

Detect returns the memoized DetectIndexMismatch verdict for (startPath, indexRoot), computing and caching it on first call. Negative verdicts (nil == "checked, no mismatch") are cached too — a bare nil lookup can't distinguish "not yet checked" from "checked, none found", so presence is tracked via the two-value map form, never a nil test (D-13).

Detect is safe to call on a nil *CachingDetector: it falls through directly to DetectIndexMismatch, uncached, so every consumer can treat the detector as optional.

BL-01: a verdict computed under a CANCELLED ctx is never written to the cache, even though it IS returned for this call (WORK-03: never block or error a read on a failed/aborted git probe). A cancelled git spawn collapses into the same nil DetectIndexMismatch returns for "checked, no mismatch" — caching it would let one cancelled call permanently poison this (startPath, indexRoot) entry for a long-lived server's entire remaining life. See the ctx.Err() check below for the mechanism.

WR-02: a startPath that is not an existing, statable directory can never be inside a working tree — DetectIndexMismatch's gate 1 (WorktreeRoot) would immediately return "" for it anyway, so this is a pure short-circuit, not a behavior change. Rejecting it BEFORE the cache lookup/store means a client that mints a fresh nonexistent "path" on every call (accidentally, via a stale reference, or a malicious/looping MCP client) cannot grow the cache at all, on top of the maxCacheEntries bound below for legitimate, existing paths.

type GitHubRemote added in v0.12.0

type GitHubRemote struct {
	Owner  string
	Repo   string
	Host   string
	Reason string
}

GitHubRemote is the outcome of parsing a repository's origin remote and classifying it against D-08 (GitHub only). On success Owner and Repo are populated and Reason is empty. On any failure — no origin, unsupported host, malformed remote, non-repo directory, or git absent — Owner and Repo are empty, Host names whatever host WAS parsed (empty if none could be), and Reason is a short, credential-free, user-facing sentence naming the SPECIFIC cause. A bare (owner, repo) pair can only say "no"; this struct says WHICH no (cycle-1 review, Codex, MEDIUM).

func RemoteGitHubRepo added in v0.12.0

func RemoteGitHubRepo(ctx context.Context, dir string) GitHubRemote

RemoteGitHubRepo parses dir's origin remote and classifies it against D-08. Every failure path populates Reason with a distinct, actionable sentence rather than collapsing to one undifferentiated refusal (cycle-1 review, Codex, MEDIUM): git absent, non-repo directory, no origin configured, and an unsupported/lookalike host are each named separately.

type Mismatch

type Mismatch struct {
	WorktreeRoot string `json:"worktreeRoot"`
	IndexRoot    string `json:"indexRoot"`
}

Mismatch describes a detected "borrowed index" situation: startPath lives in one git working tree, but the resolved CodeGraph index belongs to a different one. The json tags match the documented `--json` object shape (`{worktreeRoot, indexRoot}`) so plan 02-04 can embed this directly into StatusResult.

func DetectIndexMismatch

func DetectIndexMismatch(ctx context.Context, startPath, indexRoot string) *Mismatch

DetectIndexMismatch detects when startPath lives in one git working tree but the resolved CodeGraph index (indexRoot) belongs to a DIFFERENT working tree — the silent "worktree queries the main branch's graph" correctness bug (WORK-01). Implements the worktree/index-mismatch detection semantics (D-02); gate order and polarity are load-bearing, not incidental.

Worst case this spawns four git subprocesses (two WorktreeRoot, two CommonDir) — gates 1-3 each short-circuit before reaching CommonDir, so most calls spawn one or two. This per-call cost is what motivates CachingDetector (Task 3): a long-lived MCP server must not re-pay it on every tool call.

Returns nil ("nothing to warn about") — never an error, never a panic — on every degradation path, including git being absent, slow, or the path not being a git repo at all (WORK-03).

func (*Mismatch) Notice

func (m *Mismatch) Notice() string

Notice renders the compact, single-line form of a detected mismatch, prefixed onto the other seven read tools' output (D-12). Returns "" on a nil receiver. This is the documented message text (D-01/D-11); do not paraphrase.

func (*Mismatch) Warning

func (m *Mismatch) Warning() string

Warning renders the verbose, multi-line form of a detected mismatch, used by `status` only (D-12). Returns "" on a nil receiver so callers never need a nil guard — the same shape internal/query/render_markdown.go's staleBanner uses. This is the documented message text (D-01/D-11); do not paraphrase, including the quoted "codegraph init -i" advice.

type RemotePresence added in v0.12.0

type RemotePresence int

RemotePresence is the tri-state result of asking whether a commit is observably present on a remote-tracking branch. It is deliberately NOT a bool and NOT a (bool, error) pair: collapsing "the query ran and found nothing" and "the query could not run at all" into the same false value would make D-07's honesty requirement unimplementable at the only layer that could implement it (cycle-1 review, Codex, HIGH).

const (
	// RemotePresenceUnknown is the zero value: the containment query could
	// not run at all — git absent, dir not a repository, a malformed sha,
	// or the gitTimeout firing. This is NOT a statement about the commit.
	RemotePresenceUnknown RemotePresence = iota
	// RemotePresenceObserved means the query ran and FOUND the commit on
	// at least one remote-tracking branch. Sound in this direction: a hit
	// proves the commit is on the remote.
	RemotePresenceObserved
	// RemotePresenceNotObserved means the query ran and did NOT find the
	// commit on any remote-tracking branch. NOT proof the commit is absent
	// from the remote — only that the last fetch did not see it.
	RemotePresenceNotObserved
)

func CommitOnRemoteTrackingBranch added in v0.12.0

func CommitOnRemoteTrackingBranch(ctx context.Context, dir, sha string) RemotePresence

CommitOnRemoteTrackingBranch reports whether sha is reachable from any LOCAL remote-tracking branch (`git branch -r --contains <sha>`) — this function never fetches and performs no network I/O of any kind.

The Observed answer is sound in exactly ONE direction: a hit PROVES the commit is on a remote-tracking branch. No hit proves only that the last fetch did not see it — someone else may have pushed it since, or the user may simply not have fetched. A read-only local tool must not fetch to find out, so the "not found" case degrades to RemotePresenceNotObserved (an honest answer about uncertainty), never to a claim that the commit is absent from the remote (D-07).

Jump to

Keyboard shortcuts

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