如果我有两个提交,它们之间有许多提交的距离,并且在它们两个中提交了许多文件,那么如何最好地将一个大块从一个移动到另一个,例如:
在提交 100 中:我在文件“aa.txt”以及这个文件中有许多文件更改和许多更改:
- aaaa
- bbbb
+ cccc
10 次提交后,我又有一次提交更改了很多文件和“aa.txt”:
+dddd
我想将行更改-aaaa从前者移动到后者。
是否有任何 CLI/UI 工具可以帮助您轻松完成此操作?(我显然对历史重写没有问题)
交互式变基可能会起作用。但是很多因素可能会使这复杂化。所以你已经描述了这么多:
x -- x -- A -- B -- C -- D -- x -- x <--(master)
Run Code Online (Sandbox Code Playgroud)
有些线路有所变化A,因此也在不同B,C,D等,但你想要一个新的历史它不改变,直到D。所以你可以说
git rebase --interactive A^ master
Run Code Online (Sandbox Code Playgroud)
(A先前更改的提交的 SHA ID在哪里,请注意该^ID 末尾的 SHA ID)。在出现的文本编辑器中,您将看到一个“待办事项”列表。第一行说要pick提交A;将其更改为edit. 然后找到 for 行D(在本例中是 3 次提交之后,但在您所述的示例中可能是 10 次之后提交)。也将其标记为edit。
变基将开始,但在暂时重新应用A后会暂停,以便您可以编辑提交。
现在,如果逆转有问题的更改不是您可以手动/从内存中轻松完成的事情,那么您可以取消对该文件的更改
git reset HEAD^ -- aa.txt
Run Code Online (Sandbox Code Playgroud)
然后以“补丁模式”再次上演它们。
git add --patch -- aa.txt
Run Code Online (Sandbox Code Playgroud)
系统会提示您如何处理每个更改块。如果您要删除的更改与其他更改出现在同一块中,您可以回答提示e并编辑更改块(然后用 替换-您不再想删除的行之前的)。
现在将您的(分阶段的)编辑提交到提交中
git commit --amend
Run Code Online (Sandbox Code Playgroud)
您从索引中恢复的更改仍在工作树中;把它移开,并告诉 rebase 重新开始工作。
git stash
git rebase --continue
Run Code Online (Sandbox Code Playgroud)
随着 rebase 继续致力于 commit D,干预提交可能会发生冲突(如果他们编辑的 aa.txt 太接近您还原的更改)。这些冲突应该很容易解决。(HEAD冲突的一面将包括您不再删除的那一行;除此之外,您可能想要冲突的“另一面”。)
提交D也可能作为冲突出现。如果是这样,您的工作很简单:只需通过保留冲突的“另一面”来解决该问题(包括删除有问题的行,因为这是您最终想要这样做的地方)。然后当单独提示编辑时D(因为你edit在 TODO 列表中标记了它)你可以立即告诉 rebase 到--continue.
如果D不冲突,没什么大不了的。系统会提示您进行编辑。弹出您之前创建的存储以重新应用您从A提交中推迟的更改;add;commit --amend
现在这种方法的问题是:如果有多个 refs 可以到达A,这只会更新一个(master在上面的例子中)。所以例如
x -- A -- B -- C -- D -- x <--(master)
\
x -- x <--(branch)
Run Code Online (Sandbox Code Playgroud)
在这种情况下,branch仍然看到了古老的历史。你最终得到
x -- A' -- B' -- C' -- D' -- x <--(master)
\
A -- B -- x -- x <--(branch)
Run Code Online (Sandbox Code Playgroud)
你可以通过做类似的事情来解决这个问题
rebase --onto B' B branch
Run Code Online (Sandbox Code Playgroud)
如果有很多分支需要处理,这将很快变老。
另一个问题是 rebase 是否会遇到合并提交。在某种程度上,您可以使用它--preserve-merges来缓解这种情况,但如果合并是“邪恶的”(即可以使用默认合并策略自动解决,但以某种方式进行了编辑),它仍然会导致问题(可能默默地破坏历史记录)。在这种情况下,您可以做的最接近的事情是在段中重新设置基础并在每一步仔细地重现合并。