Skip to content

Add list ignore support for worktree paths and branch names#1743

Open
willpote wants to merge 10 commits into
max-sixty:mainfrom
willpote:list-ignore-branch-names
Open

Add list ignore support for worktree paths and branch names#1743
willpote wants to merge 10 commits into
max-sixty:mainfrom
willpote:list-ignore-branch-names

Conversation

@willpote

Copy link
Copy Markdown

Adds [list].ignore config. These are glob patterns that hide worktrees and branches from wt list output. Motivation is when using tools like Cursor, which generate worktrees for each agent running in parallel, wt list becomes cluttered with these temporary worktrees and un-usable.

  [list]
  ignore = ["tmp-*", "*/scratch/*"]

Per-project overrides work via the [projects."..."] table (inherits through OverridableConfig).

Each pattern is matched two ways:

  • As a path glob against the worktree's canonical filesystem path (e.g. /scratch/ matches worktrees inside a scratch/ directory)
  • As a string match against the branch name (e.g. tmp-* matches any branch starting with tmp-)

A worktree or branch is hidden if either matches. Filtering applies uniformly to worktrees, local branches (--branches), and remote branches (--remotes).

wt list --ignored bypasses all ignore filtering and shows everything.

When items are filtered, the summary line includes the count: Showing 3 worktrees, 1 ignored.

Changes

  • ignore: Option<Vec> field on ListConfig
  • --ignored CLI flag on wt list
  • Filtering in collect() after worktree/branch collection, before sorting — matches worktrees by path and branch name, local/remote branches by name
  • SummaryMetrics.ignored count shown in the summary line when > 0
  • glob crate added as a dependency
  • 6 integration tests: single glob, array globs, --ignored flag, parent path matching, branch name matching, --branches filtering

@worktrunk-bot worktrunk-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nice feature — the implementation is clean and well-tested. Two issues worth addressing:

  1. Invalid glob patterns are silently dropped. The .filter_map(|s| glob::Pattern::new(s).ok()) silently ignores malformed patterns. The project has precedent for surfacing pattern errors — step_commands.rs returns an error for invalid [step.copy-ignored].exclude patterns. A typo in an ignore pattern (e.g. [tmp-*) would be silently ignored with no feedback, which is hard to debug. Consider at least logging a warning.

  2. [list].ignore config option isn't documented in the list command's after_long_help. The --ignored flag is documented, but users discovering the [list].ignore config option relies on them reading the --ignored flag description and inferring the config key. A brief mention in the after_long_help (and thus the docs page) would help discoverability — similar to how --full, --branches, etc. reference their config equivalents.

Comment thread src/commands/list/collect/mod.rs Outdated

@worktrunk-bot worktrunk-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

CI is failing with three issues:

  1. Cmd::output() doesn't existtest_list_config_ignore_local_branch calls .output() on worktrunk::shell_exec::Cmd (lines 598, 600), but Cmd doesn't have that method. Use .run() instead (per project conventions, all commands go through Cmd::run()).

  2. clippy::too_many_argumentshandle_list_command now has too many parameters after adding ignored. Consider grouping into a struct or using #[allow(clippy::too_many_arguments)] if the function is a thin dispatch layer.

  3. clippy::redundant_closure — likely the .unwrap_or_else(|| wt.path.clone()) on the canonicalize result.

Comment thread tests/integration_tests/list_config.rs Outdated
Comment thread tests/integration_tests/list_config.rs Outdated
@max-sixty

Copy link
Copy Markdown
Owner

I think I get the motivation, and I've had a similar experience at times

but I worry this might not quite pull its weight...

hardly my style to suggest multiple checkouts, but one approach is to give cursor another base checkout

OTOH I can't think of an easy way to do this without the feature. and we could make the downside less confusing with "X hidden" in the footer

@willpote

willpote commented Mar 26, 2026

Copy link
Copy Markdown
Author

I think I get the motivation, and I've had a similar experience at times

but I worry this might not quite pull its weight...

hardly my style to suggest multiple checkouts, but one approach is to give cursor another base checkout

OTOH I can't think of an easy way to do this without the feature. and we could make the downside less confusing with "X hidden" in the footer

In the Cursor case, if you use the "Use multiple agents" feature when implementing something, it automatically spins up worktrees for you in ~/.cursor/worktrees, its not something another base checkout helps with unfortunately

Re: X hidden, i did add that to the summary message- not sure if this is what you meant but it says ○ Showing 1 worktree, 4 ignored, 1 with changes

Hide worktrees and branches from `wt list` using glob patterns in
user config. Patterns match against canonical worktree paths and
branch names, with `--ignored` flag to bypass filtering.

Configure in ~/.config/worktrunk/config.toml:

  [list]
  ignore = "*/tmp-*"           # single pattern
  ignore = ["*/tmp-*", "*/scratch-*"]  # multiple patterns
@willpote willpote force-pushed the list-ignore-branch-names branch from ad316ad to 67ef76a Compare March 26, 2026 04:18

@worktrunk-bot worktrunk-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Previous review points were addressed (glob warning logging, docs in after_long_help, #[allow(clippy::too_many_arguments)]). One compile-blocking issue remains from the earlier review:

repo.git_command() returns Cmd, which has no .output() method — use .run() instead (lines 598, 600 in list_config.rs). This will fail to compile.

Comment thread tests/integration_tests/list_config.rs Outdated
Comment thread tests/integration_tests/list_config.rs Outdated
@max-sixty

Copy link
Copy Markdown
Owner

ok. let me think / let's see whether anyone else has thoughts

feel free to use @worktrunk-bot to fix tests etc

@max-sixty

Copy link
Copy Markdown
Owner

Re: X hidden, i did add that to the summary message- not sure if this is what you meant but it says ○ Showing 1 worktree, 4 ignored, 1 with changes

I was thinking "8 hidden" could be here

@worktrunk-bot

Copy link
Copy Markdown
Collaborator

CI is failing because TestRepo::git_command() on main now returns worktrunk::shell_exec::Cmd (not std::process::Command), and Cmd has .run() but not .output().

The fix is in tests/integration_tests/list_config.rs lines 598 and 600 — change .output() to .run():

-                .output();
+                .run();
         }
-        let _ = repo.git_command().args(["branch", "-D", branch]).output();
+        let _ = repo.git_command().args(["branch", "-D", branch]).run();

The check-unused-dependencies failure (tree-sitter) is a pre-existing issue on main — not related to this PR.

@willpote

Copy link
Copy Markdown
Author

Re: X hidden, i did add that to the summary message- not sure if this is what you meant but it says ○ Showing 1 worktree, 4 ignored, 1 with changes

I was thinking "8 hidden" could be here

Right i added 'X ignored'. I can rename to 'X hidden' if you like

@max-sixty

Copy link
Copy Markdown
Owner

Right i added 'X ignored'.

sorry, not sure how I missed that!

@max-sixty

Copy link
Copy Markdown
Owner

@worktrunk-bot can we fix the tests, so we have a working PR to review?

@worktrunk-bot

Copy link
Copy Markdown
Collaborator

Fixed — merged main and changed .output().run() on the two Cmd calls in list_config.rs (lines 598, 600). All 1266 integration tests pass and lints are clean.

@willpote willpote force-pushed the list-ignore-branch-names branch from 0e9f972 to a238dba Compare April 4, 2026 21:49

@worktrunk-bot worktrunk-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Previous review feedback has been addressed — glob warning, docs, clippy, and the .output() compile fix are all resolved. Implementation is clean.

One remaining gap: dev/config.example.toml doesn't document the new [list].ignore field. Per project guidelines, config field changes should update the example configs since they're embedded in CLI help via include_str! and used by wt config create. Adding a line under the [list] section (after timeout-ms) would make the option discoverable:

# ignore = ["tmp-*"]    # Glob patterns to hide from output; use --ignored to show all

@willpote

willpote commented Apr 6, 2026

Copy link
Copy Markdown
Author

@worktrunk-bot update dev/config.example.toml as suggested

@willpote

willpote commented Apr 6, 2026

Copy link
Copy Markdown
Author

@max-sixty any thoughts on this change?

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@worktrunk-bot

Copy link
Copy Markdown
Collaborator

Done — added ignore = ["tmp-*"] to the [list] section in the config docs source (src/cli/mod.rs), and regenerated all downstream files:

  • dev/config.example.toml (line 88)
  • docs/content/config.md
  • skills/worktrunk/reference/config.md
  • Help text snapshots for wt config and wt config create

@max-sixty

Copy link
Copy Markdown
Owner

@worktrunk-bot do we label as experimental? can we do that if not?

thanks @willpote

@max-sixty

Copy link
Copy Markdown
Owner

@worktrunk-bot can we review whether this should be ignore vs hidden? if we can switch to them etc, it should be "hidden"; it should only be "ignored" if we totally ignore it

Mark the ignore filtering feature (config field, --ignored flag, and
docs section) with [experimental] tags to signal the interface may
change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@worktrunk-bot

Copy link
Copy Markdown
Collaborator

Done — labeled [list].ignore as experimental. Added [experimental] tags to:

  • The ## Filtering with ignore patterns docs section heading
  • The --ignored CLI flag description
  • The ignore line in the config example TOML

All downstream files regenerated (docs, skills, help snapshots).

@worktrunk-bot worktrunk-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

On the ignore vs hidden naming question:

ignore is the more accurate term for the current implementation. Matching items are filtered out at the collection stage — no git operations, diffstats, or CI status checks run against them. They're not processed and then hidden from display; they're skipped entirely. This is closer to .gitignore semantics (file exists on disk, git pretends it doesn't) than a "hidden" flag.

That said, the filtering only applies to wt list — other commands (switch, merge, etc.) still operate on these worktrees normally. From that angle, "hidden from list" is what users experience.

Precedent leans toward ignore: .gitignore, --ignored in git status, eslintignore, etc. all use "ignore" for "exists but excluded from default view, recoverable with a flag." The counterpart --ignored flag also reads naturally — --hidden would be less conventional.

This is a judgment call for the maintainer — leaving as COMMENT since the naming question is still open.

@worktrunk-bot

Copy link
Copy Markdown
Collaborator

Reviewed the diff — the filtering only applies to wt list output. Worktrees matched by these patterns are still fully accessible for switch, merge, remove, and every other command. So this is display-level hiding, not functional ignoring.

Agree hidden is the right name. The rename would touch:

  • Config field: [list].ignore[list].hidden
  • CLI flag: --ignored--hidden (or --show-hidden)
  • Docs/messages: "ignored" → "hidden" throughout (summary line, section headings, help text)
  • Struct fields: ignore/ignoredhidden in ListConfig, ShowConfig, SummaryMetrics, collect()

The glob::Pattern matching logic stays the same — just a naming change.

@willpote would you like to make this rename, or should I put up a commit on the branch?

@max-sixty

Copy link
Copy Markdown
Owner

@worktrunk-bot let's make the naming change and then if @willpote disagrees we can revert

The filtering only applies to `wt list` display — hidden worktrees are
still accessible to switch, merge, remove, etc. "Hidden" better describes
this display-level behavior than "ignore" (which implies the items are
functionally excluded).

Renames config field, CLI flag (--ignored → --hidden), all internal
variables, tests, docs, and snapshots.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@worktrunk-bot

Copy link
Copy Markdown
Collaborator

Done — renamed [list].ignore[list].hidden and --ignored--hidden throughout:

  • Config field, CLI flag, struct fields, variables, comments
  • All 6 integration tests (renamed functions + snapshot files)
  • Help text snapshots, docs, skill references, example configs

All 1339 integration tests + 492 unit tests pass, lints clean.

@max-sixty

Copy link
Copy Markdown
Owner

@worktrunk-bot merge and resolve conflicts

Resolve conflicts from main's ListArgs struct refactoring: adopt
the new `List(ListArgs)` pattern and add the `hidden` field to
the ListArgs struct.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@worktrunk-bot

Copy link
Copy Markdown
Collaborator

Merged main and resolved conflicts — main refactored List to use a ListArgs struct (along with Switch, Remove, Merge). Added the hidden field to ListArgs and updated the dispatch to pass it through. All 1393 integration tests pass, lints clean. Waiting on CI.

@max-sixty

Copy link
Copy Markdown
Owner

let's remove --hidden; I'm not sure exactly what that should be called; I'm not sure it should even be an option; and we can add later. Add a TODO re adding this option later

The flag name and whether it should be a flag at all is TBD.
The `[list].hidden` config functionality remains — only the
CLI override is removed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@worktrunk-bot

Copy link
Copy Markdown
Collaborator

Done — removed the --hidden flag and added a TODO in src/cli/mod.rs about potentially adding it back later (name TBD). The [list].hidden config filtering still works, just no CLI override for now.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@max-sixty

Copy link
Copy Markdown
Owner

@willpote is this version OK with you?

max-sixty added a commit that referenced this pull request Jun 11, 2026
…es (#3039)

A CI monitor watching PR #3035 polled the combined-status API (`gh api
.../commits/<sha>/status`) for `codecov/patch` and concluded codecov had
stopped posting on PR heads. Investigation showed nothing is broken:
codecov's GitHub App posts **check runs** on PR head commits (commit
statuses appear only on main pushes), and check runs never show up in
the legacy status API. This has been the case since at least April
(oldest surviving PR head, #1743). The check run also lands several
minutes after the `code-coverage` job finishes (~9 min on #3035).

This adds one sentence to the Coverage section so merge gating polls `gh
pr checks` rather than the status API.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

> _This was written by Claude Code on behalf of max_

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

4 participants