Documentation
¶
Overview ¶
Package git — the changed set: which files, line ranges and directories a branch touched. The concrete implementation of the core coregit.ChangedSet port.
Package git — the compile-time proof that ChangedSetValue still satisfies the core port.
Hoisted out of changed_set.go per KTN-IFACE-ASSERT-PLACEMENT: the production source carries no purely verificational declarations. Without this assertion a method renamed here would only fail at the call site that still expected the old name, which may be in another repository.
Package git — the resolver's construction parameters.
Package git — parsing git's unified-diff and name-status output into a changed set.
Package git implements the core git contract by shelling out to the git binary.
There is no VCS library dependency: git's own porcelain is the contract, and every invocation is hardened against a repository that may be hostile — see hardenedGitConfig.
Package git — resolving the git directory that governs a path, once per root.
Package git — what a working tree is at: its head commit, that commit's time, and whether tracked files differ from it.
Package git — resolving what a branch changed versus its merge-base with the default branch.
Package git — reading a file's content at a specific commit.
Index ¶
- func GitDir(ctx context.Context, root string) (gitDir string, err error)
- func Resolve(ctx context.Context, cfg Config) coregit.ResolutionValue
- func ShowFile(ctx context.Context, repoRoot, sha, relPath string) (content string, err error)
- type ChangedSetValue
- type Config
- type HeadValue
- type IncludeFunc
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func GitDir ¶
GitDir resolves the git directory governing root — the directory that holds HEAD and the index — by shelling `git rev-parse --git-dir` and memoizing the result per root. It never assumes `.git` is a directory: in linked worktrees and submodules `.git` is a `gitdir:` pointer FILE, and rev-parse follows it to the real per-worktree git directory. The returned path is absolute and cleaned. An error means root is not inside a git repository (or git failed); failures are never memoized.
The memo is re-validated on every call against two lstats — ~2.9 µs together where the subprocess they stand in for measures ~2.7 ms — and is served only while both still describe the resolution: the `.git` entry at root is the same file it was, or is still absent, and the resolved git directory is still the same directory. A repository that MOVES, one deleted and re-created, and a root that becomes its own repository under a parent one are each caught by that pair.
What the pair does not catch is a repository created at a directory BETWEEN root and the worktree top level: root's own `.git` is still absent, the memoized git directory still exists, and only rev-parse's own discovery walk would notice that a nearer one now wins.
func Resolve ¶
func Resolve(ctx context.Context, cfg Config) coregit.ResolutionValue
Resolve computes the changed set for the repository containing cfg.Root (a directory hint; "" resolves from the current working directory).
It is fail-safe by design: any condition that prevents a trustworthy diff (no repository, shallow clone, unresolved default branch, no merge-base) yields a ResolutionValue with FullFallback=true and a Reason — never an error and never a silent empty diff. The caller degrades to treating everything as in scope, and surfaces Reason.
That asymmetry is the whole design. "Nothing changed" and "I could not tell what changed" are opposite instructions to a caller, and a resolver that returned an empty set for the second would make a review pass on a branch it never looked at.
func ShowFile ¶
ShowFile returns the content of relPath at commit sha via `git show`. relPath is resolved relative to repoRoot (the "./" prefix pins git's path resolution to the -C directory instead of the repository top level).
The content is returned VERBATIM. It deliberately does not go through runGitOutput, whose trim is right for a SHA or a ref name and wrong for a file: leading and trailing whitespace is the file's own, and a whitespace-only blob would otherwise come back indistinguishable from empty.
A failure is one of two refusals and never the same one twice. PathAbsent says the commit is there and holds no object at that path, which is what a caller reconstructing history needs in order to tell "deleted" from "emptied" — an empty file is a successful read of "". CommandFailed says anything else: an unreachable commit, an unreadable object store, no git.
Types ¶
type ChangedSetValue ¶
type ChangedSetValue struct {
// contains filtered or unexported fields
}
ChangedSetValue holds the files, line ranges, and directories that a branch changed versus its merge-base. All paths are absolute and normalised through filepath.Clean so membership tests match the engine's resolved issue positions. Build it with NewChangedSetValue and the add* methods; query it with the Contains* methods.
func NewChangedSetValue ¶
func NewChangedSetValue(repoRoot string) *ChangedSetValue
NewChangedSetValue returns an empty changed-set rooted at the given absolute repository top-level.
func (*ChangedSetValue) ContainsDir ¶
func (s *ChangedSetValue) ContainsDir(absDir string) bool
ContainsDir reports whether absDir directly encloses any touched file. It is NOT recursive: the parent of a touched directory is not itself touched.
func (*ChangedSetValue) ContainsFile ¶
func (s *ChangedSetValue) ContainsFile(absFile string) bool
ContainsFile reports whether absFile appears anywhere in the diff, including as the old side of a rename or a deletion.
func (*ChangedSetValue) ContainsLine ¶
func (s *ChangedSetValue) ContainsLine(absFile string, line int) bool
ContainsLine reports whether line on absFile is within a changed hunk.
func (*ChangedSetValue) IsEmpty ¶
func (s *ChangedSetValue) IsEmpty() bool
IsEmpty reports whether the changed-set contains no touched files at all (clean branch, or a branch whose every change the caller's filter excluded).
type Config ¶
type Config struct {
// Root is a directory inside the repository to resolve from. Empty resolves
// from the process working directory.
Root string
// Include decides which files belong in the changed set. Nil admits all.
//
// This is where a caller states the policy the source implementation had
// hard-coded: a Go linter passes a test for a ".go" suffix and a
// generated-file probe; a documentation tool passes something else; a tool
// that wants everything passes nil.
Include IncludeFunc
}
Config is what Resolve needs to know: where the repository is, and which files the caller considers part of the change.
Both fields have a meaningful zero and neither is refused (ADR 0031): an empty Root resolves from the process working directory, which is what a tool run inside the repository wants, and a nil Include admits every file, which is the safe direction — a filter defaulting to "exclude" would under-report a diff.
type HeadValue ¶
type HeadValue struct {
// Time is the commit's committer date, in the offset it was recorded
// with — what `git log --format=%cI` prints.
Time time.Time
// Revision is the full object name of the commit HEAD resolves to.
Revision string
// Modified reports that a TRACKED file differs from HEAD, staged or not.
// An untracked file does not count: it is not part of what the commit
// describes, and a build stamp that turned dirty whenever an editor left
// a swap file would say nothing. This is narrower than the vcs.modified
// Go stamps into a binary, which counts untracked files too.
Modified bool
}
HeadValue is what a working tree is at: the commit HEAD names, that commit's time, and whether any tracked file differs from it.
It is the engine's own value (ADR 0074): the core git port answers what a branch CHANGED and deliberately models no commit, so a second implementation of that port would have no reason to produce this.
func Head ¶
Head reports what the working tree containing dir is at: the commit HEAD names, its time, and whether tracked files differ from it.
It runs three read-only git commands, all through the hardened runner, so a hostile .git/config cannot turn the query into command execution the way it can a bare `git status` (see hardenedGitConfig). The commit time is read from the raw commit object rather than through `git log`, because log honours log.showSignature and would hand a planted gpg.program a signature to "verify". The status runs with --no-optional-locks, so asking never takes the index lock a concurrent `git commit` in the same tree needs.
It caches nothing: modified is the one fact here that changes without a commit. A caller asking often keeps its own answer.
A dir outside any repository — or one that does not exist, or a machine with no git — is RepositoryUnresolved. A repository whose HEAD names no commit yet (an unborn branch), a bare repository, and a failed read are CommandFailed, with the subcommand and git's stderr in Private.
type IncludeFunc ¶
IncludeFunc reports whether the file at absPath belongs in the changed set.
It replaces what the source implementation hard-coded: a ".go" suffix test plus a generated-file probe. Those are one caller's policy, not the domain's — a changed set over Markdown, or one that keeps generated files, is just as legitimate — so the policy is the caller's to state.
A nil IncludeFunc includes everything. That is the safe default in the ADR 0031 sense: the failure mode of including too much is a wider scope, while a default that silently dropped files would under-report a diff, which is the one answer this package must never give.