在 git 中重写非常旧的提交
Rewriting very old commits in git
我必须重写 git 存储库中非常旧的提交,但不确定是否可行。通过重写,我的意思是压缩和重写一些提交。还值得一提的是,存储库中有很多合并提交,其中大部分应该保留。
假设我的回购协议是 A <- B <- C <- D <- E <- ... X <- Y <- Z
,我必须压缩 A
和 B
并改写 C
和 D
。
嗯,我创建了一个新分支 tmp
并做了以下事情:
- 我的
tmp
分支引用 D
提交。
- 我对所需的提交进行了压缩和改写。
- 现在在
tmp
分支中我有这样的东西 - A' <- C' <- D'
。顺便说一句,rebase 忽略 D
之前的所有合并提交实际上很好,但是必须保留 D
之后的合并提交。
现在我想以某种方式合并我的 tmp
分支和 master
分支以获得如下内容:A' <- C' <- D' <- E <- ... <- X <- Y <- Z
以及从 E
开始的所有合并提交。对我来说,它看起来像是为提交 E
更改父级,但不确定是否可以这样做。可能还有其他一些方法可以实现我的需要。
更新
我尝试在 master
分支上创建一个 git replace D D'
,在此之后 git filter-branch -- tmp
几乎成功了。不幸的是,当我在 D
移动到底部并且历史记录看起来像 A <- E <- F <- A' <- C' <- D' <- G <- ... <- X <- Y <- Z
之后执行替换一些命令时,这对我来说很奇怪,但是当我尝试 git replace C D'
时一切正常,除了重复D
和 D'
提交。不确定这是否是一个好方法,但在我的案例中看起来像唯一的方法。
很抱歉,如果有什么不明白的地方,我会尽力澄清。
您可以使用 git filter-branch
和 parent-filter
到 "re-parent" E
。文档(包括执行您想做的事情的示例)位于此处:
https://git-scm.com/docs/git-filter-branch
与往常一样,提交 E
本身无法更改;实际上,您正在创建 E'
,它具有 D'
的父级,依此类推,直到您获得 X'
、Y'
、Z'
。但是,因为您接受提交的内容 "as is" 而不是像 rebase 那样重新应用补丁,所以这适用于任何提交图(而在 rebase
处创建一个 repo 并不是很难将对合并提交做错误的事情,即使被告知保留合并)。
(你通常会变基而不是像这样重新设置父级的原因是,你通常不能假设新基与旧基具有相同的内容。但在这种情况下,听起来你可以;只有提交D'
之前的图表不同于 D
,而不是内容。)
要记住的重要一点是,您正在重写历史,这将破坏存储库的所有现有克隆。这么大的东西,最稳妥的是:
- 让每个人都知道重写即将到来,并给他们一个截止日期。告诉他们必须将所有更改推送(不一定合并)到 cut-off 到远程,并且从 cut-off 开始他们必须丢弃本地克隆。
- 截止时间过后,进行重写,并用结果替换原始仓库。
- 一旦你解除警报,每个人都会从新的来源重新克隆。
我必须重写 git 存储库中非常旧的提交,但不确定是否可行。通过重写,我的意思是压缩和重写一些提交。还值得一提的是,存储库中有很多合并提交,其中大部分应该保留。
假设我的回购协议是 A <- B <- C <- D <- E <- ... X <- Y <- Z
,我必须压缩 A
和 B
并改写 C
和 D
。
嗯,我创建了一个新分支 tmp
并做了以下事情:
- 我的
tmp
分支引用D
提交。 - 我对所需的提交进行了压缩和改写。
- 现在在
tmp
分支中我有这样的东西 -A' <- C' <- D'
。顺便说一句,rebase 忽略D
之前的所有合并提交实际上很好,但是必须保留D
之后的合并提交。
现在我想以某种方式合并我的 tmp
分支和 master
分支以获得如下内容:A' <- C' <- D' <- E <- ... <- X <- Y <- Z
以及从 E
开始的所有合并提交。对我来说,它看起来像是为提交 E
更改父级,但不确定是否可以这样做。可能还有其他一些方法可以实现我的需要。
更新
我尝试在 master
分支上创建一个 git replace D D'
,在此之后 git filter-branch -- tmp
几乎成功了。不幸的是,当我在 D
移动到底部并且历史记录看起来像 A <- E <- F <- A' <- C' <- D' <- G <- ... <- X <- Y <- Z
之后执行替换一些命令时,这对我来说很奇怪,但是当我尝试 git replace C D'
时一切正常,除了重复D
和 D'
提交。不确定这是否是一个好方法,但在我的案例中看起来像唯一的方法。
很抱歉,如果有什么不明白的地方,我会尽力澄清。
您可以使用 git filter-branch
和 parent-filter
到 "re-parent" E
。文档(包括执行您想做的事情的示例)位于此处:
https://git-scm.com/docs/git-filter-branch
与往常一样,提交 E
本身无法更改;实际上,您正在创建 E'
,它具有 D'
的父级,依此类推,直到您获得 X'
、Y'
、Z'
。但是,因为您接受提交的内容 "as is" 而不是像 rebase 那样重新应用补丁,所以这适用于任何提交图(而在 rebase
处创建一个 repo 并不是很难将对合并提交做错误的事情,即使被告知保留合并)。
(你通常会变基而不是像这样重新设置父级的原因是,你通常不能假设新基与旧基具有相同的内容。但在这种情况下,听起来你可以;只有提交D'
之前的图表不同于 D
,而不是内容。)
要记住的重要一点是,您正在重写历史,这将破坏存储库的所有现有克隆。这么大的东西,最稳妥的是:
- 让每个人都知道重写即将到来,并给他们一个截止日期。告诉他们必须将所有更改推送(不一定合并)到 cut-off 到远程,并且从 cut-off 开始他们必须丢弃本地克隆。
- 截止时间过后,进行重写,并用结果替换原始仓库。
- 一旦你解除警报,每个人都会从新的来源重新克隆。