在git中合并复制存储库的更改,而不使用真正的共同祖先

时间:2016-06-24 21:03:42

标签: git version-control merge

我有一个项目,DemoA是由git存储库构建的,Project1。

不幸的是,DemoA从Project1开始只是一个文件的副本,然后才变成一个实际的长期项目。我现在想让Project1成为DemoA的子模块,但是 - 更重要的是 - 想要在DemoA中合并对从Project1派生的代码所做的更改。

我在DemoA上做了一个子树分割,创建了一个分支P1,它对DemoA中的Project1代码库进行了所有更改。

我还设法在将DemoA实例化为存储库之前添加对DemoA所做的更改。

Project1
A - B - C - D                     - E
Demo1/P1
    (untracked changes) F - G - H - I

where the files in E are identical to F

我想要的是什么:

Project 1
A - B - C - D - E - G - H - I

显然E和F的哈希是不同的,所以当我将Demo1 / P1作为遥控器添加到Project1并尝试合并时,它抱怨没有共同的祖先。

我试过using format-patch,但是git am抱怨了

  

错误:file.xyz:已存在于索引

我正在尝试rebase onto a different branch,正在做:

git rebase -s recursive -X subtree=project1dir --onto (E-hash) (F-hash) emptybranch

但我显然不明白实际上在做什么,因为它似乎并没有真正任何事情。

有干净的方法吗?我不介意这个过程的一些手册,但我想保留历史。

1 个答案:

答案 0 :(得分:2)

这一切都是中等难度(实际难度级别因环境和您对Git的熟悉程度而异)。

如果EF中的文件完全相同,那么(或)一种简单的方法就是放入移植(使用git replace或移植文件)以便Git假装G的父提交是提交E。也就是说,你有:

A--B--C--D--E   <-- master

F-------------G--H--I   <-- refs/remotes/rem/P1

git diff master rem/P1~4根本不产生输出(master名称提交Erem/P1~4名称提交F,而E的两棵树}和F完全匹配。

希望,或许至少作为中间产品,你有这个:

A--B--C--D--E   <-- master
             \
F             G--H--I   <-- refs/remotes/rem/P1

也就是说,你希望Git至少在某些目的和一段时间内假装提交G已提交E作为其父级。

使用git replace模拟旧的可怕的黑客移植物

Git grafts正是这样做的:他们告诉Git 假装某些提交的父级是其他一些提交。但是这些已经被弃用,而不是更通用的git replace。您可以使用git replace创建一个类似于(但至少取代大多数Git命令)G'的新提交G,其中一个区别在于G' E作为其父母。

然后,您可以使用git filter-branch重新复制存储库中的提交,以便此替换变为真实且永久的,而不仅仅是副本。当然,您将为新提交获取新的提交哈希值(G'可以保留其哈希值,但您必须获得新的H'I')。请参阅this answer by Jakub Narębski,然后How do git grafts and replace differ? (Are grafts now deprecated?),其中VonC链接到Jakub的答案。

(Git移植仍然有用,你可以将提交GE的哈希值放到.git/info/graftsecho $(git rev-parse rem/P1~3) $(git rev-parse master) > .git/info/grafts中。但是他们一个可怕的黑客,如果你做这种技巧,最好立即运行你的过滤器分支,就像Jakub指出的那样。)

使用git rebase

您也可以在尝试时使用git rebase --onto,但必须使用现有的(普通的,本地的)分支名称启动此rebase(我不确定emptybranch来自何处)指向提交I。我想也许您缺少的步骤可能是制作这个常规的普通本地分支名称:

git checkout -b rewrite rem/P1
例如,假设名称rem/P1解析为提交I。或者git checkout -b rewrite <hash-of-I>,如果你面前有那个哈希,便于剪切/粘贴。那时你会有这个:

A--B--C--D--E   <-- master

F-------------G--H--I   <-- HEAD -> rewrite, rem/P1

也就是说,您现在处于这个新的rewrite分支上,该分支指向提交I。现在,您可以git rebase --onto master HEAD~3复制当前分支上的最新3次提交 - GHI。副本将为G'H'I'G'的父级为E - master点的提交 - H'的父级为G',依此类推:

              G'-H'-I'   <-- HEAD -> rewrite
             /
A--B--C--D--E   <-- master

F-------------G--H--I   <-- rem/P1

现在您可以删除远程及其远程跟踪分支,因为您拥有所需的提交链。您也可以随时快进master指向提交I',如果这是您想要的。