My dotfiles don't bite!
For tools like zsh that support "inheritance", my dotfiles stay out of your way. Your machine's .zshrc stays in your control, and simply sources mine at the beginning. You're free to override any of my config within your dotfile, or change the "root" one (at dot_zshrc.root). The local config always wins.
# installs `mise` + `chezmoi`
curl -fsSL dotfiles.stanleyxu.me | sh
# works on any OS
sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply "stanley-xu"
# or via Homebrew
brew install chezmoi && chezmoi init --apply "stanley-xu"These are chezmoi files. Think of these as blueprints that the chezmoi CLI tool will use to generate your actual dotfiles (like .zshrc). These blueprints lets chezmoi tailor the dotfile to your specific machine, if you'd like. Every file in this repo is "watched" or "managed" by chezmoi.
It's still just an ordinary git repo, so feel free to fork and maintain your own. Tips for using chezmoi below.
Here are the scripts that install packages and keeps things up-to-date.
- Chezmoi scripts (
run_*.sh) — run duringchezmoi apply.run_once_runs once per machine;run_onchange_re-runs only when the script's content hash changes (e.g. editing the brew package list triggers reinstall). - Shell rc (
main.zsh→install-tools.zsh) — runs on every shell session. Clones git-sourced tools (fzf,zinit) if missing, then activates tools for the session (mise,zoxide).
Chezmoi uses templating which allows you to write your own config, per OS. For example, dot_zshrc.root.tmpl inlines dot_zshrc.darwin only on macOS. See chezmoi templates.
For tools like zsh and git, I've set it up so the shared (.root) config files are loaded into the regular dotfile in only one direction.
| |
Shared config --> Local config
| |
Tools opt into shared configuration and the real dotfile is kept out of chezmoi's control. This way, there are no conflicts every time you (or your tooling) make edits to your dotfiles. You own your dotfiles. Your work machine's config will not bleed into your other machines!
Local config always wins
# ~/.zshrc — created once, then yours to edit
source ~/.zshrc.root # tracked baseline
# anything below overrides it# ~/.gitconfig — created once, then yours to edit
[include]
path = ~/.gitconfig.root # tracked baseline
# anything below overrides itThe merge is additive: the .root baseline applies first, your local edits override only what they explicitly set, and everything else stays. The exact merge differs by tool but the result is the same.
Treat edits to the everyday ~/.zshrc / ~/.gitconfig as a per-machine working copy. The .root file is like git's main branch — when you want a change everywhere, "merge it back upstream" by moving it into the tracked .root.
On a fresh machine the stubs are written automatically; if you / tooling created the file first, you'll need to add the source/[include] line to its top.
When you adopt a new tool whose config tooling rewrites in place (and that can source another file), repeat the pattern by hand — it's two files per tool, no shared machinery:
-
dot_<x>.root— move the real config here (the tracked baseline). Add.tmplif it needs templating. -
create_dot_<x>— the once-written stub that pulls the baseline in first, using that tool's own include syntax:- zsh:
source ~/.zshrc.root - git:
[include]/path = ~/.gitconfig.root - tmux:
source-file ~/.tmux.conf.root - vim:
source ~/.vimrc.root
The
create_attribute means chezmoi writes it once on a fresh machine, then never touches it — so the tool/your edits below the include win. - zsh:
-
Migrate machines that already have the file (one-time, per machine) —
create_only fires when the file is absent, so existing machines need a manual nudge. Split the existing config however you like, line by line:- shared across all machines → move into
~/.<tool>.root(tracked, via chezmoi). - machine-specific, or unsure → leave it in the everyday dotfile (it overrides the baseline).
Then make sure the everyday file pulls in its
.rootat the top. You don't have to remember to do this:run_after_check-overrides.shruns (read-only) after everychezmoi applyand warns you, with these instructions, until the machine is migrated. Extend its short check list when you add a tool. - shared across all machines → move into
There's deliberately no generic script driving this — with only a couple of tools, two explicit files each is simpler and lower-risk than a table-and-loop engine. Revisit that trade-off if a third or fourth tool shows up.
Common flow:
chezmoi edit ~/.zshrc # edit a managed file
chezmoi diff # preview target-side changes
chezmoi apply # write to $HOME
git commit -am "..." # commit source (then `git push`)Getting around:
chezmoi cd— jump to the source dir (also symlinked at~/dotfiles)chezmoi source-path— absolute path to the source dir (default:~/.local/share/chezmoi)chezmoi managed— list files chezmoi trackschezmoi status— summary of whatapplywould change
Adding (chezmoi add <file>):
--template— add as a Go template (source file becomes*.tmpl)--follow— resolve symlinks to the real file
Editing source files:
chezmoi edit— open the source dir in$EDITORchezmoi edit <file>— open just that file's sourcechezmoi edit --apply <file>— apply changes after editor exitschezmoi edit --watch <file>— apply on every save
Syncing:
chezmoi update—git pullthis repo +apply- Edited the real file in
$HOMEby mistake?chezmoi add <file>to re-ingest, orchezmoi merge <file>to reconcile.
See chezmoi FAQ for more.
- CLI tools
- Oh my Posh for prompt customization
- Mise for dev tooling