worktree

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Aug 21, 2026 License: AGPL-3.0 Imports: 10 Imported by: 0

Documentation

Overview

Package worktree manages per-session git worktrees rooted under <repo>/.jungi/state/worktrees/<id>/. Each session that opens against a directory inside a git repository gets its own checked-out tree on a dedicated jungi/<id> branch, so concurrent sessions in the same repo work in isolation without trampling each other's index, working files, or branch state.

The package shells out directly via os/exec rather than going through internal/tools/runshell: runshell.Execute imposes a 30s default timeout (overridable per-invocation) and 10 KiB output truncation that are wrong for git operations, and it routes through the sandbox — which is circular when the sandbox is rooted inside the worktree we are trying to create.

Index

Constants

View Source
const BranchPrefix = "jungi/"

BranchPrefix is prepended to every jungi-managed branch so they are trivially recognisable in `git branch` output and grep-friendly when correlating with session log files (which share the UUID suffix).

Variables

This section is empty.

Functions

func RepoRoot

func RepoRoot(workDir string) (string, bool, error)

RepoRoot resolves the canonical working-tree root for workDir by asking git, returning ("", false, nil) when workDir is not inside any git working tree. Other errors (e.g. workDir does not exist, git not installed) propagate.

It uses --git-common-dir so that when workDir is inside a linked worktree, the main (common) working tree is returned rather than the per-worktree checkout. Used by session.Manager to decide whether a new session should be hosted in a worktree or just run directly against the user-selected directory.

func RewriteSessionWorktreePath added in v0.2.0

func RewriteSessionWorktreePath(p string) string

RewriteSessionWorktreePath rewrites a path that falls inside a session worktree checkout (<repo>/.jungi/state/worktrees/<uuid>/...) to the equivalent path under the repo root. The UUID segment must be 8-4-4-4-12 hex; other jungi/* branch names are ignored. When p is the worktree root itself, the repo root is returned. When p is not under a session worktree, it is returned unchanged.

Used by session.Manager so that a session launched with cwd inside an existing session worktree creates its new worktree as a sibling under the main checkout rather than nested inside the current worktree.

func Sweep added in v0.2.0

func Sweep(repoRoot string) error

Sweep prunes stale worktree registrations and deletes orphan jungi/<uuid> branches that have no live worktree and whose tip is already reachable from remotes or HEAD. Only branches whose suffix is a UUID (8-4-4-4-12 hex) are considered; other jungi/* branches are left alone. Branches whose tip contains commits not reachable from any remote or HEAD are preserved. Errors from individual steps are joined; callers treat the sweep as best-effort.

Types

type Handle

type Handle struct {
	// RepoRoot is the absolute path to the canonical working tree that
	// owns the .git directory the worktree shares.
	RepoRoot string
	// Path is the absolute path to the worktree's root directory.
	Path string
	// Branch is the branch checked out in the worktree, including the
	// jungi/ prefix.
	Branch string
}

Handle owns the identity of a single worktree. It is cheap to copy and safe to share across goroutines: all methods invoke git as a subprocess, never touching package-level state.

func Create

func Create(repoRoot, id string) (Handle, error)

Create adds a new worktree at <repoRoot>/.jungi/state/worktrees/<id>/ on a fresh branch jungi/<id> branched off the current HEAD of repoRoot.

On first use in a repo, also creates .jungi/ and .jungi/.gitignore so the state/ subdir is excluded from version control. Existing .jungi/ and .jungi/.gitignore are left untouched.

Returns a Handle pinning the resulting worktree. Caller is responsible for calling Prune when the session ends.

func (Handle) CurrentBranch added in v0.2.0

func (h Handle) CurrentBranch() (string, error)

CurrentBranch returns the short symbolic name of HEAD in the worktree. Unlike Handle.Branch, this is live: it reflects checkouts performed after Create. Handle.Branch is the session-owned prune target and is not mutated.

func (Handle) MapSubpath

func (h Handle) MapSubpath(sourceWorkDir string) (string, error)

MapSubpath translates a path under repoRoot to the equivalent path under the worktree, preserving any subdirectory the user originally selected. Returns the worktree path itself when sourceWorkDir is exactly the repo root.

Both sides are resolved through EvalSymlinks before the relative path is computed so that macOS's /var → /private/var aliasing — and any user-managed symlinks pointing into the repo — don't trick filepath.Rel into reporting paths as "outside" the root.

Used by session.Manager so that a session opened against <repo>/internal/foo runs inside <worktree>/internal/foo rather than the worktree root.

func (Handle) Prune

func (h Handle) Prune(force bool) error

Prune removes the worktree directory and deletes its branch. When force is true, the underlying `git worktree remove --force` and `git branch -D` will discard uncommitted changes and unmerged commits respectively — the caller is expected to have obtained user confirmation first.

Returns an error if either step fails. Branch deletion is attempted even when worktree removal fails so partial state is cleaned up as far as possible.

func (Handle) Status

func (h Handle) Status() (Status, error)

Status reports the worktree's pending changes and unpushed commit count. A zero-valued result means the worktree is fully clean and the branch is reachable from at least one remote-tracking ref — pruning would lose nothing.

type Status

type Status struct {
	// UncommittedFiles is the list of path entries reported by
	// `git status --porcelain` — modifications, additions, deletions,
	// and untracked files alike. Names are repo-relative.
	UncommittedFiles []string
	// UnpushedCommits is the number of commits on the worktree branch
	// that are not reachable from any remote-tracking ref. In a repo
	// without remotes, every commit on the branch counts as unpushed —
	// which is the correct conservative answer.
	UnpushedCommits int
}

Status reports what would be lost if the worktree were pruned. A zero-valued Status (no uncommitted files, no unpushed commits) means a prune is safe; the TUI uses IsDirty to decide whether to require user confirmation.

func (Status) IsDirty

func (s Status) IsDirty() bool

IsDirty reports whether the worktree has any state that would be destroyed by Prune. The TUI uses this as the gate for the close confirmation overlay.

Source Files

  • worktree.go

Jump to

Keyboard shortcuts

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