文章

Dotfiles 管理-使用 git 裸仓库

传统管理

dotfiles 代指 linux/mac 中各种配置文件。我们希望有一个方法可以集中管理它们,这样切换不同机器时就能快速恢复熟悉的工作环境。目前比较流行的做法是 使用 git 来管理,借助 orphan branch 还可以给不同系统/机器 建立不同的分支,分开管理。使用 git 管理需要三个步骤:

  1. 建立一个仓库。
  2. 把所有 dotfiles 移动到仓库里。
  3. 在原来的地方建立一个软链接。

所以... 一两个文件其实还好,但没人希望给一堆文件手动建立软链接吧。

那么有一个新方案:利用 git 裸仓库。它可以在 原地 跟踪 dotfiles,无需移动,自然也无需软链接。此方案来源:Git Bare Repository - A Better Way To Manage Dotfiles

裸仓库?

我们知道 git init 可以建立一个 git 仓库。之后,这个目录就成了「工作区」,可以存放并跟踪任意文件。同时,git 会建立一个 .git 文件夹,用于存放 git 所需要的数据。不难看出,一个普通的 git 仓库分为两部分:git 数据与工作区。

所谓裸仓库,就是只包含 git 数据而没有工作区,使用 git init --bare 命令来创建。裸仓库下的文件就是普通仓库下 .git 文件夹里的那些东西。等等,没有工作区?!

是的,没有工作区,这意味着这个仓库无法像普通的那样检出、修改、提交。那有什么用?用来储存呀!比如 github 应该就是在服务器上存了裸仓库——它只需要保存 git 数据就行了。

这里要澄清一个事实:git 数据包含了已跟踪的文件的状态以及修改记录。也就是说 commit 过的文件存储在 git 数据里,而不是工作区里。工作区只是一份检出而已。

没有工作区不代表不能执行常规的 git 操作,只不过我们需要通过选项 --work-tree 来指定一下工作目录。这也是无需移动 dotfiles 的关键所在。

创建

执行:

git init --bare $HOME/.dotfiles
alias config="/usr/bin/git --git-dir $HOME/.dotfiles --work-tree=$HOME"

首先创建了一个裸仓库,然后指定了一个别名 config 方便后续操作。它执行了 git 主程序,指定仓库位置以及工作目录。这样一来 config 的用法就与 git 一样了,可以便捷地添加文件。

此时如果执行 config status 会发现列出了一大堆 untracked files,因为 home 下所有文件都还没加入 git 跟踪。但我们本来就不是要跟踪所有文件,这样全部列出来看着很乱。因此执行一下:

config config --local status.showUntrackedFiles no

第一个 config 是我们创建的别名,第二个 config 是 git 的命令。这样就不会显示没有跟踪的文件了。

至此我们完成了仓库的创建,可以像普通 git 那样来直接管理所有文件,例如:

config add .zshrc
config commit -m "add .zshrc"
config push
...

恢复

面对一台新电脑...

首先克隆裸仓库:

git clone --bare <git_url> $HOME/.dotfiles

和创建时一样,新建一个别名方便操作,然后屏蔽未跟踪的文件。

alias config="/usr/bin/git --git-dir $HOME/.dotfiles --work-tree=$HOME"
config config --local status.showUntrackedFiles no

然后 checkout 一下,就可以恢复所有文件了:

config checkout

不过,如果某些文件已经存在了,则会报错:

error: The following untracked working tree files would be overwritten by checkout:
	.zshrc
Please move or remove them before you can switch branches.
Aborting

这时候只好备份后覆盖,然后手动合并咯。