将先前的提交分成多个提交

如果不创建分支并在新分支上进行大量时髦的工作,是否有可能在将一个提交提交到本地存储库之后将其分解为几个不同的提交?

答案

git rebase -i会做。

首先,从一个干净的工作目录开始: git status应该不显示任何挂起的修改,删除或添加。

现在,您必须确定要拆分的提交。

A)分割最近的提交

要拆分最近的提交,请首先:

$ git reset HEAD~

现在,以通常的方式分别提交片段,产生所需数量的提交。

B)进一步拆分提交

这需要重新定基础 ,即重写历史记录。要找到正确的提交,您有几种选择:

  • 如果是三次提交,那么

    $ git rebase -i HEAD~3

    3是多少个提交。

  • 如果它在树中的距离比您想计算的要远,则

    $ git rebase -i 123abcd~

    其中123abcd是要拆分的提交的 SHA1。

  • 如果您打算合并到母版中的其他分支(例如要素分支)上:

    $ git rebase -i master

当您获得 rebase 编辑屏幕时,找到您想要分解的提交。在该行的开头,将pick替换为edit (简称e )。保存缓冲区并退出。 Rebase 现在将在您要编辑的提交之后停止。然后:

$ git reset HEAD~

以通常的方式分别提交片段,生成所需数量的提交,然后

$ git rebase --continue

git-rebase手册(分割提交部分)

在交互模式下,您可以使用操作 “edit” 标记提交。但是,这并不一定意味着 git rebase 希望此编辑的结果恰好是一次提交。实际上,您可以撤消提交,也可以添加其他提交。这可用于将提交分为两部分:

  • 使用git rebase -i <commit>^启动交互式 rebase,其中<commit>是要拆分的提交。实际上,任何提交范围都可以,只要它包含该提交即可。

  • 用操作 “edit” 标记要拆分的提交。

  • 在编辑提交时,执行git reset HEAD^ 。结果是 HEAD 倒退了一个,索引也随之变化。但是,工作树保持不变。

  • 现在,将更改添加到您希望在第一次提交中拥有的索引。您可以使用git add (可能是交互方式)或git gui (或两者)来做到这一点。

  • 使用现在合适的提交消息来提交当前索引。

  • 重复最后两个步骤,直到工作树干净为止。

  • 继续使用git rebase --continue

使用git rebase --interactive编辑该先前的提交,运行git reset HEAD~ ,然后git add -p添加一些,然后进行一次提交,然后添加更多并进行另一次提交,次数不限。完成后,运行git rebase --continue ,您将在堆栈中更早地获得所有拆分提交。

重要提示 :请注意,您可以随意进行并进行所有所需的更改,而不必担心丢失旧更改,因为您始终可以运行git reflog在项目中查找包含所需更改的点,(请致电a8c4ab ),然后git reset a8c4ab

这是显示其工作方式的一系列命令:

mkdir git-test; cd git-test; git init

现在添加一个文件A

vi A

添加此行:

one

git commit -am one

然后将此行添加到 A:

two

git commit -am two

然后将此行添加到 A:

three

git commit -am three

现在文件 A 看起来像这样:

one
two
three

并且我们的git log如下所示(好吧,我使用git log --pretty=oneline --pretty="%h %cn %cr ---- %s"

bfb8e46 Rose Perrone 4 seconds ago ---- three
2b613bc Rose Perrone 14 seconds ago ---- two
9aac58f Rose Perrone 24 seconds ago ---- one

假设我们要拆分第二个提交, two

git rebase --interactive HEAD~2

这将显示一条消息,如下所示:

pick 2b613bc two
pick bfb8e46 three

第一个变化picke编辑该承诺。

git reset HEAD~

git diff向我们展示了我们刚刚取消了对第二次提交所做的提交:

diff --git a/A b/A
index 5626abf..814f4a4 100644
--- a/A
+++ b/A
@@ -1 +1,2 @@
 one
+two

让我们进行一下更改,然后在文件A那一行中添加 “和第三个”。

git add .

这通常是在交互式 rebase 期间运行git rebase --continue ,因为我们通常只想返回到提交堆栈中以编辑较早的提交。但是这次,我们要创建一个新的提交。因此,我们将运行git commit -am 'two and a third' 。现在,我们编辑文件A并添加第二行two and two thirds

git add . git commit -am 'two and two thirds' git rebase --continue

我们与提交three发生冲突,所以让我们解决它:

我们会改变

one
<<<<<<< HEAD
two and a third
two and two thirds
=======
two
three
>>>>>>> bfb8e46... three

one
two and a third
two and two thirds
three

git add .; git rebase --continue

现在我们的git log -p看起来像这样:

commit e59ca35bae8360439823d66d459238779e5b4892
Author: Rose Perrone <roseperrone@fake.com>
Date:   Sun Jul 7 13:57:00 2013 -0700

    three

diff --git a/A b/A
index 5aef867..dd8fb63 100644
--- a/A
+++ b/A
@@ -1,3 +1,4 @@
 one
 two and a third
 two and two thirds
+three

commit 4a283ba9bf83ef664541b467acdd0bb4d770ab8e
Author: Rose Perrone <roseperrone@fake.com>
Date:   Sun Jul 7 14:07:07 2013 -0700

    two and two thirds

diff --git a/A b/A
index 575010a..5aef867 100644
--- a/A
+++ b/A
@@ -1,2 +1,3 @@
 one
 two and a third
+two and two thirds

commit 704d323ca1bc7c45ed8b1714d924adcdc83dfa44
Author: Rose Perrone <roseperrone@fake.com>
Date:   Sun Jul 7 14:06:40 2013 -0700

    two and a third

diff --git a/A b/A
index 5626abf..575010a 100644
--- a/A
+++ b/A
@@ -1 +1,2 @@
 one
+two and a third

commit 9aac58f3893488ec643fecab3c85f5a2f481586f
Author: Rose Perrone <roseperrone@fake.com>
Date:   Sun Jul 7 13:56:40 2013 -0700

    one

diff --git a/A b/A
new file mode 100644
index 0000000..5626abf
--- /dev/null
+++ b/A
@@ -0,0 +1 @@
+one

先前的答案涵盖了使用git rebase -i编辑要拆分的提交,并将其分部分提交的问题。

将文件拆分为不同的提交时,这种方法效果很好,但是如果您想将各个文件的更改分开,则需要了解更多信息。

到达要拆分的提交后,使用rebase -i并将其标记为edit ,您有两个选择。

  1. 使用git reset HEAD~之后,使用git add -p逐个浏览补丁,以在每次提交中选择所需的补丁

  2. 编辑工作副本以删除不需要的更改;提交临时状态;然后拉回完整提交以进行下一轮。

如果要拆分大型提交,选项 2 很有用,因为它使您可以检查过渡版本在合并过程中是否正确构建和运行。这进行如下。

使用rebase -iedit提交后,请使用

git reset --soft HEAD~

撤消提交,但将提交的文件保留在索引中。您也可以通过省略 --soft 来进行混合重置,具体取决于您的初次提交将接近最终结果。唯一的区别是,是先进行所有已分阶段的更改,还是先进行所有未分阶段的更改。

现在进入编辑代码。您可以删除更改,删除添加的文件以及执行想要构造的所有提交的所有操作。您还可以构建,运行它,并确认您拥有一致的源集。

满意后,请根据需要git gui / 取消git gui文件(为此,我喜欢使用git gui ),然后通过 UI 或命令行提交更改

git commit

那是第一次完成。现在,您想将工作副本还原为拆分提交后的状态,以便您可以在下一次提交时进行更多更改。要找到您正在编辑的提交的 sha1,请使用git status 。在状态的前几行中,您将看到当前正在执行的 rebase 命令,在其中您可以找到原始提交的 sha1:

$ git status
interactive rebase in progress; onto be83b41
Last commands done (3 commands done):
   pick 4847406 US135756: add debugging to the file download code
   e 65dfb6a US135756: write data and download from remote
  (see more in file .git/rebase-merge/done)
...

在这种情况下,我正在编辑的提交具有 sha1 65dfb6a 。知道这一点后,我可以使用git checkout的形式在我的工作目录中git checkout该提交的内容,该形式既包含提交又包含文件位置。在这里我用.作为替换整个工作副本的文件位置:

git checkout 65dfb6a .

不要错过最后的点!

这将签出并暂存文件,并按照您正在编辑的提交后的状态进行登台,但相对于您之前所做的提交,因此,您已经提交的所有更改将不属于提交。

您可以立即进行并按原样提交以完成拆分,也可以再次进行,删除提交的某些部分,然后再进行一次临时提交。

如果您想将原始提交消息重用于一个或多个提交,则可以直接从 rebase 的工作文件中使用它:

git commit --file .git/rebase-merge/message

最后,一旦您完成所有更改,

git rebase --continue

将进行并完成变基操作。

git rebase --interactive可用于将提交拆分为较小的提交。 Rebase 上Git 文档对该过程进行了简要的演练 - 拆分提交

在交互模式下,您可以使用操作 “edit” 标记提交。但是,这并不一定意味着git rebase希望此编辑的结果恰好是一次提交。实际上,您可以撤消提交,也可以添加其他提交。这可用于将提交分为两部分:

  • 使用git rebase -i <commit>^启动交互式 rebase,其中<commit>是要拆分的提交。实际上,任何提交范围都可以,只要它包含该提交即可。

  • 用操作 “edit” 标记要拆分的提交。

  • 在编辑提交时,执行git reset HEAD^ 。结果是 HEAD 倒退了一个,索引也随之变化。但是,工作树保持不变。

  • 现在,将更改添加到您希望在第一次提交中拥有的索引。您可以使用git add (可能是交互方式)或 git gui(或两者)来做到这一点。

  • 使用现在合适的提交消息来提交当前索引。

  • 重复最后两个步骤,直到工作树干净为止。

  • 继续使用git rebase --continue

如果您不确定中间版本是否一致(它们可以编译,通过测试套件等),则应在每次提交,测试和修订(如果有修正)后使用git stash存放尚未提交的更改。是必要的。

您可以进行交互式 rebase git rebase -i 。手册页正是您想要的:

http://git-scm.com/docs/git-rebase#_splitting_commits

现在,在 Windows 上最新的 TortoiseGit 中,您可以非常轻松地完成此操作。

打开变基对话框,对其进行配置 ,然后执行以下步骤。

  • 右键单击要拆分的提交,然后选择 “ Edit ”(选择,压缩,删除...)。
  • 点击 “ Start ” 开始重新设定基准。
  • 一旦到达要拆分的提交,请选中 “ Edit/Split ” 按钮,然后直接单击 “ Amend ”。提交对话框打开。
    编辑/拆分提交
  • 取消选择您要单独提交的文件。
  • 编辑提交消息,然后单击 “ commit ”。
  • 直到有文件要提交,提交对话框才会一次又一次打开。当没有更多文件要提交时,它仍然会询问您是否要添加一个提交。

非常有帮助,谢谢 TortoiseGit!

请注意,还有git reset --soft HEAD^ 。它类似于git reset (默认为--mixed ),但保留索引内容。这样,如果您添加 / 删除了文件,则它们已经在索引中了。

事实证明,在进行大型提交时非常有用。

没有交互性基准库,最简单的操作是(可能)在要拆分的分支之前先提交一个新分支,Cherry-pick -n commit,重置,存储,提交文件移动,重新应用存储和提交更改,然后与以前的分支合并,或者选择后续的提交。 (然后将以前的分支名称更改为当前的头。)(最好遵循 MBO 的建议并进行交互式的基准调整。)

我认为最好的方法是使用git rebase -i 。我创建了一个视频来展示拆分提交的步骤: https : //www.youtube.com/watch?v=3EzOz7e1ADI