-
Notifications
You must be signed in to change notification settings - Fork 2
feat: add base branch selection to interactive checkout wizard #176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
dcc5dc8
185ffb1
c3efd1c
684d241
7219a58
1829333
64401dc
570527a
44e9ec9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,3 +41,4 @@ RELEASE_CHECKLIST_GUIDE.md | |
|
|
||
| website/ | ||
| .idea/ | ||
| tea_debug.log | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3672,3 +3672,131 @@ func TestCheckout_HooksRunAlphabetically(t *testing.T) { | |
| t.Errorf("expected alphabetical order (alpha, bravo, charlie), got:\n%s", lines) | ||
| } | ||
| } | ||
|
|
||
| // TestCheckout_BaseBranch_LocalOnlyFallback tests that --base falls back to | ||
| // a local ref when the remote tracking branch does not exist. | ||
| // | ||
| // Scenario: User runs `wt checkout -b feature --base develop` where develop | ||
| // only exists locally (no origin/develop). BaseRef config is NOT set to "local". | ||
| // Expected: Branch is created from local develop ref (not origin/develop). | ||
| func TestCheckout_BaseBranch_LocalOnlyFallback(t *testing.T) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: Missing test — verify remote ref still preferred when both exist This test covers the fallback case (only local ref exists), but there's no test verifying the more important contract: when both A future refactor could accidentally flip the preference, causing branches to silently create from stale local refs. Suggestion: Add a companion test using
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added |
||
| t.Parallel() | ||
|
|
||
| tmpDir := t.TempDir() | ||
| tmpDir = resolvePath(t, tmpDir) | ||
|
|
||
| repoPath := setupTestRepo(t, tmpDir, "test-repo") | ||
|
|
||
| // Create a develop branch with a unique commit (local only, no remote tracking) | ||
| runGitCommand(repoPath, "branch", "develop") | ||
| runGitCommand(repoPath, "checkout", "develop") | ||
| addCommit(t, repoPath, "develop.txt", "Develop commit") | ||
| runGitCommand(repoPath, "checkout", "main") | ||
|
|
||
| regFile := filepath.Join(tmpDir, ".wt", "repos.json") | ||
| os.MkdirAll(filepath.Dir(regFile), 0755) | ||
|
|
||
| reg := ®istry.Registry{ | ||
| Repos: []registry.Repo{ | ||
| {Name: "test-repo", Path: repoPath, WorktreeFormat: "../{repo}-{branch}"}, | ||
| }, | ||
| } | ||
| if err := reg.Save(regFile); err != nil { | ||
| t.Fatalf("failed to save registry: %v", err) | ||
| } | ||
|
|
||
| // Note: BaseRef is NOT set to "local" — this is the default config | ||
| cfg := &config.Config{ | ||
| RegistryPath: regFile, | ||
| Checkout: config.CheckoutConfig{ | ||
| WorktreeFormat: "../{repo}-{branch}", | ||
| }, | ||
| } | ||
| ctx := testContextWithConfig(t, cfg, repoPath) | ||
| cmd := newCheckoutCmd() | ||
| cmd.SetContext(ctx) | ||
| cmd.SetArgs([]string{"-b", "feature", "--base", "develop"}) | ||
|
|
||
| if err := cmd.Execute(); err != nil { | ||
| t.Fatalf("checkout command failed: %v (should fall back to local ref when origin/develop doesn't exist)", err) | ||
| } | ||
|
|
||
| // Verify worktree was created | ||
| wtPath := filepath.Join(tmpDir, "test-repo-feature") | ||
| if _, err := os.Stat(wtPath); os.IsNotExist(err) { | ||
| t.Fatalf("worktree should exist at %s", wtPath) | ||
| } | ||
|
|
||
| // Verify the branch was created from develop (should have develop.txt) | ||
| developFile := filepath.Join(wtPath, "develop.txt") | ||
| if _, err := os.Stat(developFile); os.IsNotExist(err) { | ||
| t.Error("feature branch should have develop.txt (created from local develop)") | ||
| } | ||
| } | ||
|
|
||
| // TestCheckout_BaseBranch_PrefersRemoteOverLocal tests that --base uses the | ||
| // remote tracking branch when both local and remote refs exist. | ||
| // | ||
| // Scenario: User runs `wt checkout -b feature --base develop` where both | ||
| // local develop and origin/develop exist but have diverged (remote has extra | ||
| // content). Expected: Branch is created from origin/develop (remote content). | ||
| func TestCheckout_BaseBranch_PrefersRemoteOverLocal(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tmpDir := t.TempDir() | ||
| tmpDir = resolvePath(t, tmpDir) | ||
|
|
||
| repoPath, _ := setupTestRepoWithOrigin(t, tmpDir, "test-repo") | ||
|
|
||
| // Create develop branch with a commit and push to origin | ||
| runGitCommand(repoPath, "checkout", "-b", "develop") | ||
| addCommit(t, repoPath, "develop-base.txt", "Base develop commit") | ||
| runGitCommand(repoPath, "push", "-u", "origin", "develop") | ||
|
|
||
| // Add a remote-only commit: push, then reset local branch back | ||
| addCommit(t, repoPath, "remote-only.txt", "Remote-only commit") | ||
| runGitCommand(repoPath, "push", "origin", "develop") | ||
| runGitCommand(repoPath, "reset", "--hard", "HEAD~1") | ||
|
|
||
| // Back to main for checkout | ||
| runGitCommand(repoPath, "checkout", "main") | ||
|
|
||
| regFile := filepath.Join(tmpDir, ".wt", "repos.json") | ||
| os.MkdirAll(filepath.Dir(regFile), 0755) | ||
|
|
||
| reg := ®istry.Registry{ | ||
| Repos: []registry.Repo{ | ||
| {Name: "test-repo", Path: repoPath, WorktreeFormat: "../{repo}-{branch}"}, | ||
| }, | ||
| } | ||
| if err := reg.Save(regFile); err != nil { | ||
| t.Fatalf("failed to save registry: %v", err) | ||
| } | ||
|
|
||
| cfg := &config.Config{ | ||
| RegistryPath: regFile, | ||
| Checkout: config.CheckoutConfig{ | ||
| WorktreeFormat: "../{repo}-{branch}", | ||
| }, | ||
| } | ||
| ctx := testContextWithConfig(t, cfg, repoPath) | ||
| cmd := newCheckoutCmd() | ||
| cmd.SetContext(ctx) | ||
| cmd.SetArgs([]string{"-b", "feature", "--base", "develop"}) | ||
|
|
||
| if err := cmd.Execute(); err != nil { | ||
| t.Fatalf("checkout command failed: %v", err) | ||
| } | ||
|
|
||
| // Verify worktree was created | ||
| wtPath := filepath.Join(tmpDir, "test-repo-feature") | ||
| if _, err := os.Stat(wtPath); os.IsNotExist(err) { | ||
| t.Fatalf("worktree should exist at %s", wtPath) | ||
| } | ||
|
|
||
| // Verify the branch was created from origin/develop (should have remote-only.txt) | ||
| remoteFile := filepath.Join(wtPath, "remote-only.txt") | ||
| if _, err := os.Stat(remoteFile); os.IsNotExist(err) { | ||
| t.Error("feature branch should have remote-only.txt (created from origin/develop, not local develop)") | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Silent local-ref fallback changes behavior for ALL checkouts, not just wizard
This applies to every
wt checkout -b --baseinvocation. Previously, iforigin/developdidn't exist, git would produce a clear error. Now it silently falls back to a potentially stale local ref with no logging.Failure scenarios:
fetch→ branches from weeks-old local ref silently--base developp→ silently uses localdeveloppif it happens to existSuggestion: Add a warning log when falling back:
Alternatively, consider restricting this fallback to only the wizard code path if it's not intended as a general behavior change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added warning log when falling back to local ref. Also added a companion integration test (
TestCheckout_BaseBranch_PrefersRemoteOverLocal) that verifies remote is preferred when both exist.