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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1440050/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 07:04:09  来源:igfitidea点击:

How to split last commit into two in Git

gitversion-control

提问by Jakub Arnold

I have two working branches, masterand forumand I've just made some modifications in forumbranch, that I'd like to cherry-pick into master. But unfortunately, the commit I want to cherry-pick also contains some modifications that I don't want.

我有两个工作分支,masterforum,我刚刚在forum分支中进行了一些修改,我想挑选到master。但不幸的是,我想挑选的提交也包含一些我不想要的修改。

The solution would probably be to somehow delete the wrong commit and replace it with two separate commits, one with changes I want to pick in master, and others that doesn't belong there.

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

I've tried doing

我试过做

git reset --hard HEAD^

which deleted all changes, so I had to go back with

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

git reset ORIG_HEAD

So my question is, what is the best way to splitlast commit into two separate commits?

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

回答by hcs42

You should use the index. After doing a mixed reset ("git resetHEAD^"), add the first set of changes into the index, then commit them. Then commit the rest.

你应该使用索引。在执行混合重置(“ git resetHEAD^”)后,将第一组更改添加到索引中,然后提交它们。然后提交其余的。

You can use "git add" to put all changes made in a file to the index. If you don't want to stage every modification made in a file, only some of them, you can use "git add -p".

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

Let's see an example. Let's suppose I had a file called myfile, which contains the following text:

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

something
something else
something again

I modified it in my last commit so that now it looks like this:

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

1
something
something else
something again
2

Now I decide that I want to split it into two, and I want the insertion of the first line to be in the first commit, and the insertion of the last line to be in the second commit.

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

First I go back to HEAD's parent, but I want to keep the modifications in file system, so I use "git reset" without argument (which will do a so-called "mixed" reset):

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

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

Now I use "git add -p" to add the changes I want to commit to the index (=I stage them). "git add -p" is an interactive tool that asks you about what changes to the file should it add to the index.

现在我使用“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

Then I commit this first change:

然后我提交第一个更改:

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

Now I can commit all the other changes (namely the numeral "2" put in the last line):

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

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

Let's check the log to see what commits we have:

让我们检查日志以查看我们有哪些提交:

$ 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

回答by spazm

Goals:

目标:

  • I want to split a past commit (splitme) into two.
  • I want to maintain the commit message.
  • 我想将过去的提交 ( splitme) 一分为二。
  • 我想维护提交消息

Plan:

计划:

  1. rebase interactive from one before splitme.
  2. edit splitme.
  3. Reset the files to split into a second commit.
  4. Amend commit, maintaining message, modify as necessary.
  5. Add back the files split out from the first commit.
  6. Commit with a new message.
  7. Continue rebase.
  1. 从以前的一个 rebase 交互splitme
  2. 编辑splitme
  3. 重置文件以拆分为第二次提交。
  4. 修改提交,维护消息,根据需要进行修改。
  5. 重新添加从第一次提交中分离出来的文件。
  6. 提交一条新消息。
  7. 继续变基。

The rebase steps (1 & 7) can be skipped if the splitmeis the most recent commit.

如果splitme是最近的提交,则可以跳过变基步骤(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

If I wanted the split files to be committed first, I'd then rebase -i again and switch the order

如果我希望首先提交拆分文件,我会再次变基 -i 并切换顺序

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

回答by CB Bailey

To change the current commit into two commits, you can do something like the following.

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

Either:

任何一个:

git reset --soft HEAD^

This undoes the last commit but leaves everything staged. You can then unstage certain files:

这会撤消最后一次提交,但会保留所有内容。然后,您可以取消暂存某些文件:

git reset -- file.file

Optionally restage parts of those files:

可以选择重新暂存这些文件的部分:

git add -p file.file

Make a new first commit:

进行新的第一次提交:

git commit

The stage and commit the rest of the changes in a second commit:

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

git commit -a

Or:

或者:

Undo and unstage all of the changes from the last commit:

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

git reset HEAD^

Selectively stage the first round of changes:

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

git add -p

Commit:

犯罪:

git commit

Commit the rest of the changes:

提交其余的更改:

git commit -a

(In either step, if you undid a commit that added a brand new file and want to add this to the second commit you'll have to manually add it as commit -aonly stages changes to already tracked files.)

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

回答by Michael Krelin - hacker

Run git gui, select the "Amend last commit" radio button, and unstage (Commit > Unstage From Commit, or Ctrl-U) changes that you do not want to go into first commit. I think that's the easiest way to go about it.

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

Another thing you could do is cherry-pick the change without committing (git cherry-pick -n) and then either manually or with git guiselect desired changes before committing.

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

回答by semanticart

git reset HEAD^

the --hard is what's killing your changes.

--hard 是什么扼杀了你的变化。

回答by dahlbyk

I'm surprised nobody suggested git cherry-pick -n forum. This will stage the changes from the latest forumcommit but not commit them - you can then resetaway the changes you don't need and commit what you want to keep.

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

回答by user2394284

The double-revert-squash method

双还原壁球法

  1. Make another commit that removes the unwanted changes. (If it's per file, this is really easy: git checkout HEAD~1 -- files with unwanted changesand git commit. If not, files with mixed changes can be partially staged git reset fileand git add -p fileas an intermediate step.) Call this the revert.
  2. git revert HEAD– Make yet another commit, that adds back the unwanted changes. This is the double-revert
  3. Of the 2 commits you now made, squashthe first onto the commit to split (git rebase -i HEAD~3). This commit now becomes free of the unwanted changes, for those are in the second commit.
  1. 进行另一个提交以删除不需要的更改。(如果是每个文件,这真的很容易:git checkout HEAD~1 -- files with unwanted changes并且git commit。如果不是,则可以部分暂存具有混合更改的文件git reset filegit add -p file作为中间步骤。)将其称为revert
  2. git revert HEAD– 再次提交,添加不需要的更改。这是双回归
  3. 你现在所做的2所提交的,壁球第一到承诺拆分(git rebase -i HEAD~3)。这个提交现在没有不需要的更改,因为那些在第二次提交中。

Benefits

好处

  • Preserves the commit message
  • Works even if the commit to split is not the last one. It only requires that the unwanted changes do not conflict with later commits
  • 保留提交消息
  • 即使拆分的提交不是最后一个,也能工作。它只要求不需要的更改与以后的提交不冲突

回答by ADTC

Since you're cherry-picking, you can:

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

  1. cherry-pickit with --no-commitoption added.
  2. resetand use add --patch, add --editor just addto stage what you want to keep.
  3. committhe staged changes.
    • To re-use original commit message, you can add --reuse-message=<old-commit-ref>or --reedit-message=<old-commit-ref>options to the commitcommand.
  4. Blow away unstaged changes with reset --hard.
  1. cherry-pick--no-commit添加了选项。
  2. reset并使用add --patch,add --edit或者只是add暂存您想要保留的内容。
  3. commit阶段性的变化。
    • 要重新使用原始提交消息,您可以向命令添加--reuse-message=<old-commit-ref>--reedit-message=<old-commit-ref>选项commit
  4. 使用 消除未分阶段的更改reset --hard

Another way, preserving or editing the original commit message:

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

  1. cherry-pickthe original commit as normal.
  2. Reverse the changes you don't want and use addto stage the reversal.
    • This step would be easy if you are removing what you added, but a bit tricky if you're adding what you removed or reversing a change.
  3. commit --amendto effect the reversal on the cherry-picked commit.
    • You'll get the same commit message again, which you can keep or revise as necessary.
  1. cherry-pick原始提交正常。
  2. 反转您不想要的更改并用于add进行反转。
    • 如果您要删除已添加的内容,则此步骤会很容易,但如果要添加已删除的内容或撤消更改,则此步骤会有点棘手。
  3. commit --amend对樱桃挑选的提交进行逆转。
    • 您将再次收到相同的提交消息,您可以根据需要保留或修改。

回答by Jose Cifuentes

This might be another solution targeted for cases where there is a huge commit and a small amount of files needs to be moved into a new commit. This will work if a set of <path>files are to be extracted out of the last commit at HEAD and all moved to a new commit. If multiple commits are needed the other solutions can be used.

这可能是另一种解决方案,适用于存在大量提交并且需要将少量文件移动到新提交中的情况。如果<path>要从 HEAD 的最后一次提交中提取一组文件并将所有文件移到新的提交中,这将起作用。如果需要多次提交,可以使用其他解决方案。

First make patches into the staged and unstaged areas that would contain the changes to revert the code to before modification and after modification respectively:

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

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>

To understand what's gonna happen (arrow and comments are not part of command):

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

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

Revert <path>changes in last commit:

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

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

Create new commit with <path>changes:

使用<path>更改创建新提交:

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

This has the effect of creating a new commit containing the changes extracted out of the last commit.

这具有创建一个包含从上次提交中提取的更改的新提交的效果。