# add trail worktree path helpers

`5e630a9`→[main](/content/gh/entireio/cli/commits/main/index.html)·

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

- cmd/entire/cli

- Atrail_checkout_worktree.go+38

- Atrail_checkout_worktree_test.go+58

```go
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, "'", `\'`) + "'"
}
```

```go
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)
	}
}
```
