gitstate

package
v0.5.4 Latest Latest
Warning

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

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

Documentation

Overview

Package gitstate reads what the repository was, so a checkpoint does not have to rely on being told.

Everything else logos records is model-initiated: an agent decides to call checkpoint, and if it does not, nothing happens. That is the single largest reliability gap in the product — the core promise depends on a host model remembering a tool at the one moment it is least likely to.

Git state is the part that needs no cooperation. Branch, commit, dirty files and diffstat are all questions `git` will answer whether or not the agent thought to mention them. So a checkpoint gets two halves:

what the agent said    — task, decisions, dead ends, next step
what the repo was      — branch, sha, dirty files, diffstat

Only the first can be forgotten. The second is why an arriving agent can trust "this was true at a3f9c2" rather than "this was true at some point".

Everything here degrades to the zero value. A directory that is not a repo, a git that is not installed, a detached HEAD, a repo with no commits: each returns an empty State and no error, because a checkpoint that refuses to save because git was unavailable would be worse than one without a sha. The one exception is the working tree: a status or diff git refused is marked Unreadable, because its zero value would otherwise read as "clean".

Index

Constants

This section is empty.

Variables

View Source
var ErrUnsafe = errors.New("git: a filter or hook in this repository cannot be overridden")

ErrUnsafe is returned when git cannot be told to leave the repository's programs alone, so it was not run.

Functions

func Head(dir string) (branch, commit string)

Head is the branch and short commit of the tree at dir, each "" when git will not say. It exists for readers that only need to know whether the tree has moved since a checkpoint: Read shells out five more times for a diffstat and a file list nobody is going to look at on that path.

func Identity

func Identity(dir string) (remote, root string)

Identity is origin's URL and the main checkout's path for the repository at dir, each "" when git will not say.

Both, because either alone misjudges an ordinary case: a clone on another machine has a different path and the same remote, and a repository that got its remote after its first checkpoint has the same path and a new remote.

func NamedFiles added in v0.5.2

func NamedFiles(text string) []string

NamedFiles picks out what in text reads as a repository file: a relative path with a directory, or a bare name with an extension. Paths come first, because "internal/parse/reader.go" means one file and "reader.go" may mean several.

func NormalizeRemote added in v0.5.4

func NormalizeRemote(remote string) string

NormalizeRemote reduces a remote URL — as Identity returns it — to a form where the ssh and https spellings of the same repository compare equal: `git@github.com:Coder8124/logos.git`, `https://github.com/Coder8124/logos.git` and `https://github.com/Coder8124/logos` all become `github.com/coder8124/logos`. "" in, "" out — a repository with no origin is for the caller to mark explicitly, never by handing this an empty string and reading the result as a real identity.

plan0-6-0's Workstream S3 needs this exact normalization again, to decide whether two vaults' memories came from the same repository before merging them — reuse this function there rather than writing a second one that can drift from this one.

func SafeGit added in v0.4.9

func SafeGit(dir string, timeout time.Duration, args ...string) (string, error)

SafeGit runs git in dir with the repository's own programs turned off, bounded by timeout end to end.

func WorktreeName

func WorktreeName(dir string) string

WorktreeName is git's own name for the linked worktree at dir, and "" when dir is the main checkout, is not a repository, or git will not answer.

It is the name and not the path because this is what continuity is scoped by — a folder that gets moved is the same worktree, exactly as a project that gets moved is the same project. Git allocates the name itself, in the admin directory it keeps per worktree (<repo>/.git/worktrees/<name>), and guarantees it is unique inside the repository: two worktrees whose folders happen to share a basename still get distinct names, which a scope derived from the folder could not promise.

Types

type Anchors added in v0.5.1

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

Anchors answers Since for one repository, remembering what it already asked git: a resume reads every ruling in a project's history, and many of them name the same file at the same commit.

func NewAnchors added in v0.5.1

func NewAnchors(dir string) *Anchors

NewAnchors reads drift against the repository at dir. Nil when dir is not a repository, and a nil Anchors finds nothing, so callers need not check.

func (*Anchors) Changes added in v0.5.2

func (a *Anchors) Changes(commit, name string) (n int, ok bool)

Changes counts the commits since commit that touched the one file name. ok is false when that cannot be measured — no repository, a commit this clone does not have — which is not the same answer as zero: "has not changed since" is a claim, and Since's false covers both.

func (*Anchors) Since added in v0.5.1

func (a *Anchors) Since(commit, text string) (Drift, bool)

Since reports whether files named in text changed after commit. False when the text names no file, when nothing it names changed, or when commit is not in this repository — a ruling from another clone, or one whose commit was rebased away, has no history here to measure against, and saying nothing is truer than saying it moved.

Only files the text names are measured, never the checkpoint's whole file list. A session touches a dozen files and rules out one thing about one of them; flagging every ruling whose session touched a busy file would mark nearly all of them, and a warning on everything is read as a warning on nothing.

func (*Anchors) SinceAnchor added in v0.5.4

func (a *Anchors) SinceAnchor(commit, anchorExpr, text string) (Drift, bool)

SinceAnchor is Since's sibling for a ruling carrying an explicit `anchor:` field: a fixed AND/OR grammar over single anchors, so "stale only when this file AND this dependency changed" can be said without a general boolean evaluator. anchorExpr empty, or one that does not parse, falls back to Since unchanged — today's implicit git-only anchor, mined from text — so a record written before this field existed, or hand-edited into nonsense, is measured exactly as it always was rather than silently treated as never stale.

A single atom is a one-element AND, which this evaluates the same way Since does for one named file: both end up asking whether that one path's commit count since commit is nonzero. The grammar is not a parallel code path that happens to agree with the old behaviour on the easy case — it is the old behaviour's generalisation.

type Drift added in v0.5.1

type Drift struct {
	// Commit is where the ruling was recorded.
	Commit string `json:"commit"`
	// Paths are the files it named that changed since, as it named them.
	Paths []string `json:"paths"`
	// Commits is how many commits since touched any of them.
	Commits int `json:"commits"`
}

Drift is how far the files a ruling named have moved since it was recorded.

func (Drift) Note added in v0.5.1

func (d Drift) Note() string

Note is the drift as one clause for a reader. It says "may", because a change to the file is evidence the cause moved, not proof: the commit could have been a rename of something else in it.

type State

type State struct {
	// Branch is the current branch, or "" when detached or unavailable.
	Branch string
	// Commit is the short HEAD sha. The field that makes everything else
	// verifiable — a decision recorded against a commit can be checked out.
	Commit string
	// Subject is HEAD's commit subject, so the sha means something to a reader.
	Subject string
	// Dirty is the count of files with uncommitted changes. Zero means clean,
	// which is itself worth recording: "clean at this sha" is a much stronger
	// handoff than "somewhere near this sha".
	Dirty int
	// Files are the paths with uncommitted changes, capped. These are what the
	// session actually touched, observed rather than reported — an agent's own
	// Files list is a claim, and this is evidence.
	Files []string
	// Insertions and Deletions summarise the uncommitted diff.
	Insertions int
	Deletions  int
	// Worktree is set when this is a linked worktree rather than the main one,
	// because "same project, divergent parallel state" is exactly where
	// continuity breaks and the checkpoint should say which one it meant.
	Worktree string
	// Remote and Root say which repository this was, because a project is only
	// a name and "api" is two unrelated repositories on most machines. Remote
	// is origin's URL; Root is the main checkout, shared by its worktrees.
	Remote string
	Root   string
	// Unreadable is set when git refused the status or the diff: a corrupt
	// index, or a partial clone missing the objects a diff needs. Without it
	// the zero Dirty and zero diffstat such a read leaves behind would be
	// recorded as "clean", a claim nothing checked (#204).
	Unreadable bool
}

State is the repository as it stood when a checkpoint was written.

func Read

func Read(dir string) State

Read gathers the repository state at dir. Never returns an error: absence of git state is a normal condition, not a failure.

func (State) Empty

func (s State) Empty() bool

Empty reports whether anything was learned. Callers use it to decide whether to record a git section at all rather than writing an empty one.

func (State) Summary

func (s State) Summary() string

Summary renders the state as one line for a checkpoint's frontmatter or a report. Empty when there is nothing to say.

Jump to

Keyboard shortcuts

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