Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ git -C <repo> worktree remove --force <path>
git -C <repo> worktree prune
```

This only cleans stale Git worktree metadata, such as records left after a worktree directory was removed manually. It does not remove branches.
This only cleans stale Git worktree metadata, such as records left after a worktree directory was removed manually. By itself, `--prune` does not inspect or remove active linked worktrees. It does not remove branches.

## Help

Expand Down
30 changes: 17 additions & 13 deletions internal/wtclean/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,20 +98,24 @@ func (a *App) processRepo(ctx context.Context, opts Options, repo string, summar
return
}

paths, err := a.listLinkedWorktreePaths(ctx, repo)
if err != nil {
summary.Failed++
writef(a.stderr, "git wtclean: failed to list worktrees: %s: %v\n", repo, err)
return
}
// In pure prune mode (--prune without a delete option), only clean stale
// worktree metadata and skip listing active worktrees.
if opts.DeleteMode != "" || !opts.Prune {
paths, err := a.listLinkedWorktreePaths(ctx, repo)
if err != nil {
summary.Failed++
writef(a.stderr, "git wtclean: failed to list worktrees: %s: %v\n", repo, err)
return
}

for _, path := range paths {
summary.Found++
if opts.DeleteMode == "" {
writef(a.stdout, "Would remove: %s\n", path)
continue
for _, path := range paths {
summary.Found++
if opts.DeleteMode == "" {
writef(a.stdout, "Would remove: %s\n", path)
continue
}
a.removeWorktree(ctx, opts, repo, path, summary)
}
a.removeWorktree(ctx, opts, repo, path, summary)
}

if opts.Prune {
Expand Down Expand Up @@ -182,7 +186,7 @@ func (a *App) pruneRepo(ctx context.Context, opts Options, repo string, summary
func (a *App) printSummary(opts Options, summary Summary) {
if opts.DeleteMode == "" {
if opts.Prune {
writef(a.stdout, "Done. found=%d prune_checked=%d failed=%d\n", summary.Found, summary.PruneChecked, summary.Failed)
writef(a.stdout, "Done. prune_checked=%d failed=%d\n", summary.PruneChecked, summary.Failed)
return
}
writef(a.stdout, "Dry run. found=%d. Run %s to remove, or %s to force remove.\n", summary.Found, "'git wtclean -d'", "'git wtclean -D'")
Expand Down
12 changes: 6 additions & 6 deletions internal/wtclean/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,7 @@ func TestRunPruneQuietByDefault(t *testing.T) {
}

want := strings.Join([]string{
"Would remove: /tmp/repo1/.wt/feature-a",
"Would remove: /tmp/repo2/.wt/feature-b",
"Done. found=2 prune_checked=2 failed=0",
"Done. prune_checked=2 failed=0",
"",
}, "\n")
if stdout.String() != want {
Expand All @@ -119,6 +117,10 @@ func TestRunPruneQuietByDefault(t *testing.T) {
if got := runner.callsContaining("worktree prune"); len(got) != 2 {
t.Fatalf("prune call count = %d, want 2: %#v", len(got), got)
}

if got := runner.callsContaining("worktree list"); len(got) != 0 {
t.Fatalf("worktree list calls = %#v, want none", got)
}
}

func TestRunPruneVerbosePrintsRepositories(t *testing.T) {
Expand All @@ -131,11 +133,9 @@ func TestRunPruneVerbosePrintsRepositories(t *testing.T) {
}

want := strings.Join([]string{
"Would remove: /tmp/repo1/.wt/feature-a",
"Pruning: /tmp/repo1",
"Would remove: /tmp/repo2/.wt/feature-b",
"Pruning: /tmp/repo2",
"Done. found=2 prune_checked=2 failed=0",
"Done. prune_checked=2 failed=0",
"",
}, "\n")
if stdout.String() != want {
Expand Down