No description
Find a file
greembow 2116ef3226 Replace em/en dashes with ASCII hyphens throughout source
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 10:14:08 -07:00
cfg v1.0 2026-05-24 14:21:55 -07:00
src Replace em/en dashes with ASCII hyphens throughout source 2026-06-22 10:14:08 -07:00
.gitignore Complete core feature set and overhaul template engine 2026-06-17 21:41:30 -07:00
Cargo.lock v1.0 2026-05-24 14:21:55 -07:00
Cargo.toml v1.0 2026-05-24 14:21:55 -07:00
README.md Add full git wrapper: 21 subcommands + generic passthrough 2026-06-22 10:12:56 -07:00

dotdotdot

A variable-based dotfile manager. Define your configs once as templates, deploy them to any machine by setting a handful of key/value pairs.


How it works

Your dotfiles live in a git repository. dotdotdot clones that repo, processes each file through a template engine, and writes the results to your home directory. Variables and conditional blocks let a single set of templates produce the right output on every machine.

The typical workflow:

  1. Put your key/value pairs in ~/.config/dotdotdot/config.toml (font_size, theme, laptop, etc.)
  2. Run dotdotdot apply.

Profiles are an optional extra for switching between desktop environments or window managers on the same machine (hyprland, plasma, gnome…). They are not required for basic use.

Installation

cargo install --path .

Quick start

# First run - launches the interactive setup wizard
dotdotdot

# Or set up directly
dotdotdot init --address https://github.com/you/dotfiles

# Deploy your dotfiles (uses config [variables])
dotdotdot apply

# Override a specific variable for this run only
dotdotdot apply --override font_size=14

# Switch to your plasma profile
dotdotdot apply --profile plasma

Config file

~/.config/dotdotdot/config.toml - created by dotdotdot init.

[repository]
path    = "/home/you/.dotdotdot"
address = "https://github.com/you/dotfiles"

[general]
# Optional: default profile to use when none is passed on the CLI
# profile = "hyprland"

[variables]
# Your machine-specific key/value pairs.
# Strings need quotes; booleans and integers do not.
font_family    = "JetBrains Mono"
font_size      = 13
theme          = "catppuccin-mocha"
laptop         = false
natural_scroll = false

Template syntax

Marker lines use a leading comment character followed by .... The same syntax works for any file type. dotdotdot looks for ... and ignores whatever comment character precedes it:

# ... if <expr>      ← bash, zsh, toml, yaml, python …
// ... if <expr>     ← jsonc, JS …
-- ... if <expr>     ← lua, sql …

Conditional blocks

export EDITOR=nvim

# ... if laptop
export HIDPI=1
# ... else
export HIDPI=0
# ... fi

Blocks nest to any depth.

Line output

-- ... line TEMPLATE emits TEMPLATE as a content line with variable substitution applied. The entire marker is a comment, meaning no dummy values, and nothing for formatters to touch:

-- ... line natural_scroll = {{natural_scroll}},
-- ... line output = "{{monitor_output}}",

Use this for bare values (booleans, numbers) where putting {{varname}} directly in the code would be reformatted by a language formatter. Line markers respect conditional blocks:

-- ... if laptop
-- ... line 		natural_scroll = {{natural_scroll}},
-- ... fi

End-of-line annotation

content <comment_token> ... varname replaces the value on the line with the named variable. Quote style of the original value is preserved. The source value is what linters and formatters see, making it useful when you want a meaningful default:

natural_scroll = false,   -- ... natural_scroll
output = "DP-1",          -- ... monitor_output
scale = 1,                -- ... monitor_scale

If the variable is unset, the line is output as-is (the source value acts as the fallback).

Inline substitution

{{varname}} for user-defined variables, {{$varname}} for built-ins. Best used inside quoted strings where formatters won't interfere:

font_family = "{{font_family}}"
background  = "{{bg_color}}"
host        = "{{$hostname}}"

Spaces inside the braces are tolerated: {{ varname }} works identically.

Unknown variables are left as-is.

Condition expressions

Expression Meaning
varname true if the user variable is set and not empty, false, or 0
!varname logical negation of a user variable
$varname truthy check for a built-in variable
!$varname logical negation of a built-in variable
varname == "value" user variable equality
varname != "value" user variable inequality
$var == "value" built-in variable equality
$var != "value" built-in variable inequality

Built-in variables

Available in conditions as $name and in substitution as {{$name}}.

Name Value
hostname current machine hostname
os linux, macos, windows, etc.
arch x86_64, aarch64, etc.

Profiles

Profiles let you switch between different setups on the same machine. E.g. different window managers or desktop environments. They are not needed for managing per-machine differences; that's what [variables] in the config are for.

Create dotdotdot-profiles.toml in your dotfile repository:

[hyprland]
wm          = "hyprland"
bar         = "waybar"
compositor  = "wayland"

[plasma]
wm          = "kwin"
bar         = "plasma-panel"
compositor  = "wayland"

Profile variables override config [variables]. A default profile can be set in [general] so you don't have to pass it every time.

Variable priority

From lowest to highest:

  1. Built-ins ($hostname, $os, $arch)
  2. Config [variables]
  3. Profile variables (--profile)
  4. --override flags

Commands

dotdotdot-native

Command Description
dotdotdot Interactive setup wizard
dotdotdot init --address <url> Clone repo and write initial config
dotdotdot apply Process templates and write files to $HOME
dotdotdot add <file…> Copy files from $HOME into the repo and stage them
dotdotdot rm <file…> Remove files from the repo with git rm
dotdotdot list-profiles List profiles; --verbose shows each profile's variables
dotdotdot list-variables Show every variable used in templates with set/unset/built-in status

Sync shortcuts

Command Description
dotdotdot status Show sync state and git status
dotdotdot pull git pull
dotdotdot push git push
dotdotdot fetch [remote] git fetch

Git wrapper

All commands below run in the dotfile repository. Flags and extra arguments are passed directly to git, so every git option works as normal.

Command Description
dotdotdot commit -m "msg" Record staged changes
dotdotdot log [--oneline] [-n 10] [--graph --all] Browse commit history
dotdotdot diff [--staged] [HEAD~1] Inspect changes
dotdotdot stash [push | pop | list | apply | drop | clear | show | branch] Stash management
dotdotdot branch [-a] [-d <name>] List, create, or delete branches
dotdotdot switch <branch> [-c <new-branch>] Switch branches
dotdotdot checkout <branch|file> Checkout a branch or restore files
dotdotdot merge <branch> [--squash] [--abort] Merge branches
dotdotdot rebase <branch> [-i HEAD~3] [--abort] [--continue] Rebase
dotdotdot reset [--soft|--mixed|--hard] [HEAD~1] Reset HEAD
dotdotdot tag [v1.0] [-a v1.0 -m "msg"] [-d old-tag] Manage tags
dotdotdot remote [-v] [add origin <url>] [remove origin] Manage remotes
dotdotdot cherry-pick <commit> Apply a commit from another branch
dotdotdot revert <commit> [--no-commit] Create an undo commit
dotdotdot blame <file> Show who last changed each line
dotdotdot show [<object>] Inspect a commit, tag, tree, or blob
dotdotdot reflog [show|expire|delete] Reference log (recover lost commits)
dotdotdot clean [-n] [-f] [-fd] Remove untracked files (-n for dry run)
dotdotdot bisect <start|good|bad|reset> Binary search for a regression
dotdotdot git <any-git-command> [args…] Generic passthrough for anything above

Global flags

--verbose    Print resolved variable table and per-file detail
--quiet      Suppress all output
--config     Alternate config file path

apply flags

-p, --profile <name>       Use a named profile
-o, --override <k=v,...>   Override specific variables for this run

Repository layout

Files map directly to paths under $HOME. A file at .config/nvim/init.lua in the repo becomes ~/.config/nvim/init.lua after processing. Two files are never deployed:

  • dotdotdot-profiles.toml - profile definitions
  • .git/ - the git directory