使用pre-commit钩子时,如何在多次git commit间保留COMMIT_EDITMSG
背景
团队使用Git pre-commit钩子校验提交消息格式:执行git commit并编辑保存关闭COMMIT_EDITMSG后,钩子会检查消息合规性,返回的退出码决定提交是否成功。但校验是非交互式的,需反复调整消息才能符合要求;且常撰写带多段上下文的详细提交消息,若提交失败后无法保留草稿,每次都要重写或用临时文件编辑,耗时较多。希望找到无需临时文件的方案,在多次git commit(仅最后一次成功提交)间保留提交消息。此前尝试过Vim相关方案,但要么依赖子模块(未使用),要么需配置Vim保留备份文件。当前使用VS Code作为Git编辑器,已通过git config --global core.editor "code --wait"完成配置:在VS Code集成终端运行git commit时,会打开COMMIT_EDITMSG新标签页,关闭标签页后Git执行pre-commit钩子,无需关闭整个VS Code。
问题解答
1. 能否让Git在提交因钩子返回非零退出码失败时保留COMMIT_EDITMSG?
不能。Git默认行为是:当提交失败(如pre-commit钩子返回非零)时,会自动删除COMMIT_EDITMSG临时文件。Git没有内置配置可修改这一行为,因为该文件属于Git的临时提交上下文,提交失败后Git会自动清理这类临时文件。
2. 能否让VS Code保留已保存关闭且被外部删除的文件内容?
直接无法实现。VS Code的workbench.action.keepEditor这类功能仅用于保留编辑器标签页,无法恢复被外部(如Git)删除的文件内容。关闭COMMIT_EDITMSG标签页后,VS Code会释放对该文件的引用,外部删除文件后,VS Code没有内置机制自动保留内容副本。
3. 是否只能修改pre-commit钩子,在退出前将提交消息草稿存储到Git未知的独立文件,下次运行git commit时恢复?钩子能否在git commit运行时预填充提交消息?
这是最可行的方案,且完全可以实现,具体步骤如下:
- 保存失败提交的草稿:修改pre-commit钩子脚本,当检测到校验失败(准备返回非零退出码)时,将
COMMIT_EDITMSG的内容复制到自定义草稿文件(如.git/commit_msg_draft,需添加到.gitignore避免提交到仓库)。
示例脚本片段:# 假设VALIDATION_FAILED为校验失败的标记变量 if [ "$VALIDATION_FAILED" = "true" ]; then cp "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/commit_msg_draft" echo "提交消息校验失败,草稿已保存到.git/commit_msg_draft" exit 1 fi - 预填充提交消息:使用Git的
prepare-commit-msg钩子,该钩子会在打开编辑器前触发,检查草稿文件是否存在,若存在则将内容写入COMMIT_EDITMSG,覆盖默认内容。
示例脚本片段:DRAFT_FILE="$GIT_DIR/commit_msg_draft" if [ -f "$DRAFT_FILE" ]; then cat "$DRAFT_FILE" > "$1" # 可选:加载后删除草稿,避免重复加载;也可保留至提交成功后再删除 # rm "$DRAFT_FILE" fi - 提交成功后清理草稿:在
post-commit钩子脚本中删除草稿文件,确保仅失败提交保留草稿:DRAFT_FILE="$GIT_DIR/commit_msg_draft" if [ -f "$DRAFT_FILE" ]; then rm "$DRAFT_FILE" fi
整个流程为:提交失败时pre-commit钩子保存草稿,下次执行git commit时prepare-commit-msg钩子自动填充草稿,提交成功后post-commit钩子清理草稿,无需手动使用临时文件,也不依赖VS Code特殊配置。
内容的提问来源于stack exchange,提问作者Tomáš Hübelbauer

