Skip to content

Add --exclude-archives, the inverse of --match-archives - #10305

Open
m-altaifi wants to merge 3 commits into
borgbackup:masterfrom
m-altaifi:feat/exclude-archives
Open

Add --exclude-archives, the inverse of --match-archives#10305
m-altaifi wants to merge 3 commits into
borgbackup:masterfrom
m-altaifi:feat/exclude-archives

Conversation

@m-altaifi

Copy link
Copy Markdown

Implements #9936.

--match-archives can only narrow a selection — all given patterns must match, so there is no way to express "all archives except these" without a shell loop around borg repo-list.

--exclude-archives takes the same patterns and the same selector prefixes, and skips any archive matching one of them. An archive is considered if it matches all --match-archives patterns and none of the --exclude-archives patterns, i.e. the new option ORs where the existing one ANDs, exactly as --exclude does for paths:

borg repo-list \
    --match-archives 'sh:my-machine-name-*' \
    --exclude-archives 'sh:*-scratch' \
    --exclude-archives tags:@PROT

Implementation

The per-pattern matching was inline in _matching_info_tuples, so I pulled it out into _matching_by_pattern and now call it from both directions. That is what makes every prefix — aid:, tags:, user:, host:, date:, name: — work for exclusion as well, rather than exclusion supporting names only.

Exclusion patterns are matched against all archives rather than the already-narrowed list, so an exclusion pattern means the same thing regardless of which inclusion patterns were given. It also avoids aid: spuriously raising "needs to match precisely one archive ID" when the referenced archive had already been filtered out by an inclusion pattern.

The option is defined in define_archive_filters_group, so every command that already has -a gets it, and it is passed through the single list_considering funnel. prune reaches list() directly rather than through that funnel, so it is wired explicitly.

LegacyArchives gets the same treatment, otherwise borg transfer --exclude-archives … would silently ignore the option when the source is a borg 1.x repository.

borg help match-archives now documents both directions.

Tests

Four cases in repo_list_cmd_test.py: exclusion on its own, several exclusion patterns ORing together, exclusion combined with --match-archives, and exclusion via a non-name selector (tags:) so the shared matcher path is covered rather than just name globs.

What I could not run

Borg does not run on native Windows and I could not build the Cython extensions here, so I have not executed the test suite locally — I am relying on CI and will follow up on whatever it reports. What I did verify locally: ruff check passes on all six changed files, and black --check (24.8.0, as pinned in .pre-commit-config.yaml) reports them unchanged.

I left CHANGES.rst and the generated usage docs alone, following #10291 and the note in the contributor docs that usage docs are regenerated at release time. Happy to add a changelog entry if you would rather have it in the PR.

@ThomasWaldmann

Copy link
Copy Markdown
Member

Claude? :-)

@codecov

codecov Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.75000% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.69%. Comparing base (94bd5bd) to head (c0293ca).
⚠️ Report is 24 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/borg/legacy/archives.py 87.17% 4 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #10305      +/-   ##
==========================================
+ Coverage   87.65%   87.69%   +0.03%     
==========================================
  Files         103      103              
  Lines       18716    18735      +19     
  Branches     2881     2885       +4     
==========================================
+ Hits        16405    16429      +24     
+ Misses       1609     1604       -5     
  Partials      702      702              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@ThomasWaldmann

Copy link
Copy Markdown
Member

@m-altaifi The CI is quite red.

Maybe rebase your change onto current master branch and try to fix the failures.

m-altaifi and others added 2 commits September 3, 2026 19:49
--match-archives can only narrow a selection: every pattern given must
match, so there is no way to say "all archives except these". Doing that
needed a shell loop around borg repo-list.

Add --exclude-archives, taking the same patterns and selector prefixes.
An archive is considered if it matches all --match-archives patterns and
none of the --exclude-archives ones, so the new option ORs while the
existing one ANDs.

The per-pattern matching is factored out of _matching_info_tuples into
_matching_by_pattern so both directions share one matcher and all
prefixes (aid:, tags:, user:, host:, date:, name:) work for exclusion
too. Exclusion patterns are evaluated against all archives rather than
the already narrowed list, so an exclusion does not depend on what the
inclusion patterns happened to keep.

Wired at the shared archive filters group, so every command using
define_archive_filters_group gets it. LegacyArchives is updated the same
way so borg transfer does not silently ignore the option when reading a
borg 1.x repository.
@m-altaifi
m-altaifi force-pushed the feat/exclude-archives branch from ebdb1a6 to 6117a54 Compare September 3, 2026 20:43
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