我从来没有使用过 TFS(更不用说 TFS 到 Git 的转换程序),也不能说为什么你得到了你所做的,但这里的问题是你实际上有两个单独的根提交,这只能变成通过合并提交相关。 Git 需要每个提交通过来自每个提交节点(提交 DAG 中的顶点)的父链接(传出弧)可访问。此外,Git 的工作方式是从末尾开始(通过标签找到的最新提交)并向后工作。
让我重新绘制图表,以便我可以用一个字母标记每个提交(并添加更多间距,以增强视觉效果)。
D - Merge from Staging
|\
| C - Foo
| |
| B - Branched from $/TFS/Main
|
A - Branched from $/TFS/Staging
Commit D 是 tip 提交,大概上面有分支标签 master,这样 Git 就可以找到了。
提交 A 和 B 都是根。提交C 是一个非根提交,其父提交是B。提交D 将两个分支联系在一起:一个分支植根于B,另一个植根于(并且仅包含)A。
您的目标是完全放弃A。为此,您还必须丢弃D。这可能太激烈了。
复制并丢弃D
当您丢弃D 时,您放弃了存储在提交D 下的树快照与存储在提交C 下的树快照之间的任何差异。这可能也可能不是您想要的。 D 和 C 之间可能没有区别,或者没有显着差异。 (要找出答案,请让 Git 直接比较提交 C 和 D:例如 git diff <hash-ID-of-C> <hash-ID-of-D>。)
如果您希望保留D 的树,同时丢弃D 本身的提交,您需要将该树复制 到一个新的提交中。有很多方法可以做到这一点,但最简单的可能是挑选合并:直接检查提交C(通过哈希ID),在那里创建一个分支git cherry-pick -m <mainline-number> <hash-or-name-for-D>。您使用的“主线编号”(1 或 2)决定了 Git 带来的更改集:比较 D 和 C 时的更改,或者比较 D 和 A 时的更改。您希望在比较 D 和 C 时获得那些,以便最终提交具有与 D 相同的 tree。
(由于某些转换器大概制作了D,我不知道它在进行此合并提交时如何安排父链接。所以我不知道您是要-m 1还是-m 2。一个解决这个问题的另一种方法是使用 Git 的 管道命令,在本例中为 git commit-tree,但这更难以描述:管道命令用于脚本,而不是供人类使用。 )
因此,假设分支名称master 标识提交D:
$ git checkout <hash-id-of-C>
$ git checkout -b new-master
$ git cherry-pick -m <number> master
结果是如下图:
D - Merge from Staging (master)
|
| E - copy of D (new-master)
|\|
| C - Foo
| |
| B - Branched from $/TFS/Main
|
A - Branched from $/TFS/Staging
您现在可以继续下面的“丢弃 D”步骤。
丢弃D
如果提交D 的树 无用,您可以完全删除D,根本不用费心将树复制到新提交。这更容易,因为您不必挑选樱桃(或git commit-tree)。
或者,如果您已经成功地将D 复制到E,您现在可以丢弃D,因为E 有您想要的副本。
要做那个——现在丢弃D——你只需要强制分支名称(我在这里再次假设master)直接指向提交C:
$ git checkout master
$ git reset --hard <hash-id-of-C>
这意味着 Git 将首先查看提交 C,这是您想要保留的第二个,然后继续查看 C parent,即 B。然后 Git 会尝试继续到 B 的父级,但由于它是根提交并且 没有 没有父级,Git 会停在那里,并且您只有提交 C 和 B。原始的 D 和 A 仍然存在,在您的存储库中,通过 Git 的 reflogs 保存了一段时间。最终,reflogs 也会过期,提交 A 和 D 变得不受保护,Git 的 垃圾收集器 git gc 将它们永远删除。
重新合并副本
如果你在分支new-master 上提交了E,现在是时候拿起它了。你现在有了这个:
D - Merge from Staging [abandoned - no name]
|
| E - copy of D (new-master)
|\|
| C - Foo (master)
| |
| B - Branched from $/TFS/Main
|
A - Branched from $/TFS/Staging
我们可以在没有D 和A 的情况下重新绘制:
E - copy of D (new-master)
|
C - Foo (master)
|
B - Branched from $/TFS/Main
我们所要做的就是让 Git 将名称 master“向前”移动,使其指向提交 E:
$ git checkout master
$ git merge --ff-only new-master
例如。这给了我们:
E - copy of D (master, new-master)
|
C - Foo
|
B - Branched from $/TFS/Main
请注意,commit 是如何变化的,永远。我们只需添加 new 提交。除了新的提交之外,真正的变化是分支名称(如master)会四处移动,以便它们指向更新的提交——或者,对于像我们的git reset 这样的特殊情况,它们甚至可能“向后”移动。