使用 Git 管理 Linux 用户配置的新思路

2020年03月09日

在本系列的 上一篇文章 当中,我们已经把家目录的结构打理好了。接下来可以考虑使用强大的工具备份和同步配置。

本文介绍一种使用 git 的 bare 仓库 (裸仓库) 管理家目录的全新思路,以及我个人在该思路上延伸的方案。

GNU stow 管理用户配置文件的方案流行了很久,但我不喜欢它。stow 的理念就是在自定义的目录结构里集中化管理配置。

我所期待的方案要满足几个需求:

  • 对家目录的破坏最小

    stow 使用了软链接,这正是我所不希望看到的。

  • 轻松上手

    对我来说最熟悉的方式,最好无需引入新工具。

  • 工具没有过多的依赖

    关于这点上 stow 做得挺好。

stow?忘记它吧

因为 stow 的主要作用是聚合,通过在配置的原始路径上创建软链接,指向一个集中化的目录当中。所以通常 stow 会配合 git 来使用,stow 用于从庞大的家目录中把配置隔离出去,git 则用于备份归档。

无论如何怎么看,我都更希望于选择用 git 直接管理所有在家目录下的用户配置,而不是先用 stow 聚合组织文件。在上篇文章中已经介绍了有效组织家目录的方法,我不希望再借助额外的工具移动家目录下的文件。

git 是相当流行的工具,依赖较少。用 git 来管理 Linux 的配置文件是很稀松平常的想法。但是,就此打住真的可以了吗?有这么简单的话,这篇文章就没存在的必要了。因为真的尝试一番 git 管理家目录,很快便会捉襟见肘。

正确思路

使用 git 是最容易想到的方式,但是缺陷很明显,因为这样一来要在家目录下建立 git 仓库目录,家目录下的任何子目录都将被纳入 git 管理。无论我们切换到任何子目录,git 都忠实地如影随形。git 疯了,我也疯了。如果 zsh 和 vim 等软件集成了 git 插件之类的话,情况会更糟。想一想 zsh 在家目录下每次执行完命令,就要调用 git 插件生成新的提示符……性能堪忧。

好消息是,从 Hacker News 得到的好消息是,我们完全可以通过创建 git bare 仓库,然后分别指定 git 仓库目录和工作目录为不同的路径!

正如 StreakyCobra 所说的那样,可以使用以下方式:

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

如此一来,.myconf就是用来存放 git 的元数据和对象数据的目录了。.myconf$HOME做到了貌合神离。git 根本意识不到.myconf$HOME的 git 仓库目录,尽管看上去.myconf确实位于$HOME下。(当然你也可以让.myconf处于任何位置!)只有在我们使用config命令的时候,家目录才会被视为 git 工作目录,接下来的操作就和正常使用 git 一样普通。

Arch Wiki 上也总结了这套方法,以及作者们的总体方案,值得我们借鉴。

选项--git-dir--work-tree也可以用变量GIT_DIRGIT_WORK_TREE替代。使用变量的好处是 vim 这类集成 git 插件的软件也可以正常识别 git 目录了。

为了让 vim 的 git 插件可以正常使用,指定变量后启动 vim 就行了:

GIT_DIR=$HOME/.myconf GIT_WORK_TREE=$HOME vim

另一种思路(不推荐)

不生成 bare 仓库也是可以的,我们可以在创建普通仓库后,紧随着在 git 配置里面修改工作目录路径。

git init .myconf/
cd .myconf/
git config core.worktree ~

但是,一旦使用git status会发现文件路径都是相对路径,这对使用体验造成了负面影响。况且接下来我们要用到 bare 仓库的一些特性,比如说,允许下游推送。所以,使用普通仓库还是很勉强的,不推荐使用。

消除干扰(可选步骤)

虽然 StreakyCobra 提到了可以用config config status.showUntrackedFiles no设置默认不显示未追踪的文件,但我喜欢用config status清楚地看到哪些文件是新添加的配置文件。所以我并没有让未追踪的文件从状态信息中消失。

为了排除掉所有我没添加到 git 仓库,但实际上我并不关心的文件,可以在~/.myconf/info/exclude文件里添加它们。顶级目录里,所有的除 dot 文件(点文件)以外的文件都是可以排除的,而 dot 文件则是重点关注对象,所以要用到/*!/.*(斜杠/不能去掉,因为子目录里要追踪普通文件)。~/.myconf本身也要排除。剩下的无关的目录都统统排除:~/.cache~/.local……但是除了~/.config。在 上一篇文章 里,杂乱的文件已经被管理好了,但还是剩下一些顽固分子,只好也排除掉它们吧。

~/.myconf/info/exclude看上去大概是这样子的:

/*
!/.*
/.myconf/

# XDG
/.cache/
/.data/
/.local/

# dbus
/.dbus/
# nss
/.pki/
# ...

因为我选择不排除掉~/.config,但是其下的目录也非常多,很多都是不需要追踪的,怎么办呢?用最笨的办法,一个个去排除掉!这是项耗时耗力的任务,需要熟悉~/.config目录下的每一个子目录是干什么的。但好处也是显而易见的:因为想要找出所有需要管理的配置文件,就必须清楚地知道它们是什么,从而做到完美地整理配置。这就是我们苦苦追寻的目标呀!

实际上也花不了多少时间。就是只有少部分含混不清的目录,最好在 exclude 文件里注释一下。完成了之后任何新增软件的配置都无法逃脱 git 的管理,使用git status查看那些在主机上新安家的软件们都创建了什么文件和目录。真可谓一劳永逸。额外要提醒下的是,只要目录为空,git 就会无视,即使是多级子目录。

filter 处理敏感文件

如果考虑公开分享自己的配置,需要考虑过滤一些敏感信息。ID,邮箱和 gpg 公钥对我来说是可以公开的,需要额外注意有没有把密码等私密信息包含进去。Arch Wiki 上提到了通过 git 的filter功能可以用来替换密码等敏感信息。

~/.myconf/config

[filter "remove-secrets"]
clean = "sed -e 's/name = .*/name = NAME/'  -e 's/password = .*/password = PASSWORD/'"

~/.myconf/info/attributes

* filter=remove-secrets

当然这只是个简单的尝试,不保证安全和可靠性,最好是针对不同文件分别设置 filter。

而且,千万不要一次性把整个.config目录提交了!最好分批次确认文件内容后再提交。

多分支管理多平台的配置

原文提到了可以用分支管理不同平台主机下的配置。在~/.myconf上新建分支上想法固然好,但却美中不足。关键在于其他平台使用的配置会和当前操作的主机的配置不一致。直接使用~/.myconf仓库管理其它平台的配置需要改变 HEAD 指针,同时修改过程中导致工作区被污染,进而影响了当前正在使用的主机的配置。

当然可以用 stow 等工具隔离不同主机的配置,但这样又回到老路子上了。

我实践的结果是采用本地克隆仓库的方案,并且另外指定一个工作区。

git clone --bare .myconf <newdir>

这个新仓库代表着另一个平台的配置,所以我们可以在其中任意增删和修改文件,同时不影响家目录下的配置文件。

同时该仓库也是 bare 仓库,因为我需要它自身被克隆后,也能接受来自其它平台的推送。所以使用上也需要指定git-dirwork-tree。为此可以单独用额外的目录来管理,比如我为了管理 Android termux 的配置,我安排了~/Config(不同于~/.config),分别指定~/Config/termux.home/~/Config/termux.git,另外在~/Config/termux.sh里为 termux 指定命令:

alias config.termux='/usr/bin/git --git-dir=$HOME/Config/termux.git --work-tree=$HOME/Config/termux.home'

需要的时候zsh source就能使用config.termux管理我的 termux 配置了。

在新创建的本地克隆仓库中,存储的 git 数据对象使用了硬链接方式,意味着和~/.myconf共享相同文件,因此 git 目录不会占用很多磁盘空间;真正增加的文件在签出的工作目录中。所以不用过多担心占用了额外的存储空间。

使用 git clone 同步配置

克隆下来的仓库也采用 bare 仓库的方式(克隆时使用--bare选项),所以同样能分别指定工作目录和 git 仓库目录。

以 termux 为例,从本地局域网同步配置。需要上一节内容中提到的 bare 仓库。我已经在用$HOME/Config/termux.git管理了,所以我需要在本地主机上开启 ssh 服务,然后在 termux 上执行:

git clone --bare --single-branch --branch termux user@host:[path/to/git] ~/.myconf

就能在 termux 上恢复配置了

git --git-dir=$HOME/.myconf/ --work-tree=$HOME checkout

配置好了之后 termux 也能使用命令config

只要上游仓库是 bare 仓库(上节也提到了),双向同步是可以的。

多个远程备份

既然是 git 仓库,自然也可以推送到 GitHub 等等地方去。目前我同时推送到 GitHub 和我自行搭建的 Gogs 服务上面。

最后

这不一定是当前最好的方案,毕竟人的需求永远是要进步的,同时还要考虑到最适合自己的。至于我的目标是追求最清晰最简单的方案,所以我很喜欢 git 的方式。

欢迎前来围观我的 配置文件

知识共享许可协议

© 2020 rydesun

Linux 整理魔法:整理 Linux 家目录

开始加载评论