合并两个Git仓库,保留完整的历史记录/嫁接(不重写)

18

我想在另一个问题上进一步探讨: 合并两个Git存储库并保留主历史记录

我成功地将2个不同的存储库合并为一个存储库。我需要使用rebase来成功完成此操作。主分支是正确的,但我也希望保留合并历史记录。这可能吗?

我有2个存储库:

Repo A

Repo B

这是rebase后的结果。顶部存储库的时间是rebase时间。原始日期已丢失!

Rebase

这是我的做法:

# 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

有没有可能做到这样的效果? (涂抹解决方案!!!)

Painted

我希望保留原始时间和合并记录。

更新 - 答案

对我最有效的答案是使用嫁接点进行操作。但其他答案在其他用例中也非常有用。我已将我的结果添加到 GitHub 上,因此每个人都可以评估。

答案 1:在我的情况下最好的工作方式 'graft' 对我来说揭示了正确的工作答案。

GitHub: RepoGraft

enter image description here

答案 2 "LeGEC" 的“replace”选项对于某些用例也会产生良好的结果。但对我来说仍然存在一个异常。

GitHub: RepoHistory

enter image description here

答案 3:值得添加 'VonC' 的答案。在我的情况下,我无法使选项“--preserve-merges”工作。这可能在其他场景中有效,但我没有进行进一步测试。


1
你是想保留 repoA 仅仅为了查看它的历史记录吗?还是说 repoA 将成为一个活跃的代码库,有新的提交、拉取请求等操作? - LeGEC
@LeGEC 我只想使用分支历史记录来检查历史记录。新的工作将在生成的存储库之上进行。 - Dimitri Dewaele
Dimitri,将答案放入问题中违反了SO的政策。请将您添加到问题中的答案移动到实际答案中,以便(1)尽可能使问题简短和简单,(2)允许人们对您的答案进行投票。 - Inigo
3个回答

15

你已经发现,rebase不是你想要用来合并历史记录的命令(因为它实际上会重写历史记录)。早期版本的Git有一个专门为你尝试做的事情设计的功能(hack):嫁接点。更好的是,自1.6.5以来,你可以使用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 返回 RepoA 的初始提交)

如果你的 master 分支中还没有什么有价值的内容,你可以在第一次使用 replace 时跳过它,这样就可以得到包含 RepoB 和 RepoA 历史记录的结果。

这些命令会创建一个位于 refs/replace/* 中的条目,可以推送和拉取以与他人共享修订后的历史记录。或者,如果你不关心保留 RepoA/RepoB 的 SHA,可以运行 git filter-branch --all 将替换变为永久的,以生成所需谱系的“真实”一组提交。


2
这个答案提供了一种不同的方法来使用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
注意:此操作在您的计算机上执行,而不是远程存储库,因此应在所有新克隆上重复执行。 变体: 您可以将 RepoA:master 推送到 RepoB 以一个新名称(例如:RepoB:history/master),然后您可以在所有存储在 RepoB 中的提交上使用 git replace abcdef history/master

1

在 git rebase 中有两个选项对您的情况很有用:

p
--preserve-merges
重新创建合并提交,而不是通过重放提交来平铺历史记录。
--committer-date-is-author-date 

(来自git am)

默认情况下,该命令将电子邮件消息中的日期记录为提交作者日期,并使用提交创建时间作为提交者日期。这允许用户通过使用与作者日期相同的值来虚假提交者日期。

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

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

请注意,--committer-date-is-author-date 仍会将提交者更改为变基者,这可能是一个问题,也可能不是。你真的不想使用 rebase 来做这件事。 - dahlbyk
1
@dahlbyk 我知道。我建议这只是一个快速修复,但是旧的嫁接点也可能起作用。+1 - VonC
确实,这些是值得了解的好选择。只是对于这个特定的问题来说不太适用。 :) - dahlbyk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,