add trail worktree path helpers · Entire

add trail worktree path helpers

5e630a9→main·

pfleidi·1w ago·2 files·+96 added/-0 removed

Pure helpers for the upcoming 'trail checkout --worktree' flag: worktree directory naming under .entire/worktrees and POSIX shell quoting for the printed cd hint.

Sessions

01KX4MMNK7MGR9MZY812HXNJ44View transcript

[?
Add trail checkout --worktree SupportClaude Code·Fable 5·3 steps](/content/gh/entireio/cli/session/c5215fe7-28db-4a83-9d2e-482217df292d#timeline-01KX4MMNK7MGR9MZY812HXNJ44/index.html)

Changes

2

package cli

import (
    "fmt"
    "path/filepath"
    "strings"
)

const (
    trailWorktreesRelDir      = ".entire/worktrees"
    trailWorktreeFallbackName = "branch"
)

func defaultTrailWorktreePath(repoRoot, branch string, trailNumber int) string {
    name := sanitizeTrailWorktreeName(branch)
    if trailNumber > 0 {
        name = fmt.Sprintf("trail-%d-%s", trailNumber, name)
    }
    return filepath.Join(repoRoot, filepath.FromSlash(trailWorktreesRelDir), name)
}

func sanitizeTrailWorktreeName(branch string) string {
    name := strings.Map(func(r rune) rune {
        if r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' || r == '-' || r == '_' || r == '.' {
            return r
        }
        return '-'
    }, strings.TrimSpace(branch))
    name = strings.Trim(name, "-.")
    if name == "" {
        return trailWorktreeFallbackName
    }
    return name
}

func shellQuotePath(path string) string {
    return "'" + strings.ReplaceAll(path, "'", `\'`) + "'"
}
package cli

import (
    "path/filepath"
    "testing"
)

func TestDefaultTrailWorktreePath(t *testing.T) {
    t.Parallel()

tests := []struct {
    name        string
    branch      string
    trailNumber int
    want        string
}{
    {"with number", "peter/feature.auth", 123, filepath.Join("/repo", ".entire", "worktrees", "trail-123-peter-feature.auth")},
    {"without number", "feature/other", 0, filepath.Join("/repo", ".entire", "worktrees", "feature-other")},
}
for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        t.Parallel()
        if got := defaultTrailWorktreePath("/repo", tt.branch, tt.trailNumber); got != tt.want {
            t.Fatalf("defaultTrailWorktreePath() = %q, want %q", got, tt.want)
        }
    })
}
}

func TestSanitizeTrailWorktreeName(t *testing.T) {
    t.Parallel()

tests := []struct {
    in   string
    want string
}{
    {"feature/test", "feature-test"},
    {"Feat_1.2-x", "Feat_1.2-x"},
    {"weird name!", "weird-name"},
    {"---", trailWorktreeFallbackName},
    {"  spaced  ", "spaced"},
}
for _, tt := range tests {
    if got := sanitizeTrailWorktreeName(tt.in); got != tt.want {
        t.Fatalf("sanitizeTrailWorktreeName(%q) = %q, want %q", tt.in, got, tt.want)
    }
}
}

func TestShellQuotePath(t *testing.T) {
    t.Parallel()

got := shellQuotePath("/tmp/it's here")
    want := `'/tmp/it'\''s here'`
    if got != want {
        t.Fatalf("shellQuotePath() = %q, want %q", got, want)
    }
}