当我未触及文件时,为什么Git会说文件已被“我们删除”?

Mel*_*Dog 5 git

我调整了2个文件,然后将它们合并到Github。但是,当我运行一次推送时,出现错误:

Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#       deleted by us:   somefolder/somefile.ext
Run Code Online (Sandbox Code Playgroud)

但是,我还没有碰过这个文件。当查看我的本地目录时,我看到文件仍然完整无缺。

有谁知道这是为什么发生的,我能对此做些什么?

Von*_*onC 5

请参阅“根据 git,谁是“我们”?

当您合并时,us指的是您要合并到的分支,而不是它们,指的是要合并的分支。

所以目标分支应该删除这个文件。

请参阅“ git merge “由我们删除” 解决方案:您可以将其添加回来或将其删除。

作为torek details,在这种情况下(您没有自己删除文件)的“我们”意味着变基(如 a git pull --rebase)。

参见“ git rebase,跟踪‘本地’和‘远程’

  • 本地(“我们”)引用了部分重新提交的提交:“我们的”(上游分支)
  • 远程(“他们”)指的是传入的更改:“他们的” - 变基之前的当前分支,即您没有删除该文件的分支。


tor*_*rek 5

你说:

\n\n
\n

我调整了2个文件,然后将它们合并到Github上。

\n
\n\n

这不是 Git 的工作方式。您运行以在本地git commit(在您的存储库中)进行提交。您运行以从另一个 Git(例如在 GitHub 上运行的 Git)获取提交,这会在您的存储库本地添加更多提交。然后您可以在本地合并或变基提交。一切都发生在本地。正如我们将看到的,这一点很快就会变得重要。git fetch

\n\n

似乎在这个过程中的某个地方,您运行了git mergeor ,或者为您 运行git rebase了一些代理命令(例如git pull) ,因为:git mergegit rebase

\n\n
\n

...当我运行推送时,出现错误 [以 开头Unmerged paths]

\n
\n\n

这意味着您尚未完成合并或变基。合并或变基给您一个错误,告诉您您的一个提交系列删除了一个文件,而另一个提交系列修改了该文件。

\n\n

那么问题就变成了:

\n\n
    \n
  • 你是跑了git merge,还是跑了git rebase?这很重要,因为deleted by us当您重新设定基础时,该声明是从相反的角度出发的。

  • \n
  • 当然,您想做什么来解决这种情况?

  • \n
\n\n

整个“我们的/他们的”符号存在一个普遍问题,因为您所做的一切都是本地的。即使您收到了其他人所做的一些提交,它们仍然是在存储库中的提交。你只是没有自己做。

\n\n

我强烈怀疑您运行了git rebase,也许通过了git pull(首先运行git fetch,然后根据您给出的任何指示运行git merge或)。git rebasegit pull

\n\n

为什么需要合并或变基

\n\n

让我们看一下在存在从其他人那里引入的新提交的情况下合并和变基如何工作。我们从您的存储库中的内容开始,这是一系列提交,最后一两次或无论其中有多少都是所做的。为简单起见,我们假设您是在您的master分支上制作了它们:

\n\n
          H--I   <-- master\n         /\n...--F--G\n
Run Code Online (Sandbox Code Playgroud)\n\n

这里HI是您所做的承诺。我们将它们放在单独的行上的原因是,您的 Git 会记住其他某个Git作为master 的提交,我们在此处将其显示为 commit G

\n\n
          H--I   <-- master\n         /\n...--F--G   <-- origin/master\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在你跑了git fetch。这可能会从此处涉及的其他 Git 获取一些新的提交。让我们将这些提交称为JK,并将它们绘制出来:

\n\n
          H--I   <-- master\n         /\n...--F--G--J--K   <-- origin/master\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在,您有两个选项可以将“您的”新工作 (commit H) 与“他们的”新工作 (commit I) 结合起来。这些选项是git mergegit rebase。使用合并在概念上更简单一些,所以我们将从合并开始,但两者最终都使用我所说的 Git 内部合并机制

\n\n

合并如何工作

\n\n

Git 首先识别合并基础提交。master这是您在您的 上与“他们”在您的 上的最后一次提交origin/master。也就是说,我们从两个尖端(最右边)提交开始,向后\xe2\x80\x94向左\xe2\x80\x94工作,直到两条图形线相遇。那是在 commit 处G

\n\n

接下来,Git 有效运行:

\n\n
git diff G I   # figure out what "we" did on master\ngit diff G K   # figure out what "they" did on origin/master\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后 Git 尝试合并两组更改。假设“我们”更改了文件README,“他们”也更改了同一个文件。但我们在顶部附近添加了一行新线,而他们在底部附近删除了一行。组合这些更改很容易:只需将新添加的行保留在顶部附近,并从READMEin commit的原始版本中删除底部附近的行G即可。这给了我们新的最终版本,Git 会将其放入它将进行的新合并提交中。

\n\n

如果这是唯一的更改,Git 将继续使用新版本的README. 该提交将是一个新的提交,位于master,并且看起来像这样:

\n\n
          H--I--L   <-- master\n         /     /\n...--F--G--J--K   <-- origin/master\n
Run Code Online (Sandbox Code Playgroud)\n\n

但是假设除了“我们”(提交H--I)和“他们”(提交J--K)修改之外README,我们还触及了 中的一行somefile.ext,并且它们完全删除了。 somefile.ext这是修改/删除冲突:Git 不知道是否要从中获取文件G并保留我们的更改,或者完全删除该文件G。Git 停止,不进行合并提交L,而是说存在合并冲突,somefile.ext作为“未合并的路径”。

\n\n

如果我们询问状态,Git 会说:

\n\n
    deleted by them: somefile.ext\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是有道理的,因为“我们的”更改是提交H--I。提交J--K也是我们的,因为它们在我们的存储库中(但我们没有做出它们,我们只是从其他人那里得到它们)。

\n\n

无论如何,Git 都会留somefile.ext在我们的工作树中,以便我们可以在编辑器中打开文件并根据需要进行修改。然后我们可以git addgit rm文件,任何合适的。然后我们运行git commit来完成合并,并弄清楚如何处理修改/删除冲突。这将进行最终的合并提交L,我们将准备好(并且确定)推送。

\n\n

Rebase 稍微复杂一些,但也有同样的问题

\n\n

现在让我们看看它是如何git rebase工作的。

\n\n

Git 仍然像以前一样识别合并基础提交,但它不是尝试H--I直接将我们所做的(提交)与他们所做的合并,而是尝试将我们的更改复制新的提交。(Git 必须执行此复制,因为任何提交都无法更改。)

\n\n

Git 本质上是:

\n\n
git checkout <hash-of-K>\n
Run Code Online (Sandbox Code Playgroud)\n\n

其次是:

\n\n
git cherry-pick <hash-of-H>\n
Run Code Online (Sandbox Code Playgroud)\n\n

假设在 中H,我们修改了README. 我们已经坐在 commit 上K,其中“他们”修改了接近末尾的一行。在 commit 中H,我们修改了靠近顶部的一行。我们告诉 Git:复制其他提交的修改H并创建一个新的提交来完成所做的事情H Git可以做到这一点,因为这只是在一个安全的地方修改现有文件即可。Git 将结果作为 的副本提交H,我们可以将其称为H\',现在我们有:

\n\n
          H--I   [somewhat in limbo]\n         /\n...--F--G--J--K   <-- origin/master\n               \\\n                H\'  <-- HEAD\n
Run Code Online (Sandbox Code Playgroud)\n\n

接下来,Git 尝试挑选提交I。这是我们修改文件的地方somefile.ext。但是我们正在使用 commit H\',它基于 commit K,而 commit没有 somefile.ext. 它就这样消失了!因此,Git 遇到了更改并因合并冲突而停止。

\n\n

谁修改了文件somefile.ext?这就是 commit I,我们正在挑选的另一个(“他们”)提交。谁删除了 somefile.ext?嗯,这可能是提交K(确实是JK,但就 Git 而言,那就是我们我们删除了somefile.ext. 所以现在 Git 说:

\n\n
    deleted by us: somefile.ext\n
Run Code Online (Sandbox Code Playgroud)\n\n

和以前一样,Git 停下来让我们清理混乱。Git 不知道是否somefile.ext应该修改或完全删除,因此它将其保留在工作树中。我们必须根据我们的承诺J--K和我们的承诺做出决定H--I。我们编辑文件和git add它,或者它,以完成单独无法完成的git rm合并工作。git cherry-pick不过,这一次git commit我们应该运行 ,而不是git rebase --continue

\n\n

Git 现在将尝试完成变基,I\'使用我们从 .gitignore 获得的任何其他更改创建新副本I。但是:请注意,如果我们得到的唯一I更改是 this somefile.ext并且我们通过 still-removing 解决了问题somefile.ext,那么现在没有需要提交的更改。运行时git rebase --continue抱怨,并建议我们可能需要完全git rebase --skip放弃原始提交。I

\n\n

无论哪一个是正确的,我们都应该这样做(--continue并将其余部分复制II\',或skip并完全扔掉I),给出:

\n\n
          H--I   [abandoned]\n         /\n...--F--G--J--K   <-- origin/master\n               \\\n                H\'--I\'  <-- master\n
Run Code Online (Sandbox Code Playgroud)\n\n

或同样不带I\'.

\n\n

无论如何,我们现在已经准备好git push再次进行,就像合并后一样。

\n