Documentation
¶
Index ¶
- Constants
- func CheckoutBranch(repoPath, branchName string) error
- func CleanupWorktrees() error
- func FetchBranch(repoPath, branchName string) error
- func GetCurrentBranchName(path string) (string, error)
- func GetHeadCommitSHA(path string) (string, error)
- func InitializeProjectDirectory(path string) error
- func IsCommitOnMain(repoPath, mainBranch, sha string) (bool, error)
- func IsGitRepo(path string) bool
- func RemoteURL(repoPath, remote string) (string, error)
- type BranchStatus
- type DiffStats
- type FileStat
- type GitWorktree
- func NewGitWorktree(repoPath string, sessionName string) (tree *GitWorktree, branchname string, err error)
- func NewGitWorktreeFromCommitSHA(repoPath, sessionName, branchName, commitSHA string) (*GitWorktree, string, error)
- func NewGitWorktreeFromExisting(existingWorktreePath string, sessionName string) (*GitWorktree, error)
- func NewGitWorktreeFromExistingWithExecutor(existingWorktreePath string, sessionName string, cmdExec executor.Executor) (*GitWorktree, error)
- func NewGitWorktreeFromStorage(repoPath string, worktreePath string, sessionName string, branchName string, ...) *GitWorktree
- func NewGitWorktreeFromStorageWithExecutor(repoPath string, worktreePath string, sessionName string, branchName string, ...) *GitWorktree
- func NewGitWorktreeWithBranch(repoPath string, sessionName string, customBranch string) (tree *GitWorktree, branchname string, err error)
- func NewGitWorktreeWithBranchAndExecutor(repoPath string, sessionName string, customBranch string, ...) (tree *GitWorktree, branchname string, err error)
- func (g *GitWorktree) Cleanup() error
- func (g *GitWorktree) CommitChanges(commitMessage string) error
- func (g *GitWorktree) CreatePR(title, body string) (prURL string, prNumber int, err error)
- func (g *GitWorktree) Diff() *DiffStats
- func (g *GitWorktree) EnablePRAutoMerge(prNumber int) error
- func (g *GitWorktree) GetBaseCommitSHA() string
- func (g *GitWorktree) GetBranchName() string
- func (g *GitWorktree) GetPRStatus(prNumber int) (*PRStatus, error)
- func (g *GitWorktree) GetRepoName() string
- func (g *GitWorktree) GetRepoPath() string
- func (g *GitWorktree) GetWorktreePath() string
- func (g *GitWorktree) InvalidateDirtyCache()
- func (g *GitWorktree) IsBranchCheckedOut() (bool, error)
- func (g *GitWorktree) IsDirty() (bool, error)
- func (g *GitWorktree) IsDirtyWithHint(claudeActive bool) (bool, error)
- func (g *GitWorktree) IsPRMerged(prNumber int) (bool, error)
- func (g *GitWorktree) OpenBranchURL() error
- func (g *GitWorktree) PrimeDirtyCacheAt(t time.Time)
- func (g *GitWorktree) Prune() error
- func (g *GitWorktree) PushBranch() error
- func (g *GitWorktree) PushChanges(commitMessage string, open bool) error
- func (g *GitWorktree) Remove() error
- func (g *GitWorktree) Setup() error
- type MergeMainResult
- type PRStatus
- type ShippedCommit
Constants ¶
const IsDirtyCacheTTL = 30 * time.Second
IsDirtyCacheTTL is the duration for which a dirty (has changes) result is considered fresh. 30s keeps the review queue responsive when uncommitted changes are present. InvalidateDirtyCache() is called after commits/pushes so critical paths remain snappy.
const IsDirtyCleanCacheTTL = 5 * time.Minute
IsDirtyCleanCacheTTL is the TTL when the worktree is known to be clean. Clean worktrees won't change unless Claude commits or a user modifies files; InvalidateDirtyCache() is called on those code paths, so 5 min is safe and cuts subprocess calls by ~10x vs dirty-path TTL for quiescent sessions.
const IsDirtyErrorCacheTTL = 60 * time.Second
IsDirtyErrorCacheTTL is the TTL applied when `git status` itself fails (e.g. the worktree directory is missing — a stale path left behind by a rework/reopen cycle). Without a backoff, a broken worktree gets re-checked on every poller tick (every few seconds), burning a subprocess spawn per tick indefinitely; 60s keeps failure visible in logs at a sane rate while still recovering quickly once the worktree is fixed.
Variables ¶
This section is empty.
Functions ¶
func CheckoutBranch ¶ added in v1.37.0
CheckoutBranch checks out a branch in an existing repository.
func CleanupWorktrees ¶
func CleanupWorktrees() error
CleanupWorktrees removes all worktree directories under the configured worktrees dir. It deliberately does NOT delete the associated branches: a branch can hold commits that exist nowhere else (never pushed, never merged), and this function has no way to know whether that's true for any given one. See GitWorktree.Cleanup's doc comment — same fix, same root cause (docs/tasks/backlog-feature-improvement.md).
func FetchBranch ¶ added in v1.37.0
FetchBranch fetches a specific branch from the origin remote.
func GetCurrentBranchName ¶ added in v1.35.0
GetCurrentBranchName returns the current branch name for a git repository or worktree. Returns an error if the repo is in detached HEAD state.
func GetHeadCommitSHA ¶ added in v1.37.0
GetHeadCommitSHA returns the SHA of the HEAD commit for a git repository or worktree.
func InitializeProjectDirectory ¶ added in v1.35.0
InitializeProjectDirectory creates a directory and initializes it as a git repository. Behavior by pre-existing state:
- Path does not exist: creates with os.MkdirAll(path, 0755), runs git init, commits.
- Path exists, no .git: runs git init in place, commits.
- Path exists, already a git repo: no-op, returns nil.
- Path exists but is a regular file: returns an error.
On partial failure (dir created, git init failed): attempts os.RemoveAll to roll back the newly created directory. Logs a warning if rollback also fails.
func IsCommitOnMain ¶ added in v1.39.0
IsCommitOnMain reports whether sha has actually landed on mainBranch — either the local branch (a commit merged directly to main without ever going through a PR) or origin's copy (a PR merged remotely on GitHub that hasn't been pulled locally yet). Approval (a passing review verdict) and shipping are different questions; this answers only the second one, and does so by checking ancestry rather than trusting any cached "PR merged" flag, since that flag can be stale, absent (no PR was ever opened), or simply wrong for a manually-merged branch.
Uses go-git rather than shelling out (repo convention — see .claude/rules/prefer-go-git-over-subshells.md). The origin fetch is best-effort: a failure (offline, no such remote, nothing new) does not fail the whole check, since the local-main check alone still answers the "merged directly to main locally" case.
Types ¶
type BranchStatus ¶ added in v1.39.0
type BranchStatus struct {
// BranchExists is false once the branch has been deleted (e.g. after a
// "delete branch on merge" or manual cleanup). AheadOfMain/BehindMain are
// only meaningful when true.
BranchExists bool
AheadOfMain int
BehindMain int
}
BranchStatus describes branchName's position relative to mainBranch.
func BranchAheadBehind ¶ added in v1.39.0
func BranchAheadBehind(repoPath, branchName, mainBranch string) (BranchStatus, error)
BranchAheadBehind reports branchName's commit position relative to mainBranch: how many commits are on the branch but not on main (ahead), and vice versa (behind) — mirroring `git rev-list --left-right --count branch...main`. Checks the local branch ref only; a branch already deleted locally reports BranchExists=false rather than an error, since that's the expected state for a shipped, cleaned-up item, not a failure.
type DiffStats ¶
type DiffStats struct {
// Content is the full diff content
Content string
// Added is the number of added lines
Added int
// Removed is the number of removed lines
Removed int
// Error holds any error that occurred during diff computation
// This allows propagating setup errors (like missing base commit) without breaking the flow
Error error
}
DiffStats holds statistics about the changes in a diff
type FileStat ¶ added in v1.39.0
FileStat describes one file's change between two commits, as returned by FileStatsBetween. Path is the file's path as of headSHA — for a rename this is the new path, not the old one. Status is one of "added", "deleted", "renamed", or "modified".
func FileStatsBetween ¶ added in v1.39.0
FileStatsBetween returns the per-file diff-stat summary (path, status, additions, deletions) for every file that changed between baseSHA and headSHA in the repo at repoPath, using go-git's typed diff API — no safeexec shell-out (.claude/rules/prefer-go-git-over-subshells.md).
Renames are reported as a single entry keyed by the file's new path, not a delete+add pair: go-git's FilePatch.Files() already exposes the from/to path pair needed to detect this directly, so unlike object.Patch.Stats() (whose FileStat.Name collapses a rename into a single "old => new" display string) this walks Patch.FilePatches() itself to keep the old and new paths distinct. Binary files are silently omitted — go-git produces zero diff chunks for them (the same signal it uses to skip submodule-ref-only changes), so there is no meaningful addition/deletion count to report; this mirrors go-git's own Stats() behavior rather than the "0/0 entry" shape one might expect, a discrepancy confirmed against go-git v5.14.0's source (getFileStatsFromFilePatches in plumbing/object/patch.go) and a throwaway spike before this function was written.
type GitWorktree ¶
type GitWorktree struct {
// contains filtered or unexported fields
}
GitWorktree manages git worktree operations for a session
func NewGitWorktree ¶
func NewGitWorktree(repoPath string, sessionName string) (tree *GitWorktree, branchname string, err error)
NewGitWorktree creates a new GitWorktree instance
func NewGitWorktreeFromCommitSHA ¶
func NewGitWorktreeFromCommitSHA(repoPath, sessionName, branchName, commitSHA string) (*GitWorktree, string, error)
NewGitWorktreeFromCommitSHA creates a new GitWorktree that will branch from the given commitSHA when Setup() is called, instead of branching from the current HEAD. This is used by ForkFromCheckpoint to recreate the exact git state at checkpoint time.
func NewGitWorktreeFromExisting ¶
func NewGitWorktreeFromExisting(existingWorktreePath string, sessionName string) (*GitWorktree, error)
NewGitWorktreeFromExisting creates a GitWorktree from an existing worktree path This is used when connecting to worktrees that were created manually or by deleted sessions
func NewGitWorktreeFromExistingWithExecutor ¶
func NewGitWorktreeFromExistingWithExecutor(existingWorktreePath string, sessionName string, cmdExec executor.Executor) (*GitWorktree, error)
NewGitWorktreeFromExistingWithExecutor creates a GitWorktree from an existing worktree path with an optional executor.
func NewGitWorktreeFromStorageWithExecutor ¶
func NewGitWorktreeFromStorageWithExecutor(repoPath string, worktreePath string, sessionName string, branchName string, baseCommitSHA string, cmdExec executor.Executor) *GitWorktree
NewGitWorktreeFromStorageWithExecutor creates a GitWorktree from stored data with an optional executor. If cmdExec is nil, a default executor is used.
func NewGitWorktreeWithBranch ¶
func NewGitWorktreeWithBranch(repoPath string, sessionName string, customBranch string) (tree *GitWorktree, branchname string, err error)
NewGitWorktreeWithBranch creates a new GitWorktree instance with an optional custom branch name
func NewGitWorktreeWithBranchAndExecutor ¶
func NewGitWorktreeWithBranchAndExecutor(repoPath string, sessionName string, customBranch string, cmdExec executor.Executor) (tree *GitWorktree, branchname string, err error)
NewGitWorktreeWithBranchAndExecutor creates a new GitWorktree with optional branch name and executor. If cmdExec is nil, a default executor is used.
func (*GitWorktree) Cleanup ¶
func (g *GitWorktree) Cleanup() error
Cleanup removes the worktree. It deliberately does NOT delete the branch: branch deletion via go-git's RemoveReference is not a "safe if merged" check, it is unconditional, and a branch can hold commits that exist nowhere else (never pushed, never merged). Silently destroying those on session teardown was a live bug — see docs/tasks/backlog-feature-improvement.md ("stop_session silently deletes the git branch"). A leftover local branch ref costs nothing; a lost commit is not recoverable through this code path. Equivalent to Remove() — kept as a separate method so callers don't need to know the two used to differ.
func (*GitWorktree) CommitChanges ¶
func (g *GitWorktree) CommitChanges(commitMessage string) error
CommitChanges commits changes locally without pushing to remote
func (*GitWorktree) CreatePR ¶ added in v1.37.0
func (g *GitWorktree) CreatePR(title, body string) (prURL string, prNumber int, err error)
CreatePR creates a GitHub pull request for the current branch and returns the PR URL and number. Title defaults to the branch name if empty. If a PR already exists for the branch it is returned without creating a new one.
func (*GitWorktree) Diff ¶
func (g *GitWorktree) Diff() *DiffStats
Diff returns the git diff between the worktree and the base branch along with statistics
func (*GitWorktree) EnablePRAutoMerge ¶ added in v1.37.0
func (g *GitWorktree) EnablePRAutoMerge(prNumber int) error
EnablePRAutoMerge enables GitHub auto-merge on the given PR so it merges automatically once required CI checks pass. Best-effort: fails silently when the repo does not have auto-merge enabled in its branch protection rules.
func (*GitWorktree) GetBaseCommitSHA ¶
func (g *GitWorktree) GetBaseCommitSHA() string
GetBaseCommitSHA returns the base commit SHA for the worktree
func (*GitWorktree) GetBranchName ¶
func (g *GitWorktree) GetBranchName() string
GetBranchName returns the name of the branch associated with this worktree
func (*GitWorktree) GetPRStatus ¶ added in v1.37.0
func (g *GitWorktree) GetPRStatus(prNumber int) (*PRStatus, error)
GetPRStatus fetches the combined CI check status, reviewer decisions, mergeability, and PR comments for the given pull request number.
func (*GitWorktree) GetRepoName ¶
func (g *GitWorktree) GetRepoName() string
GetRepoName returns the name of the repository (last part of the repoPath).
func (*GitWorktree) GetRepoPath ¶
func (g *GitWorktree) GetRepoPath() string
GetRepoPath returns the path to the repository
func (*GitWorktree) GetWorktreePath ¶
func (g *GitWorktree) GetWorktreePath() string
GetWorktreePath returns the path to the worktree
func (*GitWorktree) InvalidateDirtyCache ¶ added in v1.22.0
func (g *GitWorktree) InvalidateDirtyCache()
InvalidateDirtyCache clears the IsDirty cache so the next call re-runs git status. Call this whenever worktree state changes outside of Claude's control (e.g. after a manual commit, after running git operations, or in tests after writing files directly).
func (*GitWorktree) IsBranchCheckedOut ¶
func (g *GitWorktree) IsBranchCheckedOut() (bool, error)
IsBranchCheckedOut checks if the instance branch is currently checked out. Uses go-git to read HEAD directly (no subprocess).
func (*GitWorktree) IsDirty ¶
func (g *GitWorktree) IsDirty() (bool, error)
IsDirty checks if the worktree has uncommitted changes. Results are cached for IsDirtyCacheTTL (dirty) or IsDirtyCleanCacheTTL (clean).
func (*GitWorktree) IsDirtyWithHint ¶ added in v1.22.0
func (g *GitWorktree) IsDirtyWithHint(claudeActive bool) (bool, error)
IsDirtyWithHint checks if the worktree has uncommitted changes. When claudeActive is true the subprocess is skipped entirely and the cached value is returned (or false if no cached value is available yet), because Claude never modifies worktree state while it is actively generating output.
func (*GitWorktree) IsPRMerged ¶ added in v1.37.0
func (g *GitWorktree) IsPRMerged(prNumber int) (bool, error)
IsPRMerged reports whether the given PR number has been merged.
func (*GitWorktree) OpenBranchURL ¶
func (g *GitWorktree) OpenBranchURL() error
OpenBranchURL opens the branch URL in the default browser
func (*GitWorktree) PrimeDirtyCacheAt ¶ added in v1.35.0
func (g *GitWorktree) PrimeDirtyCacheAt(t time.Time)
PrimeDirtyCacheAt sets the dirty-cache timestamp to t without running git status. Use this to stagger per-session cache expiry so sessions added to the poller within a short window don't all expire simultaneously and burst-launch git subprocesses.
func (*GitWorktree) Prune ¶
func (g *GitWorktree) Prune() error
Prune removes all working tree administrative files and directories
func (*GitWorktree) PushBranch ¶ added in v1.37.0
func (g *GitWorktree) PushBranch() error
func (*GitWorktree) PushChanges ¶
func (g *GitWorktree) PushChanges(commitMessage string, open bool) error
PushChanges commits and pushes changes in the worktree to the remote branch
func (*GitWorktree) Remove ¶
func (g *GitWorktree) Remove() error
Remove removes the worktree but keeps the branch
func (*GitWorktree) Setup ¶
func (g *GitWorktree) Setup() error
Setup creates a new worktree for the session
type MergeMainResult ¶ added in v1.39.0
type MergeMainResult struct {
// UpToDate is true when the worktree's branch already contained everything
// from mainBranch — nothing was merged in.
UpToDate bool
// Merged is true when the merge (including a fast-forward) brought in new
// commits from mainBranch.
Merged bool
// Conflicted is true when merging mainBranch produced conflicts. The merge is
// always aborted before returning, so the worktree is left clean either way —
// callers never have to clean up a half-merged tree.
Conflicted bool
// ConflictedFiles lists the paths that conflicted. Populated only when
// Conflicted is true.
ConflictedFiles []string
}
MergeMainResult describes the outcome of MergeMainIntoWorktree.
func MergeMainIntoWorktree ¶ added in v1.39.0
func MergeMainIntoWorktree(worktreePath, mainBranch string) (*MergeMainResult, error)
MergeMainIntoWorktree fetches mainBranch from origin and merges it into whatever branch is currently checked out in worktreePath. It never leaves the worktree in a conflicted state: on conflict it aborts the merge immediately (via `git merge --abort`) and reports the conflicting paths, so the caller can hand that context to whoever resolves it rather than leaving a half-merged working tree behind for the next thing that touches it.
type PRStatus ¶ added in v1.37.0
type PRStatus struct {
// CIFailing is true when at least one CI check has a terminal failure.
CIFailing bool
// HasBlockingReviews is true when a reviewer has requested changes.
HasBlockingReviews bool
// HasConflicts is true when GitHub reports mergeStateStatus == "DIRTY" or
// mergeable == "CONFLICTING" — its branch cannot be merged as-is and needs
// a rebase. Both fields are checked (see Task 1.1.1d) because gh's
// mergeable field has been observed returning stale data (cli/cli#9583).
HasConflicts bool
// IsClosed is true when the PR's state is CLOSED (rejected by a human without
// merging) rather than OPEN or MERGED. Callers must check this before treating
// "not merged" as "still open and healthy" — a closed PR will never merge on
// its own no matter how long ReconcilePRPending keeps polling it.
IsClosed bool
// IsDraft is true when the PR is still marked draft on GitHub. Captured from
// the same gh pr view call as everything else on this struct (no second API
// call) so callers such as the backlog stuck-item detector (prReadyToMergeSolo)
// can gate on it without an extra fetch.
IsDraft bool
// Mergeable is the raw upper-cased GitHub `mergeable` field ("MERGEABLE",
// "CONFLICTING", or "UNKNOWN"). HasConflicts is the belt-and-suspenders
// bool derived from this plus mergeStateStatus (see above); Mergeable is
// exposed separately for callers (prReadyToMergeSolo) that want the literal
// "MERGEABLE" check called out in ADR-001 rather than the inverse-of-conflict
// approximation.
Mergeable string
// ApprovedCount is the number of current non-dismissed APPROVED reviews.
ApprovedCount int
// ChangesRequestedCount is the number of current non-dismissed
// CHANGES_REQUESTED reviews (equivalently, len of the reviews backing
// HasBlockingReviews — exposed as a count so callers building a
// github.PRInfo-shaped value don't need to re-derive it from the bool).
ChangesRequestedCount int
// FeedbackText is a combined human-readable summary for the fix agent.
FeedbackText string
// contains filtered or unexported fields
}
PRStatus holds the CI, review, and conflict state for a pull request.
type ShippedCommit ¶ added in v1.39.0
type ShippedCommit struct {
SHA string
Summary string // first line of the commit message
AuthorAt time.Time
AuthorName string
}
ShippedCommit describes one commit in the range shipped by a work session.
func ListShippedCommits ¶ added in v1.39.0
func ListShippedCommits(repoPath, baseSHA, headSHA string) ([]ShippedCommit, error)
ListShippedCommits returns the commits reachable from headSHA but not from baseSHA — i.e. what a work session's commit range actually shipped — newest first, like a PR's "Commits" tab. Both SHAs must already be resolved commit hashes (not branch names): the caller typically has these directly from GitWorktreeData.BaseCommitSHA and the work session's LastCommitSha, which remain valid even after the branch itself has been deleted post-merge.