Vcopy (versioning copy) - Paljon on ollut keskustelua ei-teknisten käyttäjien kesken siitä, miten hankalaksi versioitu kopiointi esimerkiksi ulkoiselle medialle tai verkkojaolle on tehty, tai että palautus vaatii jonkinlaista kikkailua. Tässä Vcopyn viimeisin dokumentaatio. Ohjelma on vielä alfa-vaiheessa, mutta siksi otankin palautetta dokumentaation perusteella. Ohjelma toimii tällä hetkellä dokumentaation mukaisesti, mutta en ole julkaissut sitä, vaan olen jakanut sen vain muutamalle (hikiselle) nörtille.
Jos joku jaksaa lukea ja tulee palautetta mieleen, saa laittaa tulemaan. Laitan jakoon, kun tuntuu, että suunnitelma on oikea ja ohjelma on testattu kattavasti erilaisilla testitapauksilla virtuaalikoneissa.
Toki ohjelmat kuten Borg, Restic, Duplicati ym. toimivat. Mutta palautus niiden avulla ei aina ole niin selkeää tai suoraviivaista.
VCOPY(1) User Commands VCOPY(1)
NAME
vcopy - directly browseable, zero-link, multi-platform backup and sync
tool with historical versioning
SYNOPSIS
vcopy [OPTIONS] [SOURCES... DEST] [COMMAND]
vcopy manual [--markdown]
vcopy --manual | --manual-markdown
DESCRIPTION
vcopy is a zero-dependency, static CLI backup and filesystem
synchronization tool written in pure Rust. It creates directly
browseable, uncompressed filesystem mirrors on external drives and
non-snapshotting filesystems (exFAT, FAT32, NTFS, ext4, APFS) while
preserving a full point-in-time historical change record.
The binary embeds its complete documentation in both plain text and
Markdown formats, allowing complete standalone operation without
external documentation files or packages.
QUICK START & SIMPLE EXAMPLES
Usage first! The following examples demonstrate common backup tasks:
1. Basic Single Directory Backup
Syncs /home/user/documents into /mnt/usb/backup:
$ vcopy /home/user/documents /mnt/usb/backup
Live files are directly browseable at /mnt/usb/backup/documents/ as
regular, uncompressed files. Historical snapshots are archived under
.version-history/.
2. Multiple Sources
Pass multiple source directories or files positionally or with the
-s / --sources flag:
Positional syntax (destination is always the final argument):
$ vcopy /home/user/docs /home/user/photos /home/user/music /mnt/usb/backup
In the destination mirror, distinct top-level source folders are kept:
/mnt/usb/backup/
|-- docs/
|-- photos/
`-- music/
Using explicit -s / --sources and -d / --dest flags:
$ vcopy -s /home/user/docs -s /home/user/photos -d /mnt/usb/backup
Reading source paths from files, wildcards, or stdin (-f / --paths-from):
# Read paths from a list file (one per line, # for comments)
$ vcopy -f project_paths.txt -d /mnt/usb/backup
# Read paths matching file glob patterns
$ vcopy -f "backups_*.txt" -d /mnt/usb/backup
# Pipe paths directly from find via standard input ('-')
$ find /home/user/code -name "*.rs" | vcopy -f - -d /mnt/usb/backup
3. Numbered Versions in a Flat Directory (--flatten)
When you want all files in a single flat directory without nested
subfolders (ideal for flat USB drives, camera card ingests, or single-
folder cloud buckets), use --flatten.
Every file is placed directly in the destination root. Historical
revisions receive monotonic numbered version suffixes (.v0, .v1, ...):
$ vcopy --flatten /home/user/docs /home/user/photos /mnt/usb/backup
Destination layout with numbered versions:
/mnt/usb/backup/
|-- report.pdf # Current live version (latest)
|-- report.v0.pdf # Oldest historical version (v0)
|-- report.v1.pdf # Second historical version (v1)
|-- photo.jpg # Current live photo
|-- photo.v0.jpg # Prior version of photo
|-- notes_from_docs.txt # Stem collision disambiguated
|-- notes_from_photos.txt # Stem collision disambiguated
|-- notes_from_photos.v0.txt # Historical version of notes
|-- .version-history.lock # Advisory concurrency lock
|-- .version-history.baseline.json # Live state manifest & checksums
`-- .version-history.manifest.jsonl# Append-only snapshot ledger
4. Zero-Obfuscation Configuration Files (-c / --config)
No separate manual or syntax to learn! A configuration file uses the
exact same command-line flags and parameters you type in the terminal:
$ vcopy -c backup.conf
Direct CLI arguments format (backup.conf):
# backup.conf - exact same flags as the command line!
--sources /home/user/documents
--sources /home/user/photos
--dest /mnt/usb/backup
--flatten
--preserve-permissions
--keep-daily 7
--keep-weekly 4
Simple key-value format (backup.conf):
dest = /mnt/usb/backup
sources = /home/user/documents, /home/user/photos
flatten = true
preserve-permissions = true
keep-daily = 7
keep-weekly = 4
TOML format (backup.toml):
dest = "/mnt/usb/backup"
sources = ["/home/user/documents", "/home/user/photos"]
flatten = true
preserve-permissions = true
keep-daily = 7
keep-weekly = 4
5. Listing Snapshots
View historical snapshots, transfer metrics, and file change counts:
$ vcopy list /mnt/usb/backup
Output:
Snapshot Copied Modified Deleted Transferred
---------------------------------------------------------------------------
2026-11-09_083000 142 0 0 12491024 bytes
2026-11-09_091500 3 12 1 452010 bytes
2026-11-09_100000 (live) 0 0 0 0 bytes
6. Point-in-Time Restore
Restore files to an exact prior snapshot or the latest mirror:
# Restore latest live mirror
$ vcopy restore /mnt/usb/backup /tmp/restored
# Restore files as they existed at a specific snapshot timestamp
$ vcopy restore --target-time 2026-11-09_083000 /mnt/usb/backup /tmp/restored
# Restore with POSIX permissions and Windows readonly attributes
$ vcopy restore -p --target-time 2026-11-09_083000 /mnt/usb/backup /tmp/restored
7. Retention & Snapshot Pruning
Prune old snapshots using count limits or GFS schedules:
# Retain only the newest 5 snapshots
$ vcopy prune -n 5 /mnt/usb/backup
# Retain 24 hourly, 7 daily, 4 weekly, 12 monthly, and 1 yearly snapshots
$ vcopy prune --keep-hourly 24 --keep-daily 7 --keep-weekly 4 /mnt/usb/backup
# Dry run: preview which snapshots would be deleted without writing
$ vcopy prune -n 5 --dry-run -v /mnt/usb/backup
OPTIONS
Core Options:
-s, --sources <PATH>
Explicit source directory or file. Can be specified multiple
times or comma-separated. Alias: --source.
-d, --dest <PATH>
Destination backup directory. Overrides positional destination.
Alias: --destination.
-f, --paths-from <PATTERN_OR_STDIN>
Read source paths from a text file, glob pattern (e.g. backups*),
or stdin ('-'). Can be specified multiple times.
Aliases: --files-from, --sources-from.
-c, --config <FILE>
Path to configuration file. Accepts direct CLI arguments format,
plain key-value, TOML, or JSON.
--flatten
Single-directory flat mode: stores all live and historical files
directly in destination root using .v0, .v1 version numbers.
-p, --preserve-permissions
Record POSIX mode bits (Unix) and readonly attributes (Windows)
in manifests and restore them on compatible filesystems.
--dry-run
Simulate execution and display planned operations without
modifying the filesystem.
-v, --verbose
Enable detailed output listing every copied, modified, and
deleted file.
--target-time <TIMESTAMP>
Target snapshot timestamp (YYYY-MM-DD_HHMMSS or prefix) or
'latest' for restore.
--manual, --man
Print this complete user manual in ASCII / man-page format.
--manual-markdown, --manual-md, --markdown
Print this complete user manual in Markdown format.
Retention Policy Options (GFS & Count):
-n, --max-versions <N>
Retain only the newest N snapshots (overrides GFS).
--keep-hourly <N>
Number of hourly snapshot buckets to retain (default: 24).
--keep-daily <N>
Number of daily snapshot buckets to retain (default: 7).
--keep-weekly <N>
Number of weekly snapshot buckets to retain (default: 4).
--keep-monthly <N>
Number of monthly snapshot buckets to retain (default: 12).
--keep-yearly <N>
Number of yearly snapshot buckets to retain (default: 1).
SUBCOMMANDS
auto (Default when no subcommand is given)
Create backup snapshot and automatically prune old versions.
create
Create a new backup snapshot without running retention pruning.
restore
Restore files from destination to a target directory.
list List all snapshots in destination with summary statistics.
prune Manually prune snapshots according to retention rules.
manual
Display the user manual. Add --markdown for Markdown output.
STORAGE MODES
1. Hierarchical Snapshot Mode (Default)
The live mirror is stored in <dest>/ exactly matching the source.
When a file changes, the older version is displaced into a timestamped
directory under .version-history/<YYYY-MM-DD_HHMMSS>/.
2. Single-Directory Flat Mode (--flatten)
All files are kept in the destination root folder:
- Numbered versions: <stem>.v0.<ext>, <stem>.v1.<ext>, ...
- Collision disambiguation: <stem>_from_<source>.<ext>
- Metadata: .version-history.baseline.json,
.version-history.manifest.jsonl,
.version-history.lock
- Automatic orphan scan recovery if metadata files are missing.
SYSTEM INVARIANTS & SAFETY GUARANTEES
1. Zero Links: Never creates or follows hardlinks, symlinks, or block
reflinks. Destination mirrors are 100% portable across FAT32, exFAT,
NTFS, ext4, and cloud drives.
2. Directly Browseable: All live files and historical versions are raw,
uncompressed files readable directly in standard OS file managers.
3. Atomic Staging Writes: Writes to .<file>.tmp.<pid>.<id> and atomically
renames into place to avoid partial file corruption.
4. Nanosecond Timestamps & SIMD CRC32: Preserves nanosecond modification
timestamps and CRC32 checksums to eliminate FAT/exFAT 2s drift.
5. Reverse-Delta Pruning Consolidation: Pruning intermediate snapshots
migrates displaced files into the preceding kept snapshot before
removal, preventing data loss across restore points.
6. Resilient Advisory Locking: Non-blocking .lock with PID file fallback.
EXIT STATUS
0 Success.
1 Fatal error (e.g. invalid arguments, path collision, I/O error).
AUTHORS & LICENSE
Written by Sami Lehtinen <noreply@sami-lehtinen.net>.
Licensed under the Zero-Clause BSD (0BSD) License.
Repository: https://github.com/samilehtinen/vcopy