Git拉取会在提交日志中产生多余的“合并分支”消息。

129

我和另一位开发者正在一个项目中合作,使用的是Github作为我们的远程仓库。我使用git 1.7.7.3版本的Mac系统,他使用的是git 1.7.6的Windows系统。

以下是问题的具体表现:

  1. 我们中的其中一人(假设是开发者A,但不论是谁)将一组代码提交到了GitHub上。
  2. 另一位开发者B在本地进行了一些修改。
  3. B执行了git pull命令。
  4. B执行了git push命令。
  5. 查看提交历史日志,我发现Merge branch 'master' of github.com:foo/bar

随着时间的推移,提交记录被“Merge branch”信息弄得杂乱无章,并且显示开发者B提交了开发者A所做的更改。我们唯一找到的解决办法就是在第3步执行git pull --rebase命令,但我不知道这样做会带来什么副作用。这是我第一次在多人开发的git仓库上工作,这种情况正常吗?你们有什么解决这个问题的想法吗?


4
使用 git log --no-merges 命令可以查看没有合并记录的日志。 - wjandrea
7个回答

96
你所看到的提交是完全没问题的。执行git pull实际上运行了git fetchgit merge,因此在运行git pull时通常会发生合并。
与合并相比,使用变基(rebase)的替代方法是可行的,但通常应避免使用。变基允许您保持线性历史,但也删除了最初发生的任何分支信息。它还会导致当前分支的历史重写,重新创建所有未包含在目标分支中的提交(在您的情况下是远程)。由于重新创建的提交是不同的提交,这可能会在与他人共同开发时产生很多混乱,特别是当人们在它们被重写之前就已经检出那些提交的部分内容时(例如功能分支)。因此,作为一个经验法则,您应该从不重写已经推送的任何提交。
你看到的提交是用来组合两个(或更多)分支的。有一个仅用于合并多个分支的提交并不会有任何问题。事实上,当查看历史记录时,有一个合并提交可以非常清楚地指示哪些分支被合并。与变基相比,合并还允许您有效地查看原始历史记录,包括实际存在的分支。
长话短说:是的,有合并提交是完全没有问题的,你不需要担心它们。

2
非常好的回答。我自己尝试了rebase风格,因为它在一些开源项目的贡献指南中被推荐,但是它给我带来了问题。团队中的一个新成员也遇到了同样的问题。我认为rebase选项不适用于整天一起工作的团队,但对于有主要贡献者和其他只提交补丁的贡献者的项目来说是正确的。这些人应该在发布拉取请求之前从主存储库获取并重新设置其更改。 - Meligy
3
如果没有新的更改,那么 pull 操作中的获取部分不会做任何事情,但合并仍将执行。因此,如果您当前的本地分支不是最新的,则它将把新的更改合并到您的分支中。如果无法进行快速向前合并(如果存在分叉提交),则它将创建一个合并提交。 - poke
35
这个答案似乎暗示着像 OP 描述的那样使用 rebase 是危险的,但实际上并不是。在第三步进行 rebase 操作并不会重写整个历史记录,只有还没有推送的本地提交会被重新应用到新的 HEAD(即已推送到该分支的最新提交)之上进行重写。这可以防止产生多余的合并提交,并且没有其他副作用。 - bob esponja
1
@bobesponja 所有不在拉取的远程分支上的提交都将被重写。这可能包括其他分支上已发布的提交,例如功能分支,其他人可能已经访问过了。因此,是的,在没有考虑重新基础的内容时进行重新基础有些危险。 - poke
1
@bobesponja 如果你早期发布你的特性分支(因为其他人在上面工作,或者仅仅是作为备份),那么你不应该对其进行变基,因为其他人可能已经获取了它。正如你自己所说,变基违反了我在答案中提到的变基准则。然而,如果你不公开你的提交,那么如果你想要并且不介意线性历史记录,变基是可以的。但这取决于你的工作方式,所以一般的答案是避免使用它,除非确实安全。顺便说一下,我修改了我的答案,所以如果问题解决了,我会感激你撤销你的负评。 - poke
显示剩余14条评论

57

这个答案已经被修改,因为我的理解、图表和结论是错误的。


git pull会导致合并提交,因为 git 在合并。这可以通过将分支设置为使用 rebase 而不是 merge 来改变。在进行拉取时使用 rebase 而不是 merge 可以提供更线性的历史记录到共享仓库。另一方面,合并提交显示了分支上并行的开发工作。

例如,两个人正在同一个分支上工作。该分支初始状态为:

...->C1

第一个人完成了他们的工作并推送到分支:

...->C1->C2
第二个人完成了工作并想要进行推送,但由于需要更新,所以无法推送。第二个人的本地仓库如下:
...->C1->C3

如果设置为合并(pull),第二个人的代码库(repository)将会是这个样子。

...->C1->C3->M1
      \      /
       ->C2->

其中M1是一个合并提交。这个新分支历史将被推送到仓库。如果改为使用变基(pull --rebase),本地仓库将如下所示:

...->C1->C2->C3

没有合并提交。历史记录已经变得更加线性。

这两种选择都反映了分支的历史。Git允许您选择喜欢的历史记录。

确实有一些情况下,rebase可能会导致远程分支出现问题。但这不是这种情况。我们更喜欢使用rebase,因为它可以简化本来就很复杂的分支历史,并显示相对于共享仓库的历史版本。

您可以设置branch.autosetuprebase=always,让Git自动将您的远程分支建立为rebase而不是master。

git config --global branch.autosetuprebase always
这个设置会让git自动为每个远程分支创建一个配置项:
branch.<branchname>.rebase=true

对于已经设置好的远程分支,您可以自己进行设置。

git config branch.<branchname>.rebase true

我要感谢@LaurensHolst对我之前的说法提出质疑并追究。我确实学到了更多有关git与pull和merge commits如何工作的知识。

如果想了解更多关于merge commits的信息,您可以在ProGit-BookContributing to a Project一章中进行阅读。其中的Private Small Team部分展示了merge commits的用法。



8
在进行pull操作时,使用rebase而不是merge可以为共享仓库提供正确的历史记录。使用merge会提供虚假的历史记录。这个说法背后的逻辑是什么?有合并历史记录的方式不可能是“虚假的历史记录”。它准确地展示了事情发生的顺序。通过使用变基,您实际上正在更改历史记录,以创建一个稍微更线性的版本。您为美观性牺牲了准确性。也许这是你喜欢做的事情,但它并不更真实。 - Laurens Holst
2
使用rebase而不是merge并不会为了美观而牺牲准确性。我们在合并时使用--no-ff,因此美观并不是一个要求。准确性才是我们的期望。Rebase提供了这种准确性。 - Bill Door
2
重置基础历史记录如何更准确?您没有澄清这一点,我也不明白它为什么会更准确。 - Laurens Holst
2
历史记录是共享代码库中提交发生的时间的反映。第一天,共享仓库看到了提交C2。第二天,共享仓库看到提交C3。如果C3在C2之前出现,则时间的反映将不正确。事实上,C3并没有在C2之前发生。所有的衍合所做的就是重新组织本地代码库中的提交,以正确反映共享代码库中显示的历史记录。 - Bill Door
7
你的问题让我重新审视合并提交的理解。我的图表是错误的,我正在修改讨论内容。我的结论也是错误的。关于变基和合并的历史记录同样正确,你可以自行选择。 - Bill Door
显示剩余2条评论

15

你可以做:

git pull --rebase

然而,这会始终将你的更改放置在你的协作者之上。但是你不会收到任何污染合并消息。


12

实际上这个问题有一个更简单的解决办法。让开发者B在进行提交之前先进行拉取操作。这将防止那些合并提交的出现,因为它们是由于您在本地仓库上创建的历史记录试图与远程仓库上的提交历史记录合并而造成的。如果在执行拉取操作时收到类似“更改将被覆盖”的消息,则意味着您和另一位开发者同时修改了同一个文件,请按照以下步骤操作:

git stash
git pull
git stash pop

如果有任何合并冲突,您可以解决它们。


最令人烦恼和焦虑的是合并冲突。我宁愿避免它。 - Green
1
@Green 如果你担心合并冲突,那么即使是 git pull 也不会有所不同。 - Zoso
除了那一次你在pull之前忘记了stash,Git总是要求我时刻保持最佳状态。 - linuxNoob
需要在本地更改之前,无论如何都要使用 git pull --rebase 命令来整合远程更改。 - vonbrand

7
执行git pull命令将插入“Merge branch”消息,这就是它的作用。通过执行git pull命令,您已将远程分支合并到本地分支中。
当您执行git pull时出现冲突,git日志将显示更新的冲突文件来自解决冲突的用户。我认为这是因为修复冲突的人重新提交了文件。
据我所知,这就是git的工作方式,没有其他方法可以解决这个问题。
重新设置将清除git历史记录,因此您将无法查看合并发生的时间。

4

解决方案:

如果其他开发人员已经在远程仓库中提交了他们的更改,那么在您提交更改之前需要先执行 git pull 命令。这样做可以避免出现多余的“Merge branch”消息。

如果您已经在未执行 git pull 的情况下提交了更改,那么需要执行 git pull --rebase 命令。这样做可以避免出现多余的“Merge branch”消息。

另外,了解以下几点:

  1. 执行 git pull --rebase 命令会发生什么?Git 会撤消你所有本地的提交记录,然后拉取远程的提交记录,并在新拉取的远程提交记录之上重新应用你的本地提交记录。

  2. 当你将额外的“Merge branch”提交与你的提交一起推送时,提交图形是这样的。

commit-graph with extraneous “Merge branch” commit

  1. 而在您执行 git pull 或在提交更改后执行 git pull --rebase 命令时,提交图形是这样的。

commit-graph without extraneous “Merge branch” commit

注意:

  1. 两种情况下您所做的提交记录数量和分支结构之间的差异。

  2. 此外,所有操作都将在同一个分支上进行,该分支会与自身合并。


1

在我的情况下,我不小心使用 git config -egit config --global -e 命令设置了一些配置,导致了问题。我通过删除添加的配置来解决了这个问题。例如,在全局配置中我有以下两行:

[pull]
    ff = false

同时,在本地配置中我也有以下内容:
[pull]
    rebase = off

当我移除这两个配置时,我可以在不产生合并提交的情况下拉取更改。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接