如何在Git中把最后一次提交分成两个?

我有两个分支,masterforum,我刚刚在forum中做了一些修改,我想将这些修改精选到master中。但不幸的是,我想要选择的提交也包含一些我不想要的修改。

解决方案可能是以某种方式删除错误的提交,并将其替换为两个独立的提交,一个带有我想在master中选择的更改,另一个带有剩余的更改。

我试过了

git reset --hard HEAD^

它删除了所有的更改,所以我必须回到

git reset ORIG_HEAD

所以我的问题是,拆分最后一次提交为两个单独的提交的最佳方法是什么?

87423 次浏览

运行git gui,选择“修改最后提交”单选按钮,然后取消你不想第一次提交的更改(commit > unstage From commit,或Ctrl-U)。我认为这是最简单的方法。

你可以做的另一件事是选择没有提交的更改(git cherry-pick -n),然后手动或使用git gui在提交前选择所需的更改。

git reset HEAD^

困难是扼杀你改变的原因。

应该使用索引。在执行混合重置("git重置 HEAD^")后,添加 将第一组更改放入索引,然后提交它们。然后提交 休息。< / p > 你可以使用"git添加"将文件中所做的所有更改放到索引中。如果你 不希望在文件中执行所有修改,只需要其中的一部分即可 可以使用"git add -p".

让我们看一个例子。假设我有一个名为myfile的文件,其中包含 下面的文本:

something
something else
something again

我在上次提交时修改了它,现在它看起来是这样的:

1
something
something else
something again
2

现在我决定我想把它分成两个,我想插入 第一行是第一次提交,最后一行的插入是

.在第二次提交时 首先我回到HEAD的父目录,但是我想把修改保存在文件系统中, 所以我使用“git reset”不带参数(这将做所谓的“混合” 重置):< / p >
$ git reset HEAD^
myfile: locally modified
$ cat myfile
1
something
something else
something again
2

现在我使用"git add -p"来添加我想要提交到索引的更改(=I 阶段)。"git add -p"是一个交互式工具,它会询问你什么

.

.
$ git add -p myfile
diff --git a/myfile b/myfile
index 93db4cb..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,5 @@
+1
something
something else
something again
+2
Stage this hunk [y,n,a,d,/,s,e,?]? s    # split this section into two!
Split into 2 hunks.
@@ -1,3 +1,4 @@
+1
something
something else
something again
Stage this hunk [y,n,a,d,/,j,J,g,e,?]? y  # yes, I want to stage this
@@ -1,3 +2,4 @@
something
something else
something again
+2
Stage this hunk [y,n,a,d,/,K,g,e,?]? n   # no, I don't want to stage this

然后我提交第一个更改:

$ git commit -m "Added first line"
[master cef3d4e] Added first line
1 files changed, 1 insertions(+), 0 deletions(-)

现在我可以提交所有其他更改(即数字“2”放在最后一行):

$ git commit -am "Added last line"
[master 5e284e6] Added last line
1 files changed, 1 insertions(+), 0 deletions(-)

让我们检查一下日志,看看我们有哪些提交:

$ git log -p -n2 | cat
Commit 5e284e652f5e05a47ad8883d9f59ed9817be59d8
Author: ...
Date: ...


Added last line


Diff --git a/myfile b/myfile
Index f9e1a67..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -2,3 +2,4 @@
something
something else
something again
+2


Commit cef3d4e0298dd5d279a911440bb72d39410e7898
Author: ...
Date: ...


Added first line


Diff --git a/myfile b/myfile
Index 93db4cb..f9e1a67 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,4 @@
+1
something
something else
something again

要将当前提交更改为两次提交,可以执行以下操作。

:

git reset --soft HEAD^

这将取消上一次提交,但将所有内容都保留在舞台上。然后你可以取消某些文件:

git reset -- file.file

可选地重新部署这些文件的部分:

git add -p file.file

做一个新的第一次提交:

git commit

阶段,并在第二次commit中提交其余的更改:

git commit -a

或者:

撤销并取消上次提交的所有更改:

git reset HEAD^

有选择地进行第一轮更改:

git add -p

提交:

git commit

提交其余的更改:

git commit -a

(在任何一个步骤中,如果你撤销了一个添加了全新文件的提交,并想要将其添加到第二次提交中,你必须手动添加它,因为commit -a只会对已经跟踪的文件进行更改。)

我很惊讶没有人建议git cherry-pick -n forum。这将从最新的forum提交中暂存更改,但不提交它们——然后你可以reset删除你不需要的更改,并提交你想保留的内容。

目标:

  • 我想把一个过去的提交$splitme分成两个。
  • 我想维护提交消息

计划:

  1. $splitme的父提交上重新赋基交互式,将$splitme标记为edit
  2. 取消不应该是第一次提交的一部分的文件。
  3. 修改当前提交,维护提交消息,必要时进行修改。
  4. 回退第一次提交时遗漏的文件。
  5. 提交,并附上一条新消息。
  6. 继续变基。

如果$splitme是最近的提交,则可以跳过改基步骤(1和7)。

git rebase -i $splitme^  # mark $splitme as edit
git reset HEAD^ -- $files
git commit --amend
git add $files
git commit
git rebase --continue

如果我想将第一次提交中的文件与第二次提交中的文件交换,那么我将再次改变交互,交换它们的提交行。

双重反向挤压方法

  1. 进行另一次提交,删除不需要的更改。(如果是每个文件,这很简单:git checkout HEAD~1 -- files with unwanted changesgit commit。如果不是,混合更改的文件可以部分地执行git reset filegit add -p file作为中间步骤。)称其为回复
  2. git revert HEAD -再次提交,添加回不想要的更改。这是double-revert
  3. 在你现在做的2个提交中,南瓜是第一个提交到split的(git rebase -i HEAD~3)。这次提交现在不再有不必要的更改,因为这些更改在第二次提交中。

好处

  • 保存提交消息
  • 即使提交的分裂不是最后一个也有效。它只要求不需要的更改与以后的提交不冲突

既然你在挑选,你可以:

  1. cherry-pick添加了--no-commit选项。
  2. reset,并使用add --patchadd --edit或只是add来stage你想保留的内容。
  3. commit阶段性变化。
    • 要重用原始提交消息,可以在commit命令中添加--reuse-message=<old-commit-ref>--reedit-message=<old-commit-ref>选项。
    • 李< / ul > < / >
    • 使用reset --hard清除未分阶段的更改。

另一种方法,保存或编辑原始的提交消息:

  1. cherry-pick原始提交正常。
  2. 反转你不想要的更改,并使用add来上演反转。
    • 如果您要删除添加的内容,这一步会很简单,但如果您要添加已删除的内容或反转更改,这一步就有点棘手了。
    • 李< / ul > < / >
    • commit --amend对选定的提交执行反转。
      • 您将再次收到相同的提交消息,您可以根据需要保留或修改该消息。
      • 李< / ul > < / >

这可能是另一种解决方案,适用于需要提交大量文件而需要将少量文件移动到新提交的情况。如果一组<path>文件要从HEAD的最后一次提交中提取出来,并全部移动到新的提交中,那么这将有效。如果需要多次提交,则可以使用其他解决方案。

首先将补丁放入分段和非分段区域,这些区域将分别包含将代码恢复到修改前和修改后的更改:

git reset HEAD^ <path>


$ git status
On branch <your-branch>
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)


modified:   <path>


Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)


modified:   <path>

为了理解将会发生什么(箭头和注释不是命令的一部分):

git diff --cached   -> show staged changes to revert <path> to before HEAD
git diff            -> show unstaged changes to add current <path> changes

恢复上次提交中的<path>更改:

git commit --amend  -> reverts changes on HEAD by amending with staged changes

创建带有<path>更改的新提交:

git commit -a -m "New Commit" -> adds new commit with unstaged changes

这样做的效果是创建一个新的提交,其中包含从上次提交中提取的更改。

你可以使用git rebase -i <commit>,其中<commit>是你想保持原样的最新提交。在你想要插入一个新的分离提交的每个点上添加break。然后在每次休息时,使用git checkout -p <commit containing parts you want>来拉入你想拆分的部分,并提交它们。然后git rebase --continue从以后的提交中删除这些部分。

对于拆分最近一次提交的简单情况,如下所示:

$ git rebase -i HEAD^
# add 'break' at the start


$ git checkout -p master # or whatever your branch is called
# choose the parts you want to split out


$ git commit
# commit the newly-split-out parts


$ git rebase --continue
# rebase the remaining parts of the change onto the split-out parts

这假设您希望稍后的提交保留原始的提交消息;这就是我通常发现自己所处的情况(排除一些预备变化)。