Folder synchronisation you can trust: one-way backup, two-way sync or mirror — with a preview of every action before anything happens, verification after copying, and a log of what was done.
- Add folder pairs (source → target) — or drop two folders onto the window. Pairs are remembered.
- Preview scans both sides and lists exactly what would be copied, updated or deleted. Nothing is touched.
- Start copies: every file is written to a temporary name and renamed into place when complete, so an interrupted run never leaves a half file under the real name. Progress is by bytes, with speed and totals.
| Direction | Behaviour |
|---|---|
| One-way (default) | New and changed files go from source to target. Files only in the target are left alone. Never deletes. |
| Two-way | Both folders end up with the newest version of every file; files missing on one side are copied over. Conflicts (same time, different content) are resolved by the Conflicts rule or reported. Never deletes. |
| Mirror | Target becomes an exact copy; files and folders not in the source are deleted from the target. Asks before starting; the preset turns dry run on. |
Changed means modified time (± 2 s, for FAT/exFAT drives) or size differs; Compare by content hash reads every file on both sides instead and is the only way to spot files rewritten without a new timestamp.
Release binaries (no Python needed): download the archive for your OS from
Releases, unpack, run 11copy.
| OS | File |
|---|---|
| Windows x64 | 11copy-<version>-windows-x64.zip |
| Linux x64 | 11copy-<version>-linux-x64.tar.gz |
| macOS arm64 | 11copy-<version>-macos-arm64.tar.gz |
From source: pip install -r requirements.txt then python main.py (or run.cmd / run.sh). Python 3.10+.
No external tools.
| Tab | Setting | What it does |
|---|---|---|
| Sync | Direction | One-way / two-way / mirror (see above). |
| Compare by | Modified time + size (instant), size only, or content hash. | |
| Time tolerance | Times closer than this count as equal (2 s for FAT/exFAT). | |
| Conflicts | Two-way only: newer wins, source wins, target wins, or skip and report. | |
| Verify every copied file | Hash source and copy after writing (BLAKE2b). | |
| Preserve metadata | Keep modification times / permissions — needed for the next run's comparison. | |
| Log file | Append a one-line summary (and errors) to 11copy.log in the target. |
|
| Filters | File / folder patterns | Comma-separated globs, case-insensitive. Defaults skip Thumbs.db, .DS_Store, *.tmp, *.part, Office lock files, .git, node_modules, … |
| Size limits | Skip files smaller / larger than. | |
| Hidden files, symlinks | Include dot-files / hidden attribute; follow links (off: links are skipped). | |
| Safety | Dry run | Runs the whole copy loop without writing; totals are reported. |
| Retries | Per-file retries on read/write errors before it is counted as failed. |
Presets menu: Backup, Verified backup, Two-way sync, Mirror (dry run); save / load / import / export your own; save the current settings as defaults.
python syncer.py [-n] [--direction one_way|two_way|mirror] [--compare mtime_size|size|hash] [--validate] SRC=DST …
python syncer.py --config pairs.json # {"pairs": [{"source": "...", "target": "..."}], "settings": {...}}
syncer.py imports nothing from Qt and is what the GUI runs.
| Symptom | Setting |
|---|---|
| Everything is copied again on every run to a USB stick | The stick is FAT/exFAT: keep Time tolerance at 2 s and Preserve metadata on. |
| A file was modified in place without a new timestamp and was not picked up | Compare by content hash (slower: reads both sides). |
| Two-way sync keeps flipping a file | Both sides changed at the same second: set Conflicts to Source wins or Target wins. |
| Path too long on Windows | Handled: paths beyond 260 characters are accessed with the \\?\ prefix. |
pip install -r requirements.txt pyinstaller && python build.py → dist/11copy-<version>-<os>-<arch>.zip|tar.gz.
Tag v<APP_VERSION> (from config.py) and CI builds all three OSes, runs the unit tests and a headless verified
sync on the frozen Linux binary, and attaches the archives to the release.
- The Windows taskbar shows the app's own icon. The taskbar button takes its icon from the process's Application User Model ID rather than from the window, and with none of its own the process was grouped under whatever launched it and wore that program's icon. One is now set before any window exists, and it carries no version number so a pinned button survives an upgrade.
- Adding a folder pair that is already in the queue (same source and target, case/slash-insensitive) is rejected with a status-bar note instead of queued twice.
- Scans list folders and files in natural order (
a1, a2, a10), so plans, logs and copy progress follow the order a person expects. *.11copy-partin-flight files are always skipped by scans, even if removed from the exclude list; helper processes (open folder) are tracked and killed on quit.
- Rewritten as a PySide6 desktop app with the copy running on a worker thread (the old CustomTkinter window froze during a copy and could not be stopped), a folder-pair queue with per-pair status, progress by bytes, speed, log.
- Preview before anything is copied; Stop that actually stops and removes the partial file.
- Mirror mode (with confirmation and dry run), conflict rules for two-way, exclude patterns, size limits, hidden/symlink handling, retries, per-target log file.
- Atomic copies (temp file + rename), BLAKE2b verification instead of MD5, Windows long paths handled instead of silently skipped, FAT time tolerance, identical-file detection by time and size.
- Standalone releases for Windows, Linux and macOS (PyInstaller; Nuitka build script removed); CI self-test on the frozen binary; unit tests for the planner.
- Not compatible with 1.x:
config.jsonis no longer used — pairs are stored in the app settings and re-added once.