合并两个 Git 回购并保留历史记录

Merge two Git repos and keep the history

我想扩展我的另一个问题:

我已成功将 2 个不同的存储库合并为一个存储库。我需要一个变基才能成功地做到这一点。楼主说的对,但是我也想保留合并历史。这可能吗?

我有 2 个存储库:

这是变基后的结果。 top repo的时间就是rebase-time。原来的日期丢失了!

我是这样做的:

# Assume the current directory is where we want the new repository to be created
# Create the new repository
git init

# Before we do a merge, we have to have an initial commit, so we'll make a dummy commit
dir > Read.md
git add .
git commit -m "initial commit"

# Add a remote for and fetch the old RepoA
git remote add -f RepoA https://github.com/DimitriDewaele/RepoA

# Do the same thing for RepoB
git remote add -f RepoB https://github.com/DimitriDewaele/RepoB

# Rebase the working branch (master) on top of repoB
git rebase RepoB/master

# Rebase the working branch (master with RepoB) on top op repoA
git rebase RepoA/master

有没有可能有这样的东西?画解决方案!!!

我想保留原始时间+合并历史。

更新-答案

最适合我的答案是使用嫁接点。但其他答案在其他用例中也非常有用。我已经在github上添加了我的结果,所以大家可以评价。

答案 1:最适合我的情况 'graft' 确实为我揭示了正确的工作答案。

GitHub: RepoGraft

回答 2 "LeGEC" 中的 "replace" 选项也为某些用例提供了良好的结果。一个异常留给我:

GitHub: RepoHistory

答案3:值得补充来自'VonC'的答案。在我的案例中,我无法获得“--preserve-merges working”选项。这可能适用于其他场景,但我没有测试这个 furtner。

git rebase 中有两个选项应该对您的情况感兴趣:

p
--preserve-merges

Recreate merge commits instead of flattening the history by replaying commits a merge commit introduces.

--committer-date-is-author-date 

(来自 git am

By default the command records the date from the e-mail message as the commit author date, and uses the time of commit creation as the committer date. This allows the user to lie about the committer date by using the same value as the author date.

测试第二个 rebase 是否不会产生更好的结果:

git rebase -p --committer-date-is-author-date RepoA/master

这个答案提出了一种不同的方式来使用 RepoB 作为活跃的回购协议,并且仍然可以访问 RepoA 历史记录:

使用git replace

# start with a regular clone of the active repo :
$ git clone RepoB

# add repoA as a remote :
$ git remote add -f history https://github.com/DimitriDewaele/RepoA

# get hash of *initial* commit on repoB :
$ git log --oneline origin/master | tail -1
abcdef Initial commit

# get hash of last commit on repoA :
$ git log --oneline history/master | head -1
12345 Merge branch 'develop'

# use 'git replace' to tell git to stitch histories in the log :
$ git replace abcdef 12345

注意:这个操作是在你的机器上完成的,而不是在远程存储库上,所以应该在所有新的克隆上重复。

变体

您可以使用新名称(例如:RepoB:history/master)将 RepoA:master 推送到 RepoB,然后您可以在提交时使用 git replace abcdef history/master,这些提交都存储在RepoB.

如您所见,rebase 不是您要用来将历史缝合在一起的命令(因为它实际上重写了历史)。早期的 Git 有一个专门为你想做的事情设计的功能(hack):graft points. Even better, since 1.6.5 you can use git replace --graft 而不是:

git checkout master
git replace --graft $(git log RepoB/master --format=%H | tail -1) HEAD
git replace --graft $(git log RepoA/master --format=%H | tail -1) RepoB/master
git reset --hard RepoA/master

(git log RepoA/master --format=%H | tail -1 returns 来自 RepoA 的初始提交)

从技术上讲,如果您在 master 中实际上还没有任何有价值的东西,您可以跳过第一个 replace,只产生 RepoB + RepoA 的历史记录。

这些命令在 refs/replace/* 中创建条目,可以推送和拉取这些条目以与其他人共享您修改后的历史记录。或者,如果您不关心保留 RepoA/RepoB 的 SHA,您可以将 permanent 替换为 运行 git filter-branch --all 以生成 "real" 所需血统的提交集。