Skip to content

Commit b81dc78

Browse files
authored
Merge pull request #17 from timvw/beads-oss-tasks-z1n
fix: handle missing WORKTREE_ROOT
2 parents 6ed8cd8 + 112533e commit b81dc78

2 files changed

Lines changed: 88 additions & 5 deletions

File tree

main.go

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,26 @@ func branchExists(branch string) bool {
152152
return cmd.Run() == nil
153153
}
154154

155+
func ensureWorktreePath(repo, branch string) (string, error) {
156+
targetRoot := filepath.Join(worktreeRoot, repo)
157+
158+
info, err := os.Stat(targetRoot)
159+
switch {
160+
case err == nil:
161+
if !info.IsDir() {
162+
return "", fmt.Errorf("WORKTREE_ROOT path %s is not a directory", targetRoot)
163+
}
164+
case os.IsNotExist(err):
165+
if err := os.MkdirAll(targetRoot, 0o755); err != nil {
166+
return "", fmt.Errorf("failed to create WORKTREE_ROOT directory %s: %w", targetRoot, err)
167+
}
168+
default:
169+
return "", fmt.Errorf("failed to access WORKTREE_ROOT directory %s: %w", targetRoot, err)
170+
}
171+
172+
return filepath.Join(targetRoot, branch), nil
173+
}
174+
155175
func printCDMarker(path string) {
156176
fmt.Printf("TREE_ME_CD:%s\n", path)
157177
}
@@ -309,8 +329,6 @@ var checkoutCmd = &cobra.Command{
309329
return err
310330
}
311331

312-
path := filepath.Join(worktreeRoot, repo, branch)
313-
314332
// Check if worktree already exists
315333
if existingPath, exists := worktreeExists(branch); exists {
316334
fmt.Printf("✓ Worktree already exists: %s\n", existingPath)
@@ -323,6 +341,11 @@ var checkoutCmd = &cobra.Command{
323341
return fmt.Errorf("branch '%s' does not exist\nUse 'wt create %s' to create a new branch", branch, branch)
324342
}
325343

344+
path, err := ensureWorktreePath(repo, branch)
345+
if err != nil {
346+
return err
347+
}
348+
326349
// Create worktree
327350
gitCmd := exec.Command("git", "worktree", "add", path, branch)
328351
gitCmd.Stdout = os.Stdout
@@ -353,15 +376,18 @@ var createCmd = &cobra.Command{
353376
return err
354377
}
355378

356-
path := filepath.Join(worktreeRoot, repo, branch)
357-
358379
// Check if worktree already exists
359380
if existingPath, exists := worktreeExists(branch); exists {
360381
fmt.Printf("✓ Worktree already exists: %s\n", existingPath)
361382
printCDMarker(existingPath)
362383
return nil
363384
}
364385

386+
path, err := ensureWorktreePath(repo, branch)
387+
if err != nil {
388+
return err
389+
}
390+
365391
// Create new branch and worktree
366392
gitCmd := exec.Command("git", "worktree", "add", path, "-b", branch, base)
367393
gitCmd.Stdout = os.Stdout
@@ -493,7 +519,6 @@ func checkoutPROrMR(input string, remoteType RemoteType) error {
493519
}
494520

495521
branch := fmt.Sprintf("%s-%s", prefix, prNumber)
496-
path := filepath.Join(worktreeRoot, repo, branch)
497522

498523
// Check if worktree already exists
499524
if existingPath, exists := worktreeExists(branch); exists {
@@ -502,6 +527,11 @@ func checkoutPROrMR(input string, remoteType RemoteType) error {
502527
return nil
503528
}
504529

530+
path, err := ensureWorktreePath(repo, branch)
531+
if err != nil {
532+
return err
533+
}
534+
505535
// Fetch the PR/MR
506536
fetchCmd := exec.Command("git", "fetch", "origin", fmt.Sprintf("%s:%s", refSpec, branch))
507537
fetchCmd.Stderr = os.Stderr

main_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"os"
45
"path/filepath"
56
"strings"
67
"testing"
@@ -620,3 +621,55 @@ invalid line without proper format
620621
})
621622
}
622623
}
624+
625+
func TestEnsureWorktreePathCreatesMissingRoot(t *testing.T) {
626+
originalRoot := worktreeRoot
627+
t.Cleanup(func() {
628+
worktreeRoot = originalRoot
629+
})
630+
631+
tmpDir := t.TempDir()
632+
worktreeRoot = filepath.Join(tmpDir, "missing-root")
633+
634+
repo := "example-repo"
635+
branch := "feature/foo"
636+
637+
path, err := ensureWorktreePath(repo, branch)
638+
if err != nil {
639+
t.Fatalf("ensureWorktreePath() unexpected error: %v", err)
640+
}
641+
642+
expectedPath := filepath.Join(worktreeRoot, repo, branch)
643+
if path != expectedPath {
644+
t.Fatalf("ensureWorktreePath() = %s, want %s", path, expectedPath)
645+
}
646+
647+
repoDir := filepath.Join(worktreeRoot, repo)
648+
info, statErr := os.Stat(repoDir)
649+
if statErr != nil {
650+
t.Fatalf("expected repo directory to be created at %s: %v", repoDir, statErr)
651+
}
652+
if !info.IsDir() {
653+
t.Fatalf("expected %s to be a directory", repoDir)
654+
}
655+
}
656+
657+
func TestEnsureWorktreePathFailsWhenRootIsFile(t *testing.T) {
658+
originalRoot := worktreeRoot
659+
t.Cleanup(func() {
660+
worktreeRoot = originalRoot
661+
})
662+
663+
tmpDir := t.TempDir()
664+
fileRoot := filepath.Join(tmpDir, "file-root")
665+
666+
if err := os.WriteFile(fileRoot, []byte("not a directory"), 0o644); err != nil {
667+
t.Fatalf("failed to create file root: %v", err)
668+
}
669+
670+
worktreeRoot = fileRoot
671+
672+
if _, err := ensureWorktreePath("repo", "branch"); err == nil {
673+
t.Fatal("expected ensureWorktreePath() to fail when WORKTREE_ROOT is a file")
674+
}
675+
}

0 commit comments

Comments
 (0)