diff --git a/cmd/down.go b/cmd/down.go new file mode 100644 index 0000000..cd667fc --- /dev/null +++ b/cmd/down.go @@ -0,0 +1,88 @@ +package cmd + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" + + "github.com/javoire/stackinator/internal/git" + "github.com/javoire/stackinator/internal/stack" + "github.com/spf13/cobra" +) + +var downCmd = &cobra.Command{ + Use: "down", + Short: "Move to a child branch in the stack", + Long: `Checkout a child branch of the current branch in the stack. + +If the current branch has no children (is at the tip of the stack), +an error message will be displayed. + +If there are multiple children, you will be prompted to select one.`, + Example: ` # Move to child branch + stack down`, + Run: func(cmd *cobra.Command, args []string) { + gitClient := git.NewGitClient() + + if err := runDown(gitClient); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + }, +} + +func runDown(gitClient git.GitClient) error { + // Get current branch + currentBranch, err := gitClient.GetCurrentBranch() + if err != nil { + return fmt.Errorf("failed to get current branch: %w", err) + } + + // Get children of current branch + children, err := stack.GetChildrenOf(gitClient, currentBranch) + if err != nil { + return fmt.Errorf("failed to get children: %w", err) + } + + if len(children) == 0 { + return fmt.Errorf("no children (tip of stack)") + } + + var targetBranch string + + if len(children) == 1 { + // Only one child, checkout directly + targetBranch = children[0].Name + } else { + // Multiple children, prompt for selection + fmt.Printf("Multiple children found for %s:\n", currentBranch) + for i, child := range children { + fmt.Printf(" %d) %s\n", i+1, child.Name) + } + fmt.Print("\nSelect branch (1-" + strconv.Itoa(len(children)) + "): ") + + reader := bufio.NewReader(os.Stdin) + input, err := reader.ReadString('\n') + if err != nil { + return fmt.Errorf("failed to read input: %w", err) + } + + input = strings.TrimSpace(input) + selection, err := strconv.Atoi(input) + if err != nil || selection < 1 || selection > len(children) { + return fmt.Errorf("invalid selection: %s", input) + } + + targetBranch = children[selection-1].Name + } + + // Checkout the target branch + if err := gitClient.CheckoutBranch(targetBranch); err != nil { + return fmt.Errorf("failed to checkout child branch %s: %w", targetBranch, err) + } + + fmt.Printf("Switched to child branch: %s\n", targetBranch) + return nil +} diff --git a/cmd/root.go b/cmd/root.go index c386bb2..e369529 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -72,6 +72,8 @@ func init() { rootCmd.AddCommand(renameCmd) rootCmd.AddCommand(reparentCmd) rootCmd.AddCommand(worktreeCmd) + rootCmd.AddCommand(upCmd) + rootCmd.AddCommand(downCmd) } // Execute runs the root command diff --git a/cmd/up.go b/cmd/up.go new file mode 100644 index 0000000..5fa27a3 --- /dev/null +++ b/cmd/up.go @@ -0,0 +1,51 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/javoire/stackinator/internal/git" + "github.com/spf13/cobra" +) + +var upCmd = &cobra.Command{ + Use: "up", + Short: "Move to the parent branch in the stack", + Long: `Checkout the parent branch of the current branch in the stack. + +If the current branch has no parent (is at the root of the stack), +an error message will be displayed.`, + Example: ` # Move to parent branch + stack up`, + Run: func(cmd *cobra.Command, args []string) { + gitClient := git.NewGitClient() + + if err := runUp(gitClient); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + }, +} + +func runUp(gitClient git.GitClient) error { + // Get current branch + currentBranch, err := gitClient.GetCurrentBranch() + if err != nil { + return fmt.Errorf("failed to get current branch: %w", err) + } + + // Get parent from git config + parent := gitClient.GetConfig(fmt.Sprintf("branch.%s.stackparent", currentBranch)) + + if parent == "" { + return fmt.Errorf("already at stack root (no parent for %s)", currentBranch) + } + + // Checkout the parent branch + if err := gitClient.CheckoutBranch(parent); err != nil { + return fmt.Errorf("failed to checkout parent branch %s: %w", parent, err) + } + + fmt.Printf("Switched to parent branch: %s\n", parent) + return nil +}