worktree

package
v0.18.0 Latest Latest
Warning

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

Go to latest
Published: May 22, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package worktree manages git worktree creation and teardown for Smith workers.

Each Smith operates in an isolated git worktree under .workers/<bead-id>/ in the anvil's repository directory. The worktree is branched from origin/main with a forge-prefixed branch name.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BranchName added in v0.11.0

func BranchName(beadID string) string

BranchName returns the canonical forge branch name for a bead ID. This matches the branch created by CreateWithOptions when no Branch override is provided. Callers outside this package use this to predict the branch name without creating a worktree (e.g. checking for un-PR'd remote work).

func CheckRemoteBranchState added in v0.16.0

func CheckRemoteBranchState(ctx context.Context, anvilPath, branch, baseBranch string) (RemoteBranchState, RemoteBranchInfo, error)

CheckRemoteBranchState reports whether origin/<branch> exists and, if so, whether its tip is reachable from the resolved base ref. anvilPath must be a non-bare git repo with an "origin" remote configured.

baseBranch overrides the reachability target — pass the epic branch for crucible children so commits already merged into the epic are correctly classified as merged. Pass "" to use origin/main or origin/master.

The check is lightweight: a single git ls-remote followed (only when the branch exists) by a targeted fetch and a merge-base ancestry test. It does NOT mutate state — callers that want to clean up a merged stale branch should call DeleteRemoteBranch separately.

func CleanStaleCoreWorktree added in v0.16.0

func CleanStaleCoreWorktree(ctx context.Context, anvilPath string) error

CleanStaleCoreWorktree removes a stale core.worktree setting from the main repository's .git/config when it points to a path that no longer exists or is empty. Such a stale setting breaks tooling that resolves the main repo's gitdir — notably Go's VCS stamping, which cd's into the main repo via the worktree's .git pointer and runs `git status --porcelain`. When core.worktree points to a missing/empty path, that command fails with exit 128 ("fatal: this operation must be run in a work tree"), which in turn breaks `go build` for any worker. Forge itself does not set core.worktree on the main repo (per-worktree values live in .git/worktrees/<name>/config.worktree and are managed by `git worktree add`), but older Forge versions, manual git invocations, or third-party tools may have written one. This helper is the idempotent self-heal.

The check is conservative: it only unsets when the configured path is missing or empty. A path that exists with content is left alone, in case it was set deliberately by the user. Errors are non-fatal — callers should log and continue.

func CurrentBranch added in v0.4.0

func CurrentBranch(ctx context.Context, repoPath string) (string, error)

CurrentBranch returns the currently checked-out branch name for the repository at repoPath. Returns "HEAD" for detached HEAD state. Returns an error if git cannot determine the branch.

func DeleteRemoteBranch added in v0.16.0

func DeleteRemoteBranch(ctx context.Context, anvilPath, branch string) error

DeleteRemoteBranch removes branch from origin. Used to clean up merged stranded branches before dispatching a fresh worker. A "remote ref does not exist" failure (another process beat us to the delete) is treated as success so concurrent forges do not collide.

func GitEnv added in v0.16.0

func GitEnv(worktreePath string) []string

GitEnv returns the git environment variables that confine git operations to the given worktree even when the child process changes directory away from it. Smith runs claude inside <anvil>/.workers/<bead-id>/, which is a subdirectory of the anvil repo; without these variables, a stray "cd .." in a tool_use bash command can walk cwd into the anvil and have git apply add / commit / push to the parent repo's currently-checked-out branch (typically main). Setting GIT_DIR + GIT_WORK_TREE binds every child git invocation to the worktree regardless of cwd. GIT_CEILING_DIRECTORIES is added as belt- and-suspenders so that even if a nested shell unsets GIT_DIR, git's repo discovery cannot walk up past .workers/ into the parent repo.

The returned slice is suitable for appending directly to a Cmd.Env. If worktreePath is not a valid git worktree (e.g. an os.MkdirTemp dir used by schematic / wicket / warden-learn) the function returns nil so that those non-repo runs are unaffected.

func LinkNodeModules added in v0.15.0

func LinkNodeModules(anvilPath, worktreePath string) error

LinkNodeModules is the exported form of linkNodeModules for use by packages that need to re-establish node_modules links after git operations.

func ProbeNodeModules added in v0.15.0

func ProbeNodeModules(label, anvilPath string)

ProbeNodeModules logs the mtime and entry count of each known node_modules location inside the given anvil directory. Checks the root and each subdirectory in nodeModulesDirs (matches linkNodeModules targets) so we see activity for client/frontend/etc. setups, not only repo-root node_modules. Purely diagnostic; never fails.

func RemoveWithRetry added in v0.14.0

func RemoveWithRetry(ctx context.Context, path string) error

RemoveWithRetry is the exported form of removeWithRetry for use by daemon-level stale directory cleanup.

func SanitizePath added in v0.16.0

func SanitizePath(beadID string) string

SanitizePath converts a bead ID to a safe directory/branch name. E.g., "Forge-n1g.4.1" → "Forge-n1g.4.1" (dots are fine in git branches). Slashes and other problematic chars are replaced with dashes.

func UnlinkReparsePoints added in v0.15.0

func UnlinkReparsePoints(path string)

UnlinkReparsePoints is the exported form of unlinkReparsePoints for use by other packages that need to safely remove worktree directories.

func ValidateWorktreeDir added in v0.14.0

func ValidateWorktreeDir(worktreePath string) error

ValidateWorktreeDir checks that the given directory is safe to run Smith in. If the directory is not inside any git repository (e.g. an os.MkdirTemp dir used by schematic or wicket), the check passes — there is no parent checkout to accidentally inherit. If the directory is inside a git repo, it must have a valid worktree .git file pointer to ensure isolation from the main checkout.

func VerifyAndRecoverMain added in v0.4.0

func VerifyAndRecoverMain(ctx context.Context, repoPath string) (recovered bool, originalBranch string, err error)

VerifyAndRecoverMain checks that the repository at repoPath is checked out to main, master, or a detached HEAD. If it is on a different branch, it attempts to recover by checking out main or master. It returns a boolean indicating whether recovery was attempted, the name of the original branch, and any error that occurred. If the current branch cannot be determined, it returns false, "", and the error.

Types

type CreateOptions

type CreateOptions struct {
	// Branch overrides the target branch name. Default: forge/<beadID>.
	Branch string
	// BaseBranch overrides the base ref to branch from. Default: origin/main
	// or origin/master (auto-detected). When set, the worktree branches from
	// origin/<BaseBranch> instead.
	BaseBranch string
	// ResetBranch, when true, resets an existing worktree branch back to the
	// base ref (origin/main or origin/<BaseBranch>) instead of reusing the
	// branch as-is. This discards all previous commits on the branch,
	// preventing cascading junk from failed pipeline runs.
	ResetBranch bool
	// Quiet suppresses git command stdout/stderr output (redirects to
	// io.Discard). Use this when creating worktrees from a TUI to avoid
	// corrupting the terminal's alt-screen with git progress output.
	Quiet bool
	// LocalHead, when true, skips the assertOnMainBranch safety check and
	// git fetch, and bases the worktree from the current local HEAD rather
	// than origin/main. Use for read-only scan worktrees where "scan the
	// working tree as-is" semantics are required and remote state should not
	// be fetched.
	LocalHead bool
	// SkipNodeModulesJunction, when true, skips linking node_modules from
	// the main checkout. Used for dependency-update beads where npm install
	// must write to a local node_modules to avoid corrupting the main
	// checkout's dependencies.
	SkipNodeModulesJunction bool
}

CreateOptions controls worktree creation behaviour.

type Manager

type Manager struct {
	// WorkersDir is the directory name under each anvil for worktrees.
	// Default: ".workers"
	WorkersDir string
}

Manager handles creating and tearing down worktrees.

func NewManager

func NewManager() *Manager

NewManager creates a Manager with default settings.

func (*Manager) Create

func (m *Manager) Create(ctx context.Context, anvilPath, beadID string, branch ...string) (*Worktree, error)

Create creates a new worktree for the given bead in the given anvil directory. If branch is provided, it checks out that existing branch. Otherwise, it creates a new branch named forge/<bead-id> from origin/main or origin/master (whichever exists, resolved by resolveBaseRef).

func (*Manager) CreateEpicBranch

func (m *Manager) CreateEpicBranch(ctx context.Context, anvilPath, branchName string) error

CreateEpicBranch creates or verifies an epic feature branch from main and pushes it to origin. This is used when an epic bead is first picked up — the branch is created without any code changes so child beads can branch from it.

func (*Manager) CreateWithOptions

func (m *Manager) CreateWithOptions(ctx context.Context, anvilPath, beadID string, opts CreateOptions) (*Worktree, error)

CreateWithOptions creates a new worktree with full control over branch and base ref. When opts.BaseBranch is set, the worktree branches from origin/<BaseBranch> instead of origin/main.

func (*Manager) FetchBranch added in v0.11.0

func (m *Manager) FetchBranch(ctx context.Context, anvilPath, branchName string) error

FetchBranch fetches a single named branch from origin in the given anvil directory, updating the local remote-tracking ref. This is the canonical way for daemon code to fetch a branch without going through a full worktree create/reset cycle.

func (*Manager) List

func (m *Manager) List(anvilPath string) ([]string, error)

List returns the paths of all active worktrees under .workers/ for an anvil.

func (*Manager) Remove

func (m *Manager) Remove(ctx context.Context, anvilPath string, wt *Worktree) error

Remove tears down a worktree and cleans up its branch.

type RemoteBranchInfo added in v0.16.0

type RemoteBranchInfo struct {
	// Branch is the branch name probed on origin.
	Branch string
	// SHA is the tip commit of origin/<Branch>, or empty when the branch is
	// absent.
	SHA string
	// BaseRef is the ref used as the reachability base (e.g. "origin/main"),
	// or empty when reachability was not evaluated (Absent).
	BaseRef string
}

RemoteBranchInfo carries supplementary detail discovered by CheckRemoteBranchState — most importantly the remote SHA, which callers log in needs-attention messages so an operator can inspect the prior work.

type RemoteBranchState added in v0.16.0

type RemoteBranchState int

RemoteBranchState describes whether a worker branch exists on origin and, if so, whether the commits it carries are already reachable from the base ref.

const (
	// RemoteBranchAbsent — the branch does not exist on origin. Safe to
	// dispatch a fresh worker.
	RemoteBranchAbsent RemoteBranchState = iota
	// RemoteBranchMerged — the branch exists on origin but every commit is
	// reachable from the base ref (e.g. origin/main). The branch is a stale
	// pointer and can be deleted.
	RemoteBranchMerged
	// RemoteBranchStranded — the branch exists on origin and carries commits
	// that are NOT reachable from the base ref. A prior worker pushed work
	// without opening a PR. Dispatching a fresh worker would produce a
	// parallel implementation and a non-fast-forward push.
	RemoteBranchStranded
)

func (RemoteBranchState) String added in v0.16.0

func (s RemoteBranchState) String() string

String returns a human-readable label for the state.

type Worktree

type Worktree struct {
	// BeadID is the bead being worked on.
	BeadID string
	// AnvilPath is the root of the source repository.
	AnvilPath string
	// Path is the absolute path to the worktree directory.
	Path string
	// Branch is the git branch name.
	Branch string
	// BaseBranch is the branch this worktree was branched from.
	// Empty means the default (main/master).
	BaseBranch string
}

Worktree represents an active git worktree for a Smith worker.

Jump to

Keyboard shortcuts

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