我为一个项目创建了一个私有git存储库,并开发了一个为每个主要版本发布创建维护分支的模式。
在项目的几个月后,我现在有许多旧版本的分支机构,不再受任何形式的支持。我意识到这是处理问题的一种混乱方式。作为一个更整洁的替代方案,我想维护一个开发分支和一个发布分支,而不是为各个版本添加标签。
我最好将当前存储库转换为这种新格式。我希望在发布提交和master上的提交之间保持尽可能多的提交历史记录,但我想将所有发布分支转换为一个发布分支。
存储库仅供我使用,因此我可以使用git rebase --onto
轻松地将分支的内容粘贴到另一个分支的末尾 - 我不关心保留确切的提交哈希值在这里。
我关心保留的是与主分支的合并历史。我想重新定义每个分支的根提交,以便它基于前一个分支的提示和它最初从master分支的提交 - 将其转换为合并提交。
然后,我想保留master和该分支之间的许多其他合并,这种合并发生在链上。
我尝试了git rebase --onto branchB-divergence...branchA-tip branchB
(和一些变体,包括尝试-m和-i,同时浏览文档)
结果是意外地重复了master上的很多提交,丢失了合并历史记录,或者经常遇到需要解析的合并冲突。
我正在努力实现的目标是什么?我会遵循什么样的程序重新提出这样的一系列提交?
答案 0 :(得分:2)
好的,基于Philip的回答,我对移植物,替换物和过滤器分支进行了一些研究。
因为我想要实现的是重写历史,假装我首先正确地放置了我的存储库,所以我决定将每个分支的头部移植到尖端。上一个分支,然后运行git filter-branch以使更改成为永久更改。
在新工作流程下,当更改完成并准备进入发布时,它将合并到发布分支中。如果您检查历史记录,我希望它看起来像那些合并发生并且已经解决,无论原始分支头是什么。
如果你仔细看结果,你会注意到实际上没有合并提交 - 每个嫁接的“合并”直接进入下一个提交。这对我来说并不是一个大问题,因为有几十个备用分支浮动,所以我要留下未解决的问题。
的接枝强>
我创建了文件.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
)然后为每个分支添加一个标记最后一次生命支持提交适当的版本号。这样你就可以删除旧的分支引用并仍然可以恢复到旧版本。
或者,使用grafts
或replace
(Are grafts deprecated?)将旧分支系列加入您指定的链中。然后可以通过filter-branch
冻结这些内容。