【问题标题】:git squash changes for one filegit squash 更改一个文件
【发布时间】:2021-05-21 12:56:00
【问题描述】:

我有几个提交对文件进行无效更改,但其中一些提交与对另一个文件的相关更改相结合。所以我只想压缩对一个文件的更改。我该怎么做?

示例: 以下应该象征提交和[......]文件的内容。

这就是我所拥有的:

  • 提交 5 file A: [.....] file B: [......]
  • 提交 4 file A: [ ] file B: [.... ]
  • 提交 3 file A: [.....] file B: [... ]
  • 提交 2 file A: [ ] file B: [... ]
  • 提交 1 file A: [.....] file B: [.. ]

差异是:

  • 提交 5 file A: [+++++] file B: [....++]
  • 提交 4 file A: [-----] file B: [...+ ]
  • 提交 3 file A: [+++++] file B: [... ]
  • 提交 2 file A: [-----] file B: [..+ ]
  • 提交 1 file A: [+++++] file B: [++ ]

在这种情况下,提交 5、3 和 1 处的文件 A 是相同的。

这就是我想要的: 所以我想把它压扁。

  • 提交 5 file A: [.....] file B: [......]
  • 提交 4 file A: [.....] file B: [.... ]
  • 提交 2 file A: [.....] file B: [... ]
  • 提交 1 file A: [.....] file B: [.. ]

差异是:

  • 提交 5 file A: [.....] file B: [....++]
  • 提交 4 file A: [.....] file B: [...+ ]
  • 提交 2 file A: [.....] file B: [..+ ]
  • 提交 1 file A: [+++++] file B: [.. ]

有什么办法吗?

编辑:也许我的问题不是很清楚。我通常知道如何进行交互式变基和壁球。更重要的是,我只想“压缩”文件 A 的无效更改,同时保留文件 B 上的更改。(这样做会导致提交 3 什么都不做,因此它被删除。)

我在“正常”变基时面临的问题是,如果我只是压缩 1-5 的所有提交,我最终会得到我想要的文件 A 的结果,但是对文件 B 的所有中间更改都会丢失.

我添加了一个 diff 的表示,以便更好地描述这种情况。

【问题讨论】:

    标签: git rebase git-rebase undo


    【解决方案1】:
    1. 重新开始:

      git reset {commit 1 HASH}
      

      并根据需要重做提交。

    2. 有新的提交来撤销你之前的提交

      git revert {commit 1 HASH}
      
    3. 壁球提交

      git rebase -i {commit 1 HASH}
      

    如果您对 git-rebase 不太满意,请不要担心。挤压 提交是一种非常简单的技术,可以通过交互来实现 git-rebase 交互式 rebase 将打开编辑器。你可以 看看rebase -i 是如何完成最后三个提交的。并注意 它拥有的选项数量。让我们只关注壁球。 作为 前面提到过,让我们进行一次提交。您可以看到我们已将最后两个提交标记为 squash。您可以使用 squashs 将提交标记为 squash。

    Please continue following this tutorial on this page for squashing commits, it is very explanatory

    我建议选择选项 1。并重新开始,但这是您的选择。

    【讨论】:

      【解决方案2】:

      会有多个交互式变基。总而言之,您应该在交互式变基时编辑 commit 4commit 5。让我解释一下:

      您已经发现压缩commit 2commit 3 是第一步。当我们这样做时,提交历史的开始就像在第一次交互式 rebase 之后:

      • 提交 2 file A: [.....] file B: [... ]
      • 提交 1 file A: [.....] file B: [.. ]

      好吧,让我们分析一下场景。您的file B 更改将被保留,与commit 4commit 5 相同。但是,我们需要摆脱file A 的更改。这是我们将从交互式 rebase 中的 edit 选项获得帮助的地方。

      在交互式变基时,为commit 4commit 5 选择编辑 选项。在编辑两个提交的状态时,将文件 A 的更改与提交分开。因此,在第二次交互式 rebase 之后,示例历史将如下所示:

      1--2--4A--4B--5A--5B
      

      4A 仅包括文件Acommit 4 的更改。然后最终变基;将5A 提交移动到4A 提交旁边并将两个提交压缩到commit 2。还有魔法,因为它们是相反的提交,它们会消失。在第三次交互式 rebase 之后,提交历史将看起来像:

      • 提交 5B file A: [.....] file B: [......]
      • 提交 4B file A: [.....] file B: [.... ]
      • 提交 2 file A: [.....] file B: [... ]
      • 提交 1 file A: [.....] file B: [.. ]

      如你所愿。

      【讨论】:

      • 这个答案在描述变基方面令人困惑(而且不是很准确),并且它错误地暗示需要多个交互式变基。
      • 要应用此解决方案,您至少需要 2 个交互式变基。但是,我更喜欢 3 rebase 来采取坚定的步骤。没有错,是工作作风。
      • 你一直这么说,所以我仔细检查了一下,看看工具的性质是否发生了变化;它没有,我再次能够在 1 内做到这一点。
      【解决方案3】:

      这是可以做到的。这是历史重写,这意味着如果有遥控器,它将涉及强制推送分支,并且如果该遥控器与其他用户共享,那么您需要先与他们协调,否则您将冒着他们将撤消您的风险当他们尝试从您强制推送后遇到的错误中恢复时发生更改。您可以在“从上游 rebase 恢复”下的 git rebase 文档中找到更多关于强制推送到共享存储库的问题。


      因此,在像您的示例这样的小型/简单情况下,您可以使用交互式 rebaes 来执行此操作。

      git rebase -i HEAD~4
      

      这将加载一个包含提交 2、3、4 和 5 的变基“TODO”列表。(在您的示例中,您没有更改提交 1;如果您需要更改它,您可以使用 HEAD~5 in有历史记录的情况,或者在没有历史记录的情况下使用--root 选项。)

      “TODO”列表中的每一行都代表一个提交,并以一个命令开始,该命令告诉 rebase 如何处理该提交。在提交 3 的行上,将命令从“pick”更改为“squash”;这会将来自提交 2 和 3 的更改合并到一个提交中。在提交 4 的行上,将命令从“pick”更改为“edit”。保存并退出编辑器。

      当 rebase 准备好重写提交 4 时,它将暂停并让您在继续之前应用更改。拉取文件A的正确版本

      git checkout HEAD^ -- path/to/file_A
      git add .
      

      然后完成变基

      git rebase --continue
      

      您可以从 rebase 文档页面获取有关所有交互式 rebase 选项的信息。 (还有其他方法可以在简单的情况下获得相同的效果;但在适合小型/简单用例的方法中,rebase -i 是最适合 IMO 的。)

      此过程有点手动,对于包含合并或可从多个分支(或标签或其他参考)访问的更复杂的历史记录,它会遇到问题。在这些情况下,您可以使用git filter-repo。这是一个相当复杂的工具,因此有一些学习曲线。但它确实有不错的文档。

      【讨论】:

        猜你喜欢
        • 2021-03-20
        • 1970-01-01
        • 2019-03-27
        • 1970-01-01
        • 2017-10-27
        • 2013-03-01
        • 2011-03-03
        • 2013-12-04
        • 1970-01-01
        相关资源
        最近更新 更多