【问题标题】:How do i move partial changes (hunks) between commits?如何在提交之间移动部分更改(大块)?
【发布时间】:2017-12-16 23:05:07
【问题描述】:

如果我有两个提交,它们之间的距离很多,并且两个提交都提交了很多文件,那么最好将一个大块从一个移动到另一个,例如:

在提交 100 中: 我有很多文件更改和文件“aa.txt”中的许多更改,还有这个: - aaaa - bbbb + cccc

10 次提交之后我有另一个提交更改了很多文件以及“aa.txt”: +dddd

我想将换行-aaaa 从前者移到后者。

是否有任何 CLI/UI 工具可以帮助您轻松完成此操作? (我显然对历史重写没有问题)

【问题讨论】:

    标签: git


    【解决方案1】:

    交互式变基可能会起作用。但很多因素可能会使这复杂化。所以你已经描述了这么多:

    x -- x -- A -- B -- C -- D -- x -- x <--(master)
    

    A 中更改了某些行,因此在BCD 等中也有所不同。但是您想要一个直到D 才更改的新历史记录。所以你可以说

    git rebase --interactive A^ master
    

    (其中A 是之前进行更改的提交的SHA ID,并注意位于所述ID 末尾的^)。在出现的文本编辑器中,您会看到一个“待办事项”列表。第一行说 pick 提交 A;将其更改为edit。然后找到 D 的行(在此示例中是 3 次提交,但在您所述的示例中可能是 10 次提交)。也将其标记为edit

    rebase 将开始,但在试探性地重新应用 A 后它将暂停,以便您可以编辑提交。

    现在,如果您无法手动/从内存中轻松地撤消相关更改,那么您可以取消暂存对该文件的更改

    git reset HEAD^ -- aa.txt
    

    然后以“补丁模式”再次将它们暂存。

    git add --patch -- aa.txt
    

    系统将提示您如何处理每个更改块。如果您要删除的更改与其他更改出现在同一块中,您可以使用 e 回答提示并编辑更改块(然后将您不再想要删除的行之前的 - 替换为)。

    现在让您的(分阶段)编辑进入提交

    git commit --amend
    

    您从索引恢复的更改仍在工作树中;把它移开,然后告诉 rebase 回去工作。

    git stash 
    git rebase --continue
    

    随着 rebase 继续朝着提交 D 工作,介入的提交可能会发生冲突(如果他们编辑的 aa.txt 太接近您还原的更改)。这些冲突应该很容易解决。 (冲突的HEAD 一方将包括您不再删除的行;除了该行,您可能想要冲突的“另一方”。)

    提交D 也可能会出现冲突。如果是这样,您的工作很容易:只需通过保留冲突的“另一面”来解决该问题(包括删除有问题的行,因为这是您最终想要这样做的地方)。然后当单独提示编辑D(因为你在TODO列表中标记了edit)时,你可以立即告诉rebase到--continue

    如果D 不冲突,没什么大不了的。系统将提示您对其进行编辑。弹出您之前创建的存储以重新应用您从 A 提交延迟的更改; add; commit --amend

    现在这种方法的问题是:如果有多个引用可以到达A,这只会更新一个(上面示例中的master)。比如

    x -- A -- B -- C -- D -- x <--(master)
               \
                x -- x <--(branch)
    

    在这种情况下,branch 仍然可以看到旧历史。你最终得到了

    x -- A' -- B' -- C' -- D' -- x <--(master)
     \
      A -- B -- x -- x <--(branch)
    

    你可以通过做类似的事情来解决这个问题

    rebase --onto B' B branch
    

    如果有很多分支要处理,这将很快变老。

    另一个问题是变基是否会遇到合并提交。在某种程度上,您可以使用--preserve-merges 来缓解这种情况,但如果合并是“邪恶的”(即可以使用默认合并策略自动解决,但以某种方式进行了编辑),它仍然会导致问题(可能默默地破坏历史记录) )。在这种情况下,您可以做的最接近的事情是分段变基并在每个步骤中仔细重现合并。

    【讨论】:

    • 我最终所做的确实是git rebase {COMMIT-HASH}^ -i --preserve-mergesedit 标记两个提交,并且只是手动复制我想要的更改。我仍然想知道是否有一个 UI 工具可以更轻松地做到这一点。
    • 这非常有帮助,我希望这个 Q/A 有更多的曝光率,这样我会更容易找到它。我将 rebase 用于类似的事情,但不明白为什么我会遇到合并冲突。原来我没有从最初引入它的提交中删除大块。谢谢! +1
    • @Mark Adelsberger,您应该更新您的答案以讨论 git rebase --rebase-merges -i 解决您在答案中概述的问题。这是另一个关于 --rebase-merges stackoverflow.com/questions/15915430/… 的 SO Q/A
    • @okovko - 根据文档,--rebase-merges 解决了上述问题。它解决了交互式变基的一些问题,但它仍然无法处理冲突解决,如果这样做不冲突,它仍然希望应用默认合并结果(不考虑合并是否“邪恶”)
    • 很高兴知道新的 --rebase-merges 功能改进的局限性,谢谢 (:
    【解决方案2】:

    使用git format-patch 导出您的更改

    编辑所需的补丁并使用applyam 应用它

    $ git format-patch HEAD~10
    $ git am ...
    

    如果您想创建单个文件,可以使用--stdout 并将其打印到文件中:

    $ git format-patch master --stdout > changes.patch
    

    【讨论】:

    • 我想从提交 100 中删除特定行并将其添加到提交 110。我不明白创建补丁对它有什么帮助
    • 您可以格式化整个补丁系列,git format-patch @~10..
    猜你喜欢
    • 2016-06-06
    • 2019-07-28
    • 2011-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-16
    • 2014-02-11
    相关资源
    最近更新 更多