【问题标题】:Rewriting very old commits in git在 git 中重写非常古老的提交
【发布时间】:2019-11-25 11:29:25
【问题描述】:

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

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

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

  1. 我的tmp 分支引用了D 提交。
  2. 我为所需的提交做了一个 squash 和 reword。
  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' 时,一切正常,除了重复D 和@987654345 @提交。不确定这是否是一种好方法,但在我看来,这是唯一一种。

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

【问题讨论】:

  • "对我来说,这看起来像是为提交 E 更改父级:这永远不会发生。如果它的父级更改,它将不再是E,而是另一个提交E'每一次提交都必须改变历史以反映新的祖先。您可以在 filter-branch 周围进行一些搜索,但请注意,这是相当繁重的,甚至更多(和旧的)提交。
  • 我猜你可以一直到A进行交互式变基。但正如@RomainValeri 提到的,这也会改变每次提交。
  • @mnestorov,是的,但是有很多提交,此外,我必须保存一些合并提交,但是据我所知,使用 rebase 很难做到。
  • @RomainValeri 谢谢,我会的。
  • filter-branch 是正确的工具。与上面的 cmets 不同,这不是一个特别繁重的操作——即使对于大型历史,git 也可以非常有效地处理这个问题。然而,它一个重大的历史重写,这对团队来说是有成本的。

标签: git git-rebase git-rewrite-history


【解决方案1】:

您可以使用git filter-branchparent-filter 来“重新父”E。文档,包括执行您想做的事情的示例,都在这里:

https://git-scm.com/docs/git-filter-branch

与往常一样,提交E 本身不能更改;实际上你正在创建E',它的父级是D',依此类推,直到你得到X'Y'Z'。但是,因为您是“按原样”接受提交的内容,而不是像 rebase 那样重新应用补丁,所以这对于任何提交图都适用(而创建 rebase 将执行的 repo 并不难合并提交有问题,即使被告知要保留合并)。

(你通常是 rebase 而不是像这样 reparenting 的原因是你通常不能假设新 base 与旧 base 具有相同的内容。但在这种情况下听起来你可以;只有提交图领先最多 D'D 不同,而不是内容。)

要记住的重要一点是,您正在重写历史,这将破坏所有现有的 repo 克隆。对于如此全面的事情,最安全的做法是:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-29
    • 2021-11-20
    • 1970-01-01
    相关资源
    最近更新 更多