如何在Git中将最后一次提交拆分为两次

时间:2009-09-17 16:53:15

标签: git version-control

我有两个工作分支,主人论坛,我刚刚在论坛分支做了一些修改,我想要樱桃挑选主人。但不幸的是,我想要挑选的提交还包含一些我不想要的修改。

解决方案可能会以某种方式删除错误的提交,并将其替换为两个单独的提交,一个包含我想在master中选择的更改,另一个包含不属于那里的更改。

我已经尝试过了

git reset --hard HEAD^

删除了所有更改,因此我不得不返回

git reset ORIG_HEAD

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

10 个答案:

答案 0 :(得分:327)

您应该使用索引。执行混合重置(“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”添加我想要提交给索引的更改(= 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

答案 1 :(得分:81)

目标:

  • 我想将过去的提交(splitme)分成两部分。
  • 我想维护提交消息

安排:

  1. splitme之前与一个人进行互动。
  2. 修改splitme
  3. 重置文件以拆分为第二次提交。
  4. 修改提交,维护消息,根据需要进行修改。
  5. 添加从第一次提交中拆分的文件。
  6. 使用新邮件提交。
  7. 继续变基。
  8. 如果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'
    

答案 2 :(得分: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,只会对已经跟踪的文件进行更改。 )

答案 3 :(得分:22)

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

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

答案 4 :(得分:15)

git reset HEAD^
正在扼杀你的变化。

答案 5 :(得分:13)

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

答案 6 :(得分:3)

双重还原压缩方法

  1. 进行另一次删除不需要的更改的提交。 (如果是每个文件,这非常简单:git checkout HEAD~1 -- files with unwanted changesgit commit。如果不是,具有混合更改的文件可以部分暂存git reset filegit add -p file作为中间步骤。 )将其称为还原
  2. git revert HEAD - 进行另一次提交,即添加不需要的更改。这是双重还原
  3. 在您现在提交的2个提交中,压缩第一个提交到split(git rebase -i HEAD~3)。此提交现在可以免除不需要的更改,因为这些更改是在第二次提交中。
  4. 优势

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

答案 7 :(得分:1)

既然你正在挑选,你可以:

  1. Statement添加了cherry-pick选项。
  2. --no-commit并使用resetadd --patchadd --edit来展示您想保留的内容。
  3. add分阶段的变化。
    • 要重新使用原始提交消息,您可以向commit命令添加--reuse-message=<old-commit-ref>--reedit-message=<old-commit-ref>个选项。
  4. 使用commit打开未分期更改。
  5. 另一种方法,保留或编辑原始提交消息:

    1. reset --hard原始提交正常。
    2. 撤消您不想要的更改并使用cherry-pick进行撤消。
      • 如果要删除添加的内容,此步骤会很简单,但如果您要添加删除的内容或撤消更改,则会有点棘手。
    3. add实现樱桃选择提交的逆转。
      • 您将再次收到相同的提交消息,您可以根据需要保留或修改。

答案 8 :(得分:0)

这可能是针对大量提交且需要将少量文件移入新提交的情况的另一种解决方案。如果要从HEAD的最后一次提交中提取一组<path>文件并将其全部移至新的提交,这将起作用。如果需要多次提交,则可以使用其他解决方案。

首先将补丁添加到暂存区域和未暂存区域,这些区域将包含更改以分别将代码还原到修改之前和修改之后:

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

这具有创建一个新提交的作用,其中包含从上一次提交中提取的更改。

答案 9 :(得分:0)

您可以使用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

这是假设您希望以后的提交保留原始提交消息;我通常就是这种情况(进行一些准备性更改)。