Dubler
Directory synchronization tool.
Note: The name "dubler" comes from Polish, where it means "a person replacing an actor in film or theater" (stunt double / stand-in). Just as a dubler stands in for an actor, this tool creates backup copies that can stand in for your original files — if something happens to the original, the backup is ready to take its place.
Features
- Checksum-based comparison: Files are compared using SHA256 hashes
- Multiple destinations: Sync to multiple directories at once
- Idempotent: Running multiple times is safe - only copies missing or changed files
- Failure tracking: Tracks failed files and allows re-running
- Dry run mode: Preview changes before copying
- JSON configuration: Store sync settings in a config file
- Zero dependencies: Uses Python standard library only
Installation
No installation required! You can run dubler directly with uvx:
uvx dubler --help
If you prefer to install:
# From PyPI
pip install dubler
# or with uv
uv pip install dubler
# From source
uv install
Usage
Command Line
# Basic sync
uvx dubler --source /path/to/source --dest /path/to/dest1 --dest /path/to/dest2
# Dry run (preview without copying)
uvx dubler -s /path/to/source -d /path/to/dest --dry-run
# Verbose output
uvx dubler -s /path/to/source -d /path/to/dest -v
# Show failed files from previous runs
uvx dubler --failed
# Clear failed files from state
uvx dubler --clear-failed
Configuration File
Create a JSON config file (default: ~/.config/dubler/config.json):
{
"source": "/path/to/source",
"destinations": [
"/path/to/dest1",
"/path/to/dest2"
],
"dry_run": false,
"verbose": false
}
Then run without arguments:
uvx dubler
Or specify a custom config file:
uvx dubler --config /path/to/config.json
How It Works
- Scans the source directory recursively
- For each destination:
- Compares files using SHA256 checksums
- Copies files that don't exist or have different checksums
- Tracks any failures in
~/.local/state/dubler/state.json
- Displays summary of copied, skipped, and failed files
State
The application stores data in standard XDG Base Directory locations:
~/.config/dubler/config.json: Configuration file (optional)~/.local/state/dubler/state.json: Failed files from previous runs
Examples
# Sync photos to multiple backup drives
uvx dubler -s ~/Pictures -d /Volumes/Backup1/Photos -d /Volumes/Backup2/Photos
# Preview what would be synced
uvx dubler -s ~/Documents -d ~/Backup/Documents --dry-run -v
# After a failed run, see what failed
uvx dubler --failed
# Fix the issue and run again (idempotent)
uvx dubler -s ~/Documents -d ~/Backup/Documents
Releasing
The project uses automatic versioning based on git tags via setuptools_scm.
Creating a Release
Use the release helper script:
# Bump patch version (bug fixes)
uv run python scripts/release.py patch
# Bump minor version (new features)
uv run python scripts/release.py minor
# Bump major version (breaking changes)
uv run python scripts/release.py major
# Or specify exact version
uv run python scripts/release.py patch --version 1.2.3
This will:
- Create and push a git tag (e.g.,
v1.0.0) - Trigger the GitHub Actions release workflow
- Automatically create a published release with:
- Release notes from commits
- Installation instructions
- Full changelog link
- Publish to PyPI
Manual Release (Alternative)
# Create and push tag manually
git tag v1.0.0
git push origin v1.0.0
Version Format
Tags must follow semantic versioning: vX.Y.Z
X= Major version (breaking changes)Y= Minor version (new features, backwards compatible)Z= Patch version (bug fixes, backwards compatible)
Conventional Commits
This project uses conventional commit messages. See AGENTS.md for guidelines.
Release files for dubler 1.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| dubler-1.3.0.tar.gz | 22.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dubler-1.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 31.7 kB
Release files / dubler-1.3.0.tar.gz
| Download URL | dubler-1.3.0.tar.gz |
|---|---|
| Size | 22.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b3f49820ba2d8bdf9c6f2606522b46343b03fae04bf210e8a597fac52693187e
|
|
BLAKE2b-256 checksum How to use checksums |
e6abb78915ae6b6498bab0bcd0013a293fcc120d50ee39450f693662184165f1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / dubler-1.3.0-py3-none-any.whl
| Download URL | dubler-1.3.0-py3-none-any.whl |
|---|---|
| Size | 8.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
47a6ec6c820e33cf15e1db18eca03b8c71545ff98de0407f70c7aa2533bb2a8c
|
|
BLAKE2b-256 checksum How to use checksums |
ba051817bd2d426f18b3724758e01dc8dd2ac688803baddca0d6256d3db9a7c4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|