Skip to content

Latest commit

 

History

31 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dotkeep

dotkeep

Dotfiles, kept simple

A single plain-text manifest for syncing configuration with Git

Install · Init · Config · Backup · Restore

demo.webm

Install

Needs bash, git, rsync, tree, tput, sed, GNU coreutils, and gum

If gum is missing, dotkeep offers to install it: pacman -S gum when pacman exists, otherwise a GitHub release binary into ~/.local/bin

Important

This repo is the tool only.

Your backups live in a separate Git repo ( see Init )

git clone https://github.com/metaory/dotkeep
cd dotkeep

Layout: dotkeep is the control script (vars, flags, dispatch). Logic lives under lib/ (gum, ui, help, path, conf, size, git, tally, sync). Then put it on PATH:

# a dir you already have on PATH
ln -s "$(realpath dotkeep)" /your/path/dir/dotkeep

# or we handle it
sudo ln -s "$(realpath dotkeep)" /usr/local/bin/dotkeep

Commands

dotkeep <command> [--dry-run]

init [DIR]   create state repo (home/ root/ + .dotkeep.conf + git)
config       show .dotkeep.conf, offer to edit
check        validate + resolve paths
backup       copy live files into the state repo
restore      copy state repo files onto the live system
help

Init

# on a new empty directory
dotkeep init [DIR]

Creates your state repo:

state/
├── .dotkeep.conf
├── .gitignore
├── home/
└── root/
  • mkdir home/, root/
  • empty .dotkeep.conf
  • a default .gitignore
  • git init

Remote is optional. Add one yourself when you want push/pull:

cd ~/state
git remote add origin <url>

Tip

Or skip init: mkdir a dir and write .dotkeep.conf yourself

Next: cd into that dir, dotkeep config, then dotkeep backup

dotkeep init ~/state
cd ~/state
dotkeep config

Config

dotkeep config

Shows the full path to .dotkeep.conf and its contents

Then asks to open it with $EDITOR, else nvim, vim, vi

Note

Plain file. One path per line. # comments

Every entry must start with home/ or root/

Caution

No ~, no absolute / forms, no $VAR expansion

Prefer explicit files over whole dirs

Unreadable files are skipped The rest of the dir is copied

Backup may chmod u+r on unreadable files you own first

If both a dir and paths under it are listed the dir wins and children are dropped

Important

Live source symlinks are skipped on backup (leaf)

Restore may replace a live symlink with a regular copy from the repo

devices, fifos, and sockets are not synced

Nested .git and node_modules are stripped (content only, not repo metadata)

Directory sync respects the state dir .gitignore

Preview/check compare repo ↔ live FS content (rsync -c), not git status Notes look like +2 ~1 -0 (added / changed / deleted); dirty → diff; identical → same

Copy dotkeep.conf.sample or start from dotkeep init:

# shell / editor
home/.zshrc
home/.config/nvim
home/.config/tmux/tmux.conf

# git / terminal
home/.gitconfig
home/.config/starship.toml
home/.config/alacritty/alacritty.toml

# prefer files over whole dirs
home/.config/foo/config.toml
home/.config/foo/themes

# optional system paths
root/etc/hosts
home/.zshrc       is  $HOME/.zshrc
root/etc/hosts    is  /etc/hosts

Why

One file. .dotkeep.conf is the source of truth

One path per line, home/ or root/

Edit that file to add or drop a path

State tree. The backup dir looks like the live system:

home/.zshrc
home/.config/nvim
root/etc/hosts

Same names. Same nesting. Home and root in one list

Copies. backup and restore rsync both ways. Live paths stay regular files

Ask first. Both commands ask before writing (default no). Optional path pick via gum choose (default no = all ok). Restore parks live targets under /tmp/dotkeep.XXXXXX/ first. Missing parent dirs on the live side are created (mkdir -p). A bad path is skipped. The rest is copied

Git optional. The store is that file tree. After backup it asks to add, commit, and push (each opt-in, default no). Syncthing or a disk copy can hold the same tree

Compared with

bare git, yadm. Store is $HOME. Live files are the work tree. git add -A can commit SSH keys and caches. /etc does not fit

Stow, rcm, homeshick, dotbot. Store is the repo. Live path is a symlink. An editor that writes a tempfile and renames it over the path replaces the link. The repo keeps the old file

chezmoi, dotdrop. Store is the repo. Live path is a copy. Names are dot_zshrc and templates. Use them for per-host files or encrypted secrets

dotkeep. Store is home/ and root/ in a separate dir. Live path is a copy. Names match the disk

Limits

Platform. Linux, bash 5, git, rsync, gum, GNU coreutils. No Windows

Root. The tool does not call sudo. Restoring root/ needs write access to that path

Skip. Live source symlinks on backup. Files over 10 MiB (DOTKEEP_MAX, cap 100). Restore may replace a live symlink with a copy.

Preview. Content delta vs live FS (rsync -c), not git. Dirty → diff with +N ~N -N; identical → same

Out of scope. Templates, encryption, per-host source

Where

config / check / backup / restore run from your state dir

(the one with .dotkeep.conf). Not the tool install. Any path, any remote

Backup

dotkeep backup

Run from your state dir. Flow:

  1. Git prep (optional; skipped if no .git)
    • fetch origin if configured (fetch failure → continue local)
    • show status
    • if behind: ask pull --rebase (stash first if dirty). Default no
  2. Show manifest + preview (machine → state); content delta notes; identical paths dropped
  3. Optional pick a subset (no keeps all) via gum choose (multi-select: x toggles, enter confirms; all start selected)
  4. Ask before write. Default no → abort
  5. Copy each listed path into home/ / root/
  6. Prune ignored paths under home/ / root/ via git clean -X (if git)
  7. Rewrite README.md with a tree snapshot of the state repo
  8. Git ship (optional; only if .git and the tree is dirty)
    • ask git add -A (default no)
    • ask commit message (default snapshot YYYY-MM-DD HH:MM)
    • ask git push if origin exists (default no; warn and skip if no remote)
  9. Show short status + last 5 commits (if git)

Important

Pull, add, commit, and push are all opt-in. Default is no. Git itself is optional. No remote means no push prompt. A disk copy or Syncthing can hold the same tree.

Warning

Files over 10 MiB skipped and warned

DOTKEEP_MAX (MiB) default 10, max 100 above 100 capped and warned

GitHub rejects over 100 MiB on push committed blobs still fail (history is scanned and warned)

After yes on ship:

git add -A
git commit -m "snapshot 2026-09-16 15:40"
git push   # only if origin exists and you say yes

Restore

dotkeep restore

Run from your state dir. Flow:

  1. Same git prep as backup (optional; fetch + ask pull if behind)
  2. Show manifest + preview (state → machine); content delta notes; identical paths dropped; live symlinks may be replaced with copies
  3. Optional pick a subset (no keeps all) via gum choose (multi-select: x toggles, enter confirms; all start selected)
  4. Ask before write. Default no → abort
  5. Copy existing live targets to /tmp/dotkeep.XXXXXX/ first
  6. Prune ignored paths under state home/ / root/ (if git)
  7. Copy each listed path onto the live system (rsync --delete under those paths; parents created with mkdir -p)
  8. Print the safety bak path when anything was saved

No commit or push on restore.

New machine:

git clone <url> <state-dir>
cd <state-dir>
dotkeep restore

Existing clone:

cd <state-dir>
dotkeep restore

Note

Pull is opt-in when origin is ahead. Default is no. Dirty trees are stashed before pull, then popped. --delete means extras under a listed dir on the live side are removed. Prior live copies stay under /tmp/dotkeep.XXXXXX/ until you clear them.

Env

NO_COLOR disables color

EDITOR used by config, else nvim vim vi

DOTKEEP_MAX skip limit in MiB, default 10, max 100 above 100 capped and warned. GitHub rejects the push

DOTKEEP_DRY=1 same as --dry-run (rsync dry-run; no mkdir / git writes)

Flags come after the command:

dotkeep backup --dry-run
dotkeep restore --dry-run

Prompts use gum (confirm, input, choose, log, style, table, pager)

License

MIT

About

A single plain-text manifest for your configuration

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

26 stars

Watchers

0 watching

Forks

Used by

Contributors

Languages