我怎样才能将Git存储库结合到线性历史记录中? [英] How can I combine Git repositories into a linear history?
问题描述
我有两个git仓库 R1
和 R2
,其中包含两个句点的提交
产品的发展:1995-1997和1999-2013。
(我通过将现有的RCS和CVS存储库转换为Git来创建它们)。
$ p $ 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 =他们的R1
git merge --strategy =递归--strategy-option =它们的R2
然而,这留下了最终版本中的文件 D
,
但不是修订 K
。
我可以制作合成提交以删除合并之间的额外文件,
,但这对我来说似乎不够雅观。
此外,通过这种方法,最终结果包含
没有真正发生的合并。
使用git filter-branch
直接从 git-filter-branch 手册页:首先,创建一个新的仓库与原来的两个遥控器一样,就像你之前做的一样。我假设两者都使用分支名称master。
git init回购
cd回购
git remote add R1 /vol/R1.git
git fetch R1
git remote add R2 /vol/R2.git
git fetch R2
接下来,将master(当前分支)指向R2的主字符。
git reset --hard R2 / master
现在我们可以将R1的主历史记录移植到开头。
git filter-branch --parent-filter'seds_ ^ \ $ _- p R1 / master_'HEAD
换句话说,我们插入一个在 D
和 K
之间伪造父母提交,所以新的历史记录如下所示:
A --- B --- C --- D --- K --- L --- M --- N
对 如果你有两个以上的版本库,比如说R1(最早)到R5(最新版),只需重复 作为将 考虑将 现在,不是运行 此时,您可以查看您的历史记录(例如, code> gitk K
到 N
)来查看它看起来是否正确。如果是这样,您可以通过以下方式进行更改:是
K
的父指针改变,因此所有的SHA-1标识符都会改变。提交消息,作者,时间戳记等保持不变。
将两个以上的存储库与filter-branch合并在一起
git reset
和 git filter-branch
命令按照时间顺序排列。
$ b PARENT_REPO = R1
为R2 R3 R4 R5中的CHILD_REPO; do
git reset --hard $ CHILD_REPO / master
git filter-branch --parent-filter'sed's_ ^ \ $ _- p'$ PARENT_REPO / master'''HEAD
PARENT_REPO = $ CHILD_REPO
完成
使用移植物
- parent-filter
选项用于 filter-branch
的替代方法,您可以使用移植机制。
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
有多个根,下面只会链接其中之一。)
<$ p $ (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
git filter-branch
最后,您可以通过移除移植文件来清理所有内容。
rm .git / info / grafts
使用移植可能比使用
- parent-filter
,但它的优点是能够将两个以上的历史记录与单个过滤器分支
。 (你可以用- parent-filter
来做同样的事情,但是这个脚本会非常快速地变得非常难看)。它还有一个好处,可以让你在看到你的改变之前他们成为永久的;如果它看起来不好,只需删除移植文件即可中止。
将两个以上的存储库与移植文件合并在一起
要使用通过R5(最新)的R1(最旧)的移植方法,只需在移植文件中添加多行。 (您运行
echo
命令的顺序无关紧要。)git reset --hard R5 / master
PARENT_REPO = R1
为R2中的CHILD_REPO R3 R3 R4 R5;做
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
完成
git rebase?
其他人建议使用
git rebase R1 / master
来代替git filter-branch
命令。这将采用空提交和K
之间的差异,然后尝试将其应用于D
,结果为:A --- B --- C --- D --- K'--- L'--- M '--- N'
这很可能会导致合并冲突,甚至可能导致虚假如果文件在
D
和K $之间被删除,则在
K'
中创建文件C $ C>。唯一的情况是,如果D
和K
的树相同。
(另一个细微区别是,
git rebase
会改变K'的提交者信息
到N'
,而git filter-branch
不会。)I have two git repositories
R1
andR2
, which contain commits from two periods of a product's development: 1995-1997 and 1999-2013. (I created them by converting existing RCS and CVS repositories into Git.)R1: A---B---C---D R2: K---L---M---N
How can I combine the two repositories into a single one that contains an accurate view of the project's linear history?
A---B---C---D---K---L---M---N
Note that between
R1
andR2
files have been added, deleted, and renamed.I tried creating an empty repository and then merging their contents onto it.
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
However, this leaves in the end files that were in revision
D
, but not in revisionK
. I could craft a synthetic commit to remove the extra files between the merges, but this seems inelegant to me. Furthermore, through this approach the end-result contains merges that didn't actually occur.解决方案Using git filter-branch
Using the trick straight from the git-filter-branch man page:
First, create a new repository with the two original ones as remotes, just as you did before. I am assuming that both use the branch name "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
Next, point "master" (the current branch) to the tip of R2's "master".
git reset --hard R2/master
Now we can graft the history of R1's "master" to the beginning.
git filter-branch --parent-filter 'sed "s_^\$_-p R1/master_"' HEAD
In other words, we are inserting a fake parent commit between
D
andK
so the new history looks like:A---B---C---D---K---L---M---N
The only change to
K
throughN
is thatK
's parent pointer changes, and thus all of the SHA-1 identifiers change. The commit message, author, timestamp, etc., stay the same.Merging more than two repositories together with filter-branch
If you have more than two repositories to do, say R1 (oldest) through R5 (newest), just repeat the
git reset
andgit filter-branch
commands in chronological order.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
Using grafts
As an alternative to using the
--parent-filter
option tofilter-branch
, you may instead use the grafts mechanism.Consider the original situation of appending
R2/master
as a child of (that is, newer than)R1/master
. As before, start by pointing the current branch (master
) to the tip ofR2/master
.git reset --hard R2/master
Now, instead of running the
filter-branch
command, create a "graft" (fake parent) in.git/info/grafts
that links the "root" (oldest) commit ofR2/master
(K
) to the tip (newest) commit inR1/master
(D
). (If there are multiple roots ofR2/master
, the following will only link one of them.)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
At this point, you can look at your history (say, through
gitk
) to see if it looks right. If so, you can make the changes permanent via:git filter-branch
Finally, you can clean everything up by removing the graft file.
rm .git/info/grafts
Using grafts is likely more work than using
--parent-filter
, but it does have the advantage of being able to graft together more than two histories with a singlefilter-branch
. (You could do the same with--parent-filter
, but the script would become very ugly very fast.) It also has the advantage of allowing you to see your changes before they become permanent; if it looks bad, just delete the graft file to abort.Merging more than two repositories together with grafts
To use the graft method with R1 (oldest) through R5 (newest), just add multiple lines to the graft file. (The order in which you run the
echo
commands does not matter.)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
What about git rebase?
Several others have suggested using
git rebase R1/master
instead of thegit filter-branch
command above. This will take the diff between the empty commit andK
and then try to apply it toD
, resulting in:A---B---C---D---K'---L'---M'---N'
This will most likely cause a merge conflict, and may even result in spurious files being created in
K'
if a file was deleted betweenD
andK
. The only case in which this will work is if the trees ofD
andK
are identical.(Another slight difference is that
git rebase
alters the committer information forK'
throughN'
, whereasgit filter-branch
does not.)这篇关于我怎样才能将Git存储库结合到线性历史记录中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!