Git存储库有两个启动提交 - 如何合并它们?

时间:2017-05-23 12:31:39

标签: git git-rewrite-history

我正在将存储库从TFS转换为Git,出于某种原因,我的历史记录的开头是这样的:

*   - Merge from Staging
|\
| * - Foo
| * - Branched from $/TFS/Main
* - Branched from $/TFS/Staging

即。我有两个无关的初始提交相互合并。两个初始提交或多或少是等价的,所以我想做的是“压缩”Merge from StagingBranched from $/TFS/Staging提交,有效地删除Branched from $/TFS/Staging提交以便我的历史看起来像这样:

* - Merge from Staging
* - Foo
* - Branched from $/TFS/Main

据我所知,正常的做法是在分支分歧之前以提交开始的交互式rebase,但我没有在此存储库中提交类似的提交。

我该如何解决这个烂摊子?

2 个答案:

答案 0 :(得分:1)

如果您想修复历史记录,有以下几种方法:

来自Merge from Staging提交的

,您可以运行:

# if $hash is the hash of the '- Foo' commmit :
git rebase -i $hash

# you will see a list with one commit :
# set the command to "edit" (instead of "pick")
# close the editor

# when git says "You can amend the commit ...", run :
git rebase --continue

答案 1 :(得分:1)

我从未使用过TFS(更不用说TFS-to-Git转换程序)并且不能说你为什么得到你所做的,但问题是你实际上有两个单独的root提交,它们只会变成通过合并提交相关。 Git需要通过来自每个提交节点(提交DAG中的顶点)的父链接(传出弧)来提交每个提交 。此外,Git的工作是从最后开始 - 最近的提交,由他们的标签找到 - 并向后工作。

让我重新绘制图形,以便我可以用一个字母标记每个提交(并添加更多的间距,用于视觉强调)。

D   - Merge from Staging
|\
| C - Foo
| |
| B - Branched from $/TFS/Main
|
A - Branched from $/TFS/Staging

提交D提示提交,并且可能在其上有分支标签master,以便Git可以找到它。

提交AB都是根源。提交C是一个非root提交,其父提交为B。提交D将两个分支联系在一起:一个分支以B为根,另一个分支以A为根。(<1}}。

您的目标是完全放弃A。为此,您还必须放弃D。这可能太激烈了。

复制并放弃D

当您放弃D时,您放弃存储在提交D下的树快照与存储在提交C下的树快照之间的差异。这可能是,也可能不是你想要的。 DC之间可能没有差异,或没有显着差异。 (要了解,请直接让Git比较提交CDgit diff <hash-ID-of-C> <hash-ID-of-D>。)

如果您希望保留D,同时丢弃提交D,则需要树复制到新的承诺。有很多方法可以做到这一点,但最简单的方法可能是挑选合并:直接检查提交C(通过哈希ID),在那里创建一个分支git cherry-pick -m <mainline-number> <hash-or-name-for-D>。 &#34;主线号码&#34;您使用(1或2)确定Git带来的更改集合:将DC进行比较时,或将DA进行比较时的更改。您需要在将DC进行比较时获得的结果,以便最终提交与D具有相同的

(由于某些转换器可能是D,我不知道在进行此合并提交时如何安排父链接。所以我不知道你是否想要{{1}或者-m 1来处理这个问题的另一种方法是使用Git的管道命令,在本例中为-m 2,但是这样做了更难描述:管道命令用于脚本,而不是供人类使用。)

因此,假设分支名称git commit-tree标识提交master

D

结果是一个如下图:

$ git checkout <hash-id-of-C>
$ git checkout -b new-master
$ git cherry-pick -m <number> master

您现在可以进入&#34;丢弃D&#34;步骤如下。

放弃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)。

或者,如果您已成功将git commit-tree复制到D,则现在可以放弃E,因为D包含您想要的副本。

要执行 - 现在丢弃E - 你只需要强制分支名称(我再次假设D)直接指向提交master

C

这意味着Git将首先查看提交$ git checkout master $ git reset --hard <hash-id-of-C> ,这是您想要保留的第二个提交,然后继续查看C父级,即C。然后Git会继续尝试B的父级,但由于它是root提交而 没有父级,Git会停在那里,而你只有提交BC。原始BD仍在您的存储库中,通过Git的 reflogs 保留一段时间。最终reflogs也会过期,提交AA变得不受保护,而Git的垃圾收集器D会永久删除它们。

重新合并副本

如果您在分支git gc上提交了E,现在是时候进行提取了。你现在有这个:

new-master

我们可以在没有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

我们要做的就是让Git移动名称E - copy of D (new-master) | C - Foo (master) | B - Branched from $/TFS/Main &#34;转发&#34;所以它指向提交master

E
例如

。这给了我们:

$ git checkout master
$ git merge --ff-only new-master

请注意提交没有更改,永远。我们只需添加 new 提交。除了新提交之外,更改的是分支名称(如E - copy of D (master, new-master) | C - Foo | B - Branched from $/TFS/Main )四处移动以便它们指向较新的提交 - 或者,对于像master这样的特殊情况,他们甚至可能向后移动&#34;。