如何在 git 的“master”分支中自动修复 foxtrot-merges?

Posted

技术标签:

【中文标题】如何在 git 的“master”分支中自动修复 foxtrot-merges?【英文标题】:How to automatically fix foxtrot-merges in my the 'master' branch in git? 【发布时间】:2022-01-22 23:25:29 【问题描述】:

foxtrot 合并的问题在this question 中有详细解释。 简而言之: 在同一个分支(远程和本地)上工作时,简单的git merge 命令将远程合并提交隐藏在第二个父级下,并替换远程的第一父级历史记录(在常规情况 - “起源”)。我们认为这很糟糕。

上面链接的问题要求一种方法来检测这种情况,并拒绝推送。我的问题是如何自动修复它。

我不真的关心完整的树历史的整洁,但我希望第一父母树是完美的。并且不要打扰其他用户很重要,因此问题应该会自动解决。

目标:当 git origin 钩子检测到 foxtrot 提交推送时,它会在 old-HEAD 和 new-HEAD 之上添加另一个 merge-commit,将 old-HEAD 作为第一父级。

实现它最安全的方法是什么?

【问题讨论】:

你不能简单地禁止在 origin/master 上的快进吗?总是需要合并提交? @LasseV.Karlsen 我不明白如何做你的建议,或者它会有什么帮助。 (a) new-HEAD is 合并提交。 (b) 我不想阻止用户。我希望用户按自己的意愿推送,并且服务器会修复它。拉取请求过程(如在 TFS 中)实际上做了我想要的(他们创建新的合并提交,具有正确的第一父级)。我的问题是我们没有 PR(这对用户来说是一个额外的步骤),而且 - 我们不在 TFS 上工作。 【参考方案1】:

在the mentioned above question 和this question(在裸仓库中合并)的答案的启发下,我提出了以下解决方案。

这个脚本应该放在post-receive 挂钩文件中。根据要求,脚本会创建不隐藏旧 HEAD 的新合并提交。

脚本也是:

    收集new-HEAD第一父行的所有commit-msgs,并将其设置为new-merge-commit msg。 将 new-merge-commit 的作者和提交者伪装成 new-HEAD 的作者(同时注意名称中“或”的情况)。

命令说明:

    git read-tree 命令暂存 new-HEAD 提交的确切内容。 new-merge-commit 应该具有完全相同的内容。 git log 命令收集所需的提交消息。 git ... commit-tree 命令使用作者姓名/电子邮件、暂存数据、2 个父项和组合提交消息创建 new-merge-commit。 git update-ref 命令更新相关 ref(在本例中为 master)以指向新合并提交。

如果此脚本有问题,如果您能发现它们,我们将不胜感激。 :)

脚本:

#!/bin/sh

while read oldrev newrev refname
do
if [ "$refname" = "refs/heads/master" ]; then
    echo "Fix Foxtrot-Merges Hook..."
    MATCH=`git log --first-parent --pretty='%H %P' $oldrev..$newrev | grep $oldrev | awk ' print \$2 '`
    if [ "$oldrev" = "$MATCH" ]; then
        echo "...All is OK"
    else
        echo "...Fixing"
        
        authorName=`git log --first-parent --pretty='%an' $newrev^..$newrev`
        authorEmail=`git log --first-parent --pretty='%ae' $newrev^..$newrev`
        
        authorName=$authorName//"/\\"
        authorEmail=$authorEmail//"/\\"
        
        git read-tree -i --reset $newrev
        
        git log --first-parent --pretty='%B' $oldrev..$newrev > COMMIT_EDITMSG
        
        newCommitHash=$(git -c user.name="$authorName" -c user.email="$authorEmail" commit-tree $(git write-tree) -p $oldrev -p $newrev < COMMIT_EDITMSG)
        
        git update-ref $refname $newCommitHash
    fi
fi
done

【讨论】:

这将主要适用于最常见的情况,这可能已经足够好了。但是,这里真正的问题是它无法修复,因为您无法更改现有提交。您可以简单地不接受它(Git 可以很好地使用它),或者您可以像您在这里所做的那样pseduo-accept-it-and-rewrite-it。但是当你做伪接受时,sending Git 认为你做了一个正常的完全接受,并错误地更新了他们的远程跟踪名称。这可能会让其他人感到惊讶。如果可以的话,最好让人们一开始就停止这样做。 同时,作为一个小改进,考虑使用现有的合并提交树 (git rev-parse $rev^tree) 而不是将树读入索引然后写出索引。此外,您可以使用git log -1git log --no-walk 更轻松地提取作者姓名、电子邮件和正文。不幸的是,git log 在技术上是瓷器而不是管道,因此它患有用户配置综合症,但没有管道相当于使用。 最后:注意进行多次 foxtrot 合并的用户,然后用一个 git push 推送所有这些用户。这就是您在这里缺少的情况:您只重写了提示提交。 @torek 非常感谢您的 cmets!关于第一个,我明白你的观点。就我而言,它已经足够好了。如果出现一些需要特别管理的危机——我们可以暂时禁用它。关于第二个:好主意!我试试看。 @torek 关于最后一点,我认为你是不正确的,或者我没有理解你。 old-HEAD(= 任何其他程序员和构建服务器都知道的最新origin/master,直到当前用户推送)将始终是自动创建提交的第一步(即现在头部)。无论用户git pull 多少次,用户从哪个提交中提取都无关紧要;在底线 - old-HEAD 提交不会被隐藏。

以上是关于如何在 git 的“master”分支中自动修复 foxtrot-merges?的主要内容,如果未能解决你的问题,请参考以下文章

Git分支模型分析

Git:如何防止特定的提交被合并到另一个分支中?

在 Git 中获取从 master 到分支的更改

Git - 如何在所选文件上强制合并冲突和手动合并

使用 Git 将 master 的更改合并到所有分支中?

Git简要开发流程