跳转至

chezmoi: 跨机器 Dotfiles 管理

为什么要学 chezmoi

每个开发者都有一堆配置文件(dotfiles):.bashrc.gitconfig.vimrc.config/ 下的各种配置……当你换了一台新电脑,或者需要在多台机器上保持一致的开发环境时,管理这些配置就变成了痛苦。

传统方案(如 GNU Stow、裸 Git 仓库)虽然能用,但在以下场景中力不从心:

痛点传统方案chezmoi
不同机器需要不同配置手动维护分支模板引擎 + 条件判断
配置中包含密钥/Token不安全或需要额外加密集成 1Password/Bitwarden/age
安装到新机器需要多步手动操作chezmoi init --apply 一行完成
查看变更差异git diff (可能遗漏)chezmoi diff 精确显示
批量更新手动同步chezmoi update

chezmoi(发音 /ʃeɪ mwa/,法语"我家"的意思)用 Go 编写,是目前最成熟的 dotfiles 管理工具。


核心概念

白话解释

chezmoi 的工作方式分两层:

  1. 源状态(Source State):存储在 ~/.local/share/chezmoi/ 中的 Git 仓库,是你配置文件的"真相来源"
  2. 目标状态(Target State):你家目录中的实际配置文件

chezmoi 负责把源状态同步到目标状态。中间可以经过模板渲染、密钥解密等处理。

源仓库 (~/.local/share/chezmoi/)          目标 (~/)
├── dot_bashrc                    →       .bashrc
├── dot_gitconfig.tmpl            →       .gitconfig (模板渲染后)
├── dot_config/                   →       .config/
│   └── fish/config.fish          →       .config/fish/config.fish
└── private_dot_ssh/              →       .ssh/ (权限 0700)
    └── private_config            →       .ssh/config (权限 0600)

核心概念表

概念说明
Source Directory源仓库目录 (~/.local/share/chezmoi/)
Target Directory目标目录(通常是 ~/
文件名前缀控制文件属性的命名约定
.tmpl 后缀标记文件为 Go 模板
.chezmoi.tomlchezmoi 配置文件(在 ~/.config/chezmoi/
chezmoiignore忽略规则文件
External从外部 URL 下载的资源

文件名前缀约定

前缀效果示例
dot_目标文件名以 . 开头dot_bashrc.bashrc
private_文件权限设为 0600private_dot_ssh/private_config
readonly_只读权限readonly_config
exact_目标目录精确匹配(删除多余文件)exact_dot_config/
executable_可执行权限executable_script.sh
encrypted_加密文件encrypted_private_key
modify_修改脚本而非覆盖modify_dot_bashrc
run_运行脚本run_once_install.sh
run_once_只运行一次的脚本run_once_setup.sh
run_onchange_内容变化时运行run_onchange_reload.sh

安装配置

安装方式

macOS

brew install chezmoi

Linux

# 通用安装脚本
sh -c "$(curl -fsLS get.chezmoi.io)"

# Arch Linux
pacman -S chezmoi

# Ubuntu/Debian
sudo snap install chezmoi --classic

Windows

# Chocolatey
choco install chezmoi

# Scoop
scoop install chezmoi

初始化

# 全新初始化
chezmoi init

# 从已有的 GitHub 仓库初始化
chezmoi init https://github.com/your-username/dotfiles.git

# 初始化并立即应用
chezmoi init --apply https://github.com/your-username/dotfiles.git

配置 chezmoi

# ~/.config/chezmoi/chezmoi.toml

[data]
    name = "你的名字"
    email = "you@example.com"
    work = false

[git]
    autoCommit = true
    autoPush = false

[edit]
    command = "code"
    args = ["--wait"]

[merge]
    command = "code"
    args = ["--wait", "--merge"]

快速上手

添加第一个配置文件

# 添加现有的配置文件到 chezmoi
chezmoi add ~/.bashrc
chezmoi add ~/.gitconfig
chezmoi add ~/.config/fish/config.fish
chezmoi add ~/.ssh/config

# 添加整个目录
chezmoi add ~/.config/helix

# 查看 chezmoi 管理的文件列表
chezmoi managed

编辑配置

# 编辑 chezmoi 管理的文件(在源仓库中编辑)
chezmoi edit ~/.bashrc

# 编辑后查看差异
chezmoi diff

# 应用变更
chezmoi apply

# 编辑后直接应用
chezmoi edit --apply ~/.bashrc

查看和同步

# 查看源状态和目标状态的差异
chezmoi diff

# 应用所有变更
chezmoi apply

# 应用前预览(dry-run)
chezmoi apply --dry-run --verbose

# 进入源仓库目录
chezmoi cd
# 在这里可以 git add, commit, push 等

用 Git 管理

# 进入源仓库
chezmoi cd

# Git 操作
git add .
git commit -m "Add initial dotfiles"
git remote add origin https://github.com/you/dotfiles.git
git push -u origin main

# 退出源仓库
exit

# 或者使用 chezmoi git 子命令
chezmoi git add .
chezmoi git commit -- -m "Update configs"
chezmoi git push

在新机器上恢复

# 一行命令搞定
chezmoi init --apply https://github.com/you/dotfiles.git

# 等价于
# chezmoi init https://github.com/you/dotfiles.git
# chezmoi apply

进阶用法

模板系统

chezmoi 使用 Go 的 text/template 模板语言,可以根据机器差异生成不同配置:

# 添加文件为模板
chezmoi add --template ~/.gitconfig
# dot_gitconfig.tmpl
[user]
    name = {{ .name }}
    email = {{ .email }}

{{ if .work -}}
[url "git@github.com:company/"]
    insteadOf = https://github.com/company/
{{ end -}}

[core]
{{ if eq .chezmoi.os "darwin" -}}
    editor = code --wait
{{ else -}}
    editor = vim
{{ end -}}

模板中可用的变量:

变量说明
.chezmoi.os操作系统 (darwin/linux/windows)
.chezmoi.archCPU 架构 (amd64/arm64)
.chezmoi.hostname主机名
.chezmoi.username用户名
.chezmoi.homeDir家目录路径
.name, .emailchezmoi.toml[data] 定义的自定义数据

条件化包含文件

# .chezmoiignore
# 根据操作系统忽略不需要的文件

{{ if ne .chezmoi.os "darwin" }}
dot_config/karabiner/    # macOS 专用
Library/                  # macOS 专用
{{ end }}

{{ if ne .chezmoi.os "linux" }}
dot_config/i3/           # Linux 专用
dot_xinitrc              # Linux 专用
{{ end }}

密钥管理

使用 age 加密

# 生成 age 密钥
age-keygen -o ~/.config/chezmoi/key.txt

# 配置 chezmoi 使用 age
# ~/.config/chezmoi/chezmoi.toml
[age]
    identity = "~/.config/chezmoi/key.txt"
    recipient = "age1..."
# 添加加密文件
chezmoi add --encrypt ~/.ssh/id_ed25519
chezmoi add --encrypt ~/.config/gh/hosts.yml

# 文件在源仓库中是加密的
cat $(chezmoi source-path ~/.ssh/id_ed25519)
# → 加密内容,无法直接读取

使用 1Password

# chezmoi.toml
[onepassword]
    command = "op"
# dot_gitconfig.tmpl
[user]
    name = {{ .name }}
    email = {{ .email }}
    signingkey = {{ onepasswordRead "op://Personal/GPG Key/public key" }}

使用 Bitwarden

# dot_env.tmpl
DATABASE_URL={{ (bitwarden "item" "database-credentials").login.password }}

运行脚本

# run_once_install-packages.sh.tmpl
#!/bin/bash

{{ if eq .chezmoi.os "darwin" -}}
brew install fish helix ripgrep fd bat
{{ else if eq .chezmoi.os "linux" -}}
sudo apt install -y fish ripgrep fd-find bat
{{ end -}}
# run_onchange_fish-plugins.sh.tmpl
#!/bin/bash

# 当这个文件内容变化时重新运行
# 下面的 hash 注释确保内容变化时触发
# hash: {{ include "dot_config/fish/fish_plugins" | sha256sum }}

fisher update

External 下载

从外部 URL 自动下载资源:

# ~/.local/share/chezmoi/.chezmoiexternal.toml

# 下载 Oh My Zsh
[".oh-my-zsh"]
    type = "archive"
    url = "https://github.com/ohmyzsh/ohmyzsh/archive/master.tar.gz"
    exact = true
    stripComponents = 1
    refreshPeriod = "168h"  # 每周刷新

# 下载单个文件
[".local/bin/delta"]
    type = "file"
    url = "https://github.com/dandavison/delta/releases/latest/download/delta-x86_64-unknown-linux-gnu.tar.gz"
    executable = true

# 下载 Git 仓库
[".tmux/plugins/tpm"]
    type = "git-repo"
    url = "https://github.com/tmux-plugins/tpm.git"
    refreshPeriod = "168h"

交互式配置(首次安装提示)

# .chezmoi.toml.tmpl(注意是模板文件)
[data]
    name = {{ promptString "Your full name" | quote }}
    email = {{ promptString "Your email" | quote }}
    work = {{ promptBool "Is this a work machine" }}

{{ if .work -}}
    company = {{ promptString "Company name" | quote }}
{{ end -}}

首次 chezmoi init 时,会交互式询问这些值。


常见问题

Q1: chezmoi 和 GNU Stow 比有什么优势?

功能GNU Stowchezmoi
模板Go 模板
密钥管理age/1Password/Bitwarden
跨平台差异手动管理分支模板条件判断
安装脚本手动run_once_ 脚本
diff 预览需要手动比较chezmoi diff

Q2: 可以管理 root 用户的配置吗?

chezmoi 默认管理当前用户的家目录。root 配置需要单独的 chezmoi 实例,或使用 run_ 脚本复制文件。

Q3: 怎么处理已经存在的配置文件?

# chezmoi add 会将现有文件复制到源仓库
chezmoi add ~/.bashrc

# 如果源和目标有冲突
chezmoi merge ~/.bashrc
# 使用配置的 merge 工具解决冲突

Q4: 如何排除某些文件不被管理?

# .chezmoiignore
README.md
LICENSE
*.bak
.DS_Store

Q5: 自动提交和推送怎么设置?

# chezmoi.toml
[git]
    autoCommit = true  # 每次 chezmoi add/edit 后自动 commit
    autoPush = true    # 自动 push(谨慎使用)

Q6: chezmoi 的学习曲线如何?

基础用法(add/edit/apply/diff)5 分钟可以学会。模板和密钥管理需要额外学习,但可以渐进式采用——先简单管理文件,需要时再加模板。


参考资源

资源链接
官方网站https://www.chezmoi.io
GitHub 仓库https://github.com/twpayne/chezmoi
快速入门https://www.chezmoi.io/quick-start/
用户指南https://www.chezmoi.io/user-guide/command-overview/
模板参考https://www.chezmoi.io/user-guide/templating/
常见 dotfiles 示例https://www.chezmoi.io/links/dotfile-repos/

总结:chezmoi 是 dotfiles 管理的"终极方案"。简单场景下它和 Stow/裸 Git 一样简单,但在多机器差异化配置、密钥安全管理、自动化安装这些进阶场景下,它的模板系统和密钥集成是其他方案无法匹敌的。如果你有两台以上的机器需要保持配置同步,chezmoi 值得投入学习时间。