diff --git a/README.md b/README.md index 9d68770..b0270b1 100644 --- a/README.md +++ b/README.md @@ -136,10 +136,13 @@ sudo mv wtp /usr/local/bin/ # or add to PATH # Automatically tracks remote branch if not found locally wtp add feature/auth -# Create worktree with new branch +# Create worktree with new branch from HEAD # → Creates worktree at ../worktrees/feature/new-feature wtp add -b feature/new-feature +# Create worktree from default_branch specified in .wtp.yml +wtp add -b feature/new-feature + # Create new branch from specific commit # → Creates worktree at ../worktrees/hotfix/urgent wtp add -b hotfix/urgent abc1234 @@ -194,6 +197,9 @@ version: "1.0" defaults: # Base directory for worktrees (relative to project root) base_dir: "../worktrees" + # Optional default branch for new worktrees created with -b + # If omitted, git uses the current HEAD + default_branch: "main" hooks: post_create: diff --git a/cmd/wtp/add.go b/cmd/wtp/add.go index 582dcbe..22b0279 100644 --- a/cmd/wtp/add.go +++ b/cmd/wtp/add.go @@ -90,7 +90,7 @@ func addCommandWithCommandExecutor( } // Build git worktree command using the new command builder - worktreeCmd := buildWorktreeCommand(cmd, workTreePath, branchName, resolvedTrack) + worktreeCmd := buildWorktreeCommand(cmd, workTreePath, branchName, resolvedTrack, cfg) // Execute the command result, err := cmdExec.Execute([]command.Command{worktreeCmd}) @@ -122,7 +122,7 @@ func addCommandWithCommandExecutor( // buildWorktreeCommand builds a git worktree command using the new command package func buildWorktreeCommand( - cmd *cli.Command, workTreePath, _, resolvedTrack string, + cmd *cli.Command, workTreePath, _, resolvedTrack string, cfg *config.Config, ) command.Command { opts := command.GitWorktreeAddOptions{ Branch: cmd.String("branch"), @@ -151,6 +151,8 @@ func buildWorktreeCommand( if opts.Branch != "" && cmd.Args().Len() > 1 { commitish = cmd.Args().Get(1) } + } else if opts.Branch != "" && cfg != nil && cfg.Defaults.DefaultBranch != "" { + commitish = cfg.Defaults.DefaultBranch } return command.GitWorktreeAdd(workTreePath, commitish, opts) diff --git a/cmd/wtp/add_test.go b/cmd/wtp/add_test.go index 08db60d..3e3450e 100644 --- a/cmd/wtp/add_test.go +++ b/cmd/wtp/add_test.go @@ -328,12 +328,13 @@ func TestResolveWorktreePath(t *testing.T) { func TestAddCommand_CommandConstruction(t *testing.T) { tests := []struct { - name string - flags map[string]any - args []string - expectedCommands []command.Command - expectError bool - }{ + name string + flags map[string]any + args []string + defaultBranch string + expectedCommands []command.Command + expectError bool + }{ { name: "basic worktree creation", flags: map[string]any{ @@ -358,6 +359,31 @@ func TestAddCommand_CommandConstruction(t *testing.T) { }}, expectError: false, }, + { + name: "new branch uses configured default branch", + flags: map[string]any{ + "branch": "new-feature", + }, + args: []string{}, + defaultBranch: "develop", + expectedCommands: []command.Command{{ + Name: "git", + Args: []string{"worktree", "add", "-b", "new-feature", "/test/worktrees/new-feature", "develop"}, + }}, + expectError: false, + }, + { + name: "new branch without default branch keeps git HEAD", + flags: map[string]any{ + "branch": "new-feature", + }, + args: []string{}, + expectedCommands: []command.Command{{ + Name: "git", + Args: []string{"worktree", "add", "-b", "new-feature", "/test/worktrees/new-feature"}, + }}, + expectError: false, + }, } for _, tt := range tests { @@ -369,6 +395,7 @@ func TestAddCommand_CommandConstruction(t *testing.T) { cfg := &config.Config{ Defaults: config.Defaults{ BaseDir: "/test/worktrees", + DefaultBranch: tt.defaultBranch, }, } diff --git a/internal/config/config.go b/internal/config/config.go index 6f47715..8858050 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -18,7 +18,10 @@ type Config struct { // Defaults represents default configuration values type Defaults struct { + // BaseDir is the default directory for new worktrees. BaseDir string `yaml:"base_dir,omitempty"` + // DefaultBranch is the optional base branch for new worktrees created with -b. + DefaultBranch string `yaml:"default_branch,omitempty"` } // Hooks represents the post-create hooks configuration diff --git a/internal/config/config_test.go b/internal/config/config_test.go index edcc287..a01b6f5 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -30,6 +30,7 @@ func TestLoadConfig_ValidFile(t *testing.T) { configContent := `version: "1.0" defaults: base_dir: "../my-worktrees" + default_branch: "develop" hooks: post_create: - type: copy @@ -60,6 +61,10 @@ hooks: t.Errorf("Expected base_dir '../my-worktrees', got %s", config.Defaults.BaseDir) } + if config.Defaults.DefaultBranch != "develop" { + t.Errorf("Expected default_branch 'develop', got %s", config.Defaults.DefaultBranch) + } + if len(config.Hooks.PostCreate) != 3 { t.Errorf("Expected 3 hooks, got %d", len(config.Hooks.PostCreate)) } @@ -109,6 +114,7 @@ func TestSaveConfig(t *testing.T) { Version: "1.0", Defaults: Defaults{ BaseDir: "../test-worktrees", + DefaultBranch: "develop", }, Hooks: Hooks{ PostCreate: []Hook{ @@ -145,6 +151,10 @@ func TestSaveConfig(t *testing.T) { if loadedConfig.Defaults.BaseDir != config.Defaults.BaseDir { t.Errorf("Expected base_dir %s, got %s", config.Defaults.BaseDir, loadedConfig.Defaults.BaseDir) } + + if loadedConfig.Defaults.DefaultBranch != config.Defaults.DefaultBranch { + t.Errorf("Expected default_branch %s, got %s", config.Defaults.DefaultBranch, loadedConfig.Defaults.DefaultBranch) + } } func TestConfigValidate(t *testing.T) {