Skip to content

Manage files and directories

The commands on this page change the shared manifest, so they apply to every machine, whichever machine you run them on. Each one syncs straight away, so you see the result. add, remove and set also accept --no-sync, which only queues the change for the next sync.

Terminal window
dotsync add ~/.gitconfig -d "Git identity and aliases"

The file is copied into the repository and pushed. Every other machine installs it on its next sync, backing up any copy it already has first. (A machine set up with init --keep-existing reports a different existing copy as a conflict instead.)

The source, the file’s location under files/ in the repository, comes from its path: a leading .config/ is dropped, and so are the leading dots of what remains. ~/.gitconfig becomes gitconfig, and ~/.config/fish/config.fish becomes fish/config.fish. To choose one yourself:

Terminal window
dotsync add ~/.config/git/config --source git/config -d "Git (XDG location)"

Without -d, the description is the file name. You can add several paths at once, as in dotsync add ~/.vimrc ~/.inputrc, but -d and --source only work with a single path.

Terminal window
dotsync add ~/.config/nvim -d "Neovim" --ignore lazy-lock.json --ignore 'spell/*.spl'

A directory is managed recursively: files added, changed or deleted anywhere inside it sync too. Symlinks inside it are synced as symlinks, and each file’s executable bit is kept. Empty directories aren’t tracked.

These are always skipped: .DS_Store, ._*, *.swp, *.swo, *~, .#*, .git, __pycache__, *.pyc, .dotsync-tmp-*. The last one is the temporary file dotsync writes while replacing a file.

  • Directory~/.config/nvim/ managed as nvim
    • init.lua synced
    • Directorylua/
      • plugins.lua synced
    • lazy-lock.json skipped: --ignore lazy-lock.json
    • Directoryspell/
      • en.utf-8.spl skipped: --ignore 'spell/*.spl'
    • .DS_Store always skipped

Anything generated, machine-specific or large doesn’t belong in sync: plugin checkouts, caches, lock files that differ per machine, logs, sockets.

A pattern is matched against each file’s name and against its path inside the directory, and against every directory above the file too, so a pattern that matches a directory skips everything below it. * also matches /, ? matches one character, and […] a character class. Quote patterns so your shell doesn’t expand them.

Pattern Skips
lazy-lock.json any file or directory named lazy-lock.json, at any depth
spell/*.spl .spl files under spell/ at the top of the directory
plugin a plugin directory at any depth, and everything in it
*.log every file whose name ends in .log

Credential files in a managed directory aren’t refused. They’re held back: never uploaded, and reported as BLOCKED by sync and status. add warns about them up front:

dotsync add ~/.config/gh -d "GitHub CLI"
note: 1 file(s) under ~/.config/gh look like secrets and will not be sent (e.g. hosts.yml)
managing ~/.config/gh as 'gh'
  sent      ~/.config/gh/config.yml
  BLOCKED   ~/.config/gh/hosts.yml  (path looks like a secret (matches '.config/gh/hosts.yml'))
dotsync: 1 sent, 1 blocked — pushed 0c9c9a4

To stop seeing them, ignore them: dotsync set ~/.config/gh --ignore hosts.yml. See Keep secrets out.

add refuses:

  • paths outside your home directory, and your home directory itself
  • dotsync’s own directories: ~/.config/dotsync, ~/.local/share/dotsync and ~/.local/state/dotsync
  • anything that isn’t a regular file or a directory, such as a socket
  • a path that’s already managed, unless both entries are limited to different operating systems
  • a path inside a managed directory, or a directory containing a managed path
  • a source already used by another entry, or one that overlaps another source
  • files that look like secrets; see Keep secrets out

If the path you add is a symlink, as in an old GNU Stow setup, dotsync keeps the link and syncs the file or directory it points to, which must also be inside your home directory. Files larger than 10 MiB can’t be synced: add refuses such a file, and inside a managed directory it’s reported as an error while the rest of the directory syncs normally.

dotsync set changes an entry for every machine. Name the entry by its path on this machine or by its source:

Terminal window
dotsync set ~/.config/nvim --ignore lazy-lock.json # skip another file
dotsync set ~/.config/nvim --unignore lazy-lock.json # sync it again
dotsync set ~/.ssh/config --mode 0600 # force these permissions everywhere
dotsync set karabiner/karabiner.json --os darwin # manage it on macOS only
dotsync set ~/.gitconfig -d "Git identity, aliases and signing"
dotsync set nvim --ignore 'spell/*.spl' --mode 0600
updated 'nvim': mode = 0600; ignore + spell/*.spl
dotsync: in sync (12 files) — pushed 4e1d2a7

Changes are checked before anything is queued, so an invalid mode or OS is refused straight away. Ignore patterns are recorded as additions and removals, so if two machines change them at the same time, both changes survive. set lists every option; dotsync describe <entry> <text> is a shortcut for set <entry> -d <text>.

set can’t change an entry’s source or target. To move one, remove it and add it again.

Without --mode, each machine keeps its own permissions for a file, and only the executable bit syncs. With --mode 0600, every machine gives the file exactly those permissions whenever dotsync writes it there, which is what you want for files like ~/.ssh/config. A copy that’s already up to date keeps its current permissions until dotsync next writes it. --no-mode goes back to the default.

By default, dotsync replaces a file atomically: it writes a temporary file next to it and renames it into place, so no program ever sees a half-written file. A few programs track a file by its inode, and a container that bind-mounts a single file keeps seeing the old one after a rename. For those, --write inplace rewrites the existing file instead.

dotsync remove ~/.tmux.conf
no longer managing ~/.tmux.conf ('tmux.conf'); local copies are left in place on every machine
dotsync: in sync (14 files) — pushed 9a3f6b0

This stops managing the entry on every machine. The file itself stays exactly where it is on every machine, as an ordinary file that dotsync no longer touches. Its content stays in the repository’s history.

Every command on this page works without a network connection. The change is queued on this machine, shown as queued, not yet pushed by dotsync status, and sent by the first sync that reaches the remote. Queued changes are replayed on top of whatever the other machines pushed in the meantime, so two machines can add different files at the same moment without either change being lost.

You can also edit manifest.json directly in any clone of your dotfiles repository, commit and push. That’s handy for bulk changes. Machines pick the change up on their next sync. The manifest reference lists every field.