我有两个git存储库R1
和R2
,其中包含提交
从产品开发的两个时期: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
请注意,R1
和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
然而,这留下了修订版D
中的最终文件,
但不在修订版K
中。
我可以制作一个合成提交来删除合并之间的额外文件,
但这对我来说似乎不太优雅。
此外,通过这种方法,最终结果包含合并
实际上没有发生。
答案 0 :(得分:14)
直接使用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
换句话说,我们在D
和K
之间插入一个假的父提交,所以新的历史记录如下:
A---B---C---D---K---L---M---N
K
到N
的唯一更改是K
的父指针更改,因此所有SHA-1标识符都会更改。提交消息,作者,时间戳等保持不变。
如果要处理两个以上的存储库,请说R(最旧的)到R5(最新),只需按时间顺序重复git reset
和git 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
其他几位建议使用K
代替上面的D
命令。这将采用空提交和A---B---C---D---K'---L'---M'---N'
之间的差异,然后尝试将其应用于K'
,从而产生:
D
这很可能会导致合并冲突,如果在K
和D
之间删除了文件,甚至可能导致在K
中创建虚假文件。唯一可行的情况是git rebase
和K'
的树是否相同。
(另一个细微差别是,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。 。如果D
与K
显着不同,我不确定相同的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会回退分支的所有提交,并将它们与您正在重新定位的分支的提交合并。
根据两个分支之间的差异程度,您可能会遇到冲突。但是,使用任何其他方法都无法避免相同的冲突。
祝你好运!