Skip to content

feat(clean): add --to filter for git gtr clean --merged#167

Open
scarf005 wants to merge 6 commits intocoderabbitai:mainfrom
scarf005:feat/clean-merged-target
Open

feat(clean): add --to filter for git gtr clean --merged#167
scarf005 wants to merge 6 commits intocoderabbitai:mainfrom
scarf005:feat/clean-merged-target

Conversation

@scarf005
Copy link
Copy Markdown
Contributor

@scarf005 scarf005 commented Apr 10, 2026

Pull Request

Description

Adds --to <Ref> option to git gtr clean --merged and tightens merged cleanup so only worktrees whose current branch tip matches a merged PR/MR for the requested target ref are considered.

Motivation

For teams that merge PRs into staging or release branches before main, git gtr clean --merged --to main must not treat staging-only merges as eligible for deletion. This also needs to avoid false positives when branch names are reused.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring (no functional changes)
  • Other (please describe):

Testing

  • bats tests/cmd_clean.bats tests/provider.bats tests/cmd_help.bats tests/completion.bats
  • shellcheck lib/commands/clean.sh lib/provider.sh lib/commands/help.sh
  • ./bin/gtr clean
  • git gtr clean
  • ./scripts/generate-completions.sh --check

Manual Testing Checklist

Tested on:

  • macOS
  • Linux (specify distro: __)
  • Windows (Git Bash)

Core functionality tested:

  • git gtr new <branch> - Create worktree
  • git gtr go <branch> - Navigate to worktree
  • git gtr editor <branch> - Open in editor (if applicable)
  • git gtr ai <branch> - Start AI tool (if applicable)
  • git gtr rm <branch> - Remove worktree
  • git gtr list - List worktrees
  • git gtr config - Configuration commands (if applicable)
  • Other commands affected by this change: git gtr clean --merged --to <ref>

Test Steps

  1. Create a worktree and commit on the feature branch.
  2. Open a PR targeting a non-main branch such as dummy.
  3. Run git gtr clean --merged --to main.
  4. Verify the worktree is not removed because the PR was not merged into main.
  5. Reuse a previously merged branch name and verify cleanup only matches the current branch tip.

Expected behavior:
Only worktrees whose current branch tip belongs to a merged PR/MR into the specified target ref are removed.

Actual behavior:
git gtr clean --merged --to main ignores non-merged worktrees, rejects --to without --merged, and does not emit dirty-skip warnings for branches that were never eligible for deletion.

Breaking Changes

  • This PR introduces breaking changes
  • I have discussed this in an issue first
  • Migration guide is included in documentation

Checklist

Before submitting this PR, please check:

  • I have read CONTRIBUTING.md
  • My code follows the project's style guidelines
  • I have performed manual testing on at least one platform
  • I have updated documentation (README.md, CLAUDE.md, etc.) if needed
  • My changes work on multiple platforms (or I've noted platform-specific behavior)
  • I have added/updated shell completions (if adding new commands or flags)
  • I have tested with both git gtr (production) and ./bin/gtr (development)
  • No new external dependencies are introduced (Bash + git only)
  • All existing functionality still works

Additional Context

This also fixes the completion generator so regenerated bash/zsh/fish completions keep the --to option.


License Acknowledgment

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache License 2.0.

Summary by CodeRabbit

  • New Features

    • Added --to <ref> for git gtr clean --merged to restrict cleanup to worktrees whose PRs/MRs were merged into the specified base ref; --to is only valid with --merged.
  • Documentation

    • Updated command help, examples, and docs to document --to <ref> and its usage.
  • Shell Completions

    • Added completion support for --to across Bash, Zsh, and Fish.
  • Tests

    • Added tests covering --to <ref> behavior, validation, and merged-check scenarios.

Assisted-by: gpt-5.4-high on opencode
Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
@scarf005 scarf005 requested a review from NatoBoram as a code owner April 10, 2026 04:52
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 10, 2026

Walkthrough

Adds a new --to option that limits git gtr clean --merged to worktrees whose PRs/MRs were merged into the specified base ref; wires the option through completions, CLI parsing, merged-worktree logic, provider queries (GitHub/GitLab), tests, and help/docs.

Changes

Cohort / File(s) Summary
Documentation & Help
README.md, lib/commands/help.sh
Document new --to <ref> for git gtr clean --merged, add example git gtr clean --merged --to main, and update help text and SETUP & MAINTENANCE sections.
Shell Completions & Generation
completions/_git-gtr, completions/git-gtr.fish, completions/gtr.bash, scripts/generate-completions.sh
Add --to completion entry (accepts a ref) to Zsh, Fish, and Bash completions and update the completion generation script.
Clean command logic
lib/commands/clean.sh
Parse new --to: value option (validated only with --merged), introduce target_ref passed into _clean_merged, compute branch tip (rev-parse HEAD) and pass it to check_branch_merged.
Provider detection
lib/provider.sh
Extend check_branch_merged(provider, branch [, target_ref] [, branch_tip]); add normalize_target_ref; GitHub listing now filters by --base and optionally matches headRefOid to branch_tip; GitLab listing adds --target-branch and optionally checks MR SHA fields.
Tests
tests/cmd_clean.bats, tests/cmd_help.bats, tests/provider.bats
Add/update Bats tests for --to validation, merged-filtering by target ref, provider invocations including normalized base/target ref and head SHA matching, and help output.

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI as cmd_clean
    participant Cleaner as _clean_merged
    participant ProviderCheck as check_branch_merged
    participant Provider as GitHub/GitLab

    User->>CLI: git gtr clean --merged --to main
    CLI->>CLI: parse flags -> merged=true, target_ref="main"
    CLI->>Cleaner: _clean_merged(target_ref="main")
    Cleaner->>Cleaner: for each worktree: determine branch, branch_tip
    Cleaner->>ProviderCheck: check_branch_merged(provider, branch, "main", branch_tip)
    ProviderCheck->>Provider: query PRs/MRs filtered by base/target="main" and head==branch_tip
    Provider-->>ProviderCheck: return matches (count/list)
    ProviderCheck-->>Cleaner: merged? yes/no
    Cleaner->>Cleaner: remove only worktrees merged into "main"
    Cleaner-->>User: report cleanup results
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I nudge the branches, one by one,
"--to main" says which twigs are done,
I sniff the tip and check the base,
Only proper leaves leave my place,
Hop — tidy burrow, job well spun.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.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 clearly and concisely summarizes the main change: adding a --to filter option to the git gtr clean --merged command.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Copy link
Copy Markdown

@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: 2

🧹 Nitpick comments (1)
tests/cmd_clean.bats (1)

147-159: Strengthen this test with a non-matching target branch assertion.

Current test validates the positive path. Add a second worktree that should not be removed to lock in filter behavior and prevent false positives.

Suggested test hardening
 `@test` "cmd_clean --merged --to filters by target ref" {
   create_test_worktree "merged-to-main"
+  create_test_worktree "merged-to-develop"

   _clean_detect_provider() { printf "github"; }
   ensure_provider_cli() { return 0; }
-  check_branch_merged() { [ "$2" = "merged-to-main" ] && [ "$3" = "main" ]; }
+  check_branch_merged() {
+    [ "$3" = "main" ] && [ "$2" = "merged-to-main" ]
+  }
   run_hooks_in() { return 0; }
   run_hooks() { return 0; }

   run cmd_clean --merged --to main --yes
   [ "$status" -eq 0 ]
   [ ! -d "$TEST_WORKTREES_DIR/merged-to-main" ]
+  [ -d "$TEST_WORKTREES_DIR/merged-to-develop" ]
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cmd_clean.bats` around lines 147 - 159, The test "cmd_clean --merged
--to filters by target ref" currently only verifies removal of the matching
worktree; add a second non-matching worktree (e.g., create_test_worktree
"merged-to-feature") and stub the same helpers (_clean_detect_provider,
ensure_provider_cli, check_branch_merged, run_hooks_in, run_hooks) so that
check_branch_merged returns true only for branch "merged-to-main" -> "main" and
false for the non-matching pair, then after running run cmd_clean --merged --to
main --yes assert status is 0, assert the "merged-to-main" directory was removed
and assert the "merged-to-feature" directory still exists to lock in the
negative case.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@completions/_git-gtr`:
- Around line 82-84: The completions for the new flags (--merged, --to, --yes)
are stale; re-run the project's completion-generation script/tool to regenerate
shell completions for the gtr command, ensure the generated artifacts for all
three completion targets (gtr.bash, _git-gtr, git-gtr.fish) include the new
flags/options, and commit those regenerated files together in the same change so
CI no longer reports stale completions.

In `@lib/commands/clean.sh`:
- Around line 149-157: After parsing args in lib/commands/clean.sh, validate
that --to (_arg_to -> target_ref) is only used when --merged (_arg_merged ->
merged_mode) is enabled: if target_ref is non-empty and merged_mode is false/0,
print a clear error message and exit non-zero; implement this check near where
merged_mode and target_ref are set (after parse_args) so the script rejects the
unsupported combination early rather than silently ignoring --to.

---

Nitpick comments:
In `@tests/cmd_clean.bats`:
- Around line 147-159: The test "cmd_clean --merged --to filters by target ref"
currently only verifies removal of the matching worktree; add a second
non-matching worktree (e.g., create_test_worktree "merged-to-feature") and stub
the same helpers (_clean_detect_provider, ensure_provider_cli,
check_branch_merged, run_hooks_in, run_hooks) so that check_branch_merged
returns true only for branch "merged-to-main" -> "main" and false for the
non-matching pair, then after running run cmd_clean --merged --to main --yes
assert status is 0, assert the "merged-to-main" directory was removed and assert
the "merged-to-feature" directory still exists to lock in the negative case.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4910c592-4cf8-4508-9cbc-4c8b29a846d6

📥 Commits

Reviewing files that changed from the base of the PR and between 025de7f and 6f39450.

📒 Files selected for processing (10)
  • README.md
  • completions/_git-gtr
  • completions/git-gtr.fish
  • completions/gtr.bash
  • lib/commands/clean.sh
  • lib/commands/help.sh
  • lib/provider.sh
  • tests/cmd_clean.bats
  • tests/cmd_help.bats
  • tests/provider.bats

scarf005 and others added 2 commits April 10, 2026 14:16
Avoid dirty skip noise for non-merged worktrees and reject --to without --merged.

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
Copy link
Copy Markdown

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/provider.sh`:
- Around line 116-124: The pr_matches logic in lib/provider.sh uses gh pr list
and glab mr list but only inspects the first page, causing false negatives for
reused branch names; update each gh pr list call that assigns pr_matches (the
blocks referencing pr_matches and branch_tip) to include a sufficiently large
--limit (or use --all) and implement pagination (or loop) so all pages are
examined, and likewise change glab mr list invocations to use --all or a paging
loop; ensure the selection/filtering (the jq/map/select parts relying on
headRefOid and state) remains the same so the code still counts merged PRs/MRs
but across the complete result set.
- Around line 114-118: Normalize the target_ref variable to a plain branch name
before passing it to provider filters: detect and strip refs/heads/ and remote
prefixes (e.g., origin/) or resolve via git rev-parse --abbrev-ref to obtain the
simple branch name, then use that normalized variable in the gh pr list calls
(the target_ref used with --base and the branch_tip comparison) and in the glab
--target-branch usage mentioned around the later block (lines near the other
gh/glab calls). Ensure normalization happens once right after parsing --to and
before any use in the pr/MR queries so callers can still pass refs like
refs/heads/main or origin/main safely.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0dd3a9e3-9de4-4594-925f-765a49904572

📥 Commits

Reviewing files that changed from the base of the PR and between c06bdd8 and 0d7f148.

📒 Files selected for processing (2)
  • lib/provider.sh
  • tests/provider.bats
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/provider.bats

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
lib/provider.sh (1)

151-163: Remove redundant conditional—both branches execute identical logic.

The outer if [ -n "$normalized_target_ref" ] check is unnecessary here because the --base flag was already conditionally added in lines 148-150. Both the then (lines 152-156) and else (lines 158-162) branches contain identical code.

♻️ Proposed simplification
       if [ -n "$normalized_target_ref" ]; then
         gh_args+=(--base "$normalized_target_ref")
       fi
-      if [ -n "$normalized_target_ref" ]; then
-        if [ -n "$branch_tip" ]; then
-          pr_matches=$(gh "${gh_args[@]}" --json state,headRefOid --jq "map(select(.state == \"MERGED\" and .headRefOid == \"$branch_tip\")) | length" 2>/dev/null || true)
-        else
-          pr_matches=$(gh "${gh_args[@]}" --json state --jq 'map(select(.state == "MERGED")) | length' 2>/dev/null || true)
-        fi
+      if [ -n "$branch_tip" ]; then
+        pr_matches=$(gh "${gh_args[@]}" --json state,headRefOid --jq "map(select(.state == \"MERGED\" and .headRefOid == \"$branch_tip\")) | length" 2>/dev/null || true)
       else
-        if [ -n "$branch_tip" ]; then
-          pr_matches=$(gh "${gh_args[@]}" --json state,headRefOid --jq "map(select(.state == \"MERGED\" and .headRefOid == \"$branch_tip\")) | length" 2>/dev/null || true)
-        else
-          pr_matches=$(gh "${gh_args[@]}" --json state --jq 'map(select(.state == "MERGED")) | length' 2>/dev/null || true)
-        fi
+        pr_matches=$(gh "${gh_args[@]}" --json state --jq 'map(select(.state == "MERGED")) | length' 2>/dev/null || true)
       fi
       [ "${pr_matches:-0}" -gt 0 ]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/provider.sh` around lines 151 - 163, The outer conditional checking
normalized_target_ref is redundant because the --base flag is already handled
earlier; simplify by removing that outer if/else and keep a single branch that
sets pr_matches based on whether branch_tip is set: use gh with --json
state,headRefOid and the jq filter for MERGED+headRefOid when branch_tip is
non-empty, otherwise use gh with --json state and the jq filter for MERGED;
update references to pr_matches, gh_args, branch_tip and remove the duplicate
blocks around normalized_target_ref.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lib/provider.sh`:
- Around line 151-163: The outer conditional checking normalized_target_ref is
redundant because the --base flag is already handled earlier; simplify by
removing that outer if/else and keep a single branch that sets pr_matches based
on whether branch_tip is set: use gh with --json state,headRefOid and the jq
filter for MERGED+headRefOid when branch_tip is non-empty, otherwise use gh with
--json state and the jq filter for MERGED; update references to pr_matches,
gh_args, branch_tip and remove the duplicate blocks around
normalized_target_ref.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 201cdbb7-1db7-49ed-a7d1-9bc84a0062cf

📥 Commits

Reviewing files that changed from the base of the PR and between 0d7f148 and a26a3ae.

📒 Files selected for processing (2)
  • lib/provider.sh
  • tests/provider.bats
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/provider.bats

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.

2 participants