但是接下来该怎么办?
$ 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,我已解决的冲突文件将被重置。
我该怎么办?如果我再次执行 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
并说“在这里,进行提交B
,C
然后,如果您愿意,请将您的(来源)设置master
为指向C
?” 而且,由于 origin 上的 Git 至少知道并喜欢 Bob,并 C
指出返回到B
哪个点*
,起源 Git 说:“好的,当然,我会提出我的master
观点来提交C
。 C
记住B
并B
记住,*
所以这只是添加新东西。” 所以现在 origin 与 Bob 拥有相同的链,以及 origin 的master
提交点C
。
1 Bob 无疑也调用了 his master
,但我们不需要关心;这取决于鲍勃。对您来说重要的是在原始 Git 存储库中的内容。这就是您与世界其他地方交谈的地方。原点设置是您方便的聚会场所。这就是中央存储库的意义所在:它是市场广场,每个人都聚在一起交换提交。
git fetch
在这一点上,您运行git pull
which 运行git fetch
它与原始的 Git 联系。你的Git取得提交B
和C
并把它们在你的资料库,并使用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 给你的选项是merge
和rebase
。
事实上,合并是更简单的方法。它准确地记录了发生了什么、发生在谁身上以及何时发生。合并提交变成了一种粘合剂:“我有我的工作,你有你的,然后我把它们结合起来。” 要进行合并,你——不知何故——将你的工作和 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)
正如 所暗示的那样<-- ???
,我们很快就会忘记曾经有过原版,然后去掉所有的小勾号,将它们称为A
或D--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
. (它通常更好,你必须解决完全相同的问题:
Run Code Online (Sandbox Code Playgroud)CONFLICT (content): Merge conflict in views/upload_photo.handlebars
无论哪种方式。)