engine

package
v0.17.15 Latest Latest
Warning

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

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

Documentation

Overview

Package engine provides the core branch state management interface and implementation. It tracks branch relationships, metadata, and provides operations for querying and manipulating the branch stack. Package engine manages the state and relationships of stacked branches.

It is the core of stackit, responsible for:

  • Tracking parent-child relationships between branches
  • Storing and retrieving branch metadata (PR info, status, etc.)
  • Managing the branch stack structure
  • Coordinating branch operations like splitting, squashing, and restacking

The engine abstracts the underlying storage (git refs, notes) and provides a high-level interface for branch management.

Package engine provides undo/redo functionality through state snapshots

Index

Constants

View Source
const (
	// MaxRetries is the maximum number of retry attempts for transactional operations.
	// 5 retries with exponential backoff gives ~3 seconds total wait time.
	MaxRetries = 5

	// RetryBaseDelay is the base delay between retry attempts.
	// With exponential backoff: 100ms, 200ms, 400ms, 800ms, 1600ms (capped).
	RetryBaseDelay = 100 * time.Millisecond
)

Transaction retry configuration

View Source
const (
	// DefaultMaxUndoStackDepth is the default number of snapshots we keep
	DefaultMaxUndoStackDepth = 10
	// UndoDir is the directory where undo snapshots are stored
	UndoDir = ".git/stackit/undo"
)
View Source
const (
	// ReasonNoChanges indicates there are no changes to submit
	ReasonNoChanges = "no changes"
)

Variables

View Source
var (
	// ErrTransactionCommitted is returned when attempting to modify a committed transaction
	ErrTransactionCommitted = errors.New("transaction already committed")
	// ErrTransactionRolledBack is returned when attempting to use a rolled-back transaction
	ErrTransactionRolledBack = errors.New("transaction was rolled back")
)

Sentinel errors for transaction operations

Functions

func IsConcurrentModificationError

func IsConcurrentModificationError(err error) bool

IsConcurrentModificationError returns true if the error indicates a concurrent modification conflict (CAS failure). This includes various git update-ref errors that occur when references are modified between read and write operations.

Types

type Absorber

type Absorber interface {
	ApplyHunksToBranch(ctx context.Context, branch Branch, hunksByCommit map[string][]git.Hunk) error
	FindTargetCommitForHunk(hunk git.Hunk, commitSHAs []string) (string, int, error)
}

Absorber applies staged hunks to appropriate commits

type ApplySplitOptions

type ApplySplitOptions struct {
	BranchToSplit string   // The branch being split
	BranchNames   []string // Branch names from oldest to newest
	BranchPoints  []int    // Commit indices (0 = HEAD, 1 = HEAD~1, etc.)
	// AsSibling creates all split branches as siblings on the same parent,
	// instead of creating a linear chain. When true:
	// - All new branches share the same parent (the original branch's parent)
	// - Branches are independent rather than stacked on each other
	AsSibling bool
}

ApplySplitOptions contains options for applying a split

type BatchFreezeResult

type BatchFreezeResult struct {
	AffectedBranches []string
	Errors           map[string]error
}

BatchFreezeResult represents the result of a batch freeze/unfreeze operation

type BatchLockResult

type BatchLockResult struct {
	AffectedBranches []string
	Errors           map[string]error
}

BatchLockResult represents the result of a batch lock/unlock operation

type Branch

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

Branch represents a branch in the stack

func FilterBranches

func FilterBranches(eng StackNavigator, predicate func(Branch) bool) []Branch

FilterBranches returns branches matching the predicate.

func NewBranch

func NewBranch(name string, reader branchReader) Branch

NewBranch creates a new immutable Branch

func (Branch) CanModify

func (b Branch) CanModify() bool

CanModify checks if the branch can be modified (not locked, frozen, or a worktree anchor)

func (Branch) DefaultPRBody

func (b Branch) DefaultPRBody() string

DefaultPRBody returns the default PR body for this branch. For single commit: uses the commit body (skips subject line). For multiple commits: creates a bulleted list of subjects in chronological order.

func (Branch) DefaultPRTitle

func (b Branch) DefaultPRTitle() string

DefaultPRTitle returns the default PR title for this branch. Uses the oldest commit subject, falling back to the branch name.

func (Branch) EnsureCanModify

func (b Branch) EnsureCanModify() error

EnsureCanModify checks if the branch can be modified and returns an error if not

func (Branch) Equal

func (b Branch) Equal(other Branch) bool

Equal checks if two branches are equal by comparing their names

func (Branch) GetAllCommits

func (b Branch) GetAllCommits(format CommitFormat) ([]string, error)

GetAllCommits returns commits for this branch in various formats

func (Branch) GetCommitAuthor

func (b Branch) GetCommitAuthor() (string, error)

GetCommitAuthor returns the commit author for this branch

func (Branch) GetCommitCount

func (b Branch) GetCommitCount() (int, error)

GetCommitCount returns the number of commits for this branch

func (Branch) GetCommitDate

func (b Branch) GetCommitDate() (time.Time, error)

GetCommitDate returns the commit date for this branch

func (Branch) GetDiffStats

func (b Branch) GetDiffStats() (added int, deleted int, err error)

GetDiffStats returns the number of lines added and deleted for this branch

func (Branch) GetExplicitScope

func (b Branch) GetExplicitScope() Scope

GetExplicitScope returns the explicit scope set for this branch (no inheritance)

func (Branch) GetLockReason

func (b Branch) GetLockReason() LockReason

GetLockReason returns why the branch is locked

func (Branch) GetMergedDownstack

func (b Branch) GetMergedDownstack() []git.MergedParent

GetMergedDownstack returns the merged downstack history for this branch

func (Branch) GetName

func (b Branch) GetName() string

GetName returns the branch name. This method allows Branch to implement the engine.Branch interface without creating circular dependencies.

func (Branch) GetPRSubmissionStatus

func (b Branch) GetPRSubmissionStatus() (PRSubmissionStatus, error)

GetPRSubmissionStatus returns the PR submission status for this branch

func (Branch) GetParent

func (b Branch) GetParent() *Branch

GetParent returns the parent branch (nil if no parent)

func (Branch) GetParentOrTrunk

func (b Branch) GetParentOrTrunk() string

GetParentOrTrunk returns the parent branch name, or trunk if no parent This is used for validation where we expect a parent to exist

func (Branch) GetPrInfo

func (b Branch) GetPrInfo() (*PrInfo, error)

GetPrInfo returns PR information for this branch

func (Branch) GetRevision

func (b Branch) GetRevision() (string, error)

GetRevision returns the SHA of this branch

func (Branch) GetScope

func (b Branch) GetScope() Scope

GetScope returns the scope for this branch, inheriting from parent if not set

func (Branch) IsBranchUpToDate

func (b Branch) IsBranchUpToDate() bool

IsBranchUpToDate checks if this branch is up to date with its parent A branch is up to date if its parent revision matches the stored parent revision

func (Branch) IsFrozen

func (b Branch) IsFrozen() bool

IsFrozen checks if the branch is frozen locally

func (Branch) IsLocked

func (b Branch) IsLocked() bool

IsLocked checks if the branch is locked for modifications

func (Branch) IsTracked

func (b Branch) IsTracked() bool

IsTracked checks if this branch is tracked (has metadata)

func (Branch) IsTrunk

func (b Branch) IsTrunk() bool

IsTrunk checks if this branch is the trunk

func (Branch) IsWorktreeAnchor

func (b Branch) IsWorktreeAnchor() bool

IsWorktreeAnchor checks if the branch is a worktree anchor branch

func (Branch) ModificationBlocker

func (b Branch) ModificationBlocker() string

ModificationBlocker returns a human-readable reason why the branch cannot be modified, or an empty string if the branch can be modified. This is useful for displaying status in UIs or logs without throwing errors.

func (Branch) NeedsRestack

func (b Branch) NeedsRestack() bool

NeedsRestack returns true if the branch needs to be restacked onto its parent. This is the inverse of IsBranchUpToDate - a branch needs restacking when its parent has moved and the branch is no longer based on the current parent tip.

type BranchInfo

type BranchInfo interface {
	GetCommitDate(branch Branch) (time.Time, error)
	GetCommitAuthor(branch Branch) (string, error)
	GetRevision(branch Branch) (string, error)
	GetCommitCount(branch Branch) (int, error)
	GetDiffStats(branch Branch) (added int, deleted int, err error)
	GetAllCommits(branch Branch, format CommitFormat) ([]string, error)
	GetParentCommitSHA(commitSHA string) (string, error)
	GetCommitSHA(branchName string, offset int) (string, error)
	GetRevisionForName(branchName string) (string, error)
	BatchGetRevisions(branchNames []string) (map[string]string, []error)
	GetCurrentRevision(ctx context.Context) (string, error)
	GetRecentTrunkCommits(count int) ([]git.RecentCommit, error)
	GetReflog(ctx context.Context, count int, format string) (string, error)
	// GetDivergencePoint returns the divergence point of a branch from its parent.
	// Returns the ParentBranchRevision from metadata if valid, otherwise the parent's current revision.
	GetDivergencePoint(branchName string) (string, error)
	// PreloadBranchData batch-loads metadata and revisions for all branches
	// into their respective caches. Call before parallel annotation building
	// to eliminate per-branch cache misses and mutex contention.
	PreloadBranchData()
}

BranchInfo provides commit and diff metadata

type BranchMutations

type BranchMutations interface {
	RenameBranch(ctx context.Context, oldBranch, newBranch Branch) error
	DeleteBranch(ctx context.Context, branch Branch) error
	DeleteBranches(ctx context.Context, branches []Branch) ([]string, error)
	CheckoutBranch(ctx context.Context, branch Branch) error
	CreateAndCheckoutBranch(ctx context.Context, branch Branch) error
	UpdateBranchRef(ctx context.Context, branchName, revision string) error
	CreateBranch(ctx context.Context, branchName string, startPoint string) error
	ResetHard(ctx context.Context, revision string) error
	ResetMerge(ctx context.Context, revision string) error
	Merge(ctx context.Context, revision string, opts MergeOptions) error
	MergeMultiple(ctx context.Context, branches []string, opts MergeOptions) error
	Fetch(ctx context.Context, remote string, branch string) error
	InteractiveRebase(ctx context.Context, onto string) error
}

BranchMutations handles branch lifecycle operations

type BranchReader

type BranchReader interface {
	StackNavigator
	BranchStatus
	BranchInfo
	GitDiffer
	WorkingTree
}

BranchReader is a composite interface for backward compatibility Prefer using the smaller, focused interfaces above for new code

type BranchRemoteStatus

type BranchRemoteStatus struct {
	LocalSha       string
	RemoteSha      string
	CommonAncestor string
}

BranchRemoteStatus represents the relationship between a local branch and its remote counterpart

func (BranchRemoteStatus) Ahead

func (s BranchRemoteStatus) Ahead() bool

Ahead returns true if the local branch has commits not yet on remote

func (BranchRemoteStatus) Behind

func (s BranchRemoteStatus) Behind() bool

Behind returns true if the remote branch has commits not yet on local

func (BranchRemoteStatus) Diverged

func (s BranchRemoteStatus) Diverged() bool

Diverged returns true if both local and remote have unique commits

func (BranchRemoteStatus) Matches

func (s BranchRemoteStatus) Matches() bool

Matches returns true if local and remote SHAs are identical

func (BranchRemoteStatus) MissingRemote

func (s BranchRemoteStatus) MissingRemote() bool

MissingRemote returns true if the branch does not exist on the remote

type BranchSet

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

BranchSet provides O(1) branch name lookups with a cached set. Use BranchNames() on StackNavigator to get a cached instance.

func (*BranchSet) Contains

func (s *BranchSet) Contains(name string) bool

Contains returns true if the branch name exists in the set.

func (*BranchSet) Len

func (s *BranchSet) Len() int

Len returns the number of branches in the set.

func (*BranchSet) Names

func (s *BranchSet) Names() []string

Names returns all branch names as a slice.

type BranchState

type BranchState struct {
	Parent        string         // Parent branch name
	Scope         string         // Scope string (may be empty)
	LockReason    git.LockReason // Lock reason (empty if not locked)
	Frozen        bool           // Whether branch is frozen (local-only state)
	BranchType    git.BranchType // Branch type (worktree-anchor, utility, etc.)
	RemoteSHA     string         // Remote SHA (populated by PopulateRemoteShas)
	LocalModified bool           // Has local metadata changes not yet pushed
}

BranchState holds the cached metadata state for a single branch. This consolidates what was previously stored in separate maps.

func (*BranchState) GetScope

func (s *BranchState) GetScope() Scope

GetScope returns the scope as a Scope type.

func (*BranchState) HasScope

func (s *BranchState) HasScope() bool

HasScope returns true if this branch has an explicit scope set.

func (*BranchState) IsLocked

func (s *BranchState) IsLocked() bool

IsLocked returns true if this branch is locked.

type BranchStateMap

type BranchStateMap map[string]*BranchState

BranchStateMap is a map of branch names to their state.

func (BranchStateMap) Delete

func (m BranchStateMap) Delete(name string)

Delete removes a branch from the map.

func (BranchStateMap) Get

func (m BranchStateMap) Get(branch Branch) *BranchState

Get returns the state for a branch, or nil if not found.

func (BranchStateMap) GetByName

func (m BranchStateMap) GetByName(name string) *BranchState

GetByName returns the state for a branch name, or nil if not found.

func (BranchStateMap) GetOrCreate

func (m BranchStateMap) GetOrCreate(name string) *BranchState

GetOrCreate returns the state for a branch, creating it if it doesn't exist.

func (BranchStateMap) Has

func (m BranchStateMap) Has(branch Branch) bool

Has returns true if the branch exists in the map.

func (BranchStateMap) HasByName

func (m BranchStateMap) HasByName(name string) bool

HasByName returns true if the branch name exists in the map.

func (BranchStateMap) Set

func (m BranchStateMap) Set(name string, state *BranchState)

Set sets the state for a branch.

type BranchStatus

type BranchStatus interface {
	GetBranch(branchName string) Branch
	IsTrunk(branch Branch) bool
	IsTracked(branch Branch) bool
	IsUpToDate(branch Branch) bool
	IsMergedIntoTrunk(ctx context.Context, branchName string) (bool, error)
	IsBranchEmpty(ctx context.Context, branchName string) (bool, error)
	GetDeletionStatus(ctx context.Context, branchName string) (DeletionStatus, error)
	BatchGetDeletionStatuses(ctx context.Context, branchNames []string) (map[string]DeletionStatus, error)
	GetScope(branch Branch) Scope
	GetStackDescription(branch Branch) *git.StackDescription
	IsLocked(branch Branch) bool
	GetLockReason(branch Branch) LockReason
	IsFrozen(branch Branch) bool
	IsWorktreeAnchor(branch Branch) bool
	GetBranchType(branch Branch) git.BranchType
	GetPrInfo(branch Branch) (*PrInfo, error)
	FindMostRecentTrackedAncestors(ctx context.Context, branchName string) ([]string, error)
	GetRemote() string
	GetRemoteURL(ctx context.Context) (string, error)
	GetBranchRemoteDifference(branchName string) (string, error)
	GetBranchRemoteStatus(branch Branch) (BranchRemoteStatus, error)
	GetMergedBranches(ctx context.Context, target string) (map[string]bool, error)
}

BranchStatus provides branch state information

type BranchTracking

type BranchTracking interface {
	TrackBranch(ctx context.Context, branchName string, parentBranchName string) error
	UntrackBranch(branchName string) error
	SetParent(ctx context.Context, branch Branch, parentBranch Branch) error
	// ReparentBranch changes a branch's parent while automatically preserving
	// its divergence point. Preferred over SetParent for existing branches.
	ReparentBranch(ctx context.Context, branch Branch, newParent Branch) error
	// ReparentBranches changes multiple branches to the same new parent while
	// preserving divergence points. All divergence points are captured before
	// any reparenting begins.
	ReparentBranches(ctx context.Context, branchNames []string, newParent Branch) error
	SetScope(ctx context.Context, branch Branch, scope Scope) error
	SetBranchType(branch Branch, branchType git.BranchType) error
	SetLocked(ctx context.Context, branches []Branch, reason LockReason) (BatchLockResult, error)
	SetFrozen(ctx context.Context, branches []Branch, frozen bool) (BatchFreezeResult, error)

	// BatchMarkNeedsPRBodyUpdate marks multiple branches as needing PR body update in a single atomic operation
	BatchMarkNeedsPRBodyUpdate(branchNames []string) error
	// ClearNeedsPRBodyUpdate clears the PR body update flag for a branch
	ClearNeedsPRBodyUpdate(branchName string) error
	// GetBranchesNeedingPRBodyUpdate returns all branches that need PR body updates
	GetBranchesNeedingPRBodyUpdate() []string

	// GetStackDescription returns the stack description for a branch's stack.
	// It first checks the stack ref, then falls back to legacy branch metadata.
	GetStackDescription(branch Branch) *git.StackDescription
	// SetStackDescription sets the stack description in the stack ref for a branch.
	// Returns an error if the branch is not part of a tracked stack.
	SetStackDescription(ctx context.Context, branch Branch, desc *git.StackDescription) error
	// ClearStackDescription removes the stack description from the stack ref.
	ClearStackDescription(ctx context.Context, branch Branch) error

	// GenerateStackID creates a new stack ID for a new stack.
	// Format: {timestamp-nanos}-{sanitized-root-branch}
	GenerateStackID(rootBranch string) string
	// GetStackID returns the stack ID for a branch.
	// Returns empty string for untracked branches or trunk.
	// For legacy branches without StackID, derives it from the stack root.
	GetStackID(branch Branch) string
	// EnsureStackID returns the stack ID for a branch, creating one if it doesn't exist.
	// This is used for lazy creation of stack metadata when setting descriptions or scopes.
	EnsureStackID(ctx context.Context, branch Branch) (string, error)
	// SetStackID sets the stack ID on a branch's metadata.
	SetStackID(ctx context.Context, branch Branch, stackID string) error
	// CreateStackRef creates a new stack ref with the given metadata.
	CreateStackRef(stackID string, meta *git.StackMeta) error
	// GetStackMeta returns the stack metadata for a stack ID.
	GetStackMeta(stackID string) (*git.StackMeta, error)
	// SyncStackIDFromParent updates a branch's stack ID to match its parent's.
	// This should be called after reparenting operations to keep stack IDs consistent.
	// Returns nil if the parent is trunk (keeps existing stack ID) or if no change is needed.
	SyncStackIDFromParent(ctx context.Context, branch Branch) error
}

BranchTracking handles branch tracking operations

type BranchWriter

BranchWriter is a composite interface for backward compatibility Prefer using the smaller, focused interfaces above for new code

type CommitFormat

type CommitFormat string

CommitFormat specifies the format for commit output

const (
	// CommitFormatSHA is the full commit SHA
	CommitFormatSHA CommitFormat = "SHA" // Full SHA
	// CommitFormatReadable is a readable one-line format
	CommitFormatReadable CommitFormat = "READABLE" // Oneline format: "abc123 Commit message"
	// CommitFormatReadableWithDate includes an ISO date: "abc123\t2024-01-15T10:30:00Z\tCommit message"
	CommitFormatReadableWithDate CommitFormat = "READABLE_WITH_DATE"
	// CommitFormatMessage is the full commit message
	CommitFormatMessage CommitFormat = "MESSAGE" // Full commit message
	// CommitFormatSubject is the first line of the commit message
	CommitFormatSubject CommitFormat = "SUBJECT" // First line of commit message
)

type CommitOperations

type CommitOperations interface {
	Commit(ctx context.Context, message string, verbose int, noVerify bool) error
	CommitWithOptions(ctx context.Context, opts git.CommitOptions) error
	StageAll(ctx context.Context) error
	StagePatch(ctx context.Context) error
	StageHunks(ctx context.Context, hunks []git.Hunk) error
	StashPush(ctx context.Context, message string) (string, error)
	StashPushStaged(ctx context.Context, message string) (string, error)
	StashPop(ctx context.Context) error
}

CommitOperations handles staging and committing

type ContinueRebaseResult

type ContinueRebaseResult struct {
	Result              int    // git.RebaseResult value (0 = RebaseDone, 1 = RebaseConflict)
	BranchName          string // Only set if Result is RebaseDone
	RerereResolvedCount int    // Number of rebase continuations handled by git rerere
}

ContinueRebaseResult represents the result of continuing a rebase

type DeletionReasonKind

type DeletionReasonKind string

DeletionReasonKind is a machine-readable reason for branch deletion eligibility.

const (
	DeletionReasonNone            DeletionReasonKind = "none"
	DeletionReasonClosedPR        DeletionReasonKind = "closed_pr"
	DeletionReasonMergedPR        DeletionReasonKind = "merged_pr"
	DeletionReasonMergedIntoTrunk DeletionReasonKind = "merged_into_trunk"
	DeletionReasonEmptyWithPR     DeletionReasonKind = "empty_with_pr"
	// DeletionReasonGhost is set for branches whose metadata is still on
	// disk but whose git ref has been deleted out-of-band (e.g., the user
	// ran `git branch -D` directly). Sync synthesizes this so the metadata
	// gets cleaned up and surviving children get reparented past the gap.
	DeletionReasonGhost DeletionReasonKind = "ghost"
)

type DeletionStatus

type DeletionStatus struct {
	SafeToDelete       bool               // True if the branch is merged, closed, or empty (with PR)
	Reason             string             // Human-readable reason why it's safe (or not) to delete
	Kind               DeletionReasonKind // Machine-readable deletion reason
	HasUnpushedChanges bool               // Local branch is ahead of or diverged from remote
}

DeletionStatus represents the deletion status of a branch

type Engine

type Engine interface {
	BranchReader
	BranchWriter
	PRManager
	SyncManager
	StackRewriter
	Absorber
	UndoManager
	RemoteMetadataManager
	WorktreeRegistry
	Git() git.Runner

	// SnapshotForWorktree creates a deep copy of engine state for initializing
	// worktree engines without the cost of rebuildInternal.
	SnapshotForWorktree() WorktreeSnapshot
}

Engine is the core interface for branch state management It composes BranchReader, BranchWriter, PRManager, SyncManager, HistoryRewriter, and UndoManager for backward compatibility. New code should prefer using the smaller interfaces. Thread-safe: All methods are safe for concurrent use

func NewEngine

func NewEngine(opts Options) (Engine, error)

NewEngine creates a new engine instance

func NewEngineForWorktree

func NewEngineForWorktree(opts WorktreeEngineOptions) (Engine, error)

NewEngineForWorktree creates an engine for a worktree session using a snapshot from the parent engine. This skips rebuildInternal and maybeAutoFetchRemoteMetadata since worktrees share .git with the parent and the metadata is identical.

type FieldDiff

type FieldDiff struct {
	Field       string
	LocalValue  any
	RemoteValue any
}

FieldDiff represents a difference in a single metadata field

type GitDiffer

type GitDiffer interface {
	GetMergeBase(rev1, rev2 string) (string, error)
	GetChangedFiles(ctx context.Context, base, head string) ([]string, error)
	IsDiffEmpty(ctx context.Context, base, head string) (bool, error)
	ShowDiff(ctx context.Context, left, right string, stat bool) (string, error)
	ShowCommits(ctx context.Context, base, head string, patch, stat bool) (string, error)
	IsAncestor(ancestor, descendant string) (bool, error)
	// GetDiffBetween returns raw diff between two refs, suitable for parsing into hunks.
	GetDiffBetween(ctx context.Context, base, head string, files ...string) (string, error)
}

GitDiffer handles diff and merge operations

type IndependentStack

type IndependentStack struct {
	RootBranch string
	Branches   []string
}

IndependentStack describes a standalone stack rooted at a direct child of trunk.

func DiscoverIndependentStacks

func DiscoverIndependentStacks(eng BranchReader) []IndependentStack

DiscoverIndependentStacks returns all stacks rooted at direct children of trunk.

Each stack includes its root branch first, followed by descendants in the graph's depth-first order. Branches from separate stacks are independent of each other.

func DiscoverIndependentStacksWithSort

func DiscoverIndependentStacksWithSort(eng BranchReader, strategy SortStrategy) []IndependentStack

DiscoverIndependentStacksWithSort is like DiscoverIndependentStacks, but allows callers to choose how sibling branches are ordered.

type Initializer

type Initializer interface {
	Reset(newTrunkName string) error
	Rebuild(newTrunkName string) error
}

Initializer handles repository initialization operations

type LockReason

type LockReason = git.LockReason

LockReason is re-exported from git package

const (
	// LockReasonNone indicates the branch is not locked
	LockReasonNone LockReason = git.LockReasonNone
	// LockReasonUser indicates the branch was manually locked by the user
	LockReasonUser LockReason = git.LockReasonUser
	// LockReasonConsolidating indicates the branch is being consolidated
	LockReasonConsolidating LockReason = git.LockReasonConsolidating
	// LockReasonDraining indicates the branch is being drained (merge drain in progress)
	LockReasonDraining LockReason = git.LockReasonDraining
)

type MergeOptions

type MergeOptions struct {
	FFOnly  bool
	NoEdit  bool
	NoFF    bool
	Message string
}

MergeOptions contains options for merging branches

type MetadataDiff

type MetadataDiff struct {
	Branch      string
	LocalMeta   *git.Meta
	RemoteMeta  *git.Meta
	Differences []FieldDiff
	HasConflict bool
}

MetadataDiff represents the differences between local and remote metadata

type MetadataTx

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

MetadataTx represents an atomic metadata transaction. It batches multiple metadata updates and commits them atomically using git update-ref --stdin. All updates either succeed together or fail together.

func (*MetadataTx) Commit

func (tx *MetadataTx) Commit(ctx context.Context) error

Commit atomically applies all staged metadata changes and deletions. All updates succeed together or fail together via git update-ref --stdin. On success, the in-memory cache is updated to reflect the changes.

func (*MetadataTx) DeleteLocalMeta

func (tx *MetadataTx) DeleteLocalMeta(branch string) error

DeleteLocalMeta stages a local metadata deletion for atomic commit.

func (*MetadataTx) DeleteMeta

func (tx *MetadataTx) DeleteMeta(branch string) error

DeleteMeta stages a metadata deletion for atomic commit. The deletion uses CAS validation to ensure the ref hasn't changed.

func (*MetadataTx) IsCommitted

func (tx *MetadataTx) IsCommitted() bool

IsCommitted returns true if the transaction has been committed.

func (*MetadataTx) Rollback

func (tx *MetadataTx) Rollback()

Rollback discards all staged changes without applying them.

func (*MetadataTx) UpdateLocalMeta

func (tx *MetadataTx) UpdateLocalMeta(branch string, meta *git.LocalMeta) error

UpdateLocalMeta stages a local metadata update for atomic commit.

func (*MetadataTx) UpdateMeta

func (tx *MetadataTx) UpdateMeta(branch string, meta *git.Meta) error

UpdateMeta stages a metadata update for atomic commit. The update is not applied until Commit() is called.

type Options

type Options struct {
	// RepoRoot is the root directory of the Git repository
	RepoRoot string

	// Trunk is the primary trunk branch name (e.g., "main", "master")
	Trunk string

	// MaxUndoStackDepth is the maximum number of undo snapshots to keep.
	// If zero or negative, defaults to DefaultMaxUndoStackDepth (10).
	MaxUndoStackDepth int

	// MaxConcurrency is the maximum number of concurrent validation operations.
	// If zero or negative, defaults to min(NumCPU, 8).
	MaxConcurrency int

	// Git is the git runner to use. If nil, a default real git runner is used.
	Git git.Runner

	// Writer is the output writer for warnings and informational messages.
	// If nil, os.Stderr is used.
	Writer io.Writer
}

Options contains configuration options for creating an Engine

type OrphanedMetadataAction

type OrphanedMetadataAction string

OrphanedMetadataAction represents the action to take for orphaned metadata

const (
	// OrphanedActionDelete indicates the local metadata should be deleted
	OrphanedActionDelete OrphanedMetadataAction = "delete"
	// OrphanedActionPrompt indicates the user should be prompted
	OrphanedActionPrompt OrphanedMetadataAction = "prompt"
)

type OrphanedMetadataInfo

type OrphanedMetadataInfo struct {
	BranchName      string
	Action          OrphanedMetadataAction
	HasLocalChanges bool
	ExistsLocally   bool
	LocalMeta       *git.Meta
}

OrphanedMetadataInfo contains information about orphaned local metadata

type PRManager

type PRManager interface {
	UpsertPrInfo(ctx context.Context, branch Branch, prInfo *PrInfo) error
	GetBranchRemoteStatus(branch Branch) (BranchRemoteStatus, error)
	PopulateRemoteShas() error
	PushBranch(ctx context.Context, branch Branch, remote string, opts git.PushOptions) error
	// Navigation comment ID caching (stored in local metadata)
	GetNavigationCommentID(branch Branch) (int64, error)
	SetNavigationCommentID(branch Branch, commentID int64) error
	ClearNavigationCommentID(branch Branch) error
}

PRManager provides operations for managing pull request information Thread-safe: All methods are safe for concurrent use

type PRSubmissionStatus

type PRSubmissionStatus struct {
	Action      string // "create", "update", or "skip"
	NeedsUpdate bool   // True if the branch has changes or metadata needs update
	Reason      string // Reason for the status
	PRNumber    *int
	PRInfo      *PrInfo
}

PRSubmissionStatus represents the submission status of a branch

type PendingChange

type PendingChange struct {
	Path   string
	Status string // "A", "M", "D", "??", etc.
	Staged bool
}

PendingChange represents a changed file in the working directory

type PrInfo

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

PrInfo represents PR information for a branch PrInfo is immutable - use With* methods to create modified copies

func NewPrInfo

func NewPrInfo(number *int, title, body, state, base, url string, isDraft bool) *PrInfo

NewPrInfo creates a new PrInfo instance

func NewPrInfoFromMeta

func NewPrInfoFromMeta(meta *git.Meta) *PrInfo

NewPrInfoFromMeta creates a PrInfo from git.Meta

func NewPrInfoFull

func NewPrInfoFull(number *int, title, body, state, base, url string, isDraft bool, lockReason LockReason, mergeBranch string) *PrInfo

NewPrInfoFull creates a new PrInfo instance with all fields

func NewPrInfoWithLockReason

func NewPrInfoWithLockReason(number *int, title, body, state, base, url string, isDraft bool, lockReason LockReason) *PrInfo

NewPrInfoWithLockReason creates a new PrInfo instance including lock reason

func (*PrInfo) Base

func (p *PrInfo) Base() string

Base returns the base branch name

func (*PrInfo) Body

func (p *PrInfo) Body() string

Body returns the PR body

func (*PrInfo) IsDraft

func (p *PrInfo) IsDraft() bool

IsDraft returns whether the PR is a draft

func (*PrInfo) IsLocked

func (p *PrInfo) IsLocked() bool

IsLocked returns whether the PR footer shows it as locked

func (*PrInfo) LockReason

func (p *PrInfo) LockReason() LockReason

LockReason returns the reason why the PR is locked

func (*PrInfo) MarshalJSON

func (p *PrInfo) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler for PrInfo

func (*PrInfo) MergeBranch

func (p *PrInfo) MergeBranch() string

MergeBranch returns the name of the merge branch this PR is part of

func (*PrInfo) Number

func (p *PrInfo) Number() *int

Number returns the PR number

func (*PrInfo) State

func (p *PrInfo) State() string

State returns the PR state (MERGED, CLOSED, OPEN)

func (*PrInfo) Title

func (p *PrInfo) Title() string

Title returns the PR title

func (*PrInfo) URL

func (p *PrInfo) URL() string

URL returns the PR URL

func (*PrInfo) WithBase

func (p *PrInfo) WithBase(base string) *PrInfo

WithBase returns a new PrInfo with the base field updated

func (*PrInfo) WithBody

func (p *PrInfo) WithBody(body string) *PrInfo

WithBody returns a new PrInfo with the body field updated

func (*PrInfo) WithIsDraft

func (p *PrInfo) WithIsDraft(isDraft bool) *PrInfo

WithIsDraft returns a new PrInfo with the isDraft field updated

func (*PrInfo) WithLockReason

func (p *PrInfo) WithLockReason(reason LockReason) *PrInfo

WithLockReason returns a new PrInfo with the lockReason field updated

func (*PrInfo) WithMergeBranch

func (p *PrInfo) WithMergeBranch(branch string) *PrInfo

WithMergeBranch returns a new PrInfo with the mergeBranch field updated

func (*PrInfo) WithNumber

func (p *PrInfo) WithNumber(number *int) *PrInfo

WithNumber returns a new PrInfo with the number field updated

func (*PrInfo) WithState

func (p *PrInfo) WithState(state string) *PrInfo

WithState returns a new PrInfo with the state field updated

func (*PrInfo) WithTitle

func (p *PrInfo) WithTitle(title string) *PrInfo

WithTitle returns a new PrInfo with the title field updated

func (*PrInfo) WithTitleAndBody

func (p *PrInfo) WithTitleAndBody(title, body string) *PrInfo

WithTitleAndBody returns a new PrInfo with both title and body fields updated This is more efficient than chaining WithTitle().WithBody() as it only creates one copy

func (*PrInfo) WithURL

func (p *PrInfo) WithURL(url string) *PrInfo

WithURL returns a new PrInfo with the url field updated

type PullResult

type PullResult int

PullResult represents the result of pulling trunk

const (
	// PullDone indicates the pull was successful
	PullDone PullResult = iota
	// PullUnneeded indicates no pull was needed
	PullUnneeded
	// PullConflict indicates a conflict occurred during pull
	PullConflict
)

type RebaseSpec

type RebaseSpec struct {
	Branch      string // Branch to rebase
	NewParent   string // New upstream to rebase onto
	OldUpstream string // Current base to replay commits from
}

RebaseSpec describes a planned rebase operation

type RebaseValidation

type RebaseValidation struct {
	Success          bool                // Whether all rebases would succeed
	FailedBranch     string              // Which branch caused the conflict (if any)
	ErrorType        ValidationErrorType // Type of error (conflict vs system error)
	ErrorMessage     string              // Error message describing the failure
	ConflictingFiles []string            // Files that have conflicts (if ErrorType is ValidationErrorConflict)
	NewSHAs          map[string]string   // Branch -> resulting SHA after rebase (if successful)
	RerereResolved   map[string]int      // Branch -> number of conflicts auto-resolved by rerere during validation
}

RebaseValidation is the result of dry-run validation

type RemoteMetadataManager

type RemoteMetadataManager interface {
	IsRemoteSyncEnabled() bool
	SetRemoteSyncEnabled(enabled bool)
	SetLastModifiedBy(branchName string) error
	BatchSetLastModifiedBy(branchNames []string) error
	LoadRemoteMetadataCache() error
	ApplyRemoteMetadataIfExists(branchName string) error
	GetRemoteMetadataCache() RemoteMetadataView
	ComputeMetadataDiff(branch string) (*MetadataDiff, error)
	ComputeAllMetadataDiffs() ([]*MetadataDiff, error)
	AcceptRemoteMetadata(branch string) error
	RejectRemoteMetadata(branch string)
	HasLocalModifications(branch string) bool
	FindOrphanedLocalMetadata() ([]OrphanedMetadataInfo, error)
	DeleteLocalMetadataHash(branchName string) error
	DeleteMetadata(ctx context.Context, branchName string) error
	FetchRemoteMetadata(ctx context.Context) error
	ConfigureRemoteMetadataSync(ctx context.Context) error
	// GetStackIDsForBranches returns the unique stack IDs for the given branches.
	// This is used to determine which stack refs need to be pushed to remote.
	GetStackIDsForBranches(branches []Branch) []string
}

RemoteMetadataManager provides operations for syncing branch metadata with remote

type RemoteMetadataView

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

RemoteMetadataView provides read-only access to the remote metadata cache. It is a lightweight snapshot — no map copying is needed because the underlying map is replaced atomically on each LoadRemoteMetadataCache call.

func (RemoteMetadataView) Get

func (v RemoteMetadataView) Get(branch string) *git.Meta

Get returns the remote metadata for a branch, or nil if not present.

func (RemoteMetadataView) Has

func (v RemoteMetadataView) Has(branch string) bool

Has returns true if the branch has remote metadata.

func (RemoteMetadataView) Len

func (v RemoteMetadataView) Len() int

Len returns the number of entries in the cache.

func (RemoteMetadataView) Range

func (v RemoteMetadataView) Range(fn func(branch string, meta *git.Meta) bool)

Range iterates over all entries. The callback receives each branch name and its metadata. Return false from the callback to stop iteration.

type RestackBatchResult

type RestackBatchResult struct {
	ConflictBranch    string                         // The branch that hit a conflict
	RebasedBranchBase string                         // The parent revision for the conflict
	RemainingBranches []string                       // Branches that weren't reached
	Results           map[string]RestackBranchResult // Results for each branch attempted
}

RestackBatchResult represents the result of restacking multiple branches

type RestackBranchProgressFunc

type RestackBranchProgressFunc func(branch Branch, result RestackBranchResult)

RestackBranchProgressFunc is called after a branch has been processed during a batch restack.

type RestackBranchResult

type RestackBranchResult struct {
	Result              RestackResult
	RebasedBranchBase   string     // The new parent revision after successful rebase (only set if Result is RestackDone or RestackConflict)
	Reparented          bool       // True if the branch was reparented due to merged/deleted parent
	OldParent           string     // The old parent branch name (only set if Reparented is true)
	NewParent           string     // The new parent branch name (only set if Reparented is true)
	LockReason          LockReason // Reason why the branch is locked
	Frozen              bool       // True if the branch is frozen
	RerereResolvedCount int        // Number of rebase continuations handled by git rerere
}

RestackBranchResult represents the result of restacking a branch, including the rebased branch base

func (RestackBranchResult) IsLocked

func (r RestackBranchResult) IsLocked() bool

IsLocked returns true if the branch is locked

type RestackPlan

type RestackPlan struct {
	Specs          []RebaseSpec
	BranchMap      map[string]bool
	ApplyMap       map[string]bool
	PlannedResults map[string]RestackBranchResult
	Items          map[string]RestackPlanItem
}

RestackPlan describes validation specs, pre-skipped branch results, and per-branch apply decisions for a restack operation.

type RestackPlanAction

type RestackPlanAction int

RestackPlanAction describes how a planned branch should be applied.

const (
	// RestackPlanApplyValidated applies a SHA produced by rebase validation.
	RestackPlanApplyValidated RestackPlanAction = iota
	// RestackPlanApplyFrozen updates a frozen branch from its remote ref.
	RestackPlanApplyFrozen
	// RestackPlanApplyAnchor updates a worktree anchor to trunk.
	RestackPlanApplyAnchor
)

type RestackPlanItem

type RestackPlanItem struct {
	Branch      string
	NewParent   string
	ParentRev   string
	OldUpstream string
	TargetRev   string
	Action      RestackPlanAction
	Skip        bool
	SkipResult  RestackBranchResult
	Reparented  bool
	OldParent   string
}

RestackPlanItem describes how a branch should be handled during restack.

type RestackResult

type RestackResult int

RestackResult represents the result of restacking a branch

const (
	// RestackDone indicates the restack was successful
	RestackDone RestackResult = iota
	// RestackUnneeded indicates no restack was needed
	RestackUnneeded
	// RestackConflict indicates a conflict occurred during restack
	RestackConflict
)

type Scope

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

Scope represents a branch scope that can be empty, a regular scope, or an inheritance breaker

func Empty

func Empty() Scope

Empty returns an empty scope

func NewScope

func NewScope(value string) Scope

NewScope creates a new scope with the given value

func None

func None() Scope

None returns a scope that breaks inheritance

func (Scope) ApplyToTitle

func (s Scope) ApplyToTitle(title string) string

ApplyToTitle adds or replaces a scope prefix in a title. If the title already has a scope prefix and it differs from this scope, it's replaced. If no scope prefix exists, this scope is prepended. Returns the original title if this scope is empty.

func (Scope) Equal

func (s Scope) Equal(other Scope) bool

Equal checks if two scopes are equal

func (Scope) IsDefined

func (s Scope) IsDefined() bool

IsDefined returns true if the scope has a meaningful value (not empty and not none)

func (Scope) IsEmpty

func (s Scope) IsEmpty() bool

IsEmpty returns true if the scope is empty

func (Scope) IsNone

func (s Scope) IsNone() bool

IsNone returns true if the scope breaks inheritance

func (Scope) MarshalJSON

func (s Scope) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler

func (Scope) String

func (s Scope) String() string

String returns the string representation of the scope

func (Scope) TitleNeedsUpdate

func (s Scope) TitleNeedsUpdate(title string) bool

TitleNeedsUpdate checks if a title needs to be updated due to this scope.

func (*Scope) UnmarshalJSON

func (s *Scope) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler

type Snapshot

type Snapshot struct {
	Timestamp     time.Time         `json:"timestamp"`
	Command       string            `json:"command"`
	Args          []string          `json:"args"`
	CurrentBranch string            `json:"current_branch"`
	BranchSHAs    map[string]string `json:"branch_shas"`   // branch name -> SHA
	MetadataSHAs  map[string]string `json:"metadata_shas"` // branch name -> metadata ref SHA
}

Snapshot represents a saved state of the repository

type SnapshotInfo

type SnapshotInfo struct {
	ID          string    // Filename without extension
	Command     string    // Command name
	Args        []string  // Command arguments
	Timestamp   time.Time // When the snapshot was taken
	HeadSHA     string    // SHA of the current branch at snapshot time
	DisplayName string    // Human-readable description
}

SnapshotInfo provides metadata about a snapshot for display

type SnapshotOptions

type SnapshotOptions struct {
	Command string
	Args    []string
}

SnapshotOptions contains options for taking a snapshot

type SortStrategy

type SortStrategy string

SortStrategy specifies how branches should be sorted in displays

const (
	// SortStrategyAlphabetical sorts branches by name ascending (A-Z)
	SortStrategyAlphabetical SortStrategy = "ALPHABETICAL"
	// SortStrategySmart sorts branches by name descending (newest first) and hoists the active path
	SortStrategySmart SortStrategy = "SMART"
)

type SquashOptions

type SquashOptions struct {
	Message  string
	NoEdit   bool
	NoVerify bool
}

SquashOptions contains options for squashing commits

type StackGraph

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

StackGraph is an immutable snapshot of the branch stack relationships. It is built once from the engine and then used for traversals/rendering without further engine calls.

func BuildStackGraph

func BuildStackGraph(eng BranchReader, strategy SortStrategy, filter func(Branch) bool) *StackGraph

BuildStackGraph constructs a StackGraph using the provided engine reader and sorting strategy. The optional filter is applied to branches; filtered-out branches are omitted along with their subtrees.

func (*StackGraph) BranchesAtDepth

func (g *StackGraph) BranchesAtDepth(depth int) []Branch

BranchesAtDepth returns all branches at the specified depth. Returns nil if no branches exist at that depth.

func (*StackGraph) ChildBranches

func (g *StackGraph) ChildBranches(branch Branch) []Branch

ChildBranches returns the child branches for the given branch.

func (*StackGraph) Children

func (g *StackGraph) Children(branch Branch) []string

Children returns the child branch names for the given branch.

func (*StackGraph) CollectBranches

func (g *StackGraph) CollectBranches(root Branch) []Branch

CollectBranches returns all branches in depth-first order starting from root. The root is included as the first element.

func (*StackGraph) CurrentBranch

func (g *StackGraph) CurrentBranch() string

CurrentBranch returns the name of the currently checked-out branch.

func (*StackGraph) Downstack

func (g *StackGraph) Downstack(branch Branch, includeCurrent bool) []Branch

Downstack returns parents of the branch (downstack).

func (*StackGraph) ForEachDepth

func (g *StackGraph) ForEachDepth(fn func(depth int, branches []Branch) error) error

ForEachDepth iterates over branches grouped by depth, calling fn for each depth level. The function receives the depth (0 = trunk level) and branches at that depth. If fn returns an error, iteration stops and that error is returned. Branches at the same depth can be processed in parallel by fn. This is useful for operations like restacking where parents must complete before children.

func (*StackGraph) FullStack

func (g *StackGraph) FullStack(branch Branch) []Branch

FullStack returns the entire stack (parents + current + children).

func (*StackGraph) GetBranchesByDepth

func (g *StackGraph) GetBranchesByDepth() map[int][]string

GetBranchesByDepth returns a map from depth to branch names at that depth. This is useful for parallel operations where branches at the same depth are independent.

func (*StackGraph) GetNode

func (g *StackGraph) GetNode(branchName string) *StackNode

GetNode returns the StackNode for a branch by name, or nil if not found.

func (*StackGraph) IsDescendant

func (g *StackGraph) IsDescendant(branch Branch, potentialDescendant Branch) bool

IsDescendant returns true if potentialDescendant is a descendant of branch. This is useful for cycle detection when moving branches.

func (*StackGraph) IsLeaf

func (g *StackGraph) IsLeaf(branch Branch) bool

IsLeaf returns true if the branch has no children in the graph.

Note: Returns true if the branch is not in the graph (nil node). Callers that need fail-safe behavior (treating unknown branches as non-leaves) should check GetNode() first, as AllBranchesAreLeaves does.

func (*StackGraph) IsRelated

func (g *StackGraph) IsRelated(branch1, branch2 Branch) bool

IsRelated returns true if either branch is an ancestor or descendant of the other.

func (*StackGraph) MaxDepth

func (g *StackGraph) MaxDepth() int

MaxDepth returns the maximum depth in the graph. Returns -1 if the graph is empty.

func (*StackGraph) Node

func (g *StackGraph) Node(branch Branch) *StackNode

Node returns the StackNode for the given branch.

func (*StackGraph) Parent

func (g *StackGraph) Parent(branch Branch) string

Parent returns the parent branch name (empty string if none).

func (*StackGraph) Range

func (g *StackGraph) Range(branch Branch, rng StackRange) []Branch

Range returns branches matching the provided StackRange, ordered the same as the legacy GetRelativeStack implementation: ancestors (oldest to nearest), current, then descendants. Descendants are traversed depth-first using the graph's pre-sorted children.

func (*StackGraph) RootBranches

func (g *StackGraph) RootBranches() []string

RootBranches returns all root branch names (branches with no parent in the graph).

func (*StackGraph) TrunkName

func (g *StackGraph) TrunkName() string

TrunkName returns the trunk/main branch name.

func (*StackGraph) Upstack

func (g *StackGraph) Upstack(branch Branch, includeCurrent bool) []Branch

Upstack returns children of the branch (upstack).

type StackNavigator

type StackNavigator interface {
	AllBranches() []Branch
	BranchNames() *BranchSet
	CurrentBranch() *Branch
	Trunk() Branch
	GetBranch(branchName string) Branch
	Graph(strategy SortStrategy) *StackGraph
	BranchesDepthFirst(startBranch Branch) iter.Seq2[Branch, int]
	SortBranchesTopologically(branches []Branch) []Branch
	FindBranchForCommit(commitSHA string) (string, error)
	ValidateOnBranch() (string, error)
	IsBranchEmpty(ctx context.Context, branchName string) (bool, error)
	GetScope(branch Branch) Scope
	GetRemote() string
	GetRepoInfo(ctx context.Context) (string, string, error)
	IsInsideRepo() bool
}

StackNavigator handles stack relationship queries

type StackNode

type StackNode struct {
	Branch   Branch
	Parent   string
	Children []string
	Depth    int
	IsTrunk  bool
}

StackNode represents a branch within a stack snapshot.

type StackRange

type StackRange struct {
	RecursiveParents  bool
	IncludeCurrent    bool
	RecursiveChildren bool
}

StackRange specifies the range of branches to include in stack operations

func StackRangeDownstack

func StackRangeDownstack(includeCurrent bool) StackRange

StackRangeDownstack returns a range for parents (downstack) traversal.

func StackRangeFull

func StackRangeFull() StackRange

StackRangeFull returns a range for full stack (parents + current + children).

func StackRangeUpstack

func StackRangeUpstack(includeCurrent bool) StackRange

StackRangeUpstack returns a range for children (upstack) traversal.

type StackRewriter

type StackRewriter interface {
	// SquashCurrentBranch squashes commits on the current branch
	SquashCurrentBranch(ctx context.Context, opts SquashOptions) error

	// ApplySplitToCommits creates branches at specified commit points
	ApplySplitToCommits(ctx context.Context, opts ApplySplitOptions) error

	// Detach detaches HEAD to a specific revision
	Detach(ctx context.Context, revision string) error

	// DetachAndResetBranchChanges detaches and resets branch changes
	DetachAndResetBranchChanges(ctx context.Context, branchName string) error

	// ForceCheckoutBranch force checks out a branch
	ForceCheckoutBranch(ctx context.Context, branch Branch) error
}

StackRewriter provides operations for modifying commit history and branch structure Thread-safe: All methods are safe for concurrent use

type SyncManager

type SyncManager interface {
	// Sync operations
	PullTrunk(ctx context.Context) (PullResult, error)
	ResetTrunkToRemote(ctx context.Context) error
	PlanRestack(ctx context.Context, branches []Branch) (*RestackPlan, error)
	RestackBranches(ctx context.Context, branches []Branch) (RestackBatchResult, error)
	RestackBranchesWithProgress(ctx context.Context, branches []Branch, progress RestackBranchProgressFunc) (RestackBatchResult, error)
	RestackBranchesWithValidatedRebases(ctx context.Context, branches []Branch, validation *RebaseValidation, progress RestackBranchProgressFunc) (RestackBatchResult, error)
	RestackBranchesWithValidatedPlan(ctx context.Context, branches []Branch, validation *RebaseValidation, plan *RestackPlan, progress RestackBranchProgressFunc) (RestackBatchResult, error)
	ContinueRebase(ctx context.Context, branchName string, rebasedBranchBase string) (ContinueRebaseResult, error)
	Rebase(ctx context.Context, branchName, upstream, oldUpstream string) (RestackResult, error)

	// Validation
	ValidateRebases(ctx context.Context, specs []RebaseSpec) (*RebaseValidation, error)
	ValidateRebasesParallel(ctx context.Context, specs []RebaseSpec) (*RebaseValidation, error)
}

SyncManager provides operations for syncing and restacking branches Thread-safe: All methods are safe for concurrent use

type UndoManager

type UndoManager interface {
	TakeSnapshot(opts SnapshotOptions) error
	GetSnapshots() ([]SnapshotInfo, error)
	LoadSnapshot(snapshotID string) (*Snapshot, error)
	RestoreSnapshot(ctx context.Context, snapshotID string) error
}

UndoManager provides operations for undo/redo functionality Thread-safe: All methods are safe for concurrent use

type ValidationErrorType

type ValidationErrorType int

ValidationErrorType distinguishes between conflict errors and system errors

const (
	// ValidationErrorNone indicates no error occurred
	ValidationErrorNone ValidationErrorType = iota
	// ValidationErrorConflict indicates a merge conflict occurred
	ValidationErrorConflict
	// ValidationErrorSystem indicates a system error (not a conflict)
	ValidationErrorSystem
)

type ValidationResult

type ValidationResult int

ValidationResult represents the validation state of a branch

const (
	// ValidationResultValid indicates the branch is valid
	ValidationResultValid ValidationResult = iota
	// ValidationResultInvalidParent indicates the branch has an invalid parent
	ValidationResultInvalidParent
	// ValidationResultBadParentRevision indicates the branch has a bad parent revision
	ValidationResultBadParentRevision
	// ValidationResultBadParentName indicates the branch has a bad parent name
	ValidationResultBadParentName
	// ValidationResultTrunk indicates the branch is a trunk
	ValidationResultTrunk
)

type WorkingTree

type WorkingTree interface {
	HasStagedChanges(ctx context.Context) (bool, error)
	HasUnstagedChanges(ctx context.Context) (bool, error)
	HasUntrackedFiles(ctx context.Context) (bool, error)
	GetUnstagedDiff(ctx context.Context, files ...string) (string, error)
	GetUntrackedFileHunks(ctx context.Context) ([]git.Hunk, error)
	GetPendingChanges(ctx context.Context) ([]PendingChange, error)
	GetCommitTemplate(ctx context.Context) (string, error)
	GetUnmergedFiles(ctx context.Context) ([]string, error)
	ParseStagedHunks(ctx context.Context) ([]git.Hunk, error)
	ListWorktrees(ctx context.Context) ([]string, error)
	IsRebaseInProgress(ctx context.Context) bool
	GetRebaseHead() (string, error)
	HasUncommittedChanges(ctx context.Context) bool
	CheckoutPaths(ctx context.Context, branch string, pathspecs []string) error
	RemovePaths(ctx context.Context, pathspecs []string) error
	StashList(ctx context.Context) (string, error)
}

WorkingTree handles worktree and staging area operations

type WorktreeCheckoutMode

type WorktreeCheckoutMode int

WorktreeCheckoutMode specifies how files are checked out in a worktree.

const (
	// WorktreeCheckoutFull checks out all files (default behavior).
	WorktreeCheckoutFull WorktreeCheckoutMode = iota
	// WorktreeCheckoutShallow creates a worktree without checking out files.
	// This is faster for validation-only operations that don't need actual files.
	WorktreeCheckoutShallow
)

type WorktreeEngineOptions

type WorktreeEngineOptions struct {
	// WorktreePath is the root directory of the worktree.
	WorktreePath string

	// Snapshot is the parent engine's state snapshot.
	Snapshot WorktreeSnapshot

	// Writer is the output writer for warnings. If nil, os.Stderr is used.
	Writer io.Writer
}

WorktreeEngineOptions configures NewEngineForWorktree.

type WorktreeInfo

type WorktreeInfo struct {
	Name         string    // User-provided name for display (empty for legacy worktrees)
	Path         string    // Absolute path to worktree
	AnchorBranch string    // Anchor branch name (stack root for legacy worktrees)
	CreatedAt    time.Time // When worktree was created
	MainRepoDir  string    // Path to main repo
}

WorktreeInfo represents information about a stackit-managed worktree

type WorktreeOperations

type WorktreeOperations interface {
	AddWorktree(ctx context.Context, path string, branch string, detach bool) error
	RemoveWorktree(ctx context.Context, path string) error
	CreateTemporaryWorktree(ctx context.Context, branch string, prefix string) (path string, cleanup func(), err error)
	// CreateTemporaryWorktreeSkipPrune is like CreateTemporaryWorktree but skips the automatic
	// PruneWorktrees() call. Use this when creating multiple worktrees in parallel after
	// manually calling PruneWorktrees() once, to avoid race conditions.
	CreateTemporaryWorktreeSkipPrune(ctx context.Context, branch string, prefix string) (path string, cleanup func(), err error)
	PruneWorktrees(ctx context.Context) error
}

WorktreeOperations handles worktree management

type WorktreePruneMode

type WorktreePruneMode int

WorktreePruneMode specifies whether to prune stale worktrees before creation.

const (
	// WorktreePruneAuto prunes stale worktrees before creating a new one (default).
	WorktreePruneAuto WorktreePruneMode = iota
	// WorktreePruneSkip skips pruning. Use when the caller has already pruned
	// (e.g., parallel worktree creation).
	WorktreePruneSkip
)

type WorktreeRegistry

type WorktreeRegistry interface {
	// RegisterWorktree registers a worktree for a stack root
	RegisterWorktree(stackRoot string, path string) error
	// RegisterWorktreeWithName registers a worktree with a user-friendly name
	RegisterWorktreeWithName(anchorBranch string, path string, name string) error
	// UnregisterWorktree removes worktree registration for a stack root
	UnregisterWorktree(stackRoot string) error
	// GetWorktreeForStack returns worktree info for a stack root, or nil if none
	GetWorktreeForStack(stackRoot string) (*WorktreeInfo, error)
	// ListManagedWorktrees returns all stackit-managed worktrees
	ListManagedWorktrees() ([]WorktreeInfo, error)
	// GetStackRootForBranch returns the stack root for a given branch
	GetStackRootForBranch(branch Branch) string
	// IsInManagedWorktree checks if the current directory is a stackit-managed worktree
	// Returns true and worktree info if in a managed worktree, false otherwise
	IsInManagedWorktree() (bool, *WorktreeInfo, error)
}

WorktreeRegistry handles stackit-managed worktree tracking

type WorktreeSnapshot

type WorktreeSnapshot struct {
	Trunk           string
	Branches        []string
	BranchState     BranchStateMap
	ChildrenMap     map[string][]string
	RemoteMetaCache map[string]*git.Meta
	MaxConcurrency  int
}

WorktreeSnapshot holds a deep copy of engine state for initializing worktree engines. This avoids the cost of rebuildInternal (which reads all branches + metadata from git) since worktrees share .git with the parent and the data is identical.

Jump to

Keyboard shortcuts

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