如何通过post-commit钩子实现main分支与所有分支/工作树自动同步?
错误原因
你报错的核心原因是post-commit钩子执行时,当前工作目录正处于你刚完成提交的分支上,直接尝试checkout已经被当前工作区占用的分支必然会失败。而且频繁切换工作区还会打乱你本地的未提交变更,本身就不适合在钩子中使用。
适配你需求的解决方案
根据你补充的场景,需求可以简化为两个分支逻辑,完全不需要切换当前工作区的分支就能实现:
- 提交到main分支时,将main的最新变更合并到所有其他本地分支
- 提交到非main分支时,仅将main的最新变更合并到当前提交的分支,避免后续合并冲突
版本要求(Git 2.30+)
Git 2.30及以上版本支持git merge --into-name参数,可以在不切换到目标分支的前提下完成合并操作,完美适配钩子场景。
完整post-commit脚本
#!/bin/sh # 获取当前提交的分支名 CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) # 定义合并失败时的提示文本 MERGE_FAILED_MSG="合并main到%s失败,存在冲突,请手动解决后再提交\n" if [ "$CURRENT_BRANCH" = "main" ]; then # 提交到main分支,遍历所有本地分支合并 for BRANCH in $(git for-each-ref --format='%(refname:short)' refs/heads/ | grep -v "^main$"); do echo "正在合并main到$BRANCH..." git merge --into-name "$BRANCH" main --no-edit > /dev/null 2>&1 if [ $? -ne 0 ]; then printf "$MERGE_FAILED_MSG" "$BRANCH" # 回滚未完成的合并 git merge --abort > /dev/null 2>&1 fi done else # 提交到非main分支,仅合并main到当前分支 echo "正在合并main到$CURRENT_BRANCH..." git merge --into-name "$CURRENT_BRANCH" main --no-edit > /dev/null 2>&1 if [ $? -ne 0 ]; then printf "$MERGE_FAILED_MSG" "$CURRENT_BRANCH" git merge --abort > /dev/null 2>&1 fi fi exit 0
低版本Git兼容方案(<2.30)
如果你的Git版本不支持--into-name参数,可以用临时worktree的方式实现,全程不影响当前工作区:
#!/bin/sh CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) MERGE_FAILED_MSG="合并main到%s失败,存在冲突,请手动解决后再提交\n" # 创建临时worktree目录 TMP_WORKTREE=$(mktemp -d) trap 'rm -rf "$TMP_WORKTREE"' EXIT git worktree add --detach "$TMP_WORKTREE" > /dev/null 2>&1 cd "$TMP_WORKTREE" || exit 1 if [ "$CURRENT_BRANCH" = "main" ]; then for BRANCH in $(git for-each-ref --format='%(refname:short)' refs/heads/ | grep -v "^main$"); do echo "正在合并main到$BRANCH..." git checkout "$BRANCH" > /dev/null 2>&1 git merge main --no-edit > /dev/null 2>&1 if [ $? -ne 0 ]; then printf "$MERGE_FAILED_MSG" "$BRANCH" git merge --abort > /dev/null 2>&1 fi done else echo "正在合并main到$CURRENT_BRANCH..." git checkout "$CURRENT_BRANCH" > /dev/null 2>&1 git merge main --no-edit > /dev/null 2>&1 if [ $? -ne 0 ]; then printf "$MERGE_FAILED_MSG" "$CURRENT_BRANCH" git merge --abort > /dev/null 2>&1 fi fi
注意事项
- 由于你的配置文件对顺序敏感、容易出现冲突,脚本没有做自动冲突处理,合并失败后会直接提示你手动解决,避免生成错误的配置
- 脚本默认只在本地分支执行合并,如果需要同步到远程,可自行在合并成功的逻辑中添加
git push origin <分支名>的命令,不建议直接添加自动推送逻辑,避免错误合并被同步到远端 - 不要直接操作
.git/refs/heads下的文件获取分支列表,用git for-each-ref是更稳定的实现方式
内容的提问来源于stack exchange,提问作者Jochen
相关产品推荐
相关产品推荐

