Skip to content

analyze: replace --by-name by --group-by - #10301

Merged
ThomasWaldmann merged 1 commit into
borgbackup:masterfrom
ThomasWaldmann:analyze-group-by
Sep 2, 2026
Merged

analyze: replace --by-name by --group-by#10301
ThomasWaldmann merged 1 commit into
borgbackup:masterfrom
ThomasWaldmann:analyze-group-by

Conversation

@ThomasWaldmann

Copy link
Copy Markdown
Member

--by-name was already a grouping, with the keys hardwired to name:

archives_per_name[info.name] += 1
owner_of = {name: i + 1 for i, name in enumerate(names)}

Two problems with that:

It is wrong in a shared repository. Archive series names are not unique across hosts, so two
machines backing up a home series collapse into one row. That row's exclusive size is the combined
cost of two unrelated data sets, attributable to neither, and the report's own claim — "deleting all
archives of that name would free them"
— silently becomes a statement about both hosts' data.

It no longer matches prune. analyze's headline number is "what would I free by dropping this
row", which is only actionable if the row is the unit prune actually operates on. Since #10291 that
unit is (name, host), while analyze's was (name) — they stopped lining up in exactly the repositories
where the question is worth asking.

The change

--by-name is replaced by --group-by KEYS, taking the same comma-separated archive attributes as
prune (name, host, user, tags) and the same archive_group_key(). --group-by name is the
previous behaviour. As before, giving the option selects the decomposition mode and it cannot be
combined with archive filters, since the shared and unreferenced rows need the repository-wide view.
An empty --group-by "" is rejected: for prune "one group" is meaningful, here it would be a single
row covering the whole repository, which says nothing.

There is a second, less obvious payoff. Grouping by more attributes moves chunks from the group rows
into the shared row, where they belong: if two hosts back up identical content, --group-by name
reports it as exclusive to that name, while --group-by name,host correctly shows that deleting
either host's archives alone would not free it. The old report overstated per-name reclaimable space.

Text report row labels change from the bare name to the formatted group key, the same one prune and
repo-list print:

name                                  archives     source     stored
alpha                                        2    1.00 kB        ...
(shared by 2+ names)                              1.00 kB        ...

becomes

group                                 archives     source     stored
name='home', host='host1'                    1    1.00 kB        ...
(shared by 2+ groups)                             1.00 kB        ...

JSON

Changed without keeping compatibility, as requested:

  • by_nameby_group
  • its names list → groups
  • each entry's name string → a group object mapping each grouped-by attribute to this group's
    value, e.g. {"name": "home", "host": "host1"}
  • new group_by key listing the attributes that were grouped by

docs/internals/frontends.rst is updated to match. MAX_NAMES became MAX_GROUPS (same 2^18−2
limit, which now bounds distinct groups — more attributes mean more groups, so the guard earns its
keep).

Tests

  • test_analyze_group_by_name — the old --by-name test, now asserting the group-labelled rows
  • test_analyze_group_by_name_and_host — new: both hosts use a home series; --group-by name
    shows one group with everything exclusive to it, --group-by name,host shows two groups with the
    common file correctly moved into the shared row
  • test_analyze_group_by_rejects_filters, test_analyze_group_by_needs_a_key
  • test_analyze_json_group_by — the new JSON shape, including group_by and the group objects

Full test suite: 2935 passed, 986 skipped. ruff check clean.

Worth a CHANGES entry as a breaking change, since --by-name shipped in 2.0.0b23 and the JSON key
changes with it:

  • analyze: replaced --by-name by --group-by, which decomposes the repository by the same
    archive attributes borg prune --group-by groups by. --group-by name is the previous
    behaviour. The JSON output's by_name key became by_group.

--by-name decomposed the repository by archive name, which is a grouping with
the keys hardwired. Archive series names are not unique in a repository shared
by multiple machines or users, so different hosts backing up a "home" series
ended up in one row: its exclusive size was the combined cost of unrelated data
sets and could not be attributed to either host.

It also no longer matched what prune does. prune groups by name,host, and the
number analyze reports per row - "what would deleting this free" - is only
actionable if the row is the unit that prune applies its retention rules to.

--by-name is replaced by --group-by KEYS, taking the same comma-separated
archive attributes (name, host, user, tags) and using the same grouping as
prune, so "--group-by name" is the previous behaviour. As before, giving the
option selects the decomposition mode, and it can not be combined with archive
filters, because the shared and unreferenced rows need the repository-wide view.

Grouping by more attributes moves chunks from the group rows into the shared
row: if two hosts back up identical content, --group-by name reports it as
exclusive to that name, while --group-by name,host correctly shows that deleting
either host's archives alone would not free it.

The JSON output changes accordingly, without keeping compatibility: the by_name
key becomes by_group, its names list becomes groups, and each entry has a group
object (attribute -> value) instead of a name. The list of attributes grouped by
is in the new group_by key.
@codecov

codecov Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.28571% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.66%. Comparing base (0adbb7a) to head (c9493ed).
⚠️ Report is 3 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/borg/archiver/analyze_cmd.py 94.28% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master   #10301   +/-   ##
=======================================
  Coverage   87.65%   87.66%           
=======================================
  Files         103      103           
  Lines       18702    18717   +15     
  Branches     2877     2881    +4     
=======================================
+ Hits        16393    16408   +15     
+ Misses       1609     1607    -2     
- Partials      700      702    +2     

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

@ThomasWaldmann
ThomasWaldmann merged commit 3c6476c into borgbackup:master Sep 2, 2026
24 checks passed
@ThomasWaldmann
ThomasWaldmann deleted the analyze-group-by branch September 2, 2026 09:08
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