鉴于这种情况
A--B
| B1
| B2
| ...
| Bn label:b-branch
|
C--D1
| D2
| ...
| Dn label:d-branch
|
E
我想把Bn
挂钩到D1
所以我们有一个很好的清洁路径,即使Bn
和D1
是完全无关的。我不需要做任何花哨的合并或以其他方式影响任何实际的源代码,因为D
分支完全取代B
分支,我只是想以某种方式说Bn
现在是D1
的另一个父亲以及C
,即:
A--B
| B1
| B2
| ...
| Bn label:b-branch
| | <- Only this link is new
C--D1
| D2
| ...
| Dn label:d-branch
|
E
代码托管在私有GitLab实例上,因此我知道每当我们更改历史记录时,每个人都必须同步。
从git replace
开始。我不太确定以哪种方式阅读你的图表,所以我只想说你可以用--edit
或--graft
。这将取代一些提交,替换有你喜欢的任何改变。
新的替换实际上不在图中!例如,假设我是以下图表,我们从右侧开始阅读并向左工作以便及时返回:
A <-B <-C <-D <-E <-- master
提交E
将D
作为其父级,其中C
作为其父级,依此类推。
我们可以使用C'
创建一个新的提交A
,其父级是git replace --graft hash-of-C hash-of-A
,给出:
A--B--C--D--E <-- master
\
C' <-- refs/replace/hash-of-C
当git log
或其他类似命令走向图形时,它们可能从E
开始并显示它,然后移动到D
并显示它。接下来发生了棘手的一点:他们转移到C
,但此时,他们注意到refs/replace/hash-of-C
存在。他们完全放开了C
而转而使用C'
,并表明了这一点。他们移动到C'
s父母A
并表明。结果是B
似乎消失了。同样的伎俩适用于你的情况:如果你想引入两个只有一个父母的父母,那你就做了一个有两个父母而不是一个父母的替代品。
这种替换提交的缺点是git clone
通常会省略refs/replace/
命名空间及其提交。所以克隆存储库的人会看到原始提交 - 他们的Git根本没有refs/replace/hash
,并且从不复制替换提交。
你可以安排克隆这些克隆,但是现在你可以使用一个不用操作的git filter-branch
来“粘合”新存储库中的替换。例如,之后:
git filter-branch --tag-name-filter cat -- --all
在我上面提到的五个提交和替换存储库中,您将拥有:
A--B--C--D--E <-- refs/original/refs/heads/master
\
C' <-- refs/replace/hash-of-C
\
D'-E' <-- master
通过制作另一个克隆来丢弃refs/original/
和refs/replace/
命名空间名称,最终得到:
A--C'-D'-E' <-- master
这使嫁接成为永久性的。因此,您现在可以克隆存储库并使用克隆作为替换存储库,或者只是丢弃原始存储库中的refs/replace/
和refs/original/
名称,使其看起来与建议的克隆替换名称相同(除了原始对象倾向于逗留一段时间 - 基本上,直到垃圾收集器到处清理它们。