stack

package
v4.3.1 Latest Latest
Warning

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

Go to latest
Published: Apr 13, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AfterRebaseCallback

type AfterRebaseCallback func(result RebaseResult, g *git.Git) bool

AfterRebaseCallback is called after each successful rebase It receives the result and the git instance for the worktree Returns true if sync should continue, false to stop

type BeforeRebaseCallback

type BeforeRebaseCallback func(info SyncInfo) bool

BeforeRebaseCallback is called before each rebase to ask for confirmation It receives the sync info for the branch about to be synced Returns true to proceed with rebase, false to skip this branch

type CleanupResult

type CleanupResult struct {
	Branch             string
	Success            bool
	Error              string
	WorktreeWasDeleted bool // True if worktree was already deleted before cleanup
	WasCurrentWorktree bool // True if this was the worktree we were in when cleanup started
}

CleanupResult contains information about a branch cleanup operation

type FullyMergedStackInfo

type FullyMergedStackInfo struct {
	StackHash         string
	Stack             *config.Stack
	HasLocalArtifacts bool // true if worktrees or git branches still exist locally
}

FullyMergedStackInfo contains information about a fully merged stack

type Manager

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

Manager handles stack operations

func NewManager

func NewManager(repoDir string) (*Manager, error)

NewManager creates a new stack manager and reconciles config with git state. Use this for commands that mutate state (new, delete, sync, reparent, stack, etc.).

func NewReadOnlyManager

func NewReadOnlyManager(repoDir string) (*Manager, error)

NewReadOnlyManager creates a stack manager without reconciliation. Use this for read-only commands (diff, goto, list, status, up, down, push) where the ~100ms reconciliation cost is unnecessary.

func (*Manager) AddBranchToStack

func (m *Manager) AddBranchToStack(name, parentBranch, worktreeDir, targetStackHash string) (*config.Branch, error)

AddBranchToStack adds an existing branch to a stack (worktree should already exist) This is used when the worktree was created externally (e.g., from a remote branch) If targetStackHash is non-empty, the branch is added to that specific stack. Otherwise, the stack is found by looking up the parent branch.

func (*Manager) AddWorktreeToStack

func (m *Manager) AddWorktreeToStack(branchName, worktreePath, parentName string) (*config.Branch, error)

AddWorktreeToStack adds an unregistered worktree to a stack with the specified parent

func (*Manager) ApplyBranchRenames

func (m *Manager) ApplyBranchRenames(renames []RenamedBranchInfo) error

ApplyBranchRenames updates the config to reflect branch renames. For each rename, it updates the tree key, moves the cache entry, and preserves all metadata.

func (*Manager) CleanupMergedBranches

func (m *Manager) CleanupMergedBranches(branches []MergedBranchInfo, currentDir string) []CleanupResult

CleanupMergedBranches marks branches as merged - deletes worktrees and git branches but keeps metadata in config This allows merged PRs to still show up in ezs ls/status with strikethrough styling Returns detailed results for each branch cleanup operation

func (*Manager) CreateBranch

func (m *Manager) CreateBranch(name, parentBranch, worktreeDir, targetStackHash string) (*config.Branch, error)

CreateBranch creates a new branch in the stack. targetStackHash controls stack placement: "" for auto-detect, "new" for a new stack, or a specific stack hash to add to.

func (*Manager) CreateBranchNoWorktree

func (m *Manager) CreateBranchNoWorktree(name, parentBranch, targetStackHash string) (*config.Branch, error)

CreateBranchNoWorktree creates a new branch without a worktree and adds it to a stack. targetStackHash controls stack placement: "" for auto-detect, "new" for a new stack, or a specific stack hash to add to.

func (*Manager) CreateWorktreeOnly

func (m *Manager) CreateWorktreeOnly(name, parentBranch, worktreeDir string) error

CreateWorktreeOnly creates a worktree without adding it to a stack This is used when the user wants to create a standalone worktree from main/master

func (*Manager) DeclineStackDelete

func (m *Manager) DeclineStackDelete(stackHash string) error

DeclineStackDelete marks a stack so cleanup prompts are not repeated.

func (*Manager) DeleteBranch

func (m *Manager) DeleteBranch(branchName string, force bool) error

DeleteBranch removes a branch from the stack and deletes its worktree Returns an error if the branch has child branches

func (*Manager) DeleteStack

func (m *Manager) DeleteStack(stackHash string) (bool, error)

DeleteStack removes an entire stack from config, cleaning up any remaining worktrees and git branches. This is intended for fully merged stacks where all branches have been completed. Returns (needsCd, error) — needsCd is true when the process had to leave a worktree that was deleted, so the caller can emit a cd to the main repo.

func (*Manager) DetectFullyMergedStacks

func (m *Manager) DetectFullyMergedStacks(stacks []*config.Stack) []FullyMergedStackInfo

DetectFullyMergedStacks finds stacks where every branch is merged

func (*Manager) DetectMergedBranches

func (m *Manager) DetectMergedBranches(gh *github.Client) ([]MergedBranchInfo, error)

DetectMergedBranches finds branches in the CURRENT stack whose PRs have been merged to main These are candidates for cleanup (deleting local branch and worktree)

func (*Manager) DetectMergedBranchesAllStacks

func (m *Manager) DetectMergedBranchesAllStacks(gh *github.Client) ([]MergedBranchInfo, error)

DetectMergedBranchesAllStacks finds branches across ALL stacks whose PRs have been merged to main

func (*Manager) DetectMergedBranchesForStacks

func (m *Manager) DetectMergedBranchesForStacks(gh *github.Client, stacks []*config.Stack) ([]MergedBranchInfo, error)

DetectMergedBranchesForStacks finds branches in specific stacks whose PRs have been merged

func (*Manager) DetectMissingWorktrees

func (m *Manager) DetectMissingWorktrees() []MissingWorktreeInfo

DetectMissingWorktrees finds branches whose worktree directories no longer exist on disk This can happen when a user manually removes a worktree with `rm -rf`

func (*Manager) DetectOrphanedBranches

func (m *Manager) DetectOrphanedBranches() []string

DetectOrphanedBranches finds branches in config that no longer exist in git

func (*Manager) DetectRenamedBranches

func (m *Manager) DetectRenamedBranches(orphaned []string, untracked []git.Worktree) []RenamedBranchInfo

DetectRenamedBranches correlates orphaned branches (in config but not in git) with untracked worktrees (in git but not in config) by matching worktree paths. If an orphaned branch's cached worktree path matches an untracked worktree's path, it's almost certainly a rename (git branch -m old new).

func (*Manager) DetectSyncNeeded

func (m *Manager) DetectSyncNeeded(gh *github.Client) ([]SyncInfo, error)

DetectSyncNeeded checks for branches that need syncing in the CURRENT stack only: - Branches whose parents have been merged to main - Branches whose parent is main but are behind origin/main

func (*Manager) DetectSyncNeededAllStacks

func (m *Manager) DetectSyncNeededAllStacks(gh *github.Client) ([]SyncInfo, error)

DetectSyncNeededAllStacks checks for branches that need syncing across ALL stacks: - Branches whose parents have been merged to main - Branches whose parent is main but are behind origin/main

func (*Manager) DetectSyncNeededForBranch

func (m *Manager) DetectSyncNeededForBranch(branchName string, gh *github.Client) *SyncInfo

DetectSyncNeededForBranch checks if a specific branch needs syncing Returns SyncInfo if the branch needs syncing, nil otherwise

func (*Manager) DetectSyncNeededForStacks

func (m *Manager) DetectSyncNeededForStacks(gh *github.Client, stacks []*config.Stack) ([]SyncInfo, error)

DetectSyncNeededForStacks checks for branches that need syncing in specific stacks

func (*Manager) Fetch

func (m *Manager) Fetch() error

Fetch runs git fetch once per Manager lifetime. Subsequent calls are no-ops.

func (*Manager) FindStackForBranch

func (m *Manager) FindStackForBranch(branchName string) *config.Stack

FindStackForBranch finds which stack a branch belongs to (exported)

func (*Manager) GetAllBranchesInAllStacks

func (m *Manager) GetAllBranchesInAllStacks() []*config.Branch

GetAllBranchesInAllStacks returns all branches across all stacks

func (*Manager) GetBranch

func (m *Manager) GetBranch(name string) *config.Branch

GetBranch returns a branch by name

func (*Manager) GetChildren

func (m *Manager) GetChildren(branchName string) []*config.Branch

GetChildren returns all child branches of a given branch

func (*Manager) GetConfig

func (m *Manager) GetConfig() *config.Config

GetConfig returns the loaded global config, avoiding redundant config.Load() calls.

func (*Manager) GetCurrentStack

func (m *Manager) GetCurrentStack() (*config.Stack, *config.Branch, error)

GetCurrentStack returns the stack for the current branch

func (*Manager) GetRepoDir

func (m *Manager) GetRepoDir() string

GetRepoDir returns the main repository directory

func (*Manager) GetStackByHash

func (m *Manager) GetStackByHash(prefix string) (*config.Stack, error)

GetStackByHash finds a stack by hash prefix (minimum 3 characters). Returns error if 0 or >1 stacks match.

func (*Manager) GetStackByHashExact

func (m *Manager) GetStackByHashExact(hash string) *config.Stack

GetStackByHashExact returns a stack by its exact hash (no prefix matching).

func (*Manager) GetStackForBranch

func (m *Manager) GetStackForBranch(branchName string) *config.Stack

GetStackForBranch returns the stack containing the given branch, or nil.

func (*Manager) GetStacksWithRoot

func (m *Manager) GetStacksWithRoot(rootName string) []*config.Stack

GetStacksWithRoot returns all stacks that use rootName as their root

func (*Manager) GetTreeChildren

func (m *Manager) GetTreeChildren(branchName string) []*config.Branch

GetTreeChildren returns child branches based on the original tree structure (BaseBranch), not the effective parent. This is used for navigation (up/down) where we want to follow the tree hierarchy even when parents have been merged and children reparented.

func (*Manager) GetUnregisteredWorktrees

func (m *Manager) GetUnregisteredWorktrees() ([]git.Worktree, error)

GetUnregisteredWorktrees returns worktrees that exist but are not registered in any stack

func (*Manager) HandleMissingWorktrees

func (m *Manager) HandleMissingWorktrees(branches []MissingWorktreeInfo) error

HandleMissingWorktrees cleans up branches whose worktrees were manually removed It removes the branches from the stack config (git worktree prune should be called first)

func (*Manager) HasStackWithRoot

func (m *Manager) HasStackWithRoot(rootName string) bool

HasStackWithRoot checks if any stack uses rootName as its root

func (*Manager) IsMainBranch

func (m *Manager) IsMainBranch(name string) bool

IsMainBranch checks if a branch is the main/master branch. Use only for protection (e.g. preventing deletion of main). For stack-root logic, compare against Stack.Root or use GetStackForBranch instead.

func (*Manager) ListStacks

func (m *Manager) ListStacks() []*config.Stack

ListStacks returns all stacks sorted by name for deterministic ordering

func (*Manager) MarkBranchMerged

func (m *Manager) MarkBranchMerged(branchName string) error

MarkBranchMerged marks a branch as merged - deletes worktree and git branch but keeps metadata in config This allows merged branches to still show up in ezs ls/status with strikethrough The tree structure is NOT modified - children stay under the merged parent for display order. The effective git parent for children is computed at runtime by skipping merged ancestors.

func (*Manager) MarkBranchRemote

func (m *Manager) MarkBranchRemote(branchName, prURL, remote string) error

MarkBranchRemote marks a branch as belonging to another contributor. Remote branches are not rebased during sync. Optionally sets PR URL. If remote is non-empty, it specifies the git remote to push to (for fork PRs).

func (*Manager) RebaseChildren

func (m *Manager) RebaseChildren(useMerge ...bool) ([]RebaseResult, error)

RebaseChildren syncs all child branches after updating the current branch. When useMerge is true, git merge is used instead of git rebase. Returns results for each child branch processed.

func (*Manager) RebaseOnParent

func (m *Manager) RebaseOnParent(useMerge ...bool) error

RebaseOnParent syncs the current branch onto its updated parent. When useMerge is true, git merge is used instead of git rebase.

func (*Manager) Reconcile

func (m *Manager) Reconcile()

Reconcile silently reconciles ezstack config with git reality. It detects renamed branches, removes orphaned entries, and cleans up missing worktrees. This runs automatically when a Manager is created, so commands always see consistent state.

func (*Manager) RegisterExistingBranch

func (m *Manager) RegisterExistingBranch(branchName, worktreePath, baseBranch string) (*config.Branch, error)

RegisterExistingBranch registers an existing branch/worktree as the root of a new stack

func (*Manager) RegisterRemoteBranch

func (m *Manager) RegisterRemoteBranch(branchName, baseBranch string, prNumber int, prURL string) (string, error)

RegisterRemoteBranch creates a new stack with a remote branch as its root/base. The remote branch is NOT added to the tree — it is the stack root. PR info is stored on the Stack struct for display in stack descriptions.

func (*Manager) RemoveOrphanedBranches

func (m *Manager) RemoveOrphanedBranches(branchNames []string) error

RemoveOrphanedBranches removes branches from config that no longer exist in git

func (*Manager) ReparentBranch

func (m *Manager) ReparentBranch(branchName, newParentName string, doRebase bool, useMerge ...bool) (*ReparentResult, error)

ReparentBranch changes the parent of a branch to a new parent This handles several cases: 1. Branch is already in a stack - just update parent pointer 2. Branch is standalone (not in any stack) - add it to the new parent's stack 3. New parent is in a different stack - merge stacks or move branch If doRebase is true, performs git rebase --onto to move commits. Config changes are always saved first. If rebase has conflicts, the result will have HasConflict=true but the branch will still be returned (config is updated).

func (*Manager) SetStackName

func (m *Manager) SetStackName(stackHash, name string) error

SetStackName sets or updates the name of a stack

func (*Manager) SyncBranch

func (m *Manager) SyncBranch(branchName string, gh *github.Client, useMerge ...bool) (*RebaseResult, error)

SyncBranch syncs a specific branch, handling all cases: - Branch is behind origin/main (parent is main) - Parent branch was merged (rebase --onto main or merge) - Branch is behind its parent (rebase onto parent or merge) When useMerge is true, git merge is used instead of git rebase.

func (*Manager) SyncSpecificStacks

func (m *Manager) SyncSpecificStacks(stacks []*config.Stack, gh *github.Client, callbacks *SyncCallbacks) ([]RebaseResult, error)

SyncSpecificStacks syncs branches in the given stacks

func (*Manager) SyncStack

func (m *Manager) SyncStack(gh *github.Client, callbacks *SyncCallbacks) ([]RebaseResult, error)

SyncStack syncs branches in the CURRENT stack only that need syncing This handles three cases: - Branches whose parent is main but are behind origin/main (simple rebase) - Branches whose parent was merged (rebase onto main using --onto) - Branches whose parent is not merged but has new commits (rebase onto parent) Callbacks can be used to ask for confirmation before each rebase and push after

func (*Manager) SyncStackAll

func (m *Manager) SyncStackAll(gh *github.Client, callbacks *SyncCallbacks) ([]RebaseResult, error)

SyncStackAll syncs branches in ALL stacks that need syncing

func (*Manager) UntrackBranch

func (m *Manager) UntrackBranch(branchName string) error

UntrackBranch removes a branch from ezstack tracking without deleting the git branch or worktree Children of the untracked branch are reparented to the untracked branch's parent

type MergedBranchInfo

type MergedBranchInfo struct {
	Branch       string
	PRNumber     int
	WorktreePath string
	StackHash    string
}

MergedBranchInfo contains information about a branch whose PR has been merged

type MissingWorktreeInfo

type MissingWorktreeInfo struct {
	BranchName   string
	WorktreePath string
}

MissingWorktreeInfo contains info about a branch whose worktree was removed

type RebaseResult

type RebaseResult struct {
	Branch       string
	Success      bool
	HasConflict  bool
	Error        error
	SyncedParent string // If non-empty, parent was merged and we synced to this new parent
	WorktreePath string // Path to the worktree (useful for conflict resolution)
	BehindBy     int    // Number of commits behind (for branches that need sync with origin/main)
	StackName    string // Display name of the stack this branch belongs to
	Remote       string // Git remote to push to (empty means "origin")
}

RebaseResult represents the result of a rebase operation

type RenamedBranchInfo

type RenamedBranchInfo struct {
	OldName      string
	NewName      string
	WorktreePath string
}

RenamedBranchInfo contains info about a branch that was renamed outside of ezstack

type ReparentInfo

type ReparentInfo struct {
	BranchName string
	OldParent  string
	NewParent  string
}

ReparentInfo contains info about a branch that was reparented

type ReparentResult

type ReparentResult struct {
	Branch      *config.Branch
	HasConflict bool   // true if rebase had conflicts (config was still saved)
	ConflictDir string // worktree path where conflicts need to be resolved
}

ReparentResult holds the result of a reparent operation

type SyncCallbacks

type SyncCallbacks struct {
	BeforeRebase BeforeRebaseCallback
	AfterRebase  AfterRebaseCallback
	Autostash    bool // Stash uncommitted changes before rebase, pop after
	UseMerge     bool // Use git merge instead of git rebase
}

SyncCallbacks contains optional callbacks for sync operations

type SyncInfo

type SyncInfo struct {
	Branch       string
	MergedParent string // Non-empty if parent was merged
	BehindBy     int    // Number of commits behind target
	BehindParent string // Non-empty if behind a non-main parent
	StackRoot    string // The root branch of this branch's stack (e.g. "main", "develop")
	NeedsSync    bool   // True if branch needs to be synced
}

SyncInfo contains information about a branch that needs syncing

type UpdateResult

type UpdateResult struct {
	// Branches that were removed from config because they no longer exist in git
	RemovedBranches []string
	// Worktrees that were discovered and added to stacks
	AddedBranches []*config.Branch
	// Branches whose parent was updated based on merge-base analysis
	ReparentedBranches []ReparentInfo
	// Branches that were detected as renames and updated
	RenamedBranches []RenamedBranchInfo
}

UpdateResult contains the results of an update operation

Jump to

Keyboard shortcuts

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