Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions cmd/down.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/javoire/stackinator/internal/git"
"github.com/javoire/stackinator/internal/stack"
"github.com/javoire/stackinator/internal/ui"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -57,9 +58,9 @@ func runDown(gitClient git.GitClient) error {
targetBranch = children[0].Name
} else {
// Multiple children, prompt for selection
fmt.Printf("Multiple children found for %s:\n", currentBranch)
fmt.Printf("Multiple children found for %s:\n", ui.Branch(currentBranch))
for i, child := range children {
fmt.Printf(" %d) %s\n", i+1, child.Name)
fmt.Printf(" %d) %s\n", i+1, ui.Branch(child.Name))
}
fmt.Print("\nSelect branch (1-" + strconv.Itoa(len(children)) + "): ")

Expand All @@ -83,6 +84,6 @@ func runDown(gitClient git.GitClient) error {
return fmt.Errorf("failed to checkout child branch %s: %w", targetBranch, err)
}

fmt.Printf("Switched to child branch: %s\n", targetBranch)
fmt.Printf("Switched to child branch: %s\n", ui.Branch(targetBranch))
return nil
}
5 changes: 3 additions & 2 deletions cmd/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/javoire/stackinator/internal/git"
"github.com/javoire/stackinator/internal/stack"
"github.com/javoire/stackinator/internal/ui"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -77,7 +78,7 @@ func runNew(gitClient git.GitClient, branchName string, explicitParent string) e
}
}

fmt.Printf("Creating new branch %s from %s\n", branchName, parent)
fmt.Printf("Creating new branch %s from %s\n", ui.Branch(branchName), ui.Branch(parent))

// Create the new branch
if err := gitClient.CreateBranchAndCheckout(branchName, parent); err != nil {
Expand All @@ -91,7 +92,7 @@ func runNew(gitClient git.GitClient, branchName string, explicitParent string) e
}

if !dryRun {
fmt.Printf("✓ Created branch %s with parent %s\n", branchName, parent)
fmt.Println(ui.Success(fmt.Sprintf("Created branch %s with parent %s", ui.Branch(branchName), ui.Branch(parent))))
fmt.Println()

// Show the local stack (fast, no PR fetching)
Expand Down
5 changes: 3 additions & 2 deletions cmd/parent.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"

"github.com/javoire/stackinator/internal/git"
"github.com/javoire/stackinator/internal/ui"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -38,9 +39,9 @@ func runParent(gitClient git.GitClient) error {
parent := gitClient.GetConfig(fmt.Sprintf("branch.%s.stackparent", currentBranch))

if parent == "" {
fmt.Printf("%s (not in a stack)\n", currentBranch)
fmt.Printf("%s %s\n", ui.Branch(currentBranch), ui.Dim("(not in a stack)"))
} else {
fmt.Println(parent)
fmt.Println(ui.Branch(parent))
}

return nil
Expand Down
14 changes: 8 additions & 6 deletions cmd/prune.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/javoire/stackinator/internal/github"
"github.com/javoire/stackinator/internal/spinner"
"github.com/javoire/stackinator/internal/stack"
"github.com/javoire/stackinator/internal/ui"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -154,7 +155,7 @@ func runPrune(gitClient git.GitClient, githubClient github.GitHubClient) error {
fmt.Printf("Found %d merged branch(es) to prune:\n", len(mergedBranches))
for _, branch := range mergedBranches {
pr := prCache[branch]
fmt.Printf(" - %s (PR #%d)\n", branch, pr.Number)
fmt.Printf(" - %s (PR #%d)\n", ui.Branch(branch), pr.Number)
}
fmt.Println()

Expand All @@ -165,7 +166,7 @@ func runPrune(gitClient git.GitClient, githubClient github.GitHubClient) error {

// Prune each merged branch
for i, branch := range mergedBranches {
fmt.Printf("(%d/%d) Pruning %s...\n", i+1, len(mergedBranches), branch)
fmt.Printf("%s Pruning %s...\n", ui.Progress(i+1, len(mergedBranches)), ui.Branch(branch))

// Remove from stack tracking (if in stack)
configKey := fmt.Sprintf("branch.%s.stackparent", branch)
Expand All @@ -178,7 +179,7 @@ func runPrune(gitClient git.GitClient, githubClient github.GitHubClient) error {

// Don't delete current branch
if branch == currentBranch {
fmt.Println(" Skipping deletion (currently checked out)")
fmt.Printf(" %s Skipping deletion (currently checked out)\n", ui.WarningIcon())
fmt.Println()
continue
}
Expand All @@ -195,15 +196,16 @@ func runPrune(gitClient git.GitClient, githubClient github.GitHubClient) error {
if deleteErr != nil {
fmt.Fprintf(os.Stderr, " Warning: failed to delete branch: %v\n", deleteErr)
if !pruneForce {
fmt.Fprintf(os.Stderr, " Use 'stack prune --force' to force delete, or manually delete with: git branch -D %s\n", branch)
fmt.Fprintf(os.Stderr, " Use '%s' to force delete, or manually delete with: %s\n",
ui.Command("stack prune --force"), ui.Command(fmt.Sprintf("git branch -D %s", branch)))
}
} else {
fmt.Println(" Deleted")
fmt.Printf(" %s Deleted\n", ui.SuccessIcon())
}
fmt.Println()
}

fmt.Println("✓ Prune complete!")
fmt.Println(ui.Success("Prune complete!"))

return nil
}
Expand Down
7 changes: 4 additions & 3 deletions cmd/rename.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/javoire/stackinator/internal/git"
"github.com/javoire/stackinator/internal/stack"
"github.com/javoire/stackinator/internal/ui"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -62,7 +63,7 @@ func runRename(gitClient git.GitClient, newName string) error {
return fmt.Errorf("failed to get children: %w", err)
}

fmt.Printf("Renaming branch %s -> %s\n", oldName, newName)
fmt.Printf("Renaming branch %s -> %s\n", ui.Branch(oldName), ui.Branch(newName))
if len(children) > 0 {
fmt.Printf(" Will update %d child branch(es)\n", len(children))
}
Expand Down Expand Up @@ -94,11 +95,11 @@ func runRename(gitClient git.GitClient, newName string) error {
if err := gitClient.SetConfig(childConfigKey, newName); err != nil {
return fmt.Errorf("failed to update child %s: %w", child.Name, err)
}
fmt.Printf(" Updated child %s to point to %s\n", child.Name, newName)
fmt.Printf(" %s Updated child %s to point to %s\n", ui.SuccessIcon(), ui.Branch(child.Name), ui.Branch(newName))
}

if !dryRun {
fmt.Printf("✓ Successfully renamed branch %s -> %s\n", oldName, newName)
fmt.Println(ui.Success(fmt.Sprintf("Successfully renamed branch %s -> %s", ui.Branch(oldName), ui.Branch(newName))))
fmt.Println()

// Show the updated stack (local only, fast)
Expand Down
19 changes: 10 additions & 9 deletions cmd/reparent.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/javoire/stackinator/internal/git"
"github.com/javoire/stackinator/internal/github"
"github.com/javoire/stackinator/internal/ui"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -56,7 +57,7 @@ func runReparent(gitClient git.GitClient, githubClient github.GitHubClient, newP

// Check if new parent is the same as current parent
if currentParent != "" && newParent == currentParent {
fmt.Printf("Branch %s is already parented to %s\n", currentBranch, newParent)
fmt.Printf("Branch %s is already parented to %s\n", ui.Branch(currentBranch), ui.Branch(newParent))
return nil
}

Expand All @@ -77,9 +78,9 @@ func runReparent(gitClient git.GitClient, githubClient github.GitHubClient, newP

// Print appropriate message based on whether we're adding to stack or reparenting
if currentParent == "" {
fmt.Printf("Adding %s to stack with parent %s\n", currentBranch, newParent)
fmt.Printf("Adding %s to stack with parent %s\n", ui.Branch(currentBranch), ui.Branch(newParent))
} else {
fmt.Printf("Reparenting %s: %s -> %s\n", currentBranch, currentParent, newParent)
fmt.Printf("Reparenting %s: %s -> %s\n", ui.Branch(currentBranch), ui.Branch(currentParent), ui.Branch(newParent))
}

// Update git config
Expand All @@ -92,29 +93,29 @@ func runReparent(gitClient git.GitClient, githubClient github.GitHubClient, newP
pr, err := githubClient.GetPRForBranch(currentBranch)
if err != nil {
// Error fetching PR info, but config was updated successfully
fmt.Printf("✓ Updated parent to %s\n", newParent)
fmt.Println(ui.Success(fmt.Sprintf("Updated parent to %s", ui.Branch(newParent))))
fmt.Printf("Warning: failed to check for PR: %v\n", err)
return nil
}

if pr != nil {
// PR exists, update its base
fmt.Printf("Updating PR #%d base: %s -> %s\n", pr.Number, pr.Base, newParent)
fmt.Printf("Updating PR #%d base: %s -> %s\n", pr.Number, ui.Branch(pr.Base), ui.Branch(newParent))

if err := githubClient.UpdatePRBase(pr.Number, newParent); err != nil {
// Config was updated but PR base update failed
fmt.Printf("✓ Updated parent to %s\n", newParent)
fmt.Println(ui.Success(fmt.Sprintf("Updated parent to %s", ui.Branch(newParent))))
return fmt.Errorf("failed to update PR base: %w", err)
}

if !dryRun {
fmt.Printf("✓ Updated parent to %s\n", newParent)
fmt.Printf("✓ Updated PR #%d base to %s\n", pr.Number, newParent)
fmt.Println(ui.Success(fmt.Sprintf("Updated parent to %s", ui.Branch(newParent))))
fmt.Println(ui.Success(fmt.Sprintf("Updated PR #%d base to %s", pr.Number, ui.Branch(newParent))))
}
} else {
// No PR exists
if !dryRun {
fmt.Printf("✓ Updated parent to %s\n", newParent)
fmt.Println(ui.Success(fmt.Sprintf("Updated parent to %s", ui.Branch(newParent))))
fmt.Println(" (no PR found for this branch)")
}
}
Expand Down
6 changes: 6 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import (
"github.com/javoire/stackinator/internal/git"
"github.com/javoire/stackinator/internal/github"
"github.com/javoire/stackinator/internal/spinner"
"github.com/javoire/stackinator/internal/ui"
"github.com/spf13/cobra"
)

var (
dryRun bool
verbose bool
noColor bool
)

var rootCmd = &cobra.Command{
Expand Down Expand Up @@ -49,6 +51,9 @@ The tool helps you create, navigate, and sync stacked branches with minimal over
// Disable spinners in verbose mode to avoid visual conflicts
spinner.Enabled = !verbose

// Set color output flag
ui.SetNoColor(noColor)

// Validate we're in a git repository
gitClient := git.NewGitClient()
if _, err := gitClient.GetRepoRoot(); err != nil {
Expand All @@ -61,6 +66,7 @@ The tool helps you create, navigate, and sync stacked branches with minimal over
func init() {
rootCmd.PersistentFlags().BoolVar(&dryRun, "dry-run", false, "Show what would happen without executing")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Show detailed output")
rootCmd.PersistentFlags().BoolVar(&noColor, "no-color", false, "Disable colored output")

// Add subcommands
rootCmd.AddCommand(newCmd)
Expand Down
11 changes: 6 additions & 5 deletions cmd/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/javoire/stackinator/internal/git"
"github.com/javoire/stackinator/internal/stack"
"github.com/javoire/stackinator/internal/ui"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -49,8 +50,8 @@ func runShow(gitClient git.GitClient) error {

if len(stackBranches) == 0 {
fmt.Println("No stack branches found.")
fmt.Printf("Current branch: %s\n", currentBranch)
fmt.Println("\nUse 'stack new <branch-name>' to create a new stack branch.")
fmt.Printf("Current branch: %s\n", ui.Branch(currentBranch))
fmt.Printf("\nUse '%s' to create a new stack branch.\n", ui.Command("stack new <branch-name>"))
return nil
}

Expand All @@ -75,16 +76,16 @@ func printLocalStackTree(node *stack.TreeNode, currentBranch string, isPipe bool

marker := ""
if node.Name == currentBranch {
marker = " *"
marker = ui.CurrentBranchMarker()
}

// Print pipe if needed
if isPipe {
fmt.Println(" |")
fmt.Printf(" %s\n", ui.Pipe())
}

// Print current node (no PR info)
fmt.Printf(" %s%s\n", node.Name, marker)
fmt.Printf(" %s%s\n", ui.Branch(node.Name), marker)

// Print children vertically
for _, child := range node.Children {
Expand Down
26 changes: 14 additions & 12 deletions cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/javoire/stackinator/internal/github"
"github.com/javoire/stackinator/internal/spinner"
"github.com/javoire/stackinator/internal/stack"
"github.com/javoire/stackinator/internal/ui"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -133,17 +134,17 @@ func runStatus(gitClient git.GitClient, githubClient github.GitHubClient) error
// Wait for PR fetch to complete before returning
wg.Wait()
fmt.Println("No stack branches found.")
fmt.Printf("Current branch: %s\n", currentBranch)
fmt.Println("\nUse 'stack new <branch-name>' to create a new stack branch.")
fmt.Printf("Current branch: %s\n", ui.Branch(currentBranch))
fmt.Printf("\nUse '%s' to create a new stack branch.\n", ui.Command("stack new <branch-name>"))
return nil
}

// If tree is nil, current branch is not part of any stack
// Check this BEFORE waiting for PR fetch to avoid long delays
if tree == nil {
baseBranch := stack.GetBaseBranch(gitClient)
fmt.Printf("Current branch '%s' is not part of a stack.\n\n", currentBranch)
fmt.Printf("Add to stack with '%s' as parent? [Y/n] ", baseBranch)
fmt.Printf("Current branch '%s' is not part of a stack.\n\n", ui.Branch(currentBranch))
fmt.Printf("Add to stack with '%s' as parent? [Y/n] ", ui.Branch(baseBranch))

reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
Expand All @@ -158,7 +159,8 @@ func runStatus(gitClient git.GitClient, githubClient github.GitHubClient) error
if err := gitClient.SetConfig(configKey, baseBranch); err != nil {
return fmt.Errorf("failed to set stack parent: %w", err)
}
fmt.Printf("✓ Added '%s' to stack with parent '%s'\n\n", currentBranch, baseBranch)
fmt.Println(ui.Success(fmt.Sprintf("Added '%s' to stack with parent '%s'", ui.Branch(currentBranch), ui.Branch(baseBranch))))
fmt.Println()
// Run status again to show the stack
return runStatus(gitClient, githubClient)
}
Expand Down Expand Up @@ -270,24 +272,24 @@ func printTreeVertical(gitClient git.GitClient, node *stack.TreeNode, currentBra
// Determine the current branch marker
marker := ""
if node.Name == currentBranch {
marker = " *"
marker = ui.CurrentBranchMarker()
}

// Get PR info from cache
prInfo := ""
if node.Name != stack.GetBaseBranch(gitClient) {
if pr, exists := prCache[node.Name]; exists {
prInfo = fmt.Sprintf(" [%s :%s]", pr.URL, strings.ToLower(pr.State))
prInfo = fmt.Sprintf(" %s", ui.PRInfo(pr.URL, pr.State))
}
}

// Print pipe if needed
if isPipe {
fmt.Println(" |")
fmt.Printf(" %s\n", ui.Pipe())
}

// Print current node
fmt.Printf(" %s%s%s\n", node.Name, prInfo, marker)
fmt.Printf(" %s%s%s\n", ui.Branch(node.Name), prInfo, marker)

// Print children vertically
for _, child := range node.Children {
Expand Down Expand Up @@ -404,14 +406,14 @@ func detectSyncIssues(gitClient git.GitClient, stackBranches []stack.StackBranch
func printSyncIssues(result *syncIssuesResult) {
if len(result.issues) > 0 {
fmt.Println()
fmt.Println("⚠ Stack out of sync detected:")
fmt.Println(ui.Warning("Stack out of sync detected:"))
for _, issue := range result.issues {
fmt.Println(issue)
}
fmt.Println()
fmt.Println("Run 'stack sync' to rebase branches and update PR bases.")
fmt.Printf("Run '%s' to rebase branches and update PR bases.\n", ui.Command("stack sync"))
} else {
fmt.Println()
fmt.Println("✓ Stack is perfectly synced! All branches are up to date.")
fmt.Println(ui.Success("Stack is perfectly synced! All branches are up to date."))
}
}
Loading