git merge 以某种方式在一次提交中合并了“双向”(A 到 B 和 B 到 A)

zor*_*ang 5 git git-gui

我不明白发生了什么事。我将分支 master 合并到我的功能分支中,这是我最近经常做的,不知何故,合并提交现在存在于两个分支中,并且两个分支在提交后都处于相同的状态。我还没有找到任何关于如何发生这种情况(或者实际上可以做到)的信息。我一直在 Windows 上使用 git-gui (但在其他机器上使用 git 命令行,所以我了解基本的 git 命令)。git-gui 和 GitLab 都在历史图中显示此提交将两个分支相互合并。有知道这是怎么发生的吗?我不知道还会发生什么,我应该摆脱 git-gui 吗?

然后,由于不幸的是这个提交被推送到了远程,我仍然想做一个重置 --hard,因为如果我做一个恢复,那么当我将来再次将 master 合并到功能中时,我会得到功能中的所有内容分支作为一个冲突,因为在 master 的恢复中一切都被撤销了。关于如何最好地进行的任何意见或建议?

编辑:我以为我理解合并的含义,但从答案来看我现在更困惑了。如果我创建一个包含很多新内容的功能分支,并且我想“导入”master 中发生的一些更改,但保持 master 不变,这不是这样做吗?

git checkout feature
git merge master

master A ------------ E <-- last master commit
        \              \  (try to merge into feature)
feature  B -- C -- D -- F <-- feature now contains "E" edits?
Run Code Online (Sandbox Code Playgroud)

但我得到的是:

master A ------------ E  F <-- master now contains all of feature stuff (B, C, D).
        \              \ | 
feature  B -- C -- D --- F <-- feature now contains "E" edits.
Run Code Online (Sandbox Code Playgroud)

Edit2:如果根据 jszakmeister 的回答,发生这种情况是因为执行了 2 个合并操作,其中一个是快进操作,有什么方法可以恢复有关快进合并的信息,例如谁执行的以及何时执行的?如果提交消息说“将主功能合并到功能中”,我想这意味着它首先在那个方向上完成,然后有人快进将功能合并到主功能中,对吧?

谢谢!

Joh*_*ter 3

我可以通过一种方式看到它的发生。假设您有一个名为“feature”的功能分支。您将“master”合并到“feature”中。所以图表看起来像这样:

---o---o---o        master
            \
-o---o---o---*      feature
Run Code Online (Sandbox Code Playgroud)

如果您签出“feature”并合并master,您可能会获得快进合并,因为“master”上的新提交直接位于“feature”中的最后一次提交之前。所以图表最终看起来像这样:

---o---o---o
            \
-o---o---o---*      master, feature
Run Code Online (Sandbox Code Playgroud)

这不是额外的提交,只是“master”和“feature”现在指向同一个提交。FWIW,我和我的团队发现这种行为令人困惑,因此我在下面概述了我们可以采取的措施来帮助防止出现此问题。

另外,我不确定使用“gitk”进行提交是什么意思。据我所知,gitk 仅供查看。你的意思是“git gui”吗?我确实建议您使用“gitk”查看该图。我倾向于使用如下内容:

gitk --date-order master feature
Run Code Online (Sandbox Code Playgroud)

这将显示一张带有主分支和功能分支的图片,并帮助我辨别其中的关系。如果我在同一个提交上看到主分支和功能分支标签,这是一个好兆头,表明我不小心将合并功能快进到了主分支上。

如果您看到不同的东西,那么发布图片会很有帮助。但我怀疑这是功能分支上发生的这种快进合并。您可以通过以下方式备份功能分支:

git checkout master
git reset --hard HEAD^
Run Code Online (Sandbox Code Playgroud)

你可能必须这么做git push --force origin master。我建议使用此完整形式,因为 Git 的默认推送行为是推送所有本地跟踪分支,这可能会导致分支倒回。此外,在与他人合作时,强行推动“master”并不是一个好的答案,因此如果需要的话,您需要与您的团队进行协调。简单的答案可能就是保持原样。

根据具体情况,可能会涉及更多内容。

另外,如果你想要合并提交,那么你可以这样做:

git checkout master
git merge --no-ff feature
Run Code Online (Sandbox Code Playgroud)

这将强制创建合并提交,即使它可以快进。我和我的团队非常喜欢这个,因此我们将其设为默认值。我在下面解释这一点。

编辑:我翻转了意义以匹配问题并扩展了某些部分。

顺便说一句,自从我使用 Bazaar 和 Subversion 一段时间以来,我变得非常敏感谁是历史上的主线(最左边)父级。Git 及其快速合并使这变得更加困难,而且我发现我的团队也确实觉得它令人反感。所以我们最终做了一些事情。

首先,我们更改了配置,以便合并始终进行合并提交。您可以使用以下命令从命令行执行此操作:

git config merge.ff false
Run Code Online (Sandbox Code Playgroud)

或者在全球范围内,通过:

git config --global merge.ff false
Run Code Online (Sandbox Code Playgroud)

接下来的一点是,当从上游引入更新时,我们确实希望快进合并。因此,我们有几个别名可以提供帮助:

[alias]
    # Fetch the remote and update the current branch.
    up = !git remote update -p && git merge --ff --ff-only @{u}

    # Fast-forward pull locally
    ff = !sh -c 'git merge --ff --ff-only ${1:-@\\{u\\}}' -
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,团队将上游分支设置为他们喜欢推送和拉取的位置。因此 agit up将从服务器获取更新并快进当前分支。这通常是在“master”上完成的。 git ff 可以用来做类似的事情,但没有获取步骤。如果您需要这样的东西(有时会出现),它还可以更轻松地在您的分支之上快进另一个分支。

其中有些事情仍然比我关心的要复杂,所以我写信 git ffwd 来帮助我们。 git ffwd将进行提取,然后快进所有在远程具有等效项的本地分支。当服务器端的分支被删除时,它还会负责修剪你的遥控器,从而更容易地修饰裁判并防止它们变得混乱。 git ffwd 只会进行快进合并(git up也做了同样的事情),如果分支发散,则会失败,因此它最终成为发生有趣事情的良好标记,您需要介入并找出正确的行动方案。

所有这一切的最终效果是,现在当我们输入 时git merge foo,将会有一个合并提交。Git 会询问一条消息,如果你要做错误的事情,你可以简单地删除缓冲区的内容并保存(导致 Git 中止)或在:cqVim 中退出,这更容易一些,但会导致编辑器崩溃退出并出现错误。无论哪种方式,您都可以更好地控制,并且它在使跟踪事物变得更加容易并使我们的历史看起来清晰方面取得了很大的进步。

另请注意,我们还设置了git config push.default upstream我们是否在同一个存储库中一起处理同一个项目,或者git config push.default current 我们是否将在单独的存储库中工作。如果您使用的 Git < 2.0,那么设置此设置很重要,否则您可能会git push -f在 master 分支上执行类似的操作,但最终会倒回本地跟踪远程分支的其他分支。这是因为在 Git < 2.0 中,默认情况下 matching会推送所有远程跟踪分支,而且它们很少是最新的。

这有点啰嗦,但我想告诉你,还有另一种工作方式可以让你拥有更好的体形,消除一些困惑,从长远来看感觉更轻松——至少对我来说是这样,我的团队。

如果您有兴趣,我这里有更多配置。