背景
日常开发中,Git 提交是高频操作。虽然 git commit -m "xxx" 足够简单,但团队协作中遵循 Conventional Commits 规范能显著提升提交信息的可读性和自动化能力(自动生成 CHANGELOG、语义化版本 bump 等)。
然而每次提交都要手动输入 feat:、fix: 等前缀,偶尔还会拼错。如果能有一个交互式工具,先显示当前变更概览,再通过菜单选择提交类型,最后自动拼装并推送,显然会更高效。
本项目基于 NixOS + home-manager 管理整个系统配置,Fish Shell 作为默认交互式 Shell,自然希望将这样的工具函数纳入 Nix 声明式管理,一次部署、全局生效。
设计思路
- 用 Fish Shell 函数实现:不引入外部依赖,利用 Fish 内置的
read、switch、git status --short 等能力 - Nix 化:通过 home-manager 的
programs.fish.functions 以声明式方式定义函数,随系统 rebuild 自动部署 - 交互流程:
- 先调用
git status --short 显示变更概览(新增/修改/删除的文件) - 列出 Conventional Commits 10 种类型及中文说明
- 用户选择序号,输入提交描述
- 自动执行
git add . && git commit -m "type: desc" && git push
完整代码
文件路径:modules/apps/git_fiash_fun.nix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
| {
pkgs,
username,
...
}:
{
home-manager = {
users.${username} = { ... }: {
programs.fish.functions = {
gcp = {
description = "交互式 Git 提交与推送(Conventional Commits)";
body = ''
set -l types \
"feat — 新功能,包含实质性逻辑的新文件" \
"fix — 修复 bug" \
"docs — 仅文档变更(README、注释、文档字符串)" \
"style — 格式调整、空白、缺少分号(无代码逻辑变化)" \
"refactor — 代码重构,非新增功能或修复 bug" \
"perf — 性能改进" \
"test — 添加或修改测试" \
"build — 构建系统、依赖、CI 配置" \
"chore — 维护、工具、配置文件、gitignore" \
"revert — 回退之前的提交"
echo ""
echo "========== 变更概览 =========="
git status --short
echo "=============================="
echo ""
echo "请选择提交类型 (Conventional Commits):"
echo ""
for i in (seq (count $types))
if test $i -lt 10
echo " $i) "$types[$i]
else
echo " $i) "$types[$i]
end
end
echo ""
read -l -P "输入序号 (1-10) [默认 1]: " type_idx
if test -z "$type_idx"
set type_idx 1
end
set -l prefix
switch $type_idx
case 1; set prefix "feat"
case 2; set prefix "fix"
case 3; set prefix "docs"
case 4; set prefix "style"
case 5; set prefix "refactor"
case 6; set prefix "perf"
case 7; set prefix "test"
case 8; set prefix "build"
case 9; set prefix "chore"
case 10; set prefix "revert"
case '*'
echo "无效输入,已取消。"
return 1
end
read -l -P "输入提交描述: " commit_msg
if test -z "$commit_msg"
echo "描述不能为空,已取消。"
return 1
end
echo ""
echo "即将执行: git add . && git commit -m \"$prefix: $commit_msg\" && git push"
echo ""
git add .
git commit -m "$prefix: $commit_msg"
and git push
or echo "提交或推送失败,请检查错误信息。"
'';
};
};
};
};
}
|
在模块调度中心注册
在 app_and_modules_list.nix 中按条件引入(此处对所有机器生效):
1
| ++ importIf IsAll ./modules/apps/git_fiash_fun.nix
|
因为该函数依赖 Fish Shell,而 Fish 已通过 IsAll 条件全局启用,所以此模块也在 IsAll 下引入。
关键细节
1. 如何让 Fish 函数被 Nix 管理
home-manager 提供 programs.fish.functions 属性集,每个 key 是一个函数名,value 包含 description 和 body。rebuild 后函数文件自动生成在 ~/.config/fish/functions/gcp.fish,Fish 启动时自动 source。
2. 为什么不用 fzf
最初考虑过用 fzf 做模糊搜索选择器,体验更流畅。但为了保持零额外依赖、纯 Fish 内置能力实现,选择了编号菜单——这在 Fish 的任何安装环境下都能工作,无需额外安装 fzf。
3. 为何是 git add . 而非选择性暂存
gcp 的设计场景是"我想把当前所有变更一次性提交并推送",类似一个快捷发布命令。如果需要精细控制暂存哪些文件,应使用 lazygit、gitui 或原生 git add -p。
4. 提交信息语言
函数本身不限制提交描述语言——交互提示是中文,但实际 commit message 内容由用户自由输入。建议团队项目中保持英文描述以保持一致性。
5. Nix 特性:字符串路径 vs Nix path literal
注意 body = ''...'' 使用的是 Nix 多行字符串。函数体作为字符串嵌入 Nix 表达式中,最终由 home-manager 写入 Fish 函数文件。这是 Nix 声明式管理配置文件的典型模式——配置内容以字符串形式存在于 Nix 模块中,而非独立的配置文件。
使用方式
在任意 Git 仓库中 直接运行 gcp ,输出示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| ========== 变更概览 ==========
M modules/apps/git_fiash_fun.nix
?? modules/apps/git_fiash_fun.nix
==============================
请选择提交类型 (Conventional Commits):
1) feat — 新功能,包含实质性逻辑的新文件
2) fix — 修复 bug
3) docs — 仅文档变更(README、注释、文档字符串)
4) style — 格式调整、空白、缺少分号(无代码逻辑变化)
5) refactor — 代码重构,非新增功能或修复 bug
6) perf — 性能改进
7) test — 添加或修改测试
8) build — 构建系统、依赖、CI 配置
9) chore — 维护、工具、配置文件、gitignore
10) revert — 回退之前的提交
输入序号 (1-10) [默认 1]: 9
输入提交描述: 整理配置
|
最终执行:git commit -m "chore: 整理配置" && git push
总结
这个 gcp 函数虽然只有几十行,但体现了 NixOS 配置管理的一个核心理念:一切皆声明。工具函数不再是散落在各台机器 ~/.config/fish/config.fish 中的手写片段,而是作为 Nix 模块的一部分,版本化、可复用、按条件加载。
配合 Nix Flakes 和 home-manager,新增一个函数 = 写一个 .nix 文件 → 在调度中心注册 → rebuild,整个过程声明式、可追溯、可复现。
扩展:可以集成到nvim/ecmacs