diff --git a/cmd/worktree.go b/cmd/worktree.go index 1d850ed..5e79272 100644 --- a/cmd/worktree.go +++ b/cmd/worktree.go @@ -1,7 +1,6 @@ package cmd import ( - "bufio" "fmt" "os" "path/filepath" @@ -17,8 +16,8 @@ var worktreePrune bool var worktreeCmd = &cobra.Command{ Use: "worktree [base-branch]", - Short: "Create a worktree in .worktrees/ directory", - Long: `Create a git worktree in the .worktrees/ directory for the specified branch. + Short: "Create a worktree in ~/.stack/worktrees/ directory", + Long: `Create a git worktree in the ~/.stack/worktrees/ directory for the specified branch. If the branch exists locally or on the remote, it will be used. If the branch doesn't exist, a new branch will be created from the current branch @@ -76,19 +75,20 @@ func init() { } func runWorktree(gitClient git.GitClient, githubClient github.GitHubClient, branchName, baseBranch string) error { - // Get repo root - repoRoot, err := gitClient.GetRepoRoot() + // Get home directory + homeDir, err := os.UserHomeDir() if err != nil { - return fmt.Errorf("failed to get repo root: %w", err) + return fmt.Errorf("failed to get home directory: %w", err) } - // Ensure .worktrees is in .gitignore - if err := ensureWorktreesIgnored(repoRoot); err != nil { - return fmt.Errorf("failed to update .gitignore: %w", err) + // Get repository name + repoName, err := gitClient.GetRepoName() + if err != nil { + return fmt.Errorf("failed to get repo name: %w", err) } - // Worktree path - worktreePath := filepath.Join(repoRoot, ".worktrees", branchName) + // Worktree path: ~/.stack/worktrees// + worktreePath := filepath.Join(homeDir, ".stack", "worktrees", repoName, branchName) // Check if worktree already exists if _, err := os.Stat(worktreePath); err == nil { @@ -196,17 +196,23 @@ func createWorktreeForExisting(gitClient git.GitClient, branchName, worktreePath } func runWorktreePrune(gitClient git.GitClient, githubClient github.GitHubClient) error { - // Get repo root - repoRoot, err := gitClient.GetRepoRoot() + // Get home directory + homeDir, err := os.UserHomeDir() if err != nil { - return fmt.Errorf("failed to get repo root: %w", err) + return fmt.Errorf("failed to get home directory: %w", err) } - worktreesDir := filepath.Join(repoRoot, ".worktrees") + // Get repository name + repoName, err := gitClient.GetRepoName() + if err != nil { + return fmt.Errorf("failed to get repo name: %w", err) + } - // Check if .worktrees directory exists + worktreesDir := filepath.Join(homeDir, ".stack", "worktrees", repoName) + + // Check if ~/.stack/worktrees/ directory exists if _, err := os.Stat(worktreesDir); os.IsNotExist(err) { - fmt.Println("No .worktrees directory found.") + fmt.Printf("No ~/.stack/worktrees/%s directory found.\n", repoName) return nil } @@ -216,7 +222,7 @@ func runWorktreePrune(gitClient git.GitClient, githubClient github.GitHubClient) return fmt.Errorf("failed to list worktrees: %w", err) } - // Filter to only worktrees in .worktrees/ directory + // Filter to only worktrees in ~/.stack/worktrees/ directory var worktreesToCheck []struct { path string branch string @@ -231,7 +237,7 @@ func runWorktreePrune(gitClient git.GitClient, githubClient github.GitHubClient) } if len(worktreesToCheck) == 0 { - fmt.Println("No worktrees found in .worktrees/ directory.") + fmt.Printf("No worktrees found in ~/.stack/worktrees/%s directory.\n", repoName) return nil } @@ -291,72 +297,3 @@ func runWorktreePrune(gitClient git.GitClient, githubClient github.GitHubClient) return nil } - -func ensureWorktreesIgnored(repoRoot string) error { - gitignorePath := filepath.Join(repoRoot, ".gitignore") - - // Check if .worktrees is already in .gitignore - if _, err := os.Stat(gitignorePath); err == nil { - file, err := os.Open(gitignorePath) - if err != nil { - return err - } - defer file.Close() - - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if line == ".worktrees" || line == ".worktrees/" { - return nil // Already ignored - } - } - if err := scanner.Err(); err != nil { - return err - } - } - - if dryRun { - fmt.Println(" [DRY RUN] Adding .worktrees to .gitignore") - return nil - } - - // Append .worktrees to .gitignore - file, err := os.OpenFile(gitignorePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - return err - } - defer file.Close() - - // Check if file ends with newline, if not add one - info, err := file.Stat() - if err != nil { - return err - } - - var prefix string - if info.Size() > 0 { - // Read last byte to check for newline - tempFile, err := os.Open(gitignorePath) - if err != nil { - return err - } - defer tempFile.Close() - - buf := make([]byte, 1) - _, err = tempFile.ReadAt(buf, info.Size()-1) - if err != nil { - return err - } - if buf[0] != '\n' { - prefix = "\n" - } - } - - _, err = file.WriteString(prefix + ".worktrees/\n") - if err != nil { - return err - } - - fmt.Println("Added .worktrees/ to .gitignore") - return nil -} diff --git a/internal/git/git.go b/internal/git/git.go index e4cb4f3..eb630d7 100644 --- a/internal/git/git.go +++ b/internal/git/git.go @@ -58,6 +58,20 @@ func (c *gitClient) GetRepoRoot() (string, error) { return c.runCmd("rev-parse", "--show-toplevel") } +// GetRepoName returns the name of the git repository (directory name) +func (c *gitClient) GetRepoName() (string, error) { + repoRoot, err := c.GetRepoRoot() + if err != nil { + return "", err + } + // Extract the last component of the path (the directory name) + parts := strings.Split(repoRoot, "/") + if len(parts) == 0 { + return "", fmt.Errorf("invalid repo root path: %s", repoRoot) + } + return parts[len(parts)-1], nil +} + // GetCurrentBranch returns the name of the currently checked out branch func (c *gitClient) GetCurrentBranch() (string, error) { return c.runCmd("branch", "--show-current") diff --git a/internal/git/interface.go b/internal/git/interface.go index f98e9ec..4b412ec 100644 --- a/internal/git/interface.go +++ b/internal/git/interface.go @@ -3,6 +3,7 @@ package git // GitClient defines the interface for all git operations type GitClient interface { GetRepoRoot() (string, error) + GetRepoName() (string, error) GetCurrentBranch() (string, error) ListBranches() ([]string, error) GetConfig(key string) string diff --git a/internal/testutil/mocks.go b/internal/testutil/mocks.go index bf54b88..ca1143b 100644 --- a/internal/testutil/mocks.go +++ b/internal/testutil/mocks.go @@ -15,6 +15,11 @@ func (m *MockGitClient) GetRepoRoot() (string, error) { return args.String(0), args.Error(1) } +func (m *MockGitClient) GetRepoName() (string, error) { + args := m.Called() + return args.String(0), args.Error(1) +} + func (m *MockGitClient) GetCurrentBranch() (string, error) { args := m.Called() return args.String(0), args.Error(1)