Skip to content

dsaenztagarro/dotsync

Repository files navigation

Dotsync

Gem Version CI License: MIT Ruby Version

Warning

This gem is under active development. You can expect new changes that may not be backward-compatible.

What is Dotsync?

Dotsync is a powerful Ruby gem for managing and synchronizing your dotfiles across machines. Whether you're setting up a new development environment or keeping configurations in sync, Dotsync makes it effortless.

Key Features:

  • Bidirectional Sync Mappings: Define once, sync both ways — eliminates config duplication with [[sync.mappings]] syntax
  • XDG Shorthand DSL: Concise [[sync.home]], [[sync.xdg_config]], [[sync.xdg_data]], [[sync.xdg_bin]] syntax for common directory patterns
  • Preview Mode: See what changes would be made before applying them (dry-run by default)
  • Smart Filtering: Use force, only, and ignore options to precisely control what gets synced
  • Automatic Backups: Pull operations create timestamped backups for easy recovery
  • Live Watching: Continuously monitor and sync changes in real-time with watch command
  • Config Includes: Compose configs from a shared base + machine-specific overlays with include
  • Config Source: Point local config to your dotfiles repo with source — changes are visible immediately without syncing
  • Post-Sync Hooks: Run commands automatically after files change (e.g., codesigning, chmod, service reload)
  • Customizable Output: Control verbosity and customize icons to match your preferences
  • Auto-Updates: Get notified when new versions are available

Table of Contents

Requirements

  • Ruby: MRI 3.2+

Installation

Add this line to your application's Gemfile:

gem "dotsync"

And then execute:

$ bundle install

Or install it yourself as:

$ gem install dotsync

Quick Start

Get started with Dotsync in just a few steps:

  1. Install the gem:

    gem install dotsync
  2. Generate a default configuration:

    dotsync setup

    This creates ~/.config/dotsync.toml with example mappings.

  3. Edit the configuration (~/.config/dotsync.toml) to define your dotfile mappings using bidirectional sync:

    # Sync your shell config
    [[sync.home]]
    path = ".zshenv"
    
    # Sync XDG config directories
    [[sync.xdg_config]]
    only = ["nvim", "alacritty", "zsh", "git"]
    force = true
  4. Preview your changes (dry-run mode):

    dotsync pull   # Preview pulling from repo to local
    dotsync push   # Preview pushing from local to repo

    This shows what would be changed without modifying any files.

  5. Apply changes when you're ready:

    dotsync pull --apply   # Apply repo → local
    dotsync push --apply   # Apply local → repo

Usage

Executable Commands

Dotsync provides the following commands to manage your dotfiles:

Important

By default, both push and pull commands run in preview mode (dry-run). They will show you what changes would be made without actually modifying any files. To apply changes, you must use the --apply flag.

Core Commands

  • Push: Transfer dotfiles from your local machine to the destination repository.

    dotsync push [OPTIONS]
    dotsync push --apply [OPTIONS]  # Apply changes

    dotsync push

  • Pull: Synchronize dotfiles from the repository to your local machine.

    dotsync pull [OPTIONS]
    dotsync pull --apply [OPTIONS]  # Apply changes

    During the pull operation, Dotsync::PullAction creates a backup of the existing files on the destination. These backups are stored in a directory under the XDG path, with each backup organized by a timestamp. To prevent excessive storage usage, only the 10 most recent backups are retained. Older backups are automatically purged, ensuring efficient storage management.

    dotsync pull

  • Watch: Continuously monitor and sync changes between your local machine and the repository.

    dotsync watch [OPTIONS]

    The watch command supports the same output control options as push and pull (e.g., --quiet, --no-legend, --no-mappings).

  • Setup (alias: init): Generate a default configuration file at ~/.config/dotsync.toml with example mappings for pull, push, and watch.

    dotsync setup
    dotsync init     # Alias for setup

Utility Commands

  • Status: Display current configuration and mappings without executing any actions.

    dotsync status

    This is useful for inspecting your configuration and verifying mappings are correct.

  • Diff: Show differences that would be made (alias for push in preview mode).

    dotsync diff

    Convenient shorthand for previewing changes without typing --dry-run.

Command Options

All push and pull commands support the following options:

Action Control:

  • -a, --apply: Apply changes (without this, commands run in preview mode)
  • --dry-run: Explicitly run in preview mode without applying changes (default behavior)
  • -y, --yes: Skip confirmation prompt and auto-confirm changes
  • -c, --config PATH: Specify a custom config file path (enables multiple config workflows)

Output Control:

  • -q, --quiet: Hide all non-essential output (only errors or final status)
  • --no-legend: Hide all legends for config, mappings, and differences
  • --no-config: Hide the config section in the output
  • --no-mappings: Hide the mappings and their legend
  • --no-diff-legend: Hide the differences legend only
  • --no-diff: Hide the differences section itself
  • --only-diff: Show only the differences section
  • --only-config: Show only the config section
  • --only-mappings: Show only the mappings section
  • -v, --verbose: Force showing all available information

General:

  • --version: Display version number
  • -h, --help: Show help message

Examples

# Setup and configuration
dotsync setup                      # Create initial config file
dotsync init                       # Same as setup (alias)
dotsync status                     # View current configuration

# Preview changes (dry-run mode)
dotsync push                       # Preview push changes
dotsync pull                       # Preview pull changes
dotsync diff                       # Quick preview (alias for push)
dotsync push --dry-run             # Explicit dry-run flag

# Apply changes
dotsync push --apply               # Apply changes with confirmation
dotsync pull --apply               # Apply changes with confirmation
dotsync push -ay                   # Apply without confirmation (--apply + --yes)
dotsync pull --apply --yes         # Apply without confirmation

# Custom configuration files
dotsync -c ~/work-dotfiles.toml push      # Use work config
dotsync --config ~/.config/personal.toml pull  # Use personal config

# Output control
dotsync pull --quiet               # Minimal output
dotsync push --only-diff           # Show only differences
dotsync pull --apply --yes -q      # Silent apply for scripts

# Monitoring
dotsync watch                      # Watch with default output
dotsync watch --quiet              # Watch with minimal output

Configuration

Dotsync uses TOML configuration files to define mappings between your local machine and your dotfiles repository. The recommended approach is bidirectional sync mappings, which eliminate duplication and keep your config clean.

Tip

Set up mirror environment variables for cleaner configuration:

# Add to your ~/.zshrc or ~/.bashrc
export DOTFILES_DIR="$HOME/Code/dotfiles"
export XDG_CONFIG_HOME_MIRROR="$DOTFILES_DIR/xdg_config_home"
export XDG_DATA_HOME_MIRROR="$DOTFILES_DIR/xdg_data_home"
export HOME_MIRROR="$DOTFILES_DIR/home"

Bidirectional Sync Mappings (Recommended)

Use [[sync]] mappings to define paths that sync in both directions. This is the preferred approach as it eliminates duplication between push and pull configurations.

XDG Shorthand Syntax

The most concise way to define mappings for standard XDG directories:

# Sync home directory files
[[sync.home]]
path = ".zshenv"

# Sync multiple configs from XDG_CONFIG_HOME
[[sync.xdg_config]]
only = ["alacritty", "git", "zsh", "starship.toml"]
force = true

# Sync specific config with custom options
[[sync.xdg_config]]
path = "nvim"
force = true
ignore = ["lazy-lock.json"]

# Sync XDG data directories
[[sync.xdg_data]]
path = "git"
force = true

Supported shorthands:

Shorthand Local Remote
sync.home $HOME $HOME_MIRROR
sync.xdg_config $XDG_CONFIG_HOME $XDG_CONFIG_HOME_MIRROR
sync.xdg_data $XDG_DATA_HOME $XDG_DATA_HOME_MIRROR
sync.xdg_cache $XDG_CACHE_HOME $XDG_CACHE_HOME_MIRROR
sync.xdg_bin $XDG_BIN_HOME $XDG_BIN_HOME_MIRROR

Options:

  • path (optional): Relative path within the directory. If omitted, syncs the entire directory.
  • force, ignore, only: All standard mapping options are supported.
Explicit Sync Syntax

For custom paths that don't follow XDG conventions, use explicit [[sync.mappings]] entries:

[[sync.mappings]]
local  = "$XDG_CONFIG_HOME/nvim"
remote = "$XDG_CONFIG_HOME_MIRROR/nvim"
force  = true
ignore = ["lazy-lock.json"]

[[sync.mappings]]
local  = "$HOME/.zshenv"
remote = "$HOME_MIRROR/.zshenv"

# Sync config file to a different location in repo
[[sync.mappings]]
local  = "$XDG_CONFIG_HOME/dotsync.toml"
remote = "$XDG_CONFIG_HOME_MIRROR/dotsync/dotsync.macbook.toml"

How it works:

  • local is your local machine path (e.g., ~/.config/nvim)
  • remote is your dotfiles repository path (e.g., ~/dotfiles/config/nvim)
  • For push operations: localremote
  • For pull operations: remotelocal
  • All standard options (force, ignore, only) are supported
Complete Example

Here's a real-world configuration using bidirectional sync:

## BIDIRECTIONAL SYNC CONFIGURATION

# Home directory files
[[sync.home]]
path = ".zshenv"

# Bulk sync multiple configs
[[sync.xdg_config]]
only = [
  "alacritty",
  "brewfile",
  "git",
  "lazygit",
  "tmux",
  "zellij",
  "starship.toml"
]
force = true

# Zsh with ignored files
[[sync.xdg_config]]
path = "zsh"
ignore = [".zsh_sessions", ".zsh_history", ".zcompdump"]

# Neovim with force sync
[[sync.xdg_config]]
path = "nvim"
force = true
ignore = ["lazy-lock.json"]

# Git templates in data directory
[[sync.xdg_data]]
path = "git"
force = true

# This config file itself
[[sync.mappings]]
local  = "$XDG_CONFIG_HOME/dotsync.toml"
remote = "$XDG_CONFIG_HOME_MIRROR/dotsync/dotsync.macbook.toml"

Alternative: Unidirectional Mappings

For asymmetric sync scenarios where push and pull need different configurations, you can use separate [[push.mappings]] and [[pull.mappings]] sections:

# Pull from repo to local (repo → local)
[[pull.mappings]]
src  = "$XDG_CONFIG_HOME_MIRROR/nvim"
dest = "$XDG_CONFIG_HOME/nvim"
force = true
ignore = ["lazy-lock.json"]

# Push from local to repo (local → repo)
[[push.mappings]]
src  = "$XDG_CONFIG_HOME/alacritty"
dest = "$XDG_CONFIG_HOME_MIRROR/alacritty"
only = ["alacritty.toml", "themes"]

# Watch for live syncing
[[watch.mappings]]
src = "$XDG_CONFIG_HOME/nvim"
dest = "$XDG_CONFIG_HOME_MIRROR/nvim"

Note

You can mix [[sync.mappings]], XDG shorthands ([[sync.home]], [[sync.xdg_config]], etc.), and [[push.mappings]]/[[pull.mappings]] in the same config file. Use bidirectional sync for symmetric mappings and unidirectional for special cases.

force, only, and ignore Options in Mappings

Each mapping entry supports the following options:

force Option

A boolean (true/false) value. When set to true, it forces deletion of files in the destination directory that don't exist in the source. This is particularly useful when you need to ensure the destination stays synchronized with the source.

Example:

[[sync.xdg_config]]
path = "nvim"
force = true
ignore = ["lazy-lock.json"]

Warning

When using force = true with the only option, only files matching the only filter will be managed. Other files in the destination remain untouched.

only Option

An array of relative paths (files or directories) or glob patterns to selectively transfer from the source. This option provides precise control over which files get synchronized.

How it works:

  • Paths are relative to the src directory
  • You can specify entire directories, individual files, or glob patterns (*, ?, [charset])
  • Parent directories are automatically created as needed
  • Other files in the source are ignored
  • With force = true, only files matching the only filter are cleaned up in the destination

Example 1: Selecting specific directories

[[sync.xdg_config]]
only = ["nvim", "alacritty", "zsh"]

This transfers only the nvim/, alacritty/, and zsh/ directories.

Example 2: Selecting specific files

[[sync.xdg_config]]
path = "alacritty"
only = ["alacritty.toml", "rose-pine.toml"]

This transfers only two specific TOML files from the alacritty config directory.

Example 3: Selecting files inside nested directories

[[sync.xdg_config]]
only = ["bundle/config", "ghc/ghci.conf", "cabal/config"]

This transfers only specific configuration files from different subdirectories:

  • bundle/config file from the bundle/ directory
  • ghc/ghci.conf file from the ghc/ directory
  • cabal/config file from the cabal/ directory

The parent directories (bundle/, ghc/, cabal/) are created automatically in the destination, but other files in those directories are not transferred.

Example 4: Glob patterns

[[sync.home]]
path = "Library/LaunchAgents"
only = ["local.*.plist"]

This transfers only files matching the glob pattern — e.g., local.brew.upgrade.plist, local.ollama.plist — while ignoring system-generated plists like com.apple.*.plist.

Supported glob characters:

  • * — matches any sequence of characters (e.g., local.*.plist)
  • ? — matches any single character (e.g., config.?)
  • [charset] — matches any character in the set (e.g., log.[0-9])

Glob patterns can be mixed with exact paths in the same only array:

[[sync.home]]
path = "Library/LaunchAgents"
only = ["local.*.plist", "README.md"]

Example 5: Deeply nested paths

[[sync.xdg_config]]
only = ["nvim/lua/plugins/init.lua", "nvim/lua/config/settings.lua"]

This transfers only specific Lua files from deeply nested paths within the nvim configuration.

Important behaviors:

  • File-specific paths: When specifying individual files (e.g., "bundle/config"), only that file is managed. Sibling files in the same directory are not affected, even with force = true.
  • Directory paths: When specifying directories (e.g., "nvim"), all contents of that directory are managed, including subdirectories.
  • Glob patterns: When using patterns (e.g., "local.*.plist"), only files whose names match the pattern are managed. Non-matching files in the same directory are untouched.
  • Combining with force: With force = true and directory paths, files in the destination directory that don't exist in the source are removed. With file-specific paths or glob patterns, only matching files are managed.
ignore Option

An array of relative paths or patterns to exclude during transfer. This allows you to skip certain files or folders.

Example:

[[sync.xdg_config]]
path = "nvim"
ignore = ["lazy-lock.json", "plugin/packer_compiled.lua"]

Combining options:

[[sync.xdg_config]]
path = "nvim"
only = ["lua", "init.lua"]
ignore = ["lua/plugin/packer_compiled.lua"]
force = true

This configuration:

  1. Transfers only the lua/ directory and init.lua file (only)
  2. Excludes lua/plugin/packer_compiled.lua even though it's in the lua/ directory (ignore)
  3. Removes files in the destination that don't exist in the source (force)

Note

When ignore and only both match a path, ignore takes precedence.

These options apply when the source is a directory and are relevant for both push and pull operations.

Post-Sync Hooks

Hooks let you run commands automatically after a mapping's files are transferred. Hooks only execute when files actually changed, and only when using --apply.

Hook Types
Hook Description Valid in
post_sync Runs after sync in both directions [[sync]] mappings
post_push Runs only after push [[sync]] and [[push]] mappings
post_pull Runs only after pull [[sync]] and [[pull]] mappings

For sync mappings, hooks are resolved by direction:

  • Push: post_sync + post_push commands
  • Pull: post_sync + post_pull commands
Examples

Single command (shorthand mapping):

[[sync.xdg_bin]]
force = true

[sync.xdg_bin.hooks]
post_sync = "codesign -s - {files}"

Multiple commands (explicit sync):

[[sync.mappings]]
local  = "$XDG_CONFIG_HOME/scripts"
remote = "$XDG_CONFIG_HOME_MIRROR/scripts"

[sync.mappings.hooks]
post_sync = ["codesign -s - {files}", "chmod 700 {files}"]
post_pull = "launchctl kickstart -k gui/$(id -u)/com.example.service"

Unidirectional mapping:

[[pull.mappings]]
src = "$DOTFILES_DIR/scripts"
dest = "$HOME/Scripts"

[pull.mappings.hooks]
post_pull = ["codesign -s - {files}", "chmod 700 {files}"]
Template Variables
Variable Description
{files} Shell-quoted paths of changed destination files
{src} The mapping's source path
{dest} The mapping's destination path
Real-World Examples
# Codesign scripts after pulling (macOS Ventura+ requirement for LaunchAgents)
[[sync.xdg_bin]]
force = true

[sync.xdg_bin.hooks]
post_pull = "codesign -s - {files}"

# Reload a LaunchAgent after pulling config changes
[[pull.mappings]]
src = "$DOTFILES_DIR/LaunchAgents/com.example.plist"
dest = "$HOME/Library/LaunchAgents/com.example.plist"

[pull.mappings.hooks]
post_pull = "launchctl kickstart -k gui/$(id -u)/com.example"

Note

In preview mode (without --apply), hooks are displayed as a preview showing what commands would run. Hook failures log errors but do not abort remaining hooks or mappings.

Config Includes

Use include to compose a config from a shared base file and a machine-specific overlay. This eliminates duplication when multiple machines share most of the same mappings.

# dotsync.mbp_personal.toml (overlay — only the delta)
include = "dotsync.base.toml"

# Machine-specific mappings appended to base
[[sync.xdg_config]]
path = "claude"
only = ["settings.json", "instructions", "commands"]

[[sync.mappings]]
local  = "$XDG_CONFIG_HOME/opencode/opencode.jsonc"
remote = "$XDG_CONFIG_HOME_MIRROR/opencode/opencode.mbp_personal.jsonc"
# dotsync.base.toml (shared across all machines)
[[sync.home]]
path = ".zshenv"

[[sync.xdg_config]]
only = ["alacritty", "brewfile", "git", "nvim", "zsh"]
force = true

[watch]
src = "~/.config"
paths = ["~/.config/nvim/", "~/.config/zsh/"]

Merge semantics:

Type Behavior Example
Arrays ([[array-of-tables]]) Concatenate (base + overlay) Base [[sync.home]] entries + overlay [[sync.home]] entries
Hashes ([tables]) Recursive deep merge (overlay wins on leaves) Overlay [watch].dest overrides base [watch].dest
Scalars Overlay wins Overlay force = false overrides base force = true

Rules:

  • The include path is resolved relative to the overlay file's directory
  • Chained includes (an included file that itself has include) are not supported
  • The include key is consumed and does not appear in the merged config
  • Cache invalidation tracks both the overlay and included file's mtime/size

Config Source

Use source to point your local config at the authoritative copy in your dotfiles repository. Instead of syncing config files back and forth, dotsync reads the config directly from the repo. Changes are visible immediately — no pull needed.

The problem it solves: When config files change in your dotfiles repo, you normally need to ds pull to update local copies, then run ds pull again so the new config takes effect. With source, dotsync reads from the repo directly, eliminating this two-pass problem.

Setup:

# ~/.config/dotsync.toml (one-time setup — never changes)
source = "$XDG_CONFIG_HOME_MIRROR/dotsync/dotsync.mbp_personal.toml"
# ~/Code/dotfiles/xdg_config_home/dotsync/dotsync.mbp_personal.toml (the real config)
include = "dotsync.base.toml"

[[sync.xdg_config]]
path = "zsh"
ignore = [".zsh_sessions", ".zsh_history", ".zcompdump"]

[[sync.xdg_config]]
path = "nvim"
force = true
ignore = ["lazy-lock.json"]

The local file is a thin pointer that never changes. The real config (and its include base) live in the repo and are read directly.

Rules:

  • source must be the only key in the pointer file — no other config keys allowed
  • The source file can use include to compose with a base config (resolved relative to the source file)
  • Chained sources (a source file pointing to another source) are not supported
  • Environment variables are expanded in the source path (e.g., $XDG_CONFIG_HOME_MIRROR)
  • Cache invalidation tracks the pointer file, the source file, and any included file

Per-machine setup:

dotfiles/xdg_config_home/dotsync/
  dotsync.base.toml            # shared mappings, hooks, watch paths
  dotsync.mbp_personal.toml    # include + personal-only mappings
  dotsync.mbp_work.toml        # include + work-only mappings
  dotsync.mac_mini.toml        # include + mac-mini-only mappings

Each machine's ~/.config/dotsync.toml is a one-line pointer to its overlay. Edit the repo files and changes take effect immediately — no syncing, no two-pass.

Safety Features

Dotsync includes several safety mechanisms to prevent accidental data loss:

Confirmation Prompts

Before applying any changes with the --apply flag, Dotsync will:

  1. Show you all differences that will be applied
  2. Display the total count of files to be modified
  3. Ask for explicit confirmation: About to modify X file(s). Continue? [y/N]
  4. Only proceed if you type y and press Enter

Example:

$ dotsync push --apply

# ... shows differences ...

About to modify 15 file(s).
Continue? [y/N] y

Bypassing Confirmation:

  • Use the --yes or -y flag to skip confirmation (useful for automation):
    dotsync push --apply --yes
    dotsync pull -ay              # Short form
  • Use the --quiet flag (automatically skips prompt and suppresses output)

Note

No confirmation is shown if there are no differences to apply.

Automatic Backups

When using pull --apply, Dotsync automatically:

  • Creates timestamped backups of existing files before overwriting them
  • Stores backups in ~/.cache/dotsync/backups/YYYYMMDDHHMMSS/
  • Retains only the 10 most recent backups (older ones are purged)
  • Creates backups only when there are actual differences

To restore from a backup:

ls -la ~/.cache/dotsync/backups/
cp -r ~/.cache/dotsync/backups/20250110143022/* ~/.config/

Preview Mode (Dry-Run)

By default, all push and pull commands run in preview mode:

  • Shows exactly what would change without modifying files
  • Must explicitly use --apply flag to make changes
  • Use --dry-run flag for explicit clarity in scripts

Enhanced Error Handling

Dotsync provides clear, actionable error messages for common issues:

  • Permission Errors:

    Permission denied: /path/to/file
    Try: chmod +w <path> or check file permissions
    
  • Disk Full Errors:

    Disk full: No space left on device
    Free up disk space and try again
    
  • Symlink Errors:

    Symlink error: Target does not exist
    Check that symlink target exists and is accessible
    
  • Type Conflicts:

    Type conflict: Cannot overwrite directory with file
    Cannot overwrite directory with file or vice versa
    

Errors are reported per-mapping, allowing Dotsync to continue processing other mappings even if one fails.

Symlink Support

Dotsync properly handles symbolic links:

  • Preserves symlink targets (absolute and relative paths)
  • Handles broken symlinks gracefully
  • Detects type conflicts (e.g., file vs. directory vs. symlink)
  • Provides clear error messages for symlink-related issues

Customizing Icons

Dotsync allows you to customize the icons displayed in the console output by adding an [icons] section to your configuration file (~/.config/dotsync.toml). This is useful if you prefer different icons or need compatibility with terminals that don't support Nerd Fonts.

Available Icon Options

You can customize the following icons in your configuration:

Mapping Status Icons (shown next to each mapping):

  • force - Indicates force deletion is enabled (clears destination before transfer)
  • only - Indicates only specific files will be transferred
  • ignore - Indicates files are being ignored during transfer
  • invalid - Indicates the mapping is invalid (missing source/destination)

Difference Status Icons (shown in diff output):

  • diff_created - Shows newly created/added files
  • diff_updated - Shows updated/modified files
  • diff_removed - Shows removed/deleted files

Example Configuration

Here's a complete example showing all customizable icons using UTF-8 emojis (works without Nerd Fonts):

[icons]
# Mapping status icons
force = ""           # Force deletion enabled
only = "📋"            # Only specific files transferred
ignore = "🚫"          # Files ignored during transfer
invalid = ""         # Invalid mapping

# Diff status icons
diff_created = ""    # New files created
diff_updated = "📝"    # Files modified
diff_removed = "🗑️ "    # Files deleted

# Example sync mapping
[[sync.xdg_config]]
ignore = ["cache"]

Default Icons

If you don't specify custom icons, Dotsync uses Nerd Font icons by default. These icons will only display correctly if you're using a terminal with a patched Nerd Font installed.

Icon Default (Nerd Font) Nerd Font Code Purpose
force 󰁪 nf-md-lightning_bolt Force deletion enabled
only nf-md-filter Only mode active
ignore 󰈉 nf-md-cancel Ignoring files
invalid 󱏏 nf-md-alert_octagram Invalid mapping
diff_created nf-md-plus File created
diff_updated nf-md-pencil File updated
diff_removed nf-md-minus File removed

Note

The icons in the "Default (Nerd Font)" column may not be visible unless you're viewing this with a Nerd Font. You can find these icons at nerdfonts.com by searching for the Nerd Font Code.

Tip

You can set any icon to an empty string ("") to hide it completely, or use any UTF-8 character or emoji. The dotsync setup command generates a configuration file with some example custom icons to get you started.

Automatic Update Checks

Dotsync automatically checks for new versions once per day and notifies you if an update is available. This check is non-intrusive and will not interrupt your workflow.

To disable automatic update checks:

  • Set environment variable: export DOTSYNC_NO_UPDATE_CHECK=1

The check runs after your command completes and uses a cached timestamp to avoid excessive API calls. The cache is stored in ~/.cache/dotsync/last_version_check following the XDG Base Directory specification.

Pro Tips

  • Preview Before Applying: Always run commands without --apply first to preview changes:

    dotsync pull          # Preview changes
    dotsync diff          # Quick preview (alias)
    dotsync pull --apply  # Apply after reviewing
  • Check Configuration: Use the status command to inspect your configuration without executing any actions:

    dotsync status        # View config and mappings
  • Multiple Config Files: Use the -c flag to maintain separate configurations for different workflows:

    # Work dotfiles
    dotsync -c ~/work-dotfiles.toml push --apply
    
    # Personal dotfiles
    dotsync -c ~/.config/personal.toml pull --apply
    
    # Server configs
    dotsync --config ~/server.toml push --apply
  • Automation and Scripting: Use --yes flag to skip confirmation prompts:

    # In a script or CI/CD pipeline
    dotsync pull --apply --yes --quiet
    
    # Shorthand
    dotsync push -ayq
  • Using Environment Variables: Simplify your configuration with mirror environment variables:

    # Add to your ~/.zshrc or ~/.bashrc
    export DOTFILES_DIR="$HOME/dotfiles"
    export XDG_CONFIG_HOME_MIRROR="$DOTFILES_DIR/config"
    export HOME_MIRROR="$DOTFILES_DIR/home"
  • Backup Location: Pull operations automatically backup files to ~/.cache/dotsync/backups/ with timestamps. Only the 10 most recent backups are kept.

  • Using rbenv: To ensure the gem uses the correct Ruby version managed by rbenv:

    RBENV_VERSION=3.2.0 dotsync push
  • Global Installation: Install the gem using a globally available Ruby version to make the executable accessible anywhere:

    gem install dotsync
  • Check Version: Quickly check which version you're running:

    dotsync --version
  • Disable Update Checks: If you prefer not to see update notifications:

    export DOTSYNC_NO_UPDATE_CHECK=1
  • Quiet Mode: For use in scripts or when you only want to see errors:

    dotsync pull --apply --quiet

Common Use Cases

Here are practical examples using bidirectional sync for popular configuration files:

Syncing Neovim Configuration

[[sync.xdg_config]]
path = "nvim"
force = true
ignore = ["lazy-lock.json", ".luarc.json"]

Syncing Terminal Emulator (Alacritty)

[[sync.xdg_config]]
path = "alacritty"
only = ["alacritty.toml", "themes"]

Syncing Shell Configuration

[[sync.home]]
path = ".zshenv"

[[sync.xdg_config]]
path = "zsh"
ignore = [".zsh_sessions", ".zsh_history", ".zcompdump"]

Syncing Multiple Config Directories

[[sync.xdg_config]]
only = ["nvim", "alacritty", "git", "zsh", "tmux", "starship.toml"]
force = true

Per-Machine Configuration Files

Use include to share a base config across machines, with each machine adding only its delta:

dotfiles/xdg_config_home/dotsync/
  dotsync.base.toml            # shared mappings, hooks, watch paths
  dotsync.mbp_personal.toml    # include + personal-only mappings
  dotsync.mbp_work.toml        # include + work-only mappings
  dotsync.mac_mini.toml        # include + mac-mini-only mappings
# dotsync.mbp_personal.toml — slim overlay
include = "dotsync.base.toml"

[[sync.xdg_config]]
path = "claude"
only = ["settings.json", "instructions", "commands"]

[[sync.mappings]]
local  = "$XDG_CONFIG_HOME/dotsync.toml"
remote = "$XDG_CONFIG_HOME_MIRROR/dotsync/dotsync.mbp_personal.toml"

Each machine's overlay self-references so ds push keeps it in sync with the repo. Use -c to select a config:

dotsync -c ~/.config/dotsync/dotsync.macbook.toml push --apply

Troubleshooting

Icons Not Displaying Correctly

Problem: Icons appear as boxes, question marks, or strange characters.

Solution:

  • Install a Nerd Font and configure your terminal to use it
  • Or customize icons in ~/.config/dotsync.toml using UTF-8 emojis or regular characters:
    [icons]
    force = "!"
    only = "*"
    ignore = "x"
    invalid = "?"
    diff_created = "+"
    diff_updated = "~"
    diff_removed = "-"

Changes Not Being Applied

Problem: Running dotsync push or dotsync pull doesn't modify files.

Solution: Remember to use the --apply flag to apply changes. Without it, commands run in preview mode:

dotsync pull --apply
dotsync push --apply

You can also use the explicit --dry-run flag to make preview mode clear in scripts.

Permission Denied Errors

Problem: Getting permission errors when syncing files.

Solution:

  • Ensure you have write permissions for destination directories
  • Check file ownership: ls -la ~/.config
  • For system directories, you may need to adjust your mappings to use user-writable locations

Source or Destination Not Found

Problem: Error messages about missing source or destination paths.

Solution:

  • Verify environment variables are set correctly (e.g., echo $XDG_CONFIG_HOME)
  • Use absolute paths in your configuration if environment variables aren't available
  • Create destination directories before running pull: mkdir -p ~/.config

Restoring from Backups

Problem: Need to restore files after a pull operation.

Solution: Pull operations create automatic backups in ~/.cache/dotsync/backups/:

ls -la ~/.cache/dotsync/backups/
# Copy files from the timestamped backup directory
cp -r ~/.cache/dotsync/backups/YYYYMMDD_HHMMSS/* ~/.config/

Watch Command Not Detecting Changes

Problem: dotsync watch doesn't sync changes automatically.

Solution:

  • Verify your watch mappings are configured correctly in ~/.config/dotsync.toml
  • Ensure the source directories exist and are accessible
  • Try stopping and restarting the watch command

Confirmation Prompt Appearing

Problem: Being prompted to confirm changes when running in scripts or automation.

Solution: Use the --yes or -y flag to skip confirmation prompts:

dotsync push --apply --yes
dotsync pull -ay              # Shorthand

For completely silent operation in scripts:

dotsync push --apply --yes --quiet

Using Multiple Config Files

Problem: Need different dotfile configurations for work, personal, or different machines.

Solution: Use the --config or -c flag to specify custom config files:

dotsync -c ~/work-dotfiles.toml push
dotsync --config ~/.config/personal.toml pull

You can maintain separate config files for different environments and switch between them easily.

Config File Not Found

Problem: Error message about missing config file.

Solution: Create a config file using the setup command:

dotsync setup    # Creates ~/.config/dotsync.toml
dotsync init     # Alias for setup

Or specify a custom config file path:

dotsync -c ~/my-config.toml setup

Development

  • After checking out the repo, run bin/setup to install dependencies.
  • Then, run rake spec to run the tests.
  • You can also run bin/console for an interactive prompt that will allow you to experiment.
  • To install this gem onto your local machine, run bundle exec rake install.

Releasing a new version

Automated (recommended):

# 1. Update version in lib/dotsync/version.rb
# 2. Commit all changes
# 3. Run the full release workflow:
rake release:publish

This generates a CHANGELOG entry from commits, opens it for review, commits, creates an annotated tag (with markdown stripped to plain text), and pushes everything.

Individual tasks:

rake release:changelog        # Generate CHANGELOG entry from commits since last tag
rake release:tag              # Create annotated tag from CHANGELOG (uses Dotsync::VERSION)
rake release:tag[0.3.0]      # Create annotated tag for a specific version

Publishing the gem:

gem build dotsync.gemspec
gem push dotsync-X.Y.Z.gem

The release.yml GitHub Action automatically creates a GitHub Release when a version tag is pushed, extracting release notes from CHANGELOG.md.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/dsaenztagarro/dotsync. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the Dotsync project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.

About

No description, website, or topics provided.

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages