如何在pre-merge-commit钩子中修改文件并纳入合并提交?
Git pre-merge-commit钩子无法将修改纳入合并提交的解决方案
问题核心
你遇到的问题本质是:无冲突合并时,pre-merge-commit钩子中修改并暂存的文件无法被纳入合并提交,而是留在索引中。这是因为Git合并流程中,pre-merge-commit钩子的执行逻辑和普通pre-commit钩子存在差异——合并提交的内容在钩子执行前已被Git预先计算完成,钩子对索引的修改会被自动忽略。
合并提交的暂存内容存储位置
在无冲突合并场景下:
- Git会先将合并后的分支内容(比如你案例中的
file_1.txt改动)写入.git/index,同时在内存中保留该合并结果的树对象(tree object)。 - 钩子执行时
$GIT_INDEX_FILE确实指向.git/index,但Git后续生成合并提交时,会使用内存中预先保存的原始合并树,而非钩子修改后的索引内容。 - 这就是为什么你用
git add暂存了file_2.txt,但合并提交仅包含file_1.txt的原因——Git丢弃了钩子对索引的修改,用预先生成的树创建了提交。
如何修改钩子让修改纳入合并提交
要让钩子中的修改被合并提交包含,你需要手动覆盖Git预先生成的合并树,修改后的钩子脚本如下:
#!/bin/bash # Name of the file modified by the hook file_name="file_2.txt" # Content of the new line new_line="new line pre merge hook" # 1. 修改目标文件 echo "$new_line" >> "$file_name" # 2. 将修改加入索引 git add "$file_name" # 3. 获取合并的两个父提交ID parent1=$(git rev-parse HEAD) parent2=$(git rev-parse MERGE_HEAD) # 4. 从当前索引生成新的树对象 new_tree=$(git write-tree) # 5. 用新树创建合并提交,复用默认的合并提交信息 commit_msg=$(git log --format=%B -1 MERGE_HEAD) new_commit=$(git commit-tree "$new_tree" -p "$parent1" -p "$parent2" -m "$commit_msg") # 6. 更新当前分支HEAD到新提交 git update-ref HEAD "$new_commit" # 7. 返回非0值,阻止Git生成默认的合并提交(我们已手动生成符合要求的提交) exit 1
关键逻辑说明
- 步骤3:
HEAD是当前分支的最新提交,MERGE_HEAD是待合并分支的最新提交,二者是合并提交的父节点。 - 步骤4:
git write-tree将当前索引(包含新增的file_2.txt改动)转换为Git树对象。 - 步骤5:
git commit-tree基于新树创建合并提交,保留Git默认的合并提交信息。 - 步骤7:返回1终止Git默认的合并提交流程,避免重复生成提交。
冲突场景兼容
如果你的合并工具需要处理冲突场景,只需在钩子中添加判断:若存在MERGE_MSG文件(冲突时Git自动生成),则跳过手动创建提交的逻辑——冲突场景下Git会等待用户解决冲突后再提交,此时钩子的修改会被正常纳入后续提交。
内容的提问来源于stack exchange,提问作者tomko
相关产品推荐
相关产品推荐

