Skip to content

feat: enrich renri remove and add --merged auto-cleanup#65

Merged
yukimemi merged 2 commits into
mainfrom
feat/remove-details-and-merged
May 23, 2026
Merged

feat: enrich renri remove and add --merged auto-cleanup#65
yukimemi merged 2 commits into
mainfrom
feat/remove-details-and-merged

Conversation

@yukimemi
Copy link
Copy Markdown
Owner

@yukimemi yukimemi commented May 23, 2026

Summary

Two related improvements to make renri remove safer and ergonomic:

  • Detailed pre-remove panelrenri remove now prints a YAML-style block (name / branch / path / vcs / head id + description / status flags / PR info) before doing anything, then asks for confirmation. -y/--yes skips the prompt; --non-interactive without --yes is a hard error so the safety prompt cannot be silently bypassed.
  • --merged batch mode — sweeps every worktree whose GitHub PR is MERGED or CLOSED. Each candidate gets the same detail panel and the whole batch is confirmed once. Dirty / conflict / locked / main rows are skipped with a warning (override with --force). Per-row failures are tallied at the end rather than aborting the batch. --refresh re-fetches the PR cache before deciding.
  • -f/--force propagates to the backend's remove(force=true), so single-target removes can finally drop uncommitted worktrees (previously always force=false).

Test plan

  • cargo make check (fmt + clippy + 78 tests + lock check) passes locally
  • renri remove --help surfaces -y, -f, --merged, --refresh
  • Smoke: renri remove <name> shows the panel and confirms
  • Smoke: renri remove --merged filters correctly, skips dirty/locked, removes the rest
  • Smoke: renri remove --merged --non-interactive errors clearly without --yes

Summary by CodeRabbit

Release Notes

  • New Features
    • Added --yes, --force, --merged, and --refresh flags to the remove command for enhanced flexibility.
    • Introduced --merged mode to remove all worktrees with merged or closed GitHub PRs in a single operation.
    • Enhanced interactive confirmation prompts and safety enforcement during worktree removal.
    • Improved worktree details display and warning messages for skipped candidates.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dbfac755-d4c3-4c4b-979c-f000d2575222

📥 Commits

Reviewing files that changed from the base of the PR and between 204651d and 48313e0.

📒 Files selected for processing (1)
  • src/main.rs

📝 Walkthrough

Walkthrough

The CLI remove subcommand is refactored from simple single-worktree deletion into a two-mode workflow: targeted removal with PR-aware details display, and bulk removal of all merged/closed-PR worktrees. Both modes share confirmation, hook execution, and backend dispatch infrastructure.

Changes

Remove subcommand multi-mode expansion

Layer / File(s) Summary
Remove subcommand interface and dispatch
src/main.rs (lines 84–118, 260–266)
Command::Remove gains --yes, --force, --merged, --refresh flags; main command dispatch passes these flags to cmd_remove for mode routing.
Single-target removal with PR details and confirmation
src/main.rs (lines 1136–1206)
cmd_remove delegates to cmd_remove_merged when --merged is set; in single-target mode, loads PR cache for the named worktree, displays unified details panel (branch, path, VCS kind, status, PR state/URL), enforces confirmation rules, and executes removal via remove_one.
Merged-PR bulk removal with skip rules and tracking
src/main.rs (lines 1208–1363)
cmd_remove_merged sweeps all worktrees, filters by PR MERGED or CLOSED state, skips main worktree and applies conditional skip rules (dirty/conflict/locked unless --force), prints warnings for skipped candidates, displays details for each valid candidate, confirms once, removes each while tracking success/failure counts, and bails if any removal fails.
Shared removal and confirmation helpers
src/main.rs (lines 1365–1524)
print_worktree_details standardizes the details panel rendering; confirm_remove centralizes confirmation logic with non-interactive enforcement; remove_one runs pre_remove hooks and dispatches deletion to the correct VCS backend; load_pr_cache_for_repo wraps PR cache resolution with graceful fallback when GitHub/gh is unavailable.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CLIParser
  participant cmd_remove
  participant cmd_remove_merged
  participant PRCache
  participant Confirmation
  participant remove_one
  participant VCSBackend
  User->>CLIParser: remove [name] --merged
  alt merged mode
    CLIParser->>cmd_remove_merged: dispatch
    cmd_remove_merged->>PRCache: load_pr_cache_for_repo
    PRCache-->>cmd_remove_merged: PR state map
    cmd_remove_merged->>cmd_remove_merged: filter by MERGED/CLOSED
    cmd_remove_merged->>cmd_remove_merged: apply skip rules
    loop for each candidate
      cmd_remove_merged->>cmd_remove_merged: print_worktree_details
    end
    cmd_remove_merged->>Confirmation: confirm_remove
    Confirmation-->>cmd_remove_merged: approved
    loop remove each
      cmd_remove_merged->>remove_one: worktree, force
      remove_one->>VCSBackend: dispatch by kind
      VCSBackend-->>remove_one: removed
    end
    cmd_remove_merged-->>User: results
  else single target
    CLIParser->>cmd_remove: dispatch
    cmd_remove->>PRCache: load_pr_cache_for_repo
    PRCache-->>cmd_remove: PR state
    cmd_remove->>cmd_remove: print_worktree_details
    cmd_remove->>Confirmation: confirm_remove
    Confirmation-->>cmd_remove: approved
    cmd_remove->>remove_one: worktree, force
    remove_one->>VCSBackend: dispatch by kind
    VCSBackend-->>remove_one: removed
    remove_one-->>User: success
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

The PR introduces substantial logic expansion with two distinct workflows (single-target and bulk merged-PR removal), each with filtering, confirmation, and error handling. The additions span multiple behavioral checkpoints (CLI contract, PR-cache integration, details rendering, skip-rule evaluation, hook dispatch) distributed across a single file, with significant conditional branching and state tracking (candidate filtering, success/failure counts, skip reasons). Reviewing requires tracing both workflows end-to-end, validating skip rule correctness, and understanding the interaction between PR cache refresh modes, confirmation enforcement, and backend dispatch.

Possibly related PRs

  • yukimemi/renri#17: This PR's --refresh flag and best-effort PR cache loading are built directly on the pr_cache::load_or_refresh module introduced in #17, making it a code-level dependency for understanding the PR cache integration.

Poem

🐰 Behold! Two paths through worktrees pass,
One swift, one bulk—a merged-PR class!
With hooks and cache and --yes to spare,
Details render bright beyond compare.
Confirmed and cleared, the branches flee—
A cleaner repo, wild and free! 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 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 changes: enriching the remove command with new flags and adding a --merged auto-cleanup mode.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/remove-details-and-merged

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request enhances the remove command by introducing a --merged flag to automatically clean up worktrees linked to merged or closed GitHub PRs. It also adds detailed status panels, interactive confirmation prompts, and a --refresh flag for PR metadata. The review feedback focuses on refining the user experience: it suggests allowing the command to proceed even if the PR cache is empty to avoid misleading error messages and recommends enabling the --refresh flag for single-target removals to ensure the information displayed in the new details panel is current.

Comment thread src/main.rs Outdated
Comment on lines +1243 to +1248
if prs.is_empty() {
anyhow::bail!(
"no PR data available — install the `gh` CLI and authenticate, \
then retry (optionally with --refresh)"
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Bailing when prs is empty can lead to a misleading error message. If a repository has zero PRs, load_pr_cache_for_repo returns an empty map, which triggers this bail and incorrectly suggests that the gh CLI is missing or unauthenticated. It is better to allow the command to proceed; if no candidates are found, the user will receive the "nothing to remove" message at line 1301, which is a more accurate description of the state. If you wish to warn about missing gh CLI, it should be done by checking for its presence explicitly rather than relying on an empty PR list.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@gemini-code-assist Good catch — fixed in 48313e0. Dropped the empty-cache bail; the downstream "nothing to remove" path is now the single source of truth for the no-candidates state, and tool-missing vs zero-PRs are no longer conflated.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Understood, thanks for the update. The approach of using the downstream "nothing to remove" path as the single source of truth for the no-candidates state is much cleaner and avoids the ambiguity of the previous bail. The changes look solid.

Comment thread src/main.rs Outdated
Comment on lines +113 to +114
/// Bypass the PR cache and re-fetch from GitHub. Only meaningful
/// with `--merged`; matches `renri list --refresh`.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The documentation for the --refresh flag should be updated to reflect that it is now also applicable to single-target removals. Since the removal process now includes a detailed panel showing PR information, allowing the user to refresh the PR cache ensures they are making decisions based on up-to-date data.

Suggested change
/// Bypass the PR cache and re-fetch from GitHub. Only meaningful
/// with `--merged`; matches `renri list --refresh`.
/// Bypass the PR cache and re-fetch from GitHub.
/// Matches `renri list --refresh`.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@gemini-code-assist Done in 48313e0 — help text now documents both single-target and --merged usage.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks for the update, @yukimemi. The documentation in 48313e0 looks good and accurately reflects the usage for both single-target and --merged modes.

Comment thread src/main.rs Outdated
Comment on lines +1148 to +1150
if refresh {
anyhow::bail!("--refresh only makes sense with --merged");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This restriction on --refresh should be removed. Allowing --refresh for single-target removals is consistent with renri list and provides value by ensuring the PR status shown in the pre-removal details panel is current.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@gemini-code-assist Done in 48313e0 — removed the guard. --refresh is now valid for single-target removes too and forwards through to load_pr_cache_for_repo.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The changes look great and correctly address the suggestion to allow --refresh for single-target removals. Thank you for the update.

Comment thread src/main.rs Outdated
// Best-effort PR lookup: even when `[ui] show_pr = false` we still try
// the cache so a user who has list-with-PRs configured elsewhere gets
// the same signal here. No `gh` / no cache → silently no PR info.
let prs = load_pr_cache_for_repo(&opened, &loaded.config, &vcs_ctx, false);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Pass the refresh flag to load_pr_cache_for_repo so that single-target removals can benefit from up-to-date PR information in the details panel, assuming the bail at line 1148 is removed.

Suggested change
let prs = load_pr_cache_for_repo(&opened, &loaded.config, &vcs_ctx, false);
let prs = load_pr_cache_for_repo(&opened, &loaded.config, &vcs_ctx, refresh);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@gemini-code-assist Done in 48313e0 — refresh is now passed through. Paired with removing the guard above so the suggested call site behaves as you described.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The changes look great, and passing the refresh flag correctly ensures consistency across both single-target and batch operations. Thank you for implementing this.

yukimemi added 2 commits May 23, 2026 14:30
Two related improvements to make `renri remove` safer to call:

- Print a detailed pre-remove panel — name, branch, path, vcs, head id +
  description, status flags (clean / main / stale / dirty / conflict /
  locked / bare), and PR info (number, state, URL) when the PR cache
  has a hit. Prompts for confirmation by default; -y/--yes skips it.
  --non-interactive without --yes is now an error so the guard cannot
  be silently bypassed.

- Add --merged to sweep every worktree whose GitHub PR is MERGED or
  CLOSED in one go. dirty / conflict / locked / main rows are skipped
  with a warning (unless --force is also set). Each removal is per-row
  best-effort; failures are tallied and reported at the end. --refresh
  forces a re-fetch of the PR cache before deciding.

- Add -f/--force which propagates to the backend's remove(force=true),
  so single-target removes can now drop uncommitted worktrees too
  (previously always force=false).
- Drop `if prs.is_empty()` bail in cmd_remove_merged. Empty cache is
  ambiguous (no `gh` / network failure / genuinely zero PRs); the
  downstream "nothing to remove" message is accurate either way and
  conflating tool-missing with no-PRs produces misleading errors in
  a fresh repo.
- Remove the `--refresh only makes sense with --merged` guard in
  cmd_remove. Single-target removes now forward refresh through to
  the PR cache so the pre-remove details panel reflects current
  state, matching `renri list --refresh` semantics.
- Update the `--refresh` help text to document both modes.
@yukimemi yukimemi force-pushed the feat/remove-details-and-merged branch from a4ceaa1 to 48313e0 Compare May 23, 2026 05:32
@yukimemi yukimemi merged commit cde7bc6 into main May 23, 2026
13 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