在 git 中重写非常旧的提交

Rewriting very old commits in git

我必须重写 git 存储库中非常旧的提交,但不确定是否可行。通过重写,我的意思是压缩和重写一些提交。还值得一提的是,存储库中有很多合并提交,其中大部分应该保留。

假设我的回购协议是 A <- B <- C <- D <- E <- ... X <- Y <- Z,我必须压缩 AB 并改写 CD

嗯,我创建了一个新分支 tmp 并做了以下事情:

  1. 我的 tmp 分支引用 D 提交。
  2. 我对所需的提交进行了压缩和改写。
  3. 现在在 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' 时一切正常,除了重复DD' 提交。不确定这是否是一个好方法,但在我的案例中看起来像唯一的方法。

很抱歉,如果有什么不明白的地方,我会尽力澄清。

您可以使用 git filter-branchparent-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,而不是内容。)

要记住的重要一点是,您正在重写历史,这将破坏存储库的所有现有克隆。这么大的东西,最稳妥的是:

  1. 让每个人都知道重写即将到来,并给他们一个截止日期。告诉他们必须将所有更改推送(不一定合并)到 cut-off 到远程,并且从 cut-off 开始他们必须丢弃本地克隆。
  2. 截止时间过后,进行重写,并用结果替换原始仓库。
  3. 一旦你解除警报,每个人都会从新的来源重新克隆。