Skip to content

Commit e2e95b4

Browse files
committed
feat: expand default base dir with repo name
1 parent 780af9d commit e2e95b4

File tree

14 files changed

+179
-102
lines changed

14 files changed

+179
-102
lines changed

README.md

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ functionality with automated setup, branch tracking, and project-specific hooks.
1212
solution:** `wtp add feature/auth`
1313

1414
wtp automatically generates sensible paths based on branch names. Your
15-
`feature/auth` branch goes to `../worktrees/feature/auth` - no redundant typing,
15+
`feature/auth` branch goes to `../worktrees/<repo-name>/feature/auth` - no redundant typing,
1616
no path errors.
1717

1818
### 🧹 Clean Branch Management
@@ -127,20 +127,20 @@ sudo mv wtp /usr/local/bin/ # or add to PATH
127127

128128
```bash
129129
# Create worktree from existing branch (local or remote)
130-
# → Creates worktree at ../worktrees/feature/auth
130+
# → Creates worktree at ../worktrees/<repo-name>/feature/auth
131131
# Automatically tracks remote branch if not found locally
132132
wtp add feature/auth
133133

134134
# Create worktree with new branch
135-
# → Creates worktree at ../worktrees/feature/new-feature
135+
# → Creates worktree at ../worktrees/<repo-name>/feature/new-feature
136136
wtp add -b feature/new-feature
137137

138138
# Create new branch from specific commit
139-
# → Creates worktree at ../worktrees/hotfix/urgent
139+
# → Creates worktree at ../worktrees/<repo-name>/hotfix/urgent
140140
wtp add -b hotfix/urgent abc1234
141141

142142
# Create new branch tracking a different remote branch
143-
# → Creates worktree at ../worktrees/feature/test with branch tracking origin/main
143+
# → Creates worktree at ../worktrees/<repo-name>/feature/test with branch tracking origin/main
144144
wtp add -b feature/test origin/main
145145

146146
# Remote branch handling examples:
@@ -188,7 +188,8 @@ wtp uses `.wtp.yml` for project-specific configuration:
188188
version: "1.0"
189189
defaults:
190190
# Base directory for worktrees (relative to project root)
191-
base_dir: "../worktrees"
191+
# ${WTP_REPO_BASENAME} expands to the repository directory name
192+
base_dir: "../worktrees/${WTP_REPO_BASENAME}"
192193

193194
hooks:
194195
post_create:
@@ -213,6 +214,10 @@ hooks:
213214
work_dir: "."
214215
```
215216
217+
The `${WTP_REPO_BASENAME}` placeholder expands to the repository's directory
218+
name when resolving paths, ensuring zero-config isolation between different
219+
repositories. You can combine it with additional path segments as needed.
220+
216221
### Copy Hooks: Main Worktree Reference
217222

218223
Copy hooks are designed to help you bootstrap new worktrees using files from
@@ -326,7 +331,7 @@ evaluates `wtp shell-init <shell>` once for your session—tab completion and
326331

327332
## Worktree Structure
328333

329-
With the default configuration (`base_dir: "../worktrees"`):
334+
With the default configuration (`base_dir: "../worktrees/${WTP_REPO_BASENAME}"`):
330335
331336
```
332337
<project-root>/
@@ -335,12 +340,13 @@ With the default configuration (`base_dir: "../worktrees"`):
335340
└── src/
336341

337342
../worktrees/
338-
├── main/
339-
├── feature/
340-
│ ├── auth/ # wtp add feature/auth
341-
│ └── payment/ # wtp add feature/payment
342-
└── hotfix/
343-
└── bug-123/ # wtp add hotfix/bug-123
343+
└── <repo-name>/
344+
├── main/
345+
├── feature/
346+
│ ├── auth/ # wtp add feature/auth
347+
│ └── payment/ # wtp add feature/payment
348+
└── hotfix/
349+
└── bug-123/ # wtp add hotfix/bug-123
344350
```
345351

346352
Branch names with slashes are preserved as directory structure, automatically

cmd/wtp/cd.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func isWorktreeManagedCd(worktreePath string, cfg *config.Config, mainRepoPath s
3030
// Create default config when none is available
3131
defaultCfg := &config.Config{
3232
Defaults: config.Defaults{
33-
BaseDir: "../worktrees",
33+
BaseDir: config.DefaultBaseDir,
3434
},
3535
}
3636
cfg = defaultCfg
@@ -278,10 +278,7 @@ func getWorktreeNameFromPathCd(worktreePath string, cfg *config.Config, mainRepo
278278
}
279279

280280
// Get base_dir path
281-
baseDir := cfg.Defaults.BaseDir
282-
if !filepath.IsAbs(baseDir) {
283-
baseDir = filepath.Join(mainRepoPath, baseDir)
284-
}
281+
baseDir := cfg.ResolveWorktreePath(mainRepoPath, "")
285282

286283
// Calculate relative path from base_dir
287284
relPath, err := filepath.Rel(baseDir, worktreePath)

cmd/wtp/cd_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func TestCdCommand_AlwaysOutputsAbsolutePath(t *testing.T) {
2020
HEAD abc123
2121
branch refs/heads/main
2222
23-
worktree /Users/dev/project/worktrees/feature/auth
23+
worktree /Users/dev/project/worktrees/main/feature/auth
2424
HEAD def456
2525
branch refs/heads/feature/auth
2626
@@ -41,13 +41,13 @@ branch refs/heads/feature/auth
4141
{
4242
name: "feature worktree by branch name",
4343
worktreeName: "feature/auth",
44-
expectedPath: "/Users/dev/project/worktrees/feature/auth",
44+
expectedPath: "/Users/dev/project/worktrees/main/feature/auth",
4545
shouldSucceed: true,
4646
},
4747
{
4848
name: "feature worktree by directory name",
4949
worktreeName: "auth",
50-
expectedPath: "/Users/dev/project/worktrees/feature/auth",
50+
expectedPath: "/Users/dev/project/worktrees/main/feature/auth",
5151
shouldSucceed: true, // Directory-based resolution works as expected
5252
},
5353
{

cmd/wtp/init.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const configFileMode = 0o600
1616

1717
// Variable to allow mocking in tests
1818
var osGetwd = os.Getwd
19+
var writeFile = os.WriteFile
1920

2021
// NewInitCommand creates the init command definition
2122
func NewInitCommand() *cli.Command {
@@ -54,7 +55,8 @@ version: "1.0"
5455
# Default settings for worktrees
5556
defaults:
5657
# Base directory for worktrees (relative to repository root)
57-
base_dir: ../worktrees
58+
# ${WTP_REPO_BASENAME} expands to the repository directory name
59+
base_dir: ../worktrees/${WTP_REPO_BASENAME}
5860
5961
# Hooks that run after creating a worktree
6062
hooks:
@@ -87,7 +89,7 @@ hooks:
8789
`
8890

8991
// Write configuration file with comments
90-
if err := os.WriteFile(configPath, []byte(configContent), configFileMode); err != nil {
92+
if err := writeFile(configPath, []byte(configContent), configFileMode); err != nil {
9193
return errors.DirectoryAccessFailed("create configuration file", configPath, err)
9294
}
9395

cmd/wtp/init_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func TestInitCommand_Success(t *testing.T) {
138138
// Check for required sections
139139
assert.Contains(t, contentStr, "version: \"1.0\"")
140140
assert.Contains(t, contentStr, "defaults:")
141-
assert.Contains(t, contentStr, "base_dir: ../worktrees")
141+
assert.Contains(t, contentStr, "base_dir: ../worktrees/${WTP_REPO_BASENAME}")
142142
assert.Contains(t, contentStr, "hooks:")
143143
assert.Contains(t, contentStr, "post_create:")
144144

@@ -198,6 +198,12 @@ func TestInitCommand_WriteFileError(t *testing.T) {
198198

199199
cmd := NewInitCommand()
200200
ctx := context.Background()
201+
originalWriteFile := writeFile
202+
writeFile = func(string, []byte, os.FileMode) error {
203+
return assert.AnError
204+
}
205+
defer func() { writeFile = originalWriteFile }()
206+
201207
err = cmd.Action(ctx, &cli.Command{})
202208

203209
assert.Error(t, err)

cmd/wtp/list.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ func isWorktreeManagedList(worktreePath string, cfg *config.Config, mainRepoPath
172172
// Create default config when none is available
173173
defaultCfg := &config.Config{
174174
Defaults: config.Defaults{
175-
BaseDir: "../worktrees",
175+
BaseDir: config.DefaultBaseDir,
176176
},
177177
}
178178
cfg = defaultCfg

cmd/wtp/list_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func TestListCommand_CommandConstruction(t *testing.T) {
109109
cmd := &cli.Command{}
110110

111111
cfg := &config.Config{
112-
Defaults: config.Defaults{BaseDir: "../worktrees"},
112+
Defaults: config.Defaults{BaseDir: config.DefaultBaseDir},
113113
}
114114
err := listCommandWithCommandExecutor(cmd, &buf, mockExec, cfg, "/test/repo")
115115

@@ -178,7 +178,7 @@ func TestListCommand_Output(t *testing.T) {
178178
cmd := &cli.Command{}
179179

180180
cfg := &config.Config{
181-
Defaults: config.Defaults{BaseDir: "../worktrees"},
181+
Defaults: config.Defaults{BaseDir: config.DefaultBaseDir},
182182
}
183183
err := listCommandWithCommandExecutor(cmd, &buf, mockExec, cfg, "/test/repo")
184184

@@ -223,7 +223,7 @@ func TestListCommand_ExecutionError(t *testing.T) {
223223
cmd := &cli.Command{}
224224

225225
cfg := &config.Config{
226-
Defaults: config.Defaults{BaseDir: "../worktrees"},
226+
Defaults: config.Defaults{BaseDir: config.DefaultBaseDir},
227227
}
228228
err := listCommandWithCommandExecutor(cmd, &buf, mockExec, cfg, "/test/repo")
229229

@@ -245,7 +245,7 @@ func TestListCommand_NoWorktrees(t *testing.T) {
245245
cmd := &cli.Command{}
246246

247247
cfg := &config.Config{
248-
Defaults: config.Defaults{BaseDir: "../worktrees"},
248+
Defaults: config.Defaults{BaseDir: config.DefaultBaseDir},
249249
}
250250
err := listCommandWithCommandExecutor(cmd, &buf, mockExec, cfg, "/test/repo")
251251

@@ -306,7 +306,7 @@ func TestListCommand_InternationalCharacters(t *testing.T) {
306306
cmd := &cli.Command{}
307307

308308
cfg := &config.Config{
309-
Defaults: config.Defaults{BaseDir: "../worktrees"},
309+
Defaults: config.Defaults{BaseDir: config.DefaultBaseDir},
310310
}
311311
err := listCommandWithCommandExecutor(cmd, &buf, mockExec, cfg, "/test/repo")
312312

@@ -374,7 +374,7 @@ func TestListCommand_LongPaths(t *testing.T) {
374374
cmd := &cli.Command{}
375375

376376
cfg := &config.Config{
377-
Defaults: config.Defaults{BaseDir: "../worktrees"},
377+
Defaults: config.Defaults{BaseDir: config.DefaultBaseDir},
378378
}
379379
err := listCommandWithCommandExecutor(cmd, &buf, mockExec, cfg, "/test/repo")
380380

@@ -424,7 +424,7 @@ branch refs/heads/feature/test
424424
cmd := &cli.Command{}
425425

426426
cfg := &config.Config{
427-
Defaults: config.Defaults{BaseDir: "../worktrees"},
427+
Defaults: config.Defaults{BaseDir: config.DefaultBaseDir},
428428
}
429429
err := listCommandWithCommandExecutor(cmd, &buf, mockExec, cfg, "/test/repo")
430430

@@ -455,7 +455,7 @@ func TestListCommand_HeaderFormatting(t *testing.T) {
455455
cmd := &cli.Command{}
456456

457457
cfg := &config.Config{
458-
Defaults: config.Defaults{BaseDir: "../worktrees"},
458+
Defaults: config.Defaults{BaseDir: config.DefaultBaseDir},
459459
}
460460
err := listCommandWithCommandExecutor(cmd, &buf, mockExec, cfg, "/test/repo")
461461

@@ -558,7 +558,7 @@ branch refs/heads/feature/awesome
558558
cmd := &cli.Command{}
559559

560560
cfg := &config.Config{
561-
Defaults: config.Defaults{BaseDir: "../worktrees"},
561+
Defaults: config.Defaults{BaseDir: config.DefaultBaseDir},
562562
}
563563
err := listCommandWithCommandExecutor(cmd, &buf, mockExec, cfg, "/repo")
564564

@@ -693,7 +693,7 @@ branch refs/heads/hoge
693693
}
694694

695695
// Special handling for the base_dir test case
696-
baseDir := "../worktrees"
696+
baseDir := config.DefaultBaseDir
697697
if tt.name == "paths relative to main worktree when in subdirectory" {
698698
baseDir = ".worktrees"
699699
mainRepoPath = "/Users/satoshi/dev/src/github.com/satococoa/giselle"
@@ -784,7 +784,7 @@ branch refs/heads/feature/long-branch-name-that-might-also-be-truncated
784784
cmd := &cli.Command{}
785785

786786
cfg := &config.Config{
787-
Defaults: config.Defaults{BaseDir: "../worktrees"},
787+
Defaults: config.Defaults{BaseDir: config.DefaultBaseDir},
788788
}
789789
err := listCommandWithCommandExecutor(cmd, &buf, mockExec, cfg, "/repo")
790790

cmd/wtp/remove.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func isWorktreeManaged(worktreePath string, cfg *config.Config, mainRepoPath str
3333
// Create default config when none is available
3434
defaultCfg := &config.Config{
3535
Defaults: config.Defaults{
36-
BaseDir: "../worktrees",
36+
BaseDir: config.DefaultBaseDir,
3737
},
3838
}
3939
cfg = defaultCfg
@@ -288,10 +288,7 @@ func getWorktreeNameFromPath(worktreePath string, cfg *config.Config, mainRepoPa
288288
}
289289

290290
// Get base_dir path
291-
baseDir := cfg.Defaults.BaseDir
292-
if !filepath.IsAbs(baseDir) {
293-
baseDir = filepath.Join(mainRepoPath, baseDir)
294-
}
291+
baseDir := cfg.ResolveWorktreePath(mainRepoPath, "")
295292

296293
// Calculate relative path from base_dir
297294
relPath, err := filepath.Rel(baseDir, worktreePath)

0 commit comments

Comments
 (0)