15

我有两个 git 存储库R1R2,其中包含来自产品开发的两个时期的提交:1995-1997 和 1999-2013。(我通过将现有的 RCS 和 CVS 存储库转换为 Git 来创建它们。)

R1:
A---B---C---D

R2:
K---L---M---N

如何将两个存储库组合成一个包含项目线性历史准确视图的存储库?

A---B---C---D---K---L---M---N

请注意,已添加、删除和重命名了 betweenR1和文件。R2

我尝试创建一个空存储库,然后将它们的内容合并到其中。

git remote add R1 /vol/R1.git
git fetch R1

git remote add R2 /vol/R2.git
git fetch R2

git merge --strategy=recursive --strategy-option=theirs R1
git merge --strategy=recursive --strategy-option=theirs R2

但是,这会保留在 revisionD中但不在 revision 中的文件K。我可以制作一个合成提交来删除合并之间的额外文件,但这对我来说似乎不优雅。此外,通过这种方法,最终结果包含实际上并未发生的合并。

4

4 回答 4

15

使用 git 过滤器分支

直接使用git-filter-branch手册页中的技巧:

首先,创建一个新的存储库,将两个原始存储库作为远程存储库,就像您之前所做的那样。我假设两者都使用分支名称“master”。

git init repo
cd repo
git remote add R1 /vol/R1.git
git fetch R1
git remote add R2 /vol/R2.git
git fetch R2

接下来,将“master”(当前分支)指向 R2 的“master”的尖端。

git reset --hard R2/master

现在我们可以将R1的“主人”的历史嫁接到开始了。

git filter-branch --parent-filter 'sed "s_^\$_-p R1/master_"' HEAD

换句话说,我们在之间插入了一个假的父提交DK所以新的历史看起来像:

A---B---C---D---K---L---M---N

Kthrough的唯一变化N是它K的父指针发生了变化,因此所有的 SHA-1 标识符都发生了变化。提交消息、作者、时间戳等保持不变。

将两个以上的存储库与 filter-branch 合并在一起

如果您有两个以上的存储库要做,比如 R1(最旧的)到 R5(最新的),只需按时间顺序重复git resetandgit filter-branch命令。

PARENT_REPO=R1
for CHILD_REPO in R2 R3 R4 R5; do
    git reset --hard $CHILD_REPO/master
    git filter-branch --parent-filter 'sed "s_^\$_-p '$PARENT_REPO/master'"' HEAD
    PARENT_REPO=$CHILD_REPO
done

使用移植物

作为使用 to--parent-filter选项的替代方法filter-branch,您可以改用嫁接机制。

R2/master考虑作为 (即,newer than) 的子元素追加的原始情况R1/master。和以前一样,首先将当前分支 ( master)指向R2/master.

git reset --hard R2/master

现在,不用运行filter-branch命令,而是创建一个“graft”(假父).git/info/grafts,将 () 的“根”(最旧)提交链接到R2/master( K) 中的尖端(最新)R1/master提交D。(如果 的根有多个R2/master,下面只链接其中一个。)

ROOT_OF_R2=$(git rev-list R2/master | tail -n 1)
TIP_OF_R1=$(git rev-parse R1/master)
echo $ROOT_OF_R2 $TIP_OF_R1 >> .git/info/grafts

此时,您可以查看您的历史记录(例如,通过gitk),看看它是否正确。如果是这样,您可以通过以下方式使更改永久生效:

git filter-branch

最后,您可以通过删除移植文件来清理所有内容。

rm .git/info/grafts

使用移植可能比使用更多工作--parent-filter,但它确实具有能够将两个以上的历史移植在一起的优势filter-branch。(您可以对 执行相同的操作--parent-filter,但脚本会很快变得非常难看。)它还有一个优点是可以让您在更改变为永久之前看到它们;如果它看起来很糟糕,只需删除移植文件即可中止。

将两个以上的存储库与移植物合并在一起

要使用 R1(最旧)到 R5(最新)的嫁接方法,只需在嫁接文件中添加多行即可。(运行命令的顺序echo无关紧要。)

git reset --hard R5/master

PARENT_REPO=R1
for CHILD_REPO in R2 R3 R4 R5; do
    ROOT_OF_CHILD=$(git rev-list $CHILD_REPO/master | tail -n 1)
    TIP_OF_PARENT=$(git rev-parse $PARENT_REPO/master)
    echo "$ROOT_OF_CHILD" "$TIP_OF_PARENT" >> .git/info/grafts
    PARENT_REPO=$CHILD_REPO
done

git rebase 怎么样?

其他几个人建议使用git rebase R1/master而不是git filter-branch上面的命令。这将获取空提交之间的差异K,然后尝试将其应用于D,从而导致:

A---B---C---D---K'---L'---M'---N'

这很可能会导致合并冲突,如果在和K'之间删除文件,甚至可能导致创建虚假文件。唯一可行的情况是和的树是相同的。DKDK

(另一个细微的区别是它git rebase改变了提交者的信息K'through N',而git filter-branch没有。)

于 2013-04-04T04:06:06.163 回答
2

原始海报说:

R1:
A---B---C---D

R2:
K---L---M---N

如何将两个存储库组合成一个包含项目线性历史准确视图的存储库?

如何将两个存储库组合成一个包含项目线性历史准确视图的存储库?

A---B---C---D---K---L---M---N

请注意,R1R2之间的文件已被添加、删除和重命名。

所以我肯定知道,如果新仓库的K第一次提交与旧仓库的最后一次提交相同或略有修改D,那么您可以简单地将 fetchR1的历史记录到R2中,然后将提交图重新定位R2到图上来自R1

# From R2
git fetch R1
git checkout master
git rebase --onto R1/master --root

非线性历史(当你有合并提交时)

那是假设R2的图是线性的。如果它有合并提交,您可以通过指定要保留合并提交来尝试做同样的事情,

git rebase --preserve-merges --onto R1/master --root

但是,如果您必须解决任何您正在变基的合并中的冲突,您可能需要再次重新解决它们,这可能会很麻烦。

结合两种截然不同的历史?

原海报说:

请注意,R1R2之间的文件已被添加、删除和重命名。

正如我上面所指出的,如果新仓库的K第一次提交 , 与旧仓库的最后一次提交相同或仅略有不同,则简单的 rebase 应该可以工作D。我不确定如果K实际上与D. K我想在最坏的情况下,您可能必须在 rebase的第一次应用期间解决很多冲突。

文档

于 2014-07-22T00:09:07.097 回答
1

这就是我所做的工作:

git init
git remote add R1 /vol/R1.git
git fetch R1
git remote add R2 /vol/R2.git
git fetch R2
git co -B master R2/master
git rebase R1/master
git push -f
于 2013-06-07T01:09:18.073 回答
0

您所需要的只是: git rebase紧随其后的是您要变基的分支。

简而言之,rebase 会回滚分支的所有提交,并将它们与您正在变基的分支的提交合并。

根据两个分支之间的差异程度,您可能会遇到冲突。但是使用任何其他方法都无法避免相同的冲突。

祝你好运!

于 2013-04-04T04:12:51.607 回答