gitmeta

package
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Aug 13, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

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, ported from TS sync/git-hooks.js isGitRepo).

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 TS's own 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 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 TS's `--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). Ported verbatim from TS sync/worktree.js's detectWorktreeIndexMismatch (D-02), gate order and polarity preserved exactly.

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. Ported verbatim from TS sync/worktree.js's worktreeMismatchNotice (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. Ported verbatim from TS sync/worktree.js's worktreeMismatchWarning (D-01/D-11); do not paraphrase, including the quoted "codegraph init -i" advice.

Jump to

Keyboard shortcuts

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