2

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

在项目的几个月后,我现在有许多旧版本的分支,它们不再受到任何形式的支持。我已经意识到这是处理问题的一种混乱方式。作为一个更整洁的选择,我想维护一个开发分支和一个发布分支,而不是使用各个版本的标签。

理想情况下,我希望将当前存储库转换为这种新格式。我想尽可能多地保留发布提交和 master 上提交之间的关系的提交历史记录,但我想将所有发布分支转换为一个发布分支。

所需的存储库布局 - 之前和之后

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

我关心的是保留与主分支的合并历史记录。我想重新设置每个分支的根提交,这样既基于前一个分支的尖端,也基于它最初从 master 分支的提交——把它变成一个合并提交。

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

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

结果要么意外地复制了 master 上的大量提交,并丢失了合并历史记录,要么不断出现需要解决的合并冲突。

我正在努力实现的目标是可能的吗?我将遵循什么样的程序来重新处理这样的一系列提交?

4

2 回答 2

2

好的,根据 Philip 的回答,我对移植、替换和过滤分支进行了一些研究。

因为我想要实现的是重写历史,假设我首先正确地布置了我的存储库,所以我决定解决这个问题的方法是将每个分支的头部嫁接到前一个分支的尖端,然后运行 ​​git filter-branch 使更改永久化。

在新的工作流程下,当更改完成并准备好发布时,它将被合并到发布分支中。如果您检查历史记录,我希望它看起来像是发生了这些合并,并且已经解决了有利于原始分支头的任何问题。

如果您过于仔细地查看结果,您会注意到实际上没有合并提交——每个嫁接的“合并”都直接导致下一次提交。这对我来说并不是一个真正的问题,因为有几十个备用分支漂浮在周围,所以我打算让它悬而未决。



嫁接 在嫁接之前和之后提交带有哈希标签的树

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

我犯的第一个错误是忘记包含提交的原始父级——我对 master 的一半附件消失了,我花了大约一半的过程才注意到。

第二次我得到了嫁接文件的内容

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



打扫

我最终得到了一些未跟踪的文件,这些文件已在我的工作目录中浮动的早期提交中被删除。我不确定它们如何在那里结束的机制,但我知道它们保存在我的提交历史中,所以我只是删除了工作目录副本并运行git reset --hard良好的措施。

branch-C 已经指向新发布分支的尖端,所以我将重命名它:

% git branch -m branch-C release

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

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

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

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

由于移植,git 没有可抱怨的悬挂提示,因此删除工作顺利进行。

我还在异地同步存储库,所以我的跟踪分支也会保留那些死树。这将在稍后通过 a 解决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
于 2013-06-05T03:41:13.617 回答
1

不要那样做。为什么不--orphan为“LifeSupport”创建一个分支,它只是一个最终的安息地,然后将每个死分支合并到它上面(合并策略--ours),然后为每个最后的生命支持提交添加一个标签及其适当的版本数字。这样你就可以删除旧的分支引用,并且仍然能够恢复到它们的旧版本。

或者使用graftsreplace (是否不推荐使用移植物?)将旧分支系列加入您指定的链中。然后可以将这些冻结在 via 中filter-branch

于 2013-06-04T12:30:24.557 回答