Skip to content

feat: keep a directory given with its subtree when archiving - #605

Open
AzmainMahatab wants to merge 1 commit into
rustic-rs:mainfrom
AzmainMahatab:keep-given-subtrees
Open

AzmainMahatab wants to merge 1 commit into
rustic-rs:mainfrom
AzmainMahatab:keep-given-subtrees

Conversation

@AzmainMahatab

Copy link
Copy Markdown

What

A ReadSource may now give a directory node whose subtree is already set. The
archiver keeps that tree as it is - the node is added to its parent like a file -
instead of opening the directory and building its tree again from the entries below it.
A kept directory counts as an unmodified directory in the summary.

The change is one condition in Archiver::archive (node.is_dir() && node.subtree.is_none())
and the summary branch in TreeArchiver::add_file.

Why

A tool that already knows which directories changed since the parent snapshot (for
example a sync engine with its own record of the last snapshot) can then list only the
changed directories and pass every unchanged one as its parent node, subtree and all.
Today the public API offers no way to do that: archive always rebuilds every directory
from its entries, and the tree rewriter (TreeModifier) is crate-private. So a backup of
a few changed files in a large tree still walks, matches and re-serializes every
directory.

Measured with a source of 200,000 files in 21,660 directories, 3 files changed:

Before After
directories rebuilt 21,660 9
entries handed to archive ~220,000 238
archive time 7.5 s 0.6 s

Compatibility

LocalSource, StdinSource and ChildStdoutSource never set a directory's subtree, so
backups through them behave exactly as before. Only a custom ReadSource that sets it
sees the new behaviour, and it is responsible for the subtree being in the repository's
index - as it is for a file node's content it passes with open: None.

Test

test_archive_keeps_a_directory_given_with_its_subtree backs up the tar test data, then
archives a source giving only one of its directories, with its subtree, under a new name.
The new snapshot holds that directory with the same subtree, and the summary counts one
unmodified directory. The test fails without the change (the directory comes out empty).

On Windows, five integration tests fail on main without this change as well
(test_backup_stdin_command, test_chunker_params, test_backup_with_tar_gz_passes,
test_backup_dry_run_with_tar_gz_passes, test_rewrite); they fail the same with it. One
prune case failed once in a full parallel run and passed in three reruns.

🤖 Generated with Claude Code

A `ReadSource` can now give a directory node that already carries a
subtree: the archiver stores that tree as it is, like a file, instead of
building the directory again from entries below it. A source that knows
which directories did not change since the parent snapshot can then list
only the changed ones and hand over the others whole, so a backup costs
what changed rather than the whole tree.

Directory nodes from `LocalSource` and the other built-in sources carry no
subtree, so their behaviour is unchanged. A kept directory counts as an
unmodified directory in the summary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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