Dotfiles 统一管理:symlink + Git 方案(2026 年 3 月记录)
历史说明(2026-08-01):正文写于 2026 年 3 月,记录的是当时的 symlink + Git 方案。同年 8 月这套方案迁到 chezmoi(源
github.com:Zhou-Ruichen/dotfiles.git,跨 5 台机器同步),SSH config 拆成 chezmoi 共享层与本机config.local,WezTerm/Zed 配置随后也纳入同步;设备命名按多设备命名统一全部改过,正文里的air/macmini/tower/thinkpad等都是当时的名字。下文按 2026 年 3 月的状态保留。
2026 年 3 月做这件事时,手上有 11 台设备,全在同一个 Tailscale 网络里,通过 hostname 互相访问。实际纳入 dotfiles 仓库统一管理的是其中 7 台开发设备,覆盖 .zshrc、.gitconfig、starship.toml、SSH config、wezterm.lua 和 VS Code 的 settings.json;两台 OpenWrt 路由器与两部手机单独处理。
当时的设备清单
- air: MacBook Air,主力便携开发机,macOS,在德国宿舍和学校之间通勤
- macmini: Mac mini,家里的常驻 macOS 设备,偶尔远程上去处理事情
- tower: 组装台式机,Windows + WSL,重度使用,日常写代码和跑实验
- thinkbook: ThinkBook 笔记本,Windows,放在德国宿舍
- thinkpad: ThinkPad,装了 Linux Mint,中国那边的主要开发工作站
- server: 家里的服务器,Linux,跑各种自托管服务
- vps: 云服务器,Linux,主要跑 DERP 节点和一些对外服务
- router-de: 德国宿舍的 OpenWrt 路由器
- router-sd: 家里的 OpenWrt 路由器
- phone-1, phone-2: 两部手机,主要就是 Tailscale 客户端
真正纳入仓库的是前 7 台。两个路由器的配置结构和常规 Linux 差异较大,/etc/config/ 下面那些东西放进 dotfiles 仓库没什么意义;手机也不适合放进同一套配置同步流程里。
仓库怎么组织
仓库按工具分目录,让每类配置文件有固定路径,再通过 symlink 映射到系统期望的位置。纳入管理的文件:
.zshrc(shell 配置).gitconfig(Git 全局配置)starship.toml(终端 prompt)- SSH config
- WezTerm 配置(
wezterm.lua) - VS Code 的
settings.json
目录结构:
dotfiles/
├── shell/
│ └── .zshrc
├── git/
│ └── .gitconfig
├── ssh/
│ └── config
├── wezterm/
│ └── wezterm.lua
├── vscode/
│ └── settings.json
├── starship/
│ └── starship.toml
└── install.sh
macOS 和 Linux 上直接 ln -s:
ln -sf ~/dotfiles/shell/.zshrc ~/.zshrc
ln -sf ~/dotfiles/git/.gitconfig ~/.gitconfig
ln -sf ~/dotfiles/starship/starship.toml ~/.config/starship.toml
Windows 上用 PowerShell:
New-Item -ItemType SymbolicLink -Path "$env:USERPROFILE\.gitconfig" -Target "D:\dotfiles\git\.gitconfig"
这些命令最后汇总成 install.sh,clone 完仓库跑一遍就把所有 symlink 建好。新机器到手,git clone 加一个 ./install.sh,基本就能用。
SSH Config 大整理
SSH config 这部分也顺手整理了一遍,目标是统一命名和统一入口。旧配置里的 Host 是随手起的别名,跨设备迁移时很难记住每个 Host 对应哪台机器、哪个端口。当时统一用 Tailscale hostname 作为 SSH Host 名:
Host thinkpad
HostName thinkpad
User ruichen
Port 55905
Host server
HostName server
User ruichen
Port 22
Host vps
HostName vps
User root
Port 22
名字就是机器名,不用额外记忆,也不容易混淆。
比较麻烦的是多通道。同一台机器有好几种连法:Tailscale 直连最优先,ZeroTier 备用,某些场景还要 FRP 公网穿透。用不同的 Host 条目区分:
Host thinkpad
HostName thinkpad
ProxyCommand nc -X 5 -x router-de:1080 %h %p
Host thinkpad-frp
HostName my-frp-server.example.com
Port 6001
User ruichen
从外部网络连回家时,流量路径是:笔记本 → Tailscale SOCKS5(路由器上的)→ 目标设备。ProxyCommand 把这条链路封装起来,日常只管敲 ssh thinkpad。这套写法在 8 月迁到 chezmoi、命名统一之后整体重写过,上面的例子是 3 月的版本。
各种 App 的配置同步
配置文件不只是 shell 和 SSH,还有一批应用层面的内容。
Rime 输入法的词库同步单独跑了一套方案:在 installation.yaml 里配 sync_dir 指向 OneDrive 路径,macOS 的鼠须管和 Windows 的小狼毫各自同步到同一个目录下,每台机器用自己的 installation_id 做子目录,不会互相覆盖。这套方案当时已经跑了几个月,一直没问题。
VS Code 是个特殊情况。我同时用稳定版和 Insiders 两套,各自有独立的 Settings Sync:稳定版主要做远程开发,Insiders 做本地和 LaTeX。两者的 settings 大部分相同但有差异——远程版关掉了 git.autorefresh,Insiders 多了 LaTeX Workshop 的配置——所以不能合成一套。Settings Sync 管了大部分,有些 keybindings 还是靠手动复制。
WezTerm 的配置是 Lua,天然适合 symlink,把仓库里的 wezterm/wezterm.lua 链接到 WezTerm 期望的路径就行,所有用 WezTerm 的设备看到的终端配置是同一份。Starship 同理,starship.toml 统一管理,symlink 到 ~/.config/starship.toml,在哪台机器打开终端,prompt 样式都一样。
到三月中旬的状态
写这篇时,前 7 台里大部分已经用上 dotfiles 仓库,新机器的首配流程变成:装好系统 → 装 Git → git clone → ./install.sh → 基本可用。
当时还剩三个没解决的问题。Windows 上建 symlink 需要管理员权限,每次都要“以管理员身份运行 PowerShell”才能执行 New-Item -ItemType SymbolicLink,虽然可以开启开发者模式绕过,但我总觉得不够干净。路由器配置没法纳入同一个仓库,OpenWrt 的文件系统结构和常规 Linux 差太多,这部分仍然手动管理,最多做个备份。VS Code 的 extensions 列表也还在手动管——code --list-extensions 导出加 code --install-extension 批量安装的流程一直没脚本化,每次新机器还是手动装插件。