【问题标题】:Files missing after git mergegit合并后文件丢失
【发布时间】:2016-08-03 19:07:49
【问题描述】:

我已经阅读了有关此的其他问题,但我的问题是在本地 git 中丢失文件的特殊情况,没有踪迹。

我克隆了一个存储库:

 git clone https://github.com/uruddarraju/kubernetes
 cd kubernetes/

我使用以下命令获取项目 kubernetes 的上游:

 git remote add upstrm https://github.com/kubernetes/kubernetes
 git fetch upstrm

这带来了一堆标签如下:

uruddarraju$ git tag
v1.3.2-beta.0
v1.3.3
v1.3.3-beta.0
v1.3.4

我正在尝试将 v1.3.4 合并到分支 tess 或远程源中

git merge v1.3.4

我必须解决很多合并冲突,但有趣的是,我还看到本地文件系统中现在缺少一个添加到 upstrm/v1.3.4 的新文件,我没有看到该文件在 git 状态下。

这怎么可能?我期待这些文件作为合并的一部分添加,但显然它们不是。

uruddarraju$ ls pkg/genericapiserver/
resource_encoding_config.go

我正在寻找文件 resource_config.go 我放弃了合并并签入 v1.3.4 以验证该文件是否存在于该分支中:

uruddarraju$ git checkout -b merege1.3 v1.3.4
uruddarraju$ ls pkg/genericapiserver
resource_config.go
resource_config_test.go
resource_encoding_config.go

我在两个分支上做了一个 git diff,我清楚地看到文件已经添加到 upstrm/v1.3.4 我在 v1.3.4 上运行了以下内容

uruddarraju$ git diff master --name-status
A       pkg/genericapiserver/resource_config.go
A       pkg/genericapiserver/resource_config_test.go
A       pkg/genericapiserver/resource_encoding_config.go

当我反其道而行之时,在 master 上并与 v1.3.4 进行比较时,我看到了:

uruddarraju$ git diff v1.3.4 --name-status
D       pkg/genericapiserver/resource_config.go
D       pkg/genericapiserver/resource_config_test.go
D       pkg/genericapiserver/resource_encoding_config.go

编辑 1:

合并基础如下图:

uruddarraju$ git merge-base --all HEAD v1.3.4
812b9a47d6625b0fd02af18f9b147720f3c6bfce

编辑 2:

我还看到以下警告:

warning: inexact rename detection was skipped due to too many files.
warning: you may want to set your merge.renamelimit variable to at least 4292 and retry the command.

我显然遗漏了一些东西。任何帮助将不胜感激。

【问题讨论】:

  • 您的HEAD 提交和v1.3.4 标识的提交的合并基础是什么?也就是说,运行git merge-base --all HEAD v1.3.4。理想情况下,这将打印出单个 Git 哈希,这是合并基础的 ID。如果有多个 ID,事情会变得有点困难,所以让我们先找到它。
  • LM-SJN-00877661:kubernetes uruddarraju$ git merge-base --all HEAD v1.3.4 812b9a47d6625b0fd02af18f9b147720f3c6bfce

标签: git github merge git-merge


【解决方案1】:

git merge 做了什么

在 Git 中,merge 操作用于将第二条开发线合并到第一条开发线中。

假设 Alice 和 Bob 都在编写一些代码。它们从代码的通用版本开始。 Alice 添加了一项新功能,Bob 添加了一项不同的新功能。

在此过程中,Alice 可能会创建或删除文件,或修改现有文件。 Bob 同样可能创建或删除文件。假设,只是为了论证,Alice 能够在不添加或删除任何文件的情况下进行所有更改,但 Bob 添加文件 n(对于新文件)并删除文件 o(对于旧文件)。

Alice 和 Bob 都提交了他们的更改——可能是一次提交,也可能是多次提交——然后 Alice 将她的工作推送回 origin,以便 Bob 可以看到它。

现在假设 Bob 决定合并 Alice 的工作,即获取她所做的更改。他可能会跑:

$ git fetch origin

这让 Bob 可以获取 Alice 对 Bob 存储库的提交,名称可能为 origin/alice。 (实际名称取决于爱丽丝在git push时使用的名称。)

接下来,Bob 将运行:

$ git merge origin/alice

此合并必须首先找到 合并基础:Alice 和 Bob 分歧并做出 不同 提交的点。让我们假设 Bob 在分支 bob,并绘制一个提交图,以便我们可以看到这个“合并基础”是什么:

...--o--*--o--o--B     <-- bob
         \
          o--o--o--A   <-- origin/alice

在这种情况下,Alice 通过四次提交构建了她的功能,这些提交没有添加和删除新文件,但可能修改了一些现有文件。 Bob 通过三个提交构建了他的特性,其中一些添加了新文件 (new) 和/或删除了现有文件 (old)。合并基础是我标记为* 的提交,而Alice 和Bob 的分支的尖端分别是提交AB

Git 的工作是组合 Alice 的更改,来自四个没有添加或删除任何文件但确实修改了一些文件的提交,加上 Bob 的更改,来自三个确实添加和删除文件的提交。如果一切顺利——如果没有要解决的冲突——最终结果将仍然有一个新文件 new 和一个已删除文件 old,并且 Bob 将获得一个新的 合并提交 @ 987654339@:

...--o--*--o--o--B---M   <-- bob
         \          /
          o--o--o--A     <-- origin/alice

不过,在此合并之前和之后,如果 Bob 要求 Git 区分 BA 的提交,他会看到一些已删除的文件和一些添加的文件。 这些不是 Alice 添加或删除的文件;相反,这些是 Bob 自己添加和删除的文件。

您的特殊情况与 Bob 的情况

Bob 对此可能不会有任何问题,因为他可能记得他做了什么。但是,在您的情况下,更像是您在 Bob 离开小组之后出现,现在正在尝试合并 Alice 和 Bob 的更改,而不知道 Bob 做了什么。

既然我们现在知道(从git merge-base 命令输出)只有一个合并基,我们可以使用带有git diff 的三点语法来比较该合并基与两个分支提示。实际上,这将让我们“弄清楚 Bob 做了什么”和“弄清楚 Alice 做了什么”。

您的提交图可能也比上面示例中的复杂得多,但原则上至少,您的任务是相同的:您有两个特定的提交 A 和 @您正在合并的 987654346@,其中commit B 是您当前分支的HEAD 提交,commit A 是您通过名称v1.3.4 标识的那个。所以你有:

...--o--*--o--o--B     <-- HEAD
         \
          o--o--o--A   <-- v1.3.4

(旁白:尽管v1.3.4 是一个标签,但它在这里的作用和分支一样好。名称HEAD 和你当前的分支名称一样,所以我使用它而不是分支名称.)

提交* 是合并基础——其哈希为812b9a47d6625b0fd02af18f9b147720f3c6bfce。我们可以运行这两个git diff 命令:

git diff 812b9a47d6625b0fd02af18f9b147720f3c6bfce HEAD
git diff 812b9a47d6625b0fd02af18f9b147720f3c6bfce v1.3.4

这需要输入哈希(或复制粘贴)。相反,我们可以使用这种更短的形式:

git diff v1.3.4...HEAD
git diff HEAD...v1.3.4

因为只有一个合并基(812b9a47d6625b0fd02af18f9b147720f3c6bfce),所以这两组命令做同样的事情。第一个向我们展示了“我们做了什么”,第二个向我们展示了“他们做了什么”。 Git 将尝试合并这两组更改。

(请注意,我们可以将--name-status 和/或--stat 添加到这些差异命令中,以获得更改内容的简短摘要,而不是完整差异。这可能对您的情况有用。)

当您遇到冲突时(正如您所做的那样),这仅仅意味着 Git 无法协调“他们的”更改(来自第二个差异)与“我们的”更改(来自第一个差异)。如果他们的更改包括添加文件,Git 不会有任何问题:它会添加这些文件。合并结果中不会缺少它们。不过,据推测,这里发生的事情是“我们”(在“我们的”分支上)删除了文件。

大概我们不需要这些文件,但是在我们通过合并添加到代码中的更改中,它们确实需要这些文件。 Git 无法知道这一点——Git 不知道任何代码的含义意味着。我们只需将这些文件恢复为某种合适的形式,并在必要时修复我们的代码以允许这些文件存在;或者我们将不得不修改他们的代码,以便它在没有这些文件的情况下工作;或两者的某种组合。

旁白:如果不完全是一个合并基地

很少有,但并非不可能,有两个或更多的合并基,甚至有没有个合并基。在这种情况下,git merge 知道该做什么,但git diff 不知道。如果您遇到这种情况,简单的git diff A...B; git diff B...A 方法将无法(完全)向您显示合并过程的输入。没有简单的方法来处理这种情况。在使用git diff A...B 样式差异来确定git merge 看到的内容之前,最好检查(使用git merge-base --all)以确保这不会造成干扰。

【讨论】:

  • Presumably, though, what happened here is that "we" (on "our" branch) removed files. 不正确。我可以git log -- pkg/genericapiserver/resource_config.go 没有任何结果。这意味着我从未删除过该文件,也从未在我的树中拥有该文件。
  • 使用git log 是不确定的,因为如果文件在合并中被删除,它可能会欺骗您。差异(从基础到 HEAD)肯定会告诉你。
猜你喜欢
  • 2012-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-13
  • 1970-01-01
相关资源
最近更新 更多