dotpilot 0.12.0
Symlink-based dotfiles manager with auto-sync
Requires Python: >=3.12

# dotpilot

Symlink-based dotfiles manager. Your repo mirrors your home directory — no `dot_` prefixes, no templates, no source state. Edit a file, it's already applied.

```
uvx dotpilot
```

Requires [uv](https://docs.astral.sh/uv/getting-started/installation/). Install it with:

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```

## Why dotpilot?

Most dotfile managers copy files and require you to re-run a command after every edit. dotpilot **symlinks** — your dotfiles repo IS your home directory. Change a file in `~/dotfiles/.zshrc`, and `~/.zshrc` updates instantly because it's a symlink.

|  | dotpilot | chezmoi | GNU Stow | bare git |
|---|---|---|---|---|
| Edits apply instantly | Yes (symlinks) | No (must re-apply) | Yes (symlinks) | Yes |
| Repo structure | Mirrors ~/ | `dot_`, `private_`, `executable_` prefixes | Mirrors ~/ | Mirrors ~/ |
| New machine setup | `uvx dotpilot clone` | `chezmoi init` | Manual clone + stow | Manual clone |
| Dry-run by default | Yes | No | No | N/A |
| Tracks what you're NOT managing | `uvx dotpilot untracked` | No | No | No |
| Auto-sync from remote | Built-in cron | External | External | External |
| Auto-backup before apply | Yes | No | No | No |
| Templating | No | Yes (Go templates) | No | No |
| Secret management | No | Yes | No | No |
| Dependencies | Python (uv) | None (Go binary) | Perl | Git |

dotpilot trades templating and secrets for **simplicity**. If you need machine-specific config, use `.localrc` patterns or `.overwrite` files. If you need secrets, use a separate tool. Most people don't need either.

## How it works

Your dotfiles live in a git repo (default `~/dotfiles`). dotpilot creates symlinks from `~/` into that repo.

```
~/dotfiles/.zshrc  ←symlink←  ~/.zshrc
~/dotfiles/.config/nvim  ←symlink←  ~/.config/nvim
```

Special file conventions:

- **`.overwrite` suffix** — copied instead of symlinked, for files that don't work as symlinks (e.g. `.config/git/gitk.overwrite`)
- **`.delete` suffix** — removes the corresponding file from `~/` (e.g. `.vimrc.delete` removes `~/.vimrc`)
- **`.dotpilotignore`** — fnmatch patterns for files to skip (one per line, `#` comments). `.gitignore` patterns are also respected.

## Quick start

### Starting fresh

Set up a new dotfiles repo and start tracking your config:

```bash
git init ~/dotfiles && cd ~/dotfiles && git remote add origin git@github.com:you/dotfiles.git

uvx dotpilot track ~/.zshrc          # moves file into repo, symlinks back
uvx dotpilot track ~/.config/nvim    # works with directories too
uvx dotpilot untracked               # see what else you might want to track
```

### Cloning an existing repo

Already have dotfiles on another machine? Pull them down:

```bash
uvx dotpilot clone git@github.com:you/dotfiles.git
uvx dotpilot apply               # dry-run: shows what would change
uvx dotpilot apply --apply       # back up + symlink everything
```

### Day-to-day

It's just a git repo. Edit files, commit, push:

```bash
cd ~/dotfiles
git add -A && git commit -m "update zshrc"
git push
```

Pull changes on another machine:

```bash
uvx dotpilot sync --apply      # fetch + merge + re-apply symlinks
```

Or set up auto-sync so you never have to think about it:

```bash
uvx dotpilot cron --apply      # syncs every minute via cron
```

## Auto-sync

The `cron` command installs a job that runs every minute (configurable with `--schedule`):

```bash
uvx dotpilot cron               # dry-run: shows what cron entry would be added
uvx dotpilot cron --apply        # install the cron job
uvx dotpilot cron --apply --remove  # remove it
```

The cron job runs `uvx dotpilot@latest sync --apply --auto-apply`, which fetches from origin, fast-forward merges, and re-applies symlinks if anything changed.

If the working directory is dirty or the merge isn't a fast-forward, sync bails out — it won't clobber your local changes.

## Commands

All commands default to `~/dotfiles`. Override with `--dotfiles DIR`. Use `-h` on any command for help.

| Command | Description |
|---|---|
| `uvx dotpilot git <args>` | Run git commands in the dotfiles repo |
| `uvx dotpilot clone <repo>` | Clone a dotfiles repo and dry-run apply |
| `uvx dotpilot apply` | Symlink dotfiles into ~/ (dry-run by default, auto-backs up on `--apply`) |
| `uvx dotpilot sync` | Fetch and fast-forward merge from origin |
| `uvx dotpilot track <path>` | Start tracking a file — moves it to repo, symlinks back |
| `uvx dotpilot untrack <path>` | Stop tracking — removes symlink, moves file back |
| `uvx dotpilot untracked` | List dotfiles in ~/ and ~/.config you're not managing |
| `uvx dotpilot status` | Git sync status — fetches remote, shows dirty/ahead/behind |
| `uvx dotpilot backup` | Back up all managed dotfiles from ~/ into a timestamped directory |
| `uvx dotpilot restore <dir>` | Restore dotfiles from a backup directory |
| `uvx dotpilot doctor` | Check git, repo, SSH keys, upstream config |
| `uvx dotpilot completion` | Install zsh completions |
| `uvx dotpilot uninstall` | Remove symlinks, restore files, delete the dotfiles repo |
| `uvx dotpilot cron` | Set up auto-sync every 30 minutes |

Every mutating command is **dry-run by default**. Pass `--apply` to execute. Add `-v` to `apply` for verbose output.

## Backup and restore

`apply --apply` automatically creates a backup before making changes. Backups are stored in `~/.local/state/dotpilot/backups/`.

```bash
uvx dotpilot backup                      # manual backup
uvx dotpilot backup -o /tmp              # backup to custom location
uvx dotpilot restore ~/.local/state/dotpilot/backups/dotfiles-backup-* # dry-run
uvx dotpilot restore ~/.local/state/dotpilot/backups/dotfiles-backup-* --apply
```

Restore is symlink-aware — if symlinks are still in place, it writes through them into the repo. If symlinks are gone (e.g. after uninstall), it copies directly to `~/`.

## Uninstall

```bash
uvx dotpilot uninstall           # dry-run: shows symlinks, cron, unpushed warnings
uvx dotpilot uninstall --apply   # restore files, remove cron, delete repo
```

Warns about uncommitted changes and unpushed commits before deleting anything. Removes the cron job if one is installed.

## License

MIT