如何在Git中将最后一次提交分成两部分


277

我有两个工作分支,即masterforum,并且我刚刚在forum分支中做了一些修改,我想将它们引入master。但是不幸的是,我想要选择的提交还包含了一些我不想要的修改。

解决的办法可能是以某种方式删除错误的提交,并用两个单独的提交替换它,一个带有我要在master中选择的更改,另一个不属于该提交。

我试着做

git reset --hard HEAD^

删除了所有更改,所以我不得不回去

git reset ORIG_HEAD

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

Answers:


332

您应该使用索引。完成混合重置(“ git reset HEAD ^”)之后,将第一组更改添加到索引中,然后提交它们。然后提交其余的。

您可以使用“ git add ”将文件中所做的所有更改放入索引。如果您不想暂存文件中所做的所有修改,只需其中一部分修改,则可以使用“ git add -p”。

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

something
something else
something again

我在上一次提交中对其进行了修改,因此现在看起来像这样:

1
something
something else
something again
2

现在,我决定将其分为两部分,并希望第一行的插入在第一次提交中,而最后一行的插入在第二次提交中。

首先,我回到HEAD的父级,但是我想将修改保留在文件系统中,所以我使用不带参数的“ git reset”(这将执行所谓的“混合”重置):

$ git reset HEAD^
myfile: locally modified
$ cat myfile
1
something
something else
something again
2

现在,我使用“ git add -p”将要提交的更改添加到索引(=我暂存它们)。“ 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

1
在过去的一周半时间里,我已经逐渐习惯于从Mercurial进行git调试,并且有一个方便的快捷命令git reset [--patch|-p] <commit>,您可以使用它来省去git add -p复位后的麻烦。我对吗?使用git 1.7.9.5。
trojjer 2014年

2
以下是有关此技术的更多信息,包括在较旧的提交或需要将N个提交更改为M个提交时重新定级emmanuelbernard.com/blog/2014/04/14/…
克里斯·威斯汀

84

目标:

  • 我想将过去的commit(splitme)一分为二。
  • 我要维护提交消息

计划:

  1. 从以前的基础上调整交互式splitme
  2. 编辑splitme
  3. 重置文件以拆分为第二个提交。
  4. 修改提交,维护消息,并根据需要进行修改。
  5. 重新添加从第一次提交中拆分出的文件。
  6. 提交新消息。
  7. 继续变基。

如果splitme是最新提交,则可以跳过rebase步骤(1&7)。

git rebase -i splitme^
# mark splitme commit with 'e'
git reset HEAD^ -- $files
git commit --amend
git add $files
git commit -m "commit with just some files"
git rebase --continue

如果我想先提交拆分文件,然后再重新设置-i并切换顺序

git rebase -i splitme^
# swap order of splitme and 'just some files'

1
git reset HEAD^是拼图中缺失的部分。也很好用-p。谢谢!
Marius Gedminas 2013年

10
请务必注意的-- $files论点git reset。通过传递的路径,git reset可以将这些文件恢复为引用的提交状态,但不会更改任何提交。如果您不遵循这些路径,则可以“丢失”下一步要修改的提交。
Duelin标志

2
与接受的答案相比,此方法使您不必再次复制并粘贴第一条提交消息。
加尔文

另外:如果您想重置所有文件,请使用git reset HEAD^ -- .。极其令人惊讶的是,这并非的行为git reset HEAD^
allidoiswin

52

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

要么:

git reset --soft HEAD^

这会撤消上一次提交,但会保留一切。然后可以取消暂存某些文件:

git reset -- file.file

可以选择重新整理这些文件的一部分:

git add -p file.file

进行新的第一次提交:

git commit

暂存并在第二次提交中提交其余更改:

git commit -a

要么:

撤消和取消上一次提交的所有更改:

git reset HEAD^

选择性地进行第一轮变更:

git add -p

承诺:

git commit

提交其余的更改:

git commit -a

(在任一步骤中,如果您撤消了添加了一个全新文件的提交并将其添加到第二个提交中,则必须手动添加它,因为commit -a只有阶段更改了已跟踪的文件。)


22

运行git gui,选择“修改最后的提交”单选按钮,然后取消暂存(“提交”>“从提交暂存”,或Ctrl- U)不希望进行的第一次更改。我认为这是最简单的方法。

您可以做的另一件事是在不提交(git cherry-pick -n)的情况下选择更改,然后手动git gui提交或选择所需的更改,然后再提交。



13

我很惊讶没有人建议git cherry-pick -n forum。这将从最新的forum提交中分阶段进行更改,但不提交它们-然后您可以reset取消不需要的更改并提交要保留的内容。


3

双重还原壁球方法

  1. 再次提交以删除不需要的更改。(如果是每个文件,这确实很容易:git checkout HEAD~1 -- files with unwanted changesgit commit。如果不是,则可以部分暂存具有混合更改的文件,git reset file并将其git add -p file作为中间步骤。)将此称为revert
  2. git revert HEAD–再次提交,以重新添加不需要的更改。这是双重还原
  3. 你现在所做的2所提交的,壁球第一到承诺拆分(git rebase -i HEAD~3)。现在,此提交将摆脱不必要的更改,因为那些更改在第二个提交中。

好处

  • 保留提交消息
  • 即使拆分的提交不是最后一个,也可以使用。它只要求不需要的更改不与以后的提交冲突

1

由于您在挑选樱桃,因此您可以:

  1. cherry-pick--no-commit添加了选项。
  2. reset并使用add --patchadd --edit或者只是add登台要保留的内容。
  3. commit 分阶段的变化。
    • 要重新使用原始提交消息,可以在命令中添加--reuse-message=<old-commit-ref>--reedit-message=<old-commit-ref>选项commit
  4. 用吹掉未进行的变更reset --hard

另一种方式,保留或编辑原始提交消息:

  1. cherry-pick 原始提交正常。
  2. add撤消不需要的更改并用于进行撤消。
    • 如果要删除添加的内容,此步骤将很容易,但是如果要添加删除的内容或撤消更改,则此步骤将有些棘手。
  3. commit --amend 以实现对经过精心挑选的提交的逆转。
    • 您将再次收到相同的提交消息,可以根据需要保留或修改该消息。

0

这可能是针对大量提交且需要将少量文件移入新提交的情况的另一种解决方案。如果<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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.