如何解决与远程 Git 仓库的冲突?

6

首先,我对git一窍不通。

我们的工作流程是克隆远程仓库,进行工作,进行提交等等,直到我们满意,然后推送到origin / branch。然后进行拉取请求。(这可能有些疯狂,我觉得容易出问题,但我对这方面一无所知,只是盲目地按照程序操作:-)) 我这样做...发现有一个冲突,因为有人在他们自己的分支中编辑了一个文件,而我是在克隆主分支之后合并的。我的提交使得该文件变得多余,所以我想要的快速答案是,如何让git覆盖掉这个文件呢?但从长远来看,显然这不是每次遇到冲突时的正确答案 :-) 但是我在Google上找到的所有内容似乎都假定我是直接在相关的仓库上工作。像"git status"这样的东西告诉我一切正常,因为它正在查看我克隆的仓库。我找不到如何在远程仓库中解决这个问题。

1个回答

17

首先,让我说一下(虽然有点简化但足够准确):git pull只是一个方便的脚本,它执行了两个底层的git命令,即git fetchgit merge。如果你从fetch的角度去思考而不是pull(fetch + merge),那么所有的事情都会变得更加清晰。

在使用git时,你不能工作于“远程存储库”,一切都是本地的。当你进行fetch或push操作时,你的本地git会联系远程存储库,可以将其看作是通过互联网电话与其他人联系。你的git和他们的git交换信息,之后你可以向他们提供东西(git push)或者他们向你提供东西(git fetch)。

当他们向你提供东西时,你的git会记录他们拥有哪些分支,并更新你对“远程存储库上的情况”的理解。这些内容存储在你的(本地!)“远程分支”中,例如origin/masterorigin/develop,或者他们所使用的任何分支名称:因为你正在执行git fetch origin,所以你的git会在分支名称前加上origin/。这种分支重命名技巧非常重要。特别的,这意味着无论你正在工作的是哪个本地分支,运行git fetch始终是安全的,因为它仅更新你的“远程分支”(再次强调,这些分支是本地的,在你的.git目录中,而不是远程存储库中,每当你的git通过互联网电话与他们的git进行通讯时,你都会同步它们,随时添加、更改和删除你的副本)。

然而,当你向他们提供东西时,就没有进行任何重命名操作了。当你执行git push origin master时,你的git会对你所称之为的origin说:“嘿,我建议你们将你们的master更改为我正在提供的新提交。”接受与否取决于他们。通常情况下,他们(无论是谁)会接受“快进”,但不接受“非快进”。关于这一点(更多内容),请参见这篇更长的回答

当使用拉取请求(并将其与用户发起的推送结合使用)时,通常会将提交推送到每个用户位置: 例如,您可能不会从您的 master 推送到远程的 master,而是推送到to-everyone/from-JohnOliver/master。或者,您可以将其推送到仅您进行推送的存储库,但其他所有人都可以看到。这里的想法是,您在此处执行的推送永远不会与任何其他人发生冲突,它只是使您的提交可见,之后您发出“拉取请求”,告诉某人去查看这些提交。如果他们(无论他们是谁)喜欢它们,他们就会将它们添加到远程的 master 中。如果不行,他们就让你重写它们。
请注意,如果您重写提交,则会获得新的、不同的提交。每个提交都有一个作为其完整“真名”的 SHA-1 ID。该ID是提交的全部内容的加密校验和:涉及的所有文件、您的姓名和电子邮件、日期/时间戳以及您的提交的父提交。如果您更改任何内容,都会获得新的、不同的提交。(您的新旧提交可以使用相同的父-ID、用户名和电子邮件。如果您足够快(您可以在一秒钟内提交多少次?:-),或者调整时间,甚至可以获得相同的时间戳。但如果文件不同,您将获得不同的提交 ID。这没关系:这就是我们想要的。如果您获得了相同的提交ID,则所有文件都是相同的,您的姓名和时间也是相同的,因此它仍然是同一个旧提交。)
现在,回到您的问题......
当您执行 pull 时,您正在执行 fetch,然后执行 mergefetch 带来了其他人的提交,这些提交现在可以在origin/master(或任何分支;我只会假设这里是master)中找到。
如果您已经提交了您的工作,则您有自己的提交,在您的master上可以找到。(如果您没有提交,可能不应该执行 git merge。但是,您上面的工作流程表明您已经提交了。那么让我们假设您确实拥有自己的提交。) 让我们绘制结果“提交图”,这只是一种以某些提交为基础的写法(在白板上或作为 ASCII 艺术品等方式)来展示谁做了什么:
             A   <-- master <-- HEAD
           /
...* <-- *
           \
             B   <-- origin/master

你的提交 A 在你的 master 分支上。他们的提交 B 在你的 origin/master 分支上(从他们的 master 复制过来)。AB 都有相同的父提交,即图表中最右边的那个 * 节点。那个 * 指向它的父节点,以此类推。(/\ 表示的线应该是指向 * 的箭头,但箭头只在某些浏览器中工作。)

git merge 的作用是尝试将你的更改 A 与他们的更改 B 结合起来。当 Git 无法完成自己的合并时,就会出现合并冲突。

... 我的提交让那个文件变得多余了,所以我想知道的快速答案是,如何让 Git 覆盖掉这个文件?但从长远来看,显然这不是每次出现冲突时的正确答案 :-) 但是 Google 上找到的所有内容似乎都假定我直接使用相关存储库。 "git status" 等内容告诉我一切正常...

如果 git status 认为合并顺利完成,而 git merge 没有停止并说有冲突,那么 Git 没有意识到存在冲突。它认为你的更改和他们的更改可以合并成“所有更改的总和”。然后,merge 会创建一个新的提交:

             A
           /   \
...* <-- *       M   <-- master <-- HEAD
           \   /
             B   <-- origin/master

问题在于合并结果中与文件相关的M是错误的:正如你所说,它们的更改(或整个文件本身)应该“消失”,但是git认为你的更改没有冲突,因此git保留了两个版本。

请注意:

我在Google上查找到的所有内容都假设我直接从有问题的repo中工作。

你确实是这样做的。你正在你的repo中工作。如果你git push或以其他方式邀请他人从你的repo中获取东西,你就会分享你所做的所有内容,但在此之前,这都是属于你的。它对你私人可见,直到你发布它为止,这意味着你也可以更改它。

如果你有一个类似这样的虚假合并提交,需要在发布之前删除或修复它。(你当然可以事后修复它,但在发布之前修复它对其他人更友好。)

在stackoverflow上搜索如何用git reset删除合并或修复合并, 或者如何修补合并(如果您想保留合并本身,可以通过git commit --amend修补合并提交。如果你删除了合并提交,可以将你的提交“变基”到你选择的新提交上,但要小心:由于git在合并时没有检测到任何冲突,它几乎肯定也不会在变基时检测到冲突,并且您需要在您的提交使他们的更改不再需要(或整个文件不再需要)的地方重新做一个文件。交互式变基为您提供了所有必要的工具,当然,在此过程中还有很多关于git的知识需要学习。有关变基的更多stackoverflow答案。


最后,如果你确实遇到了冲突,但解决了它(可能是错误的)并且git commit了结果,那么治疗方法仍然与以上相同:删除或修复合并。如果你想删除合并提交,那么重复执行合并操作,但不要使用git merge --no-commit提交生成的文件。Git将尽力自动合并所有内容,但在不提交的情况下停止。这为您提供了撤消他们的更改的位置,甚至可以进行如下操作:

git rm file.dat # no longer needed at all

然后你就可以执行git commit命令提交结果(作为一个合并提交)。


1如果你执行git fetch batman命令,那么你的git会给他们的分支加上batman/master等名称。基本上,远程只是“你给他们的名字”,他们的分支会被放在前面,你的git以这种方式跟踪它们的分支。这就是为什么它们被称为“远程分支”,有时也被称为稍微长一点的“远程跟踪分支”。

如果你只是运行git fetch命令而没有指定origin,git会自动找到要使用的远程;通常这仍然是origin(但谁知道呢,如果你设置了,它也可能是batman或者其他名称)。无论如何,适当的远程名称都会被添加到“远程分支”的前面。


1
这里是一个隐藏的宝藏,位于互联网上如此安静的页面! :-o 精彩清晰且有用的介绍内容!(虽然我可以看出来,如果John想要的话,它并不完全是“我想要的快速答案”,他或许会接受的。;))仍有一个问题(我认为这也是OP的问题):他的 rm file + push到这里,然后(一些“隐含”的)add file再次导致在一个愉快而沉默的git的“头”之间发生拉锯战,只能通过纯粹的人类协调才能解决吗?或者你可以在远程端为那个冗余的文件触发冲突(rm会造成冲突吗)? - Sz.
1
@Sz.:如果你完全删除了某个文件(git rm file)并提交了该更改,而且他们(无论是谁)已经接受了该更改,然后第三方在他的合并基础中有一个包含该文件的提交,并对该文件进行了一些更改,然后尝试合并,则第三方将会得到一个“修改/删除”合并冲突。但是,如果第三方没有触及文件file,则第三方的file自合并基础以来就没有发生过变化。请注意,“合并基础”在此处不断出现:理解合并的关键是理解Git如何找到合并基础,然后找到两组更改。 - torek

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