Skip to main content

A comprehensive solution to managing your dotfiles

Project description

dotsync

dotsync Python Platform License

Mirror-only dotfiles manager — edit configs in $HOME, mirror them to git, restore on new machines.

InstallQuick startCommandsFilelistTest coverageDocs


dotsync watches paths under your home directory, copies their content into a git repo (default ~/.dotfiles), and pushes to a remote. Config files stay as regular files in $HOME — the repo is a mirror, not a symlink farm. Use categories to sync subsets of your dotfiles per machine; use @tree lines to watch whole directories that grow over time.

Installation

Quick install (installs uv if needed; first run ~30s, then instant):

curl -fsSL https://raw.githubusercontent.com/HarveyGG/dotsync/main/install.sh | bash

Homebrew:

brew tap HarveyGG/tap
brew install dotsync

pip:

pip install dotsync-cli

Requires git. Runtime uses uv to resolve the CLI (handled by the install methods above).

Quick start

Machine that already has your configs:

dotsync track ~/.zshrc shell
dotsync track ~/.config/nvim editor
dotsync save    # mirror → commit → push (prompts for remote on first push)

New machine:

dotsync restore
# or non-interactive:
dotsync restore --remote git@github.com:you/dotfiles.git --categories shell,editor --yes

track bootstraps ~/.dotfiles and git init on first use. restore clones (or uses an existing repo), pulls latest, then copies mirrors back to $HOME.

Filelist

Watch rules live in ~/.dotfiles/filelist:

# path:category[,category...]  optional |encrypt
.zshrc:shell,common
.ssh/config:tools|encrypt

# whole directory — re-scanned on every save
@tree:.config/nvim:editor
@tree:.local/share/my-app/custom-*:tools

# host → category groups (optional)
macbook=shell,editor,common
  • Atomic paths — single files (or expanded directory contents when added via track on a directory).
  • @tree — dynamic membership; new files under the pattern are picked up on the next save. Globs supported (*, ?, […]).
  • Symlinks inside trees — targets are materialized into the repo (internal links keep layout; external targets go under .dotsync/materialized/).

Commands

Command Description
init [dir] Create repo skeleton at ~/.dotfiles (or given path)
track <path> [category] [--encrypt] [--no-auto-update] Add to filelist; mirrors immediately unless --no-auto-update
untrack <path> [--purge-repo] Stop watching; home file kept
encrypt <path> Convert an already-tracked plain path to encrypted
list [categories] [--top-level] List watched paths (atomic files and @tree summaries, not every tree member); --top-level groups by first path component
categories Show category groups from filelist
save [categories] [-m msg] [--dry-run] [--no-push] Expand @tree, mirror home → repo, commit, push
restore [categories] [--remote URL] [--yes] [--skip-pull] [--conflict …] Pull (unless skipped), then repo → home; diff on conflict
update [categories] Mirror home → repo without commit
diff [categories] Show git diff plus pending mirror changes
commit [-m msg] Commit staged repo changes
clean [categories] Remove repo mirrors no longer in filelist
passwd Set or change encryption password
showpw Print stored encryption password (local machine only)

Common flags: --non-interactive, --dry-run, --encrypt, --keep-going.

Full reference: ReadTheDocs.

Test coverage

290 tests total (37 black-box E2E scenarios against the real CLI in an isolated sandbox). All scenarios below pass on v2.0.3.

uv run pytest tests/blackbox/ -v
uv run pytest tests/ -v
ID Area What it verifies
L1 Lifecycle tracksave (push) → restore on a fresh machine
L2 Lifecycle untrack removes from filelist; home file kept
L3 Lifecycle untrack --purge-repo deletes mirror
L4 Lifecycle track bootstraps repo when missing
L5a Lifecycle Default track mirrors immediately
L5b Lifecycle track --no-auto-update defers mirror until save
M1 Mirror Home paths stay regular files after save + restore
M2 Mirror Atomic file round-trip is byte-identical
M3 Mirror Nested paths restore as files, not symlinks
RP1 Pull restore pulls remote before copying to home
RP2 Pull Diverged repo aborts restore with no home writes
RP3 Pull --skip-pull uses local HEAD only
RP4 Pull No remote: restore from local HEAD
C1 Conflicts Cancel on diff aborts entire restore
C2 Conflicts Overwrite continues for remaining paths
C3 Conflicts Identical home file skipped without prompt
C4 Conflicts --conflict overwrite --non-interactive
C5 Conflicts --conflict abort --non-interactive
T1 @tree New tree member picked up on second save
T2 @tree Glob excludes non-matching paths
T3 @tree Removed member pruned from repo mirror
T4 @tree Tree save → restore on clean home
S1 Symlinks Internal symlink materialized with manifest
S2 Symlinks External symlink under .dotsync/materialized/
S3 Symlinks Broken symlink skipped cleanly
S4 Symlinks Dedup when link target also watched
S5 Symlinks Restore recreates internal symlink
S6 Symlinks External symlink save → restore round-trip
E1 Encrypt Encrypt track → save → restore
E2 Encrypt showpw prints stored password
E3 Encrypt showpw fails when no password set
B1 Boundaries Empty filelist save is a no-op
B2 Boundaries Declined remote URL aborts save
B3 Boundaries Push failure exits non-zero
B4 Boundaries Binary conflict; cancel leaves home unchanged
B5 Boundaries --no-push durability warning
B6 Boundaries Save without remote exits quickly (no hang)

Scenario details: tests/blackbox/test_plan.md.

Contributing

git clone https://github.com/HarveyGG/dotsync.git && cd dotsync
uv sync
uv run pytest tests/

Pull requests welcome.

License

Non-Commercial License — free for personal, educational, and non-profit use. See LICENSE. Commercial use requires permission: harvey.wanghy@gmail.com

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dotsync_cli-2.0.3.tar.gz (38.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dotsync_cli-2.0.3-py3-none-any.whl (44.0 kB view details)

Uploaded Python 3

File details

Details for the file dotsync_cli-2.0.3.tar.gz.

File metadata

  • Download URL: dotsync_cli-2.0.3.tar.gz
  • Upload date:
  • Size: 38.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.14

File hashes

Hashes for dotsync_cli-2.0.3.tar.gz
Algorithm Hash digest
SHA256 38c1191426c1fb5bacd0b9b06ae4de53a2d863527ca59d5913a8a2f006bc0558
MD5 45a17743fc2c68b904c0b6583634c5d9
BLAKE2b-256 32aa3679e7c89740718a25f9fb3e92caa1ce482825fdbf601862efad3962359a

See more details on using hashes here.

File details

Details for the file dotsync_cli-2.0.3-py3-none-any.whl.

File metadata

  • Download URL: dotsync_cli-2.0.3-py3-none-any.whl
  • Upload date:
  • Size: 44.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.14

File hashes

Hashes for dotsync_cli-2.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 081463213ae1e72735fcf6ba7b6fbe6e324828e9159f14f40d212736c716cb8a
MD5 8bd2b21eae10fe68a5f0f7529ca16054
BLAKE2b-256 23304bcb076108aa99fb77750d1008d4893f6181b799ea7b302ca6ac945fa2b1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page