加入同一存储库的旧版本的历史记录

Join history of older version of the same repository

我有一个包含这段历史的存储库:

A---B---C---D

然后,这个存储库被“拆分”(基本上,创建了另一个存储库,它的历史从 'D' 开始,使用 git-子树)。

现在,我有另一个具有这段历史的回购协议:

# The same D as the other
D---E---F---G

如何将同一项目故事情节的这两个“部分”合并到一个存储库中?
最后的结果一定是:

A---B---C---D---E---F---G

我已经尝试了很多方法,但所有方法都包括合并,但这不是我想要的,因为合并不会保留一些更改,例如已删除的文件。
此外,我尝试为存储库的最后一个版本的所有更改生成补丁并将它们应用到旧版本中,但是出现了很多 error: <file> already exists in index 错误。

更新

我发现 this other question 关于重新提交提交,这正是解决我的问题的方法,git replace --graftgit filter-branch 的组合。

更新 2

现在我的任务完成了,我发布了下面问题的完整、正确的答案。

您可以创建一个新的存储库,将两个存储库添加为远程存储库,然后将第二个存储库变基到第一个:

这是回购协议 1:

repo1[master]/$ git log --oneline
b3ae047 D
5c68b5e C
4a0bfe9 B
0d88f30 A
repo1[master]/$ git grep -e .
a:a
b:b
c:c
d:d

这是回购协议 2:

$ cd ../repo2/
repo2[master]/ $ git log --oneline
7b05da3 G
3a72ace F
acd2388 E
5bfa6b3 D
repo2[master]/$ git grep -e .
a:a
b:b
c:c
d:d
e:e
f:f
g:g

以与 repo 1 相同的提交 D 开头:

repo2[master]/$ git log --oneline HEAD~3
5bfa6b3 D
repo2[master]/$ git grep -e . HEAD~3
HEAD~3:a:a
HEAD~3:b:b
HEAD~3:c:c
HEAD~3:d:d

现在让我们创建一个连接它们的存储库:

repo2[master]/$ mkdir ../repo3
repo2[master]/$ cd ../repo3
repo3$ git init
repo3[master]/$ git remote add r1 ../repo1
repo3[master]/$ git remote add r2 ../repo2
repo3[master]/$ git fetch r1 && git fetch r2
...boring output omitted...

现在我们要从 repo 2 的顶端变基:

fat:repo3[master]/$ git reset --hard r2/master
HEAD is now at 7b05da3 G

您需要执行 git rebase -i r1/master,并删除第一个提交 D,因为它重复 r1/master。如果你使用完整的命令行(并且没有在 gitconfig 中配置编辑器):

repo3[master]/$ export EDITOR='sed -ibak 1d'
repo3[master]/$ git rebase -i r1/master
Successfully rebased and updated refs/heads/master.
repo3[master]/$ git log --oneline
fc2eb8e G
de5161b F
e85ce17 E
b3ae047 D
5c68b5e C
4a0bfe9 B
0d88f30 A
repo3[master]/ (INT)$ git grep -e .
a:a
b:b
c:c
d:d
e:e
f:f
g:g

更新 - 真正完美的方法:

准备

# Inside the older repo
$ cd old_repo

# Add the remote to newer repo with updated content
$ git remote add <remote name> <new_repo>

# Fetch the remote
$ git fetch <remote name>

# Track all branches of the remote so you have all of it's history in your older git (be aware of the remote's name in the command)
$ for b in `git branch -r | grep -v -- '->'`; do git branch --track ${b##<remote name>/} $b; done

# Delete the remote so you avoid messing up with the newer repo
$ git remote remove <remote name>

现在,我强烈建议您在此回购中使用可视化工具(如 Gitkraken),因为现在那里有点乱。您将拥有两个相互独立的历史记录,可能有很多重复提交。

现在,选择要操作的提交。让我们用散列 A 调用较旧历史记录中的提交,它现在将是最新历史记录 B 提交的 parent。现在,您可以使用下面的脚本(或 运行 手动命令)来加入树并清理留下的混乱(trim 提交 B 时的较新历史记录,丢弃所有 parents,因为现在它有一个新的 parent).
(您必须安装 git-replace 和 git-filter-repo

#!/bin/sh

# Argument "" is commit A, and argument "" is commit B of the explanation above

if [ -z "" ] || [ -z "" ]
then
        echo "You must provide two commit hashes for this script";
        exit 1;
fi

git replace --graft  
result="$?"

[ "$result" = "0" ] && git filter-repo --force

年长者,不重要(仅用于学习不该做什么),请在下面回答。

首先我尝试了 git-rebase 的方法,但由于多种原因没有成功,最大的一个原因是它有点矫枉过正,比如只改变 parent对另一个提交的提交,即使它与历史无关。
然后我尝试 git cherry-pick 将点 E..G 的所有历史记录重新应用到旧存储库,但由于多种原因也没有奏效,但主要的原因是它没有递归复制其他分支。

尝试过的方法

$ git replace --graft <commit> <new parent to this commit>
现在,将 HEAD 放在新历史记录的顶端(您要保留的主线中最近的提交),然后:
$ git filter-branch <new parent to this commit>..HEAD

您可能会丢失尚未合并到 HEAD 所在的分支的分支,我暂时找不到解决方法。