重新授予git提交以拥有两个父项,合并多个分支,并保留将来的合并历史记录

时间:2013-06-04 11:24:00

标签: git git-rebase

我为一个项目创建了一个私有git存储库,并开发了一个为每个主要版本发布创建维护分支的模式。

在项目的几个月后,我现在有许多旧版本的分支机构,不再受任何形式的支持。我意识到这是处理问题的一种混乱方式。作为一个更整洁的替代方案,我想维护一个开发分支和一个发布分支,而不是为各个版本添加标签。

我最好将当前存储库转换为这种新格式。我希望在发布提交和master上的提交之间保持尽可能多的提交历史记录,但我想将所有发布分支转换为一个发布分支。

desired repository layout - before and after

存储库仅供我使用,因此我可以使用git rebase --onto轻松地将分支的内容粘贴到另一个分支的末尾 - 我不关心保留确切的提交哈希值在这里。

我关心保留的是与主分支的合并历史。我想重新定义每个分支的根提交,以便它基于前一个分支的提示和它最初从master分支的提交 - 将其转换为合并提交。

然后,我想保留master和该分支之间的许多其他合并,这种合并发生在链上。

我尝试了git rebase --onto branchB-divergence...branchA-tip branchB(和一些变体,包括尝试-m和-i,同时浏览文档)

结果是意外地重复了master上的很多提交,丢失了合并历史记录,或者经常遇到需要解析的合并冲突。

我正在努力实现的目标是什么?我会遵循什么样的程序重新提出这样的一系列提交?

2 个答案:

答案 0 :(得分:2)

好的,基于Philip的回答,我对移植物,替换物和过滤器分支进行了一些研究。

因为我想要实现的是重写历史,假装我首先正确地放置了我的存储库,所以我决定将每个分支的头部移植到尖端。上一个分支,然后运行git filter-branch以使更改成为永久更改。

在新工作流程下,当更改完成并准备进入发布时,它将合并到发布分支中。如果您检查历史记录,我希望它看起来像那些合并发生并且已经解决,无论原始分支头是什么。

如果你仔细看结果,你会注意到实际上没有合并提交 - 每个嫁接的“合并”直接进入下一个提交。这对我来说并不是一个大问题,因为有几十个备用分支浮动,所以我要留下未解决的问题。



接枝 commit tree with hash labels, before and after grafting

我创建了文件.git/info/grafts。对于此示例,grafts文件中将有两个条目 - 每个新分支父级一个。

我犯的第一个错误就是忘记了提交的原始父母 - 我对主人的一半附件消失了,我花了大约一半的时间来注意。

第二次我得到了移植文件的内容

[commit] [parent] [parent]
hash2    p2       hash1
hash4    p3       hash3



<强>清洗

我收到了一些未经跟踪的文件,这些文件在我工作目录中的早期提交中被删除了。我不确定它们如何在那里结束的机制,但我知道它们已保存在我的提交历史中,因此我只删除了工作目录副本并运行git reset --hard以获得良好的衡量标准。

branch-C已经指向新发布分支的提示,所以我只是要重命名它:

% git branch -m branch-C release

嫁接过程的下一步是使移植物永久化。 这需要使用git filter-branch完全重写提交树

现在重写历史对我来说没问题,因为我是唯一真正使用此存储库的人。有趣的故事,我清理它的主要原因是因为我打算很快与其他人分享回购,我不想在其他人依赖稳定之后试图修复布局时头疼历史。

因为我将重新创建所有这些提交,所以我不希望旧的分支指针挂起,保持旧的,死的提交树可见。

% git branch -d branch-A
Deleted branch branch-A (was hash1).
% git branch -d branch-B
Deleted branch branch-B (was hash3).

由于移植物,没有悬挂技巧可以让git抱怨,所以删除工作顺利进行。

我还在异地同步存储库,因此我的跟踪分支也将保留那些死树。这将在稍后使用push --force解决,但是现在,我只是放弃我的遥控器,这样当我在本地双重检查我的更改时它不会让我感到困惑。

% git remote remove origin



重写历史记录

好的,是时候做不可想象的了。我的本地git存储库具有正确的分支布局,除了发布和主分支的提示之外,没有指向任何提交的指针。

我已经检查了release分支。

没有任何参数,git filter-branch将沿着树走下来并重写历史记录,以使我的所有移植物永久化。

% git filter-branch
Rewrite (...) (73/73)
Ref 'refs/heads/release' was rewritten

到目前为止,我还没有在我的原始计划中创建任何我想要的闪亮标签 - 这是因为标签指针会指向已解散的提交哈希值。

现在我已经检查了结果,我的存储库看起来就像我想要的那样,我将清理移植文件并添加这些标记。

.git/info/grafts现在正在嫁接我不关心的提交,因此,我可以放心地删除该文件,或清除有问题的条目。

 % rm .git/info/grafts

提交树看起来仍然正确,所以我已经完成并添加了我想要的各种标签。

重写历史记录还有最后一步 - 我喜欢同步到的远程存储库。

我正在重新添加我的遥控器,这将使我的提交树看起来一直纠结和生病了一段时间。

% git remote add origin (...)

起初我尝试过做一个普通的git push --force --all,但实际上并没有删除已经失效的分支。有一个选项,它被称为--prune

% git push --force --all
Counting objects: 161, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (61/61), done.
Writing objects: 100% (86/86), 9.06 KiB, done.
Total 86 (delta 48), reused 0 (delta 0)
To (...)
* [new branch]      release -> release

% git push --force --all --prune 
To (....)
 - [deleted]         branch-A
 - [deleted]         branch-B
 - [deleted]         branch-C

一切都很完美,所以我也删除了git filter-branch为发布分支制作的备份信息:

% rm .git/refs/original/refs/heads/release

答案 1 :(得分:1)

不要这样做。为什么不为'LifeSupport'创建一个--orphan分支,它只是一个最后的休息位置,然后将每个死分支合并到它上面(合并策略--ours)然后为每个分支添加一个标记最后一次生命支持提交适当的版本号。这样你就可以删除旧的分支引用并仍然可以恢复到旧版本。

或者,使用graftsreplace (Are grafts deprecated?)将旧分支系列加入您指定的链中。然后可以通过filter-branch冻结这些内容。