我正在将存储库从TFS转换为Git,出于某种原因,我的历史记录的开头是这样的:
* - Merge from Staging
|\
| * - Foo
| * - Branched from $/TFS/Main
* - Branched from $/TFS/Staging
即。我有两个无关的初始提交相互合并。两个初始提交或多或少是等价的,所以我想做的是“压缩”Merge from Staging
和Branched from $/TFS/Staging
提交,有效地删除Branched from $/TFS/Staging
提交以便我的历史看起来像这样:
* - Merge from Staging
* - Foo
* - Branched from $/TFS/Main
据我所知,正常的做法是在分支分歧之前以提交开始的交互式rebase,但我没有在此存储库中提交类似的提交。
我该如何解决这个烂摊子?
答案 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可以找到它。
提交A
和B
都是根源。提交C
是一个非root提交,其父提交为B
。提交D
将两个分支联系在一起:一个分支以B
为根,另一个分支以A
为根。(<1}}。
您的目标是完全放弃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>
。 &#34;主线号码&#34;您使用(1或2)确定Git带来的更改集合:将D
与C
进行比较时,或将D
与A
进行比较时的更改。您需要在将D
与C
进行比较时获得的结果,以便最终提交与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会停在那里,而你只有提交B
和C
。原始B
和D
仍在您的存储库中,通过Git的 reflogs 保留一段时间。最终reflogs也会过期,提交A
和A
变得不受保护,而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;。