如何将Git存储库组合成线性历史记录?

时间:2013-04-03 07:08:14

标签: git merge git-rewrite-history git-branch-sculpting

我有两个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

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

我尝试创建一个空的存储库,然后合并它们的内容 在它上面。

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

然而,这留下了修订版D中的最终文件, 但不在修订版K中。 我可以制作一个合成提交来删除合并之间的额外文件, 但这对我来说似乎不太优雅。 此外,通过这种方法,最终结果包含合并 实际上没有发生。

4 个答案:

答案 0 :(得分:14)

使用git filter-branch

直接使用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" master"到了开头。

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

换句话说,我们在DK之间插入一个假的父提交,所以新的历史记录如下:

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

KN的唯一更改是K的父指针更改,因此所有SHA-1标识符都会更改。提交消息,作者,时间戳等保持不变。

将两个以上的存储库与filter-branch

合并

如果要处理两个以上的存储库,请说R(最旧的)到R5(最新),只需按时间顺序重复git resetgit 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

使用移植物

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

考虑将R2/master附加为R1/master的孩子(即比master更新的原始情况。和以前一样,首先将当前分支(R2/master)指向git reset --hard R2/master 的提示。

filter-branch

现在,不是运行.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更多的工作,但它确实具有能够使用单个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 执行相同操作,但脚本会非常快速地变得非常丑陋。)它还具有允许您在更改永久之前查看更改的优点。如果看起来不好,只需删除移植文件即可中止。

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

要使用R1(最旧)到R5(最新)的移植方法,只需在移植文件中添加多行。 (运行git rebase R1/master命令的顺序无关紧要。)

git filter-branch

git rebase怎么样?

其他几位建议使用K代替上面的D命令。这将采用空提交和A---B---C---D---K'---L'---M'---N' 之间的差异,然后尝试将其应用于K',从而产生:

D

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

(另一个细微差别是,N'会改变git filter-branch到{{1}}的提交者信息,而{{1}}则不会。)

答案 1 :(得分:2)

原始海报说明:

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

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

如何将两个存储库合并为一个包含的存储库   准确了解项目的线性历史?

     

如何将两个存储库合并为一个包含的存储库   准确了解项目的线性历史?

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

请注意, R1 R2 文件之间已添加,删除和删除   重命名。

所以我确定如果新的仓库K的第一次提交与旧仓库的最后一次提交D相同或略有修改,那么您可以简单地获取{ {1}}将历史记录转换为R1,然后将R2的提交图表从R2重新绑定到图表上:

R1

非线性历史记录(当您有合并提交时)

假设# From R2 git fetch R1 git checkout master git rebase --onto R1/master --root 的图形是线性。如果它有合并提交,您可以通过指定要保留合并提交来尝试执行相同的操作,

R2

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

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

原来的海报说:

  

请注意, R1 R2 文件之间已添加,删除和删除   重命名。

正如我上面所指出的,如果较新的repo的第一次提交git rebase --preserve-merges --onto R1/master --root 与旧的repo的最后一次提交相同或仅略有不同,K,则应该使用简单的rebase。 。如果DK显着不同,我不确定相同的rebase是否会干净利落。我想在最糟糕的情况下,您可能必须在第一次应用D期间解决很多冲突。

文档

答案 2 :(得分: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

答案 3 :(得分:0)

您需要的只是: git rebase后面跟着你正在改变的分支。

简而言之,rebase会回退分支的所有提交,并将它们与您正在重新定位的分支的提交合并。

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

祝你好运!