交互式变基可能会起作用。但很多因素可能会使这复杂化。所以你已经描述了这么多:
x -- x -- A -- B -- C -- D -- x -- x <--(master)
在A 中更改了某些行,因此在B、C、D 等中也有所不同。但是您想要一个直到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 来缓解这种情况,但如果合并是“邪恶的”(即可以使用默认合并策略自动解决,但以某种方式进行了编辑),它仍然会导致问题(可能默默地破坏历史记录) )。在这种情况下,您可以做的最接近的事情是分段变基并在每个步骤中仔细重现合并。