我调整了2个文件并将它们合并到Github。但是,当我运行推送时出现错误:
Unmerged paths:
# (use "git add/rm <file>..." as appropriate to mark resolution)
# deleted by us: somefolder/somefile.ext
但是,我没有碰过这个文件。查看我的本地目录时,我发现该文件仍然完好无损。
有谁知道为什么会这样,我能做些什么呢?
答案 0 :(得分:4)
请参阅“Who is “us” according to git?”
合并时,
us
引用您要合并的分支,而不是它们,即要合并的分支。
因此目标分支应删除此文件。
有关解决方法,请参阅“git merge “deleted by us””:您可以将其添加回来或将其删除。
作为torek details,在这种情况下(您自己没有删除文件)的“我们”意味着一个rebase(如git pull --rebase
)。
请参阅“git rebase, keeping track of 'local' and 'remote'”
答案 1 :(得分:4)
你说:
我调整了2个文件并将它们合并到Github。
这不是Git的工作方式。您运行git commit
以在本地提交 (在您的存储库中)。您运行git fetch
以从另一个Git(例如在GitHub上运行的Git)获取提交,这会在您的存储库中添加更多提交 local 。然后,您可以合并或重新提交本地提交。一切都在当地发生。正如我们所见,这真的开始变得重要。
似乎在整个过程中,您运行了git merge
或git rebase
,或者有一些代理命令(例如git pull
)运行git merge
或{{1}对你而言,因为:
...当我进行推送时,我收到错误[以
git rebase
开始]
这意味着您尚未完成合并或rebase。 merge-or-rebase给出了一个错误,告诉你一个提交系列删除了一个文件,另一个提交系列修改了该文件。
然后问题变成:
您运行Unmerged paths
,还是运行git merge
?这很重要,因为git rebase
声明来自于您在变基时反转的观点。
当然,您希望如何解决这种情况?
这整个&#34;我们/他们的&#34;是一个普遍存在的问题。因为你所做的一切都是本地的。即使您有其他人提交了一些提交,他们仍然会在您的存储库中您的提交。你只是没有亲自制作它们。
我强烈怀疑您是通过deleted by us
(首先运行git rebase
)运行git pull
,然后根据任何内容运行git fetch
或git merge
您给出的说明git rebase
)。
让我们看看合并和rebase如何在其他人带来的新提交的情况下工作。我们从您在存储库中拥有的内容开始,这是一系列提交,最后一个或两个,或者其中很多是您制作的。为简单起见,我们假设您在git pull
分支上创建了它们:
master
此处 H--I <-- master
/
...--F--G
和H
是您提交的内容。我们在另一条线上绘制它们的原因是你的Git记得一些其他 Git拥有其主机的提交,我们在这里作为提交显示I
:
G
现在你运行 H--I <-- master
/
...--F--G <-- origin/master
。这可能会从这里涉及的其他Git获得一些新的提交。我们将这些提交称为git fetch
和J
,并将其绘制在:
K
您现在有两个选项来组合&#34;您的&#34;新工作(提交 H--I <-- master
/
...--F--G--J--K <-- origin/master
)与&#34;他们的&#34;新工作(提交H
)。这些选项为I
和git merge
。使用merge在概念上稍微简单一点,所以我们将从合并开始,但最终都使用我称之为Git的内部合并机制。
Git首先确定合并基础提交。这是您git rebase
上与master
上的&#34;他们&#34;有共同的最后一次提交。也就是说,我们从两个 tip (最右边)提交开始,向后 - 向左 - 直到两个图形线相遇。那是在提交origin/master
。
接下来,Git有效地运行:
G
然后,Git尝试组合这两组更改。假设&#34;我们&#34;更改了文件git diff G I # figure out what "we" did on master
git diff G K # figure out what "they" did on origin/master
和&#34;他们&#34;还更改了相同的文件。但是我们在顶部附近添加了一条新线,他们在底部附近删除了一条线。组合这些更改很容易:只需将新添加的行保留在顶部附近,然后从提交README
中README
的原始版本中删除底部附近的行。这给了我们新的最终版本,Git将把它放在它将要进行的新合并提交中。
如果那是唯一的更改,Git会继续使用新版本G
进行新提交。提交将是README
上的新提交,看起来像这样:
master
但是假设除了这两个&#34;我们&#34; (提交 H--I--L <-- master
/ /
...--F--G--J--K <-- origin/master
)和&#34;他们&#34; (提交H--I
)修改J--K
,我们也触及README
中的一行,他们完全删除 somefile.ext
。这是修改/删除冲突:Git不知道是从somefile.ext
获取文件并保留我们的更改,还是完全从G
删除文件。 Git停止,不进行合并提交G
,而是说存在合并冲突,L
为&#34;未合并的路径&#34;。< / p>
如果我们询问状态,Git会说:
somefile.ext
这是有道理的,因为&#34;我们的&#34;更改是提交 deleted by them: somefile.ext
。提交H--I
也是我们的,因为它们在我们的存储库中(但我们没有制作它们,我们只是从其他人那里获得它们。)
在任何情况下,Git都会在我们的工作树中留下J--K
,这样我们就可以在编辑器中打开文件并根据需要进行修改。然后我们可以somefile.ext
或git add
文件,无论什么是合适的。然后我们运行git rm
来完成合并,我们弄清楚如何处理修改/删除冲突。这将使最终合并提交git commit
,我们将准备好(并且可以)推送。
现在让我们看一下L
的工作原理。
Git仍像以前一样识别合并基础提交,但不是尝试将我们所做的事情(提交git rebase
)直接与他们所做的一起合并,而是Git尝试复制我们的更改 new 提交。 (Git必须执行此复制,因为不能更改任何提交。)
Git基本上做了:
H--I
接下来是:
git checkout <hash-of-K>
我们在git cherry-pick <hash-of-H>
中说,我们修改了H
。我们已经在提交README
,其中&#34;他们&#34;在末尾附近修改了一条线。在提交K
中,我们修改了顶部附近的一行。我们告诉Git:从H
复制其他提交的修改,并进行一次新的提交,执行H
所做的事情。 Git 可以这样做是因为它只是在一个安全的好地方修改现有文件来做到这一点。 Git将结果提交为H
的副本,我们可以将其称为H
,我们现在拥有:
H'
接下来,Git试图挑选提交 H--I [somewhat in limbo]
/
...--F--G--J--K <-- origin/master
\
H' <-- HEAD
。这是我们修改文件I
的地方。但我们依赖于提交somefile.ext
,它基于提交H'
,其中没有 K
。它刚刚消失了!所以Git偶然发现了变化并因合并冲突而停止。
谁修改了文件somefile.ext
?那个提交somefile.ext
,另一个(&#34;他们&#34;)提交我们正在挑选。谁已移除 I
?好吧,这可能是提交somefile.ext
(肯定是K
或J
),但就Git而言,那是我们 。 我们删除了K
。所以现在Git说:
somefile.ext
和以前一样,Git停下来让我们收拾残局。 Git不知道是否应该完全修改或删除 deleted by us: somefile.ext
,因此它将它留在工作树中。我们必须根据我们的提交somefile.ext
和我们的提交J--K
做出决定。我们编辑文件并H--I
或git add
,以完成git rm
无法独立完成的合并工作。但是,这一次,我们应该运行git cherry-pick
。
git commit
Git现在将尝试完成rebase,使用git rebase --continue
获得的任何其他更改制作新副本I'
。但是:请注意,如果我们从I
获得的仅更改为此I
,和,我们通过仍然删除{{}来解决问题1}},现在没有提交更改。运行somefile.ext
会抱怨,并建议我们可能需要somefile.ext
完全删除原始提交git rebase --continue
。
无论哪个是正确的,我们都应该这样做(git rebase --skip
并将I
的休息复制到--continue
或I
并抛弃完全I'
,给予:
skip
或没有I
的情况。
无论如何,我们现在再次准备 H--I [abandoned]
/
...--F--G--J--K <-- origin/master
\
H'--I' <-- master
,与合并后相同。