Dotfiles 统一管理:symlink + Git 方案(2026 年 3 月记录)

2026年3月15日| 更新于 2026年8月1日| Ruichen Zhou| 约 9 分钟阅读

历史说明(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.gitconfigstarship.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 批量安装的流程一直没脚本化,每次新机器还是手动装插件。

评论