Skip to content

Change directory to repo root when deleting current worktree#18

Merged
agarcher merged 1 commit intomainfrom
exit-bug
Jan 12, 2026
Merged

Change directory to repo root when deleting current worktree#18
agarcher merged 1 commit intomainfrom
exit-bug

Conversation

@agarcher
Copy link
Owner

@agarcher agarcher commented Jan 12, 2026

Summary

  • When deleting a worktree that the user is currently in (wt delete or wt cleanup), automatically change to the repository root
  • Previously users would be left in a deleted directory and had to manually run wt exit or cd

Test plan

  • All existing tests pass
  • Linter passes
  • Manual test: wt create test-delete && wt delete - should return to repo root
  • Manual test: Create merged worktree, run wt cleanup from within it - should return to repo root

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

Improvements

  • Automatically navigate to the repository root when deleting the worktree you're currently in
  • Enhanced shell wrapper support for delete and cleanup operations

✏️ Tip: You can customize this high-level summary in your review settings.

When a user deletes a worktree they're currently in (via `wt delete` or
`wt cleanup`), the shell wrapper now automatically changes to the
repository root instead of leaving them in a deleted directory.

- delete.go: Output repo root to stdout when user is in deleted worktree
- cleanup.go: Track if current directory is in any deleted worktree
- shell.go: Add delete/cleanup to commands that handle directory changes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Jan 12, 2026

📝 Walkthrough

Walkthrough

These changes enhance the worktree deletion and cleanup flow by detecting when users are operating from within a worktree being deleted, then outputting the repository root path to enable automatic directory navigation via the shell wrapper.

Changes

Cohort / File(s) Summary
Worktree Deletion Detection
internal/commands/delete.go, internal/commands/cleanup.go
Replaces hard warnings with dynamic detection of whether the current working directory is inside the target worktree(s) using strings.HasPrefix. After successful deletion/cleanup, outputs repository root to stdout when applicable.
Shell Wrapper Post-Execution Handling
internal/shell/shell.go
Extends cd-handling logic to treat delete and cleanup commands identically to create, extracting and evaluating the last output line. If it's a valid directory, performs automatic cd; otherwise, prints the line. Updates bash/zsh generator blocks to include these commands in switch cases.

Sequence Diagram

sequenceDiagram
    actor User
    participant Shell as Shell Wrapper
    participant Cmd as Command Handler
    participant FS as File System
    
    User->>Shell: wt delete [worktree]
    Shell->>Cmd: Execute delete command
    
    Note over Cmd: Check if cwd inside worktree
    Cmd->>FS: Verify current directory location
    FS-->>Cmd: inDeletedWorktree status
    
    Cmd->>Cmd: Perform deletion
    Cmd->>FS: Remove worktree
    
    alt User was in deleted worktree
        Cmd-->>Shell: Output repo root (last line)
        Shell->>FS: Evaluate last line as directory
        Shell->>Shell: cd to repo root
    else User not affected
        Cmd-->>Shell: Output status message
        Shell->>User: Display output
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A worktree falls, but fear not the plight,
The root path whispers, guiding us right,
No more stranded in deleted terrain,
Our shells now dance through the directory lane,
Cleanup and delete, with grace they align! 🎯

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: automatically changing directory to repo root when deleting the current worktree, which is the primary objective of this PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
internal/commands/cleanup.go (1)

193-201: Path prefix check may produce false positives for sibling directories.

Using strings.HasPrefix(cwd, c.path) can incorrectly match if a worktree path is a prefix of another directory name (e.g., /repo/worktrees/foo would match /repo/worktrees/foobar). Consider appending a path separator or using filepath.Rel for a more robust check.

Additionally, the error from os.Getwd() is silently ignored. If Getwd fails, cwd will be empty and the prefix check will always be false, which is a reasonable fallback, but worth noting.

♻️ Suggested fix using path separator
 	// Check if user is in any of the worktrees being deleted
 	cwd, _ := os.Getwd()
 	inDeletedWorktree := false
 	for _, c := range candidates {
-		if strings.HasPrefix(cwd, c.path) {
+		if cwd == c.path || strings.HasPrefix(cwd, c.path+string(filepath.Separator)) {
 			inDeletedWorktree = true
 			break
 		}
 	}
internal/commands/delete.go (1)

126-128: Same path prefix false-positive concern and potential code duplication.

  1. The strings.HasPrefix(cwd, worktreePath) check has the same issue as in cleanup.go - it could match sibling directories with similar prefixes.

  2. Note that cwd is retrieved via os.Getwd() twice: once at line 67 (in the auto-detect branch) and again at line 127. When args is provided (explicit worktree name), the first retrieval is skipped, so this second call is necessary. The current structure is correct.

♻️ Suggested fix for path comparison
 	// Check if user is in the worktree being deleted
 	cwd, _ := os.Getwd()
-	inDeletedWorktree := strings.HasPrefix(cwd, worktreePath)
+	inDeletedWorktree := cwd == worktreePath || strings.HasPrefix(cwd, worktreePath+string(filepath.Separator))
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0650413 and 0ade83e.

📒 Files selected for processing (3)
  • internal/commands/cleanup.go
  • internal/commands/delete.go
  • internal/shell/shell.go
🧰 Additional context used
📓 Path-based instructions (3)
internal/shell/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Place shell integration function generation in internal/shell/ package

Files:

  • internal/shell/shell.go
internal/commands/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

internal/commands/**/*.go: Use go test -v -run TestName ./internal/commands/ to run a single test
Place Cobra command implementations in internal/commands/ package
Use cmd.Println() for stderr output and fmt.Fprintln(cmd.OutOrStdout(), ...) for stdout output (paths, listings) in Cobra commands

Files:

  • internal/commands/cleanup.go
  • internal/commands/delete.go
internal/{git,commands}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Detect if running in main repo vs worktree by checking if .git is a file (worktree) or directory (main repo)

Files:

  • internal/commands/cleanup.go
  • internal/commands/delete.go
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: agarcher/wt PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T02:19:51.920Z
Learning: `wt` is a Git worktree manager CLI built with Go and Cobra that manages worktree lifecycle with hooks
Learnt from: CR
Repo: agarcher/wt PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T02:19:51.920Z
Learning: Use shell wrapper function (`wt()`) generated by `wt init <shell>` to handle the actual `cd`, solving the subprocess directory change limitation
Learnt from: CR
Repo: agarcher/wt PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T02:19:51.920Z
Learning: Applies to internal/{git,hooks}/**/*.go : Store worktree numeric index in `.git/worktrees/<name>/wt-index` for use in port offsets and resource isolation
Learnt from: CR
Repo: agarcher/wt PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T02:19:51.920Z
Learning: Applies to internal/{git,commands}/**/*.go : Detect if running in main repo vs worktree by checking if `.git` is a file (worktree) or directory (main repo)
Learnt from: CR
Repo: agarcher/wt PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T02:19:51.920Z
Learning: Applies to internal/git/**/*.go : Place Git worktree command wrappers and porcelain output parsing in internal/git/ package
📚 Learning: 2026-01-12T02:19:51.920Z
Learnt from: CR
Repo: agarcher/wt PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T02:19:51.920Z
Learning: Use shell wrapper function (`wt()`) generated by `wt init <shell>` to handle the actual `cd`, solving the subprocess directory change limitation

Applied to files:

  • internal/shell/shell.go
📚 Learning: 2026-01-12T02:19:51.920Z
Learnt from: CR
Repo: agarcher/wt PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T02:19:51.920Z
Learning: Applies to internal/shell/**/*.go : Place shell integration function generation in internal/shell/ package

Applied to files:

  • internal/shell/shell.go
📚 Learning: 2026-01-12T02:19:51.920Z
Learnt from: CR
Repo: agarcher/wt PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T02:19:51.920Z
Learning: `wt` is a Git worktree manager CLI built with Go and Cobra that manages worktree lifecycle with hooks

Applied to files:

  • internal/shell/shell.go
  • internal/commands/cleanup.go
  • internal/commands/delete.go
📚 Learning: 2026-01-12T02:19:51.920Z
Learnt from: CR
Repo: agarcher/wt PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T02:19:51.920Z
Learning: Applies to internal/git/**/*.go : Place Git worktree command wrappers and porcelain output parsing in internal/git/ package

Applied to files:

  • internal/shell/shell.go
  • internal/commands/cleanup.go
  • internal/commands/delete.go
📚 Learning: 2026-01-12T02:19:51.920Z
Learnt from: CR
Repo: agarcher/wt PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T02:19:51.920Z
Learning: Applies to cmd/wt/main.go : Entry point should be cmd/wt/main.go and delegate to `commands.Execute()`

Applied to files:

  • internal/shell/shell.go
📚 Learning: 2026-01-12T02:19:51.920Z
Learnt from: CR
Repo: agarcher/wt PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T02:19:51.920Z
Learning: Applies to internal/{git,hooks}/**/*.go : Store worktree numeric index in `.git/worktrees/<name>/wt-index` for use in port offsets and resource isolation

Applied to files:

  • internal/shell/shell.go
  • internal/commands/cleanup.go
  • internal/commands/delete.go
📚 Learning: 2026-01-12T02:19:51.920Z
Learnt from: CR
Repo: agarcher/wt PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T02:19:51.920Z
Learning: Applies to internal/{git,commands}/**/*.go : Detect if running in main repo vs worktree by checking if `.git` is a file (worktree) or directory (main repo)

Applied to files:

  • internal/commands/cleanup.go
  • internal/commands/delete.go
📚 Learning: 2026-01-12T02:19:51.920Z
Learnt from: CR
Repo: agarcher/wt PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T02:19:51.920Z
Learning: Applies to internal/hooks/**/*.go : Pass standardized hook environment variables: `WT_NAME`, `WT_PATH`, `WT_BRANCH`, `WT_REPO_ROOT`, `WT_WORKTREE_DIR`, `WT_INDEX`

Applied to files:

  • internal/commands/delete.go
🔇 Additional comments (5)
internal/commands/cleanup.go (1)

248-252: Output follows the correct pattern for shell wrapper integration.

The repo root is printed to stdout using fmt.Fprintln(cmd.OutOrStdout(), repoRoot), which aligns with the coding guidelines for path output. The shell wrapper will read the last line and cd into it if it's a directory.

One minor note: the condition deleted > 0 correctly ensures we only output the path when at least one worktree was actually deleted, preventing the shell wrapper from attempting a cd when nothing changed.

internal/commands/delete.go (1)

158-162: Output correctly uses stdout for shell wrapper integration.

The implementation follows the coding guidelines: fmt.Fprintln(cmd.OutOrStdout(), repoRoot) outputs the path to stdout for the shell wrapper to consume. Unlike cleanup.go, there's no deleted > 0 check needed here since reaching this point means the deletion succeeded.

internal/shell/shell.go (3)

162-181: Shell wrapper logic correctly extended for delete and cleanup commands.

The change properly extends the create handling to also cover delete and cleanup. The logic:

  1. Prints all but the last line via sed '$d'
  2. Checks if the last line is a directory and cds into it
  3. Otherwise prints the last line

This handles both scenarios:

  • create: last line is the new worktree path → cd into it
  • delete/cleanup: last line is repo root (if in deleted worktree) → cd into it; otherwise command output is printed normally

The return $exit_code correctly propagates the exit status.


351-367: Bash wrapper matches zsh implementation.

The bash implementation mirrors the zsh logic correctly, maintaining consistency across shells.


501-516: Fish wrapper correctly updated.

The fish implementation follows the same pattern as bash and zsh, using fish-specific syntax (test, switch/case). The logic is consistent across all three shells.

@agarcher agarcher merged commit d93506f into main Jan 12, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant