Skip to content

Skip excluded directory subtrees during copy#176

Merged
helizaga merged 1 commit intomainfrom
codex/skip-excluded-copy-subtrees
Apr 28, 2026
Merged

Skip excluded directory subtrees during copy#176
helizaga merged 1 commit intomainfrom
codex/skip-excluded-copy-subtrees

Conversation

@helizaga
Copy link
Copy Markdown
Collaborator

@helizaga helizaga commented Apr 28, 2026

Summary

  • skip excluded direct child subtrees while copying configured includeDirs
  • keep the existing fast directory copy path when there are no nested excludes
  • support excluded children under nested include paths such as vendor/bundle/cache
  • handle glob-prefix excludes by using the deepest matching copied-directory prefix
  • treat trailing-slash direct-child excludes as skippable before copy
  • still apply post-copy exclude cleanup for deeper nested exclude patterns
  • add copy safety coverage and an Unreleased changelog entry

Why

copy_directories previously cloned an included parent directory first and deleted excluded subdirectories afterward. For configs like:

[copy]
    includeDirs = .claude
    excludeDirs = .claude/worktrees

that meant copying large excluded trees before immediately removing them. The new selective path detects excludes beneath an included directory and copies only the direct children that are not excluded.

The exclude matching now also handles include paths with /, glob-prefix patterns like */bundle/cache/tmp, and trailing-slash excludes like .claude/worktrees/.

Fixes #175.

Validation

  • git diff --check
  • shellcheck bin/gtr bin/git-gtr lib/*.sh lib/commands/*.sh adapters/editor/*.sh adapters/ai/*.sh
  • ./scripts/generate-completions.sh --check
  • bats tests/copy_safety.bats
  • bats tests/cmd_copy.bats
  • bats tests/

Summary by CodeRabbit

  • Bug Fixes

    • Directory copy now omits excluded child subtrees during the copy operation instead of copying them and deleting afterward.
  • Tests

    • Added comprehensive tests verifying exclusion behavior, selective copying of directory children, glob-pattern handling, and integration scenarios to prevent excluded subtrees from appearing in destinations.
  • Documentation

    • Updated changelog to record the directory-copy exclusion behavior fix.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: af465db5-ade6-4a03-8e92-f2d34884a919

📥 Commits

Reviewing files that changed from the base of the PR and between f82ddc4 and 971269c.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • lib/copy.sh
  • tests/copy_safety.bats
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md

Walkthrough

copy_directories now detects exclude patterns that target direct children and, when present, copies a directory by enumerating and copying only non-excluded direct children. Post-copy exclusion still runs for deeper patterns; whole-directory fast copy is retained when no subdir-level excludes are detected.

Changes

Cohort / File(s) Summary
Changelog
CHANGELOG.md
Updated Unreleased section to note that excluded child subtrees are omitted during copy rather than copied and then removed.
Copy implementation
lib/copy.sh
Added _directory_exclude_suffix, _has_subdir_excludes, and _selective_copy_dir; refactored _apply_directory_excludes to compute deletable targets relative to a copied dir; copy_directories now chooses selective child-by-child copying when subdir excludes exist and fixes the dst_root argument to _apply_directory_excludes.
Tests
tests/copy_safety.bats
Added unit tests for _has_subdir_excludes and _apply_directory_excludes, tests for _selective_copy_dir, and integration tests ensuring excluded subtrees are not created and that the fast-copy helper is not invoked for excluded children.

Sequence Diagram(s)

sequenceDiagram
    participant Caller as Client
    participant CD as copy_directories
    participant SEL as _selective_copy_dir
    participant FAST as _fast_copy_dir
    participant APPLY as _apply_directory_excludes
    participant FS as Filesystem

    Caller->>CD: request copy of includeDirs (with excludeDirs)
    CD->>CD: evaluate excludes for dir_path
    alt subdir-level excludes detected
        CD->>SEL: perform selective copy for dir_path
        SEL->>SEL: enumerate direct children
        SEL->>FAST: for each child not excluded -> copy child
        FAST->>FS: copy child subtree
        SEL->>APPLY: apply deeper excludes under copied children
        APPLY->>FS: remove deeper excluded targets if present
    else no subdir excludes
        CD->>FAST: fast whole-directory copy of dir_path
        FAST->>FS: copy entire dir_path
        CD->>APPLY: apply excludes post-copy
        APPLY->>FS: remove excluded subtrees
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hop through trees and skip the trails,
Leaving out the branches with heavy tails,
I copy leaves that matter most,
Nibble junk and bounce, not coast —
Light paws, tidy burrow, joyful trails. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Skip excluded directory subtrees during copy' clearly and concisely summarizes the main change: implementing selective copying to skip excluded subtrees during the directory copy operation.
Linked Issues check ✅ Passed The PR successfully addresses issue #175 by implementing selective copying that detects and skips excluded direct-child subtrees during copy operations, reducing wasteful clone-then-delete cycles.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the linked issue: CHANGELOG.md documentation, copy.sh implementation of selective copying logic, and safety tests for the new functionality.

✏️ 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 codex/skip-excluded-copy-subtrees

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

@helizaga helizaga marked this pull request as ready for review April 28, 2026 20:32
@helizaga helizaga requested a review from NatoBoram as a code owner April 28, 2026 20:32
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: 1

🤖 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/copy.sh`:
- Around line 356-371: The code in _has_subdir_excludes currently strips
exclude_pattern to its first segment (pattern_prefix) which misses excludes like
exclude_pattern="vendor/bundle/cache" when dir_path="vendor/bundle"; change the
logic so when exclude_pattern contains a slash you directly test whether the
exclude_pattern is under dir_path (e.g. use a case test like case
"$exclude_pattern" in "$dir_path"/*) return 0 ;; esac) instead of comparing only
the first segment; keep the existing .git exclusions and other checks but
replace the pattern_prefix handling with this prefix-with-slash match using the
existing exclude_pattern and dir_path variables.
🪄 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: Enterprise

Run ID: 636ff3ee-691f-435e-9b8b-aaf4ae64b451

📥 Commits

Reviewing files that changed from the base of the PR and between 86563fb and 16abe9e.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • lib/copy.sh
  • tests/copy_safety.bats

Comment thread lib/copy.sh Outdated
@helizaga helizaga force-pushed the codex/skip-excluded-copy-subtrees branch from 16abe9e to f82ddc4 Compare April 28, 2026 20:46
Copy link
Copy Markdown
Contributor

@averyjennings averyjennings left a comment

Choose a reason for hiding this comment

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

Looks good — no blockers found.

The selective copy path is a clean optimization for the .claude + .claude/worktrees style configs. The follow-up commit f82ddc4 neatly resolved the multi-segment include-path gap by extracting _directory_exclude_suffix and using it for both the new predicate and the pre-existing cleanup, and also fixed a latent dest_parent -> dst_root path-doubling bug for multi-segment includes. New bats coverage is appropriate.

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/copy.sh`:
- Around line 413-415: The check uses child_rel literally so excludes with or
without a trailing slash don't match; update the exclusion logic to normalize
trailing slashes before comparison by passing a slash-stripped child path (or by
normalizing entries inside is_excluded). Specifically, change the call site that
invokes is_excluded (the if is_excluded "$child_rel" "$excludes" check) to use a
normalized child identifier (e.g., strip any trailing slash from child_rel) or
modify is_excluded to treat patterns as equivalent whether they end with a
slash, ensuring ".claude/worktrees" and ".claude/worktrees/" are treated the
same.
- Around line 281-300: The prefix-match branch currently returns on the first
match causing early termination and incorrect suffixes; instead, when the case
"$dir_path" in $prefix) branch matches, capture the current suffix into a
variable (e.g., matched_suffix="$suffix") and continue the loop so the prefix is
refined further; after the loop ends, print the deepest matched_suffix (if set)
and return 0 so _apply_directory_excludes sees the correctly refined suffix (use
variables dir_path, prefix, suffix and the matched_suffix accumulator).
🪄 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: Enterprise

Run ID: e7fbbb04-504e-4d9e-97a3-7460798fb326

📥 Commits

Reviewing files that changed from the base of the PR and between 16abe9e and f82ddc4.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • lib/copy.sh
  • tests/copy_safety.bats
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/copy_safety.bats

Comment thread lib/copy.sh
Comment thread lib/copy.sh Outdated
@helizaga helizaga force-pushed the codex/skip-excluded-copy-subtrees branch from f82ddc4 to 971269c Compare April 28, 2026 21:53
@helizaga helizaga merged commit d14582f into main Apr 28, 2026
4 checks passed
@helizaga helizaga deleted the codex/skip-excluded-copy-subtrees branch April 28, 2026 22:29
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.

copy_directories: excludeDirs post-hoc delete wastes time cloning large excluded subtrees

2 participants