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 ¶
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 ¶
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
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
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 ¶
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
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 ¶
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 ¶
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.
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.
Source Files
¶
- worktree.go