使用 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
换句话说,我们在之间插入了一个假的父提交D
,K
所以新的历史看起来像:
A---B---C---D---K---L---M---N
K
through的唯一变化N
是它K
的父指针发生了变化,因此所有的 SHA-1 标识符都发生了变化。提交消息、作者、时间戳等保持不变。
将两个以上的存储库与 filter-branch 合并在一起
如果您有两个以上的存储库要做,比如 R1(最旧的)到 R5(最新的),只需按时间顺序重复git reset
andgit 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'
之间删除文件,甚至可能导致创建虚假文件。唯一可行的情况是和的树是相同的。D
K
D
K
(另一个细微的区别是它git rebase
改变了提交者的信息K'
through N'
,而git filter-branch
没有。)