git

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Jul 1, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package git is agentsync's local-only destination-versioning helper. It wraps go-git to give each rendered destination agent dir (~/.claude, ~/.codex, …) its own git repo so a bad `agentsync apply` is a quick rollback away (issue #118).

These repos are a LOCAL ROLLBACK HISTORY and are NEVER pushed to a remote. The rendered files they version contain secrets resolved to cleartext at apply time; keeping the repos local-only is what makes that acceptable (the canonical ~/.agentsync/ source — the thing users commit and push — only ever holds secret references). Accordingly this package exposes NO remote/push surface at all: there is no function that adds a remote or pushes, and TestNoPushSurface guards that the surface never grows one. Do not add one.

Index

Constants

View Source
const NoticeFile = "AGENTSYNC_LOCAL_HISTORY.md"

NoticeFile is written at the repo root on init and committed in the first checkpoint, so anyone browsing the history sees what it is and that it must not be pushed.

Variables

View Source
var DefaultIdentity = Identity{Name: "agentsync", Email: "agentsync@localhost"}

DefaultIdentity is used when the config supplies no override.

Functions

func HasNestedRepoBelow

func HasNestedRepoBelow(dir string) (bool, error)

HasNestedRepoBelow reports whether any git repository exists STRICTLY below dir (a `.git` entry in a subdirectory). agentsync refuses to init a repo that would wrap another repo (the cross-run nesting hazard: a child dir was versioned in an earlier run before a parent-dir agent was enabled). Short-circuits on the first hit; a missing dir is reported as false.

func OwnsExactly

func OwnsExactly(dir string) (bool, error)

OwnsExactly reports whether dir is itself the root of an agentsync-owned repo — an EXACT `.git` at dir carrying the marker — without the upward search Detect does. Use this where you must act on the repo AT dir (Open/commit/revert), since Detect's DetectDotGit can match a parent repo that merely contains dir.

Types

type Checkpoint

type Checkpoint struct {
	Hash    string    // full hash
	Short   string    // first 7 chars of Hash
	Subject string    // first line of the commit message
	When    time.Time // author time
}

Checkpoint is one commit in a destination repo's history.

type FileChange

type FileChange struct {
	Path string
	Kind string // "create" | "modify" | "delete"
}

FileChange describes one file a restore would touch, for `revert --dry-run`.

type Identity

type Identity struct {
	Name  string
	Email string
}

Identity is the author/committer recorded on checkpoint commits. agentsync uses a dedicated identity (not the user's git identity) so machine-authored checkpoints are distinct and committing works even when no global git identity is configured.

type Repo

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

Repo wraps a go-git repository plus its absolute work-tree root.

func Init

func Init(dir string) (*Repo, error)

Init creates a new agentsync-owned git repo at dir: PlainInit, stamp the [agentsync] managed=true marker into .git/config (so Detect recognizes it as ours), tighten the .git dir to 0o700 (the history may carry cleartext secrets), and write the local-only notice file. Callers MUST gate on Detect == StateUntracked first; Init errors if a repo already exists at dir.

func Open

func Open(dir string) (*Repo, error)

Open opens an existing repo at dir (an exact .git at dir, not a nested parent). Callers gate on Detect first; Open does not re-check the marker.

func (*Repo) CommitStaged

func (r *Repo) CommitStaged(message string, id Identity) (string, error)

CommitStaged records one commit of the current index authored by id, or returns ("", nil) when the index has nothing to commit (so an apply that changed nothing produces no empty checkpoint).

func (*Repo) Dir

func (r *Repo) Dir() string

Dir returns the repo's work-tree root.

func (*Repo) HasMultipleCheckpoints

func (r *Repo) HasMultipleCheckpoints() (bool, error)

HasMultipleCheckpoints reports whether the repo has at least two commits, so a default "undo the most recent apply" (HEAD~1) has somewhere to land.

func (*Repo) IsClean

func (r *Repo) IsClean() (bool, error)

IsClean reports whether the worktree has no uncommitted changes to TRACKED files (untracked files are ignored — agentsync never commits them).

func (*Repo) Log

func (r *Repo) Log(n int) ([]Checkpoint, error)

Log returns up to n checkpoints, newest first. n <= 0 returns the full history.

func (*Repo) Plan

func (r *Repo) Plan(targetRev string) (targetHash string, changes []FileChange, err error)

Plan computes what Restore(targetRev) would change in the worktree relative to the current HEAD, WITHOUT writing anything. Returns the resolved target hash and the file changes (sorted by path).

func (*Repo) Resolve

func (r *Repo) Resolve(rev string) (string, error)

Resolve turns a revision (e.g. "HEAD", "HEAD~1", a short or full hash) into a full commit hash, erroring clearly when it does not resolve.

func (*Repo) Restore

func (r *Repo) Restore(targetRev, message string, id Identity) (string, error)

Restore makes the worktree match the targetRev checkpoint and records the result as a NEW commit on top of the current HEAD. It is append-only: HEAD advances, nothing is rewritten or lost, so the bad apply stays in history and the revert is itself revertible. Returns the new commit hash, or ("", nil) when the worktree already matches target (no checkpoint recorded).

func (*Repo) SnapshotDirtyTracked

func (r *Repo) SnapshotDirtyTracked(message string, id Identity) (string, error)

SnapshotDirtyTracked commits any uncommitted changes to already-TRACKED files (modifications and deletions) as a safety checkpoint, so a subsequent destructive operation (notably revert's hard reset) cannot lose them. Untracked files — the user's own scratch files — are never touched. Returns the snapshot commit hash, or ("", nil) when the worktree has no tracked changes. This is what keeps the append-only promise true of the WORKTREE, not just of history.

func (*Repo) Stage

func (r *Repo) Stage(relPaths []string) error

Stage adds each of relPaths (slash-relative to the repo root) to the index. A path that is gone from the worktree is staged as a deletion if it is tracked.

func (*Repo) StageTrackedDeletions

func (r *Repo) StageTrackedDeletions() ([]string, error)

StageTrackedDeletions stages the removal of any already-TRACKED file that is now missing from the worktree — so an apply that deleted a managed file (e.g. a dropped MCP server) records that deletion in the checkpoint. It deliberately does NOT touch untracked files (a `?` status) the user may have dropped into the dir: agentsync only versions what it wrote. Returns the staged paths.

type State

type State int

State classifies how a destination dir is tracked, so the apply tail knows whether to init, commit, or stay out of the user's way.

const (
	// StateUntracked: no git work tree at or above the dir — eligible for init.
	StateUntracked State = iota
	// StateAgentsyncOwned: a work tree agentsync created (carries the marker).
	StateAgentsyncOwned
	// StateForeign: a work tree the user owns (no marker) — leave it alone.
	StateForeign
)

func Detect

func Detect(dir string) (State, error)

Detect reports how dir is tracked. It uses DetectDotGit so a destination nested inside an existing repo (a dotfiles user who keeps ~/.claude under git) is reported StateForeign rather than StateUntracked — agentsync must not init a nested repo or commit into someone else's history.

func (State) String

func (s State) String() string

String renders the state for diagnostics/doctor.

Jump to

Keyboard shortcuts

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