git pull 后解决冲突

Jes*_*son 1 git github

但是接下来该怎么办?

$ git pull
Auto-merging views/upload_photo.handlebars
CONFLICT (content): Merge conflict in views/upload_photo.handlebars
Automatic merge failed; fix conflicts and then commit the result.

James@DESKTOP-8VLS7EG MINGW64 ~/Documents/project (master|MERGING)
$ git merge --abort

James@DESKTOP-8VLS7EG MINGW64 ~/Documents/project.my (master)
$ git status
On branch master
Your branch and 'origin/master' have diverged,
and have 1 and 2 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        views/upload_photo_BACKUP_4736.handlebars
        views/upload_photo_BASE_4736.handlebars
        views/upload_photo_LOCAL_4736.handlebars
        views/upload_photo_REMOTE_4736.handlebars

nothing added to commit but untracked files present (use "git add" to track)
Run Code Online (Sandbox Code Playgroud)

我该怎么办?如果我再次执行 git pull,我已解决的冲突文件将被重置。

tor*_*rek 8

TL;DR:你有三个选择

我该怎么办?如果我再次执行 git pull,我已解决的冲突文件将被重置。

您的三个选项是:

  • 使用git merge(这是git pull正在尝试做的)。解决冲突并提交。“解决冲突”部分是困难的部分。看到很多其他关于合并的评论,包括其他 SO 帖子,例如,与 git 合并 - 我如何找出“新”代码在哪里结束?

  • 使用git rebase(您可以告诉git pull它而不是git merge)。这会给您留下完全相同的问题,您必须以相同的方式解决:解决冲突并提交。(见下文。)

  • 放弃。:-) 不是一个很有吸引力的选择,但它一个选择!见下文。

长答案

git pull命令首先运行(的次要变体)git fetch,然后运行git merge。(或者,您可以git pull将运行git rebase作为其第二步。)

正如root545 在评论中所说,其他人已经推送了一些提交——准确地说是其中两个。您的git fetch步骤将这些提交带入您的存储库。同时,您自己的提交(或提交,单数,因为您只有一个)也保留在您的存储库中。

我不知道做了另外两个提交,但让我们假设它是一个叫 Bob 的人。他提交这些提交的方式是:(1) 获取,然后在必要时合并或变基——即——将git pull其他人的工作放到Bob的存储库中,然后 (2) 进行他的两次新提交,然后 (3)git push他的新工作到 Git 上的原点。你做你的工作时,他完成了所有这三个步骤。等式的关键部分是,他您设法git push对原产地的承诺之前完成了第 3 步。

你现在Bob一次提交,但也Bob两次提交。无论 Bob 是否在第 3 步击败你,这都是正确的——但是因为他确实赢得了那场特定的比赛,所以他在你把你的东西拿到那里之前把他的东西放到了原始的 Git 存储库中。收拾烂摊子现在是你的问题。如果您git push origin master在 Bob 执行第 3 步之前完成了第 3 步 ( ),这将是他的问题。(如果你不被允许推动,那么你永远不会做第 3 步,而且你总是会像这样落后。这不一定是一个问题,但请继续阅读。)

整体流程如何运作

让我们花点时间画出 Git 在这一点上看到的东西。Git 喜欢从commits开始工作,就像你做的一个,以及 Bob 做的两个。提交代表一个完整的快照:您需要(或 Bob 需要)的所有文件都放在一个存储库中,就像您(或 Bob)说的那样: 这很好,这一切都会起作用。 您可以通过运行来制作每个快照git commit,这会将您的“索引”(您git add用来更新的东西)中的任何内容转换为下一次提交。

你和 Bob 都从一个共同的快照(提交)开始,你1称之为master,你从 中获得origin,所以你也称这个共同快照origin/master。此提交位于一些长提交链的顶端:

... <- o <- o <- o   <-- master, origin/master
Run Code Online (Sandbox Code Playgroud)

这个链中的每个提交,由一个 round 表示o,指向前一个提交的“倒退”。不过,这些向后的箭头大多很烦人,所以让我们使用连接线来绘制它。一次提交较早这一事实很明显,因为我们将其绘制到左侧。而且,因为当前的提示对我们接下来要做的事情非常重要,让我们给它一个星:

...--o--o--*   <-- master, origin/master
Run Code Online (Sandbox Code Playgroud)

现在进行提交(同时 Bob 正在提交,但我们不会担心)。这是一个新的提交,所以它指向前一个提示master,你的 Git 仍然为你记住它origin/master,所以现在绘图看起来像这样:

             A   <-- master
            /
...--o--o--*     <-- origin/master
Run Code Online (Sandbox Code Playgroud)

我给它起了一个单字母的名字,以便我们可以讨论它:“commit A”。

现在让我们来看看鲍勃的作品。他没有你的A;他不可能;你还没有分享它。他在工作开始时有这个:

...--o--o--*   <-- master, origin/master
Run Code Online (Sandbox Code Playgroud)

即,相同的启动设置。但他做出了两次承诺,现在有:

...--o--o--*     <-- origin/master
            \
             B--C   <-- master
Run Code Online (Sandbox Code Playgroud)

现在鲍勃git push迈出了他的一步。

什么是对origin的,在这一点上,原来的链条,在结束--o--*。他的 Git 与一个 Git 对话master并说“在这里,进行提交BC然后,如果您愿意,请将您的(来源)设置master为指向C?” 而且,由于 origin 上的 Git 至少知道并喜欢 Bob, C指出返回到B哪个点*,起源 Git 说:“好的,当然,我会提出我的master观点来提交CC记住BB记住,*所以这只是添加新东西。” 所以现在 origin 与 Bob 拥有相同的链,以及 origin 的master提交点C


1 Bob 无疑也调用了 his master,但我们不需要关心;这取决于鲍勃。对来说重要的是在原始 Git 存储库中的内容。这就是您与世界其他地方交谈的地方。原点设置是您方便的聚会场所。这就是中央存储库的意义所在:它是市场广场,每个人都聚在一起交换提交。


进入 git fetch

在这一点上,运行git pullwhich 运行git fetch它与原始的 Git 联系。你的Git取得提交BC并把它们在你的资料库,并使用Git的调整你的 origin/master指向C,因为这是现在你的Git的从回忆origin。所以你的存储库现在看起来像这样:

             A     <-- master
            /
...--o--o--*
            \
             B--C   <-- origin/master
Run Code Online (Sandbox Code Playgroud)

您拥有所有三个新提交。事实上,你是唯一一个同时具备这三者的人。 这就是为什么现在你的工作是解决这个问题。

解决混乱

您有两种选择来解决这个问题,当然还有第三种选择:根本不去修复它。

标准选项是使用git merge,它git pull为您运行。第二个通常更好的选择是使用git rebase,这通常更复杂。

第三种选择是最简单的。您永远不必合并,您可以继续发展并永远分道扬镳。你永远不会拿起 Bob 或任何其他人的任何新工作,永远,不管你从 Git 的起源中拿起了什么,如果有的话。(在这种情况下,您可能希望停止调用它master并开始调用它jessie。名称并不是那么重要,但是通过给它您自己的名字,而不是master,您可能能够更好地记住您在做什么,如果你必须把它放在一边,然后再回来。)

这第三种方法的优点和缺点应该是显而易见的:它真的很容易,它不需要你做任何工作,但当然你也永远不会从其他人的工作中受益。因此,假设您决定不走这条路。您将如何将您工作与他们的工作结合起来?同样,Git 给你的选项是mergerebase

合并

事实上,合并是更简单的方法。它准确地记录了发生了什么、发生在谁身上以及何时发生。合并提交变成了一种粘合剂:“我有我的工作,你有你的,然后我把它们结合起来。” 要进行合并,你——不知何故——你的工作和 Bob 的工作结合起来,并进行 Git 所谓的合并提交。你想出如何让你的新快照A与 Bob 的 一起工作C,并从中制作一个新快照。让我们假设“不知何故”是纯粹的魔术,并得出结果:

             A------M   <-- master
            /      /
...--o--o--*      /
            \    /
             B--C   <-- origin/master
Run Code Online (Sandbox Code Playgroud)

这个新提交M是您新的“合并提交”:一个git log将显示为merge branch 'origin/master'merge branch 'origin' of ...取决于您给git merge. 我们可能想重新绘制一下,使其看起来像这样,因为它不那么凌乱:

...--o--o--*--A---M  <-- master
            \    /
             B--C   <-- origin/master
Run Code Online (Sandbox Code Playgroud)

如果你再git push这样成功,即在Git的原产知道并喜欢你,至少一点点,A以及M添加到其提交2 -然后会发生什么事是,在原产地的Git会将它的主人为好,你的 Git 会更新你记住的origin/master,你会得到这个:

...--o--o--*--A---M  <-- master, origin/master
            \    /
             B--C
Run Code Online (Sandbox Code Playgroud)

git merge就是全部内容:您将自己的工作与其他人的工作一起加入,进行合并提交,并可能推送结果。


2由于M指向A C,这实际上只是添加到原始 Git 中存储的提交。好吧,只要您的git push第 3 步在其他人的git push. 你的工作不仅是合并或变基,而且如果你要推动,还要击败其他人推动。如果你输了这场比赛,你可以再试一次!——尽管这次可能是从一个更好的起点。您现有的合并或重订遗体你的,这可能是一个很多现在更好的起点。

请注意,如果您从不推送,则将来肯定必须再次合并或变基。但是您刚刚所做的工作为您下次提供了同样的“更好的起点”。如果您经常合并或变基,则每次可能不会有太多工作要做。


合并留下了混乱的历史

假设你在这方面成功了,现在领先于Bob将不得不接手你的新工作。事实上,其他使用 Git on origin 共享工作的人也是如此:他们都必须接受您的合并。

随着时间的推移,人们——无论是谁——进行大量合并会导致大量这种形成小分支的提交模式,然后是合并。所有合并提交的价值——如果有任何价值——就是它准确地显示了谁做了什么,什么时候,以及他们如何把它们粘在一起。缺点是,它准确地显示了谁做了什么,何时以及如何将它们粘合在一起,而且大多数时候,只有“谁做了什么”部分才是真正重要的。

“以及何时”部分每天都变得不那么重要了。一年后,谁在乎你和鲍勃是否在同一时间工作?如果你Bob 完成做所有事情,结果会不会一样?

更糟糕的是,“他们如何将它们粘在一起”只会让人分心。 如果已经开始鲍勃完成刚过,你仍然会得到相同的结果,对不对?然后根本没有将粘合在一起的步骤。您将拥有如下所示的提交历史记录:

...--o--o--*--B--C--A--o--o--...
Run Code Online (Sandbox Code Playgroud)

如果您发现自己稍后再回来处理它,则处理起来容易得多

变基

git rebase就是为了。假设我们回到运行后的设置git fetch

             A     <-- master
            /
...--o--o--*
            \
             B--C   <-- origin/master
Run Code Online (Sandbox Code Playgroud)

如果你可以,现在,很容易把你的承诺A,将其复制到轻微的变异,我们称之为A'-和已经A'来了提交C?那看起来像这样:

             A     <-- ???
            /
...--o--o--*
            \
             B--C   <-- origin/master
                 \
                  A'   <-- master
Run Code Online (Sandbox Code Playgroud)

这就是git rebase全部。是什么让git rebase复杂得多git merge的是,如果你有一个以上的承诺,它必须复制所有这些的。也就是说,假设你有:

             D--E--F   <-- master
            /
...--o--o--*
            \
             B--C      <-- origin/master
Run Code Online (Sandbox Code Playgroud)

你需要得到:

             D--E--F   <-- ???
            /
...--o--o--*
            \
             B--C        <-- origin/master
                 \
                  D'-E'-F'   <-- master
Run Code Online (Sandbox Code Playgroud)

正如 所暗示的那样<-- ???,我们很快就会忘记曾经有过原版,然后去掉所有的小勾号,将它们称为AD--E--F。这样做的好处是,一旦你让一切git push顺利并成功,当你在一年或多长时间内回到这一切时,你有:

...--o--o--*--B--C--D--E--F--o--o--
Run Code Online (Sandbox Code Playgroud)

或与 相同A,这可能是我们都希望在一年或多长时间内拥有的。

更多的工作。Git 自动化了大部分工作,但是当它出错时会更加混乱——如果你git merge在一次A提交中遇到冲突,你将在提交中遇到相同的冲突git rebase。是否切换到git rebase. (它通常更好,你必须解决完全相同的问题:

CONFLICT (content): Merge conflict in views/upload_photo.handlebars
Run Code Online (Sandbox Code Playgroud)

无论哪种方式。)