package commands import ( "fmt" "os" "git.dws.rip/DWS/onyx/internal/core" "github.com/spf13/cobra" ) // NewSwitchCmd creates the switch command func NewSwitchCmd() *cobra.Command { var force bool cmd := &cobra.Command{ Use: "switch ", Short: "Switch to a different workstream", Long: `Switch to a different workstream. This command will: 1. Check for uncommitted changes (unless --force is used) 2. Load the target workstream 3. Checkout the latest commit in the target workstream 4. Update the current workstream pointer ⚠️ Warning: Switching discards uncommitted changes in your working directory. Use 'onx save' to commit your work before switching, or use --force to bypass the safety check.`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return runSwitch(args[0], force) }, } cmd.Flags().BoolVarP(&force, "force", "f", false, "Force switch even with uncommitted changes") return cmd } // runSwitch executes the switch command func runSwitch(name string, force bool) error { // Get current directory cwd, err := os.Getwd() if err != nil { return fmt.Errorf("failed to get current directory: %w", err) } // Check if this is an Onyx repository if !core.IsOnyxRepo(cwd) { return fmt.Errorf("not an Onyx repository") } // Open the repository repo, err := core.Open(cwd) if err != nil { return fmt.Errorf("failed to open repository: %w", err) } defer repo.Close() // Create workstream manager wsManager := core.NewWorkstreamManager(repo) // Get current workstream name before switching currentName, err := wsManager.GetCurrentWorkstreamName() if err != nil { currentName = "none" } // Check if we're already on the target workstream if currentName == name { fmt.Printf("Already on workstream '%s'\n", name) return nil } // Check for uncommitted changes unless force is enabled if !force { gitRepo := repo.GetGitRepo() worktree, err := gitRepo.Worktree() if err != nil { return fmt.Errorf("failed to get worktree: %w", err) } status, err := worktree.Status() if err != nil { return fmt.Errorf("failed to check status: %w", err) } if !status.IsClean() { // Show which files have changes fmt.Println("Error: You have uncommitted changes:") for file, fileStatus := range status { if fileStatus.Worktree != ' ' || fileStatus.Staging != ' ' { fmt.Printf(" %c%c %s\n", fileStatus.Staging, fileStatus.Worktree, file) } } fmt.Println("\nPlease commit your changes or use --force to discard them:") fmt.Printf(" onx save -m \"WIP\" # Save your work\n") fmt.Printf(" onx switch %s --force # Discard changes and switch\n", name) return fmt.Errorf("uncommitted changes present") } } // Use ExecuteWithTransaction to capture state_before and state_after err = core.ExecuteWithTransaction(repo, "switch", fmt.Sprintf("Switched from '%s' to '%s'", currentName, name), func() error { return wsManager.SwitchWorkstream(name) }) if err != nil { return err } // Get the workstream we just switched to targetWorkstream, err := wsManager.GetCurrentWorkstream() if err != nil { return fmt.Errorf("failed to get workstream after switch: %w", err) } // Display success message fmt.Printf("Switched to workstream '%s'\n", name) // Show workstream info commitCount := targetWorkstream.GetCommitCount() if commitCount == 0 { fmt.Printf("\nThis is a new workstream based on '%s' with no commits yet.\n", targetWorkstream.BaseBranch) fmt.Printf("Make changes and save them with 'onx save -m \"message\"'\n") } else { commitText := "commit" if commitCount != 1 { commitText = "commits" } fmt.Printf("\nThis workstream has %d %s.\n", commitCount, commitText) // Show the latest commit if latestCommit, err := targetWorkstream.GetLatestCommit(); err == nil { fmt.Printf("Latest commit: %s\n", latestCommit.Message) } } return nil }