From 4a4797e95959e89839cae1eaf1402eac77d5a031 Mon Sep 17 00:00:00 2001 From: Maksim Shulga Date: Thu, 6 Aug 2026 17:20:47 +0000 Subject: [PATCH] Document Python/external-rsync backend differences (#20) * Document Python/external-rsync backend differences - Note that mixing --external-rsync (which can recreate device/socket entries via --archive's -D) with the default Python hardlink-forward step is unsafe: _recursive_hardlink() only understands files, symlinks, and directories and errors on anything else. - Note that --dry-run is I/O-free with --external-rsync but still fully copies into a throwaway staging directory with the Python backend, though neither leaves a lasting change. Co-Authored-By: Claude Sonnet 5 * Clarify external-rsync dry-run description Copilot review feedback on PR #20: rsync's --dry-run still reads and stats source files to compute what would change, it just avoids writing to the destination - "performs no I/O" overstated that. Co-Authored-By: Claude Sonnet 5 --------- Co-authored-by: Maxim Shulga Co-authored-by: Claude Sonnet 5 --- IMPROVEMENTS.md | 2 +- README.md | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/IMPROVEMENTS.md b/IMPROVEMENTS.md index a30e651..e58ffb6 100644 --- a/IMPROVEMENTS.md +++ b/IMPROVEMENTS.md @@ -141,7 +141,7 @@ Acceptance criteria: - [x] Document behavior for ACLs, xattrs, flags, sparse files, source hardlinks, ownership, permissions, and timestamps. - [x] Add an explicit one-filesystem policy. -- [ ] Make Python and external implementations provide equivalent guarantees or +- [x] Make Python and external implementations provide equivalent guarantees or document every difference. - [ ] Test supported behavior on Linux and macOS. diff --git a/README.md b/README.md index 29f6f1a..e1d18af 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,22 @@ backups/ root.** In the Python backend a failed `chown` is caught and logged, not fatal; with `--external-rsync`, rsync itself just skips ownership it isn't permitted to set. +- **Do not mix backends across runs of the same backup.** The default + Python backend excludes special entries (see above) and its default + hardlink-forward step only understands files, symlinks, and + directories - it raises an error on anything else. `--external-rsync` + can recreate device nodes and sockets (as `root`, via `--archive`'s + `-D`), so a snapshot it created can contain entries the Python + backend's hardlinker cannot carry forward into the next backup. Pick + one of `--external-rsync`/`--external-hardlink` or the Python + defaults and use it consistently for a given backup destination. +- **`--dry-run` behaves differently per backend, though neither leaves + a lasting change.** With `--external-rsync`, rsync itself avoids + writing to the destination (it still reads and stats files to + compute what would change). With the Python backend, files are still + fully copied into a throwaway staging directory, which is then + deleted - correct, but slower and more I/O-heavy than the external + backend's dry run. ## Development