将先前的提交分解为多个提交

kob*_*las 1113 git

如果没有创建分支并在新分支上做一堆时髦的工作,是否有可能在提交到本地存储库后将单个提交分解为几个不同的提交?

Way*_*rad 1668

git rebase -i 会做的.

首先,从一个干净的工作目录开始:git status应显示没有待处理的修改,删除或添加.

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

$ git reset HEAD~
Run Code Online (Sandbox Code Playgroud)

现在以通常的方式单独提交各个部分,根据需要生成尽可能多的提交.

如果它再远在树上,那么

$ git rebase -i HEAD~3
Run Code Online (Sandbox Code Playgroud)

这里3是多少提交回它.

如果它在树中比你想要的更远,那么

$ git rebase -i 123abcd~
Run Code Online (Sandbox Code Playgroud)

123abcd您想要拆分的提交的SHA1 在哪里.

当您获得rebase编辑屏幕时,找到您要拆分的提交.在该行的开头,替换pickedit(e简称).保存缓冲区并退出.现在,Rebase将在您要编辑的提交后立即停止.然后:

$ git rebase -i master
Run Code Online (Sandbox Code Playgroud)

以通常的方式单独提交各个部分,然后根据需要生成尽可能多的提交

$ git reset HEAD~
Run Code Online (Sandbox Code Playgroud)

  • 您还希望利用`git add -p`来仅添加文件的部分部分,可能使用`e`选项来编辑差异以仅提交一些块.如果你想要进行一些工作但是从当前提交中删除它,那么`git stash`也很有用. (45认同)
  • [manojlds的回答](http://stackoverflow.com/a/6217210/456814)实际上有[此链接](http://git-scm.com/docs/git-rebase#_splitting_commits)到[git]的文档-scm](http://git-scm.com/docs),它也解释了非常清楚地分割提交的过程. (16认同)
  • @CraigRinger 比“git add -p”更好的是“git add -i”,但它似乎不太为人所知。 (3认同)
  • @kralyk在`git reset HEAD~`之后,HEAD中新提交的文件将保留在磁盘上.他们不会丢失. (3认同)
  • 考虑在新分支上完成这项工作,以便为自己提供重新启动的方法 (3认同)
  • 谢谢你的回答.我想在暂存区域中有一些以前提交的文件,所以对我的说明有点不同.在我可以'git rebase --continue`之前,我实际上必须要`git add(要添加的文件)`,`git commit`,然后`git stash`(用于其余文件).在`git rebase --continue`之后,我使用`git checkout stash .`来获取剩余的文件 (2认同)
  • 如果要拆分_and_重新排序提交,我想做的是拆分_first_,然后使用另一个`git rebase -i HEAD ^ 3'命令分别重新排序。这样,如果拆分变坏,您不必撤消太多工作。 (2认同)
  • @Benjamin `^` 没有数字表示“第一父母”。我认为它相当于没有数字的`~`,意思是“始祖”。如果给定数字,例如“^2”或“~2”,则它们不一定相等。我将编辑答案以使其更加一致。 (2认同)
  • @kralyk `git reset HEAD~` 不会丢失新添加的文件,也不会丢失对跟踪文件所做的修改。它需要“--hard”开关才能使“git重置”做不安全的事情。也就是说,我建议从一棵干净的树开始这个序列。 (2认同)
  • @FarrukhChishti 这意味着有人在您拉动和推动之间推送了提交。这不是 rebase 的问题。我很高兴你能够解决它。 (2认同)
  • @Svish我认为你是对的。引入了导致此错误的编辑;我将恢复该编辑。 (2认同)
  • @GeoffLangenderfer 版本引用末尾的“~”意味着返回一代。例如,“HEAD”是当前分支中的最新提交。“HEAD~”(“HEAD~1”的缩写)是最近一次提交之前的提交。 (2认同)
  • @GeoffLangenderfer ttps://stackoverflow.com/questions/2221658/whats-the-difference- Between-head-and-head-in-git (2认同)
  • 很好的答案!我建议使用`git reset HEAD~1 -p`通过一一检查所有更改的代码块来将提交分成几个。 (2认同)
  • 如果你想破坏初始提交,请使用``git rebase -i --root```。 (2认同)
  • 我想我每两个月都会回来一次这个答案 (2认同)
  • @CraigRinger另外,如果您只使用`git add -p`的`e`,则可以使用快捷方式`git add -e`。 (2认同)

MBO*_*MBO 296

git-rebase手册(SPLITTING COMMITS部分)

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

  • 启动交互式rebase git rebase -i <commit>^,其中<commit>包含要分割的提交.实际上,只要包含该提交,任何提交范围都可以.

  • 使用"编辑"操作标记要拆分的提交.

  • 在编辑提交时,执行git reset HEAD^.结果是HEAD被一个重绕,索引也随之而来.但是,工作树保持不变.

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

  • 使用现在适当的提交消息提交now-current索引.

  • 重复最后两步,直到工作树干净.

  • 继续使用rebase git rebase --continue.

  • 在Windows上,你使用`~`而不是`^`. (12认同)
  • @ user420667是的,当然.毕竟,我们"重置"提交 - 包含消息.谨慎的做法是,如果你知道你要分裂一个提交但想保留它的部分/全部信息,那就是取一条消息.所以,`git show`在`rebase`之前的提交,或者如果你忘记或更喜欢这个:稍后通过`reflog`回到它.它们实际上都不会"丢失",直到它在2周或其他任何时候被垃圾收集掉. (11认同)
  • 谨慎提醒:通过这种方法,我丢失了提交消息. (9认同)
  • 即使在Windows上,`~`和`^`也是不同的东西.您仍然需要插入符号^ ^,因此您只需要根据shell调整它.在PowerShell中,它是``HEAD` ^``.使用cmd.exe,您可以将其加倍以像`HEAD ^^`一样进行转义.在大多数(全部?)shell中,你可以用"HEAD ^"这样的引号括起来. (4认同)
  • 您也可以执行git commit --reuse-message = abcd123`。它的简称是-C。 (4认同)
  • @ user420667提交消息可以在reflog(`git reflog`)中恢复;`git log &lt;sha&gt;` (2认同)
  • 或者如果您想更改它,请使用“--reedit-message=&lt;commit&gt;”。短选项是`-c` (2认同)

Ros*_*one 39

使用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
Run Code Online (Sandbox Code Playgroud)

我们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
Run Code Online (Sandbox Code Playgroud)

假设我们要分割第二次提交,two.

git rebase --interactive HEAD~2

这会显示一条如下所示的消息:

pick 2b613bc two
pick bfb8e46 three
Run Code Online (Sandbox Code Playgroud)

将第pick一个更改为a e以编辑该提交.

git reset HEAD~

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

diff --git a/A b/A
index 5626abf..814f4a4 100644
--- a/A
+++ b/A
@@ -1 +1,2 @@
 one
+two
Run Code Online (Sandbox Code Playgroud)

让我们进行更改,然后在文件中添加"和第三个" 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
Run Code Online (Sandbox Code Playgroud)

one
two and a third
two and two thirds
three
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)


And*_*mer 34

以前的答案涵盖了git rebase -i编辑要拆分的提交以及将其部分提交的用法.

这在将文件拆分为不同的提交时效果很好,但如果要拆分对各个文件的更改,则需要了解更多信息.

完成了你想要分割,使用rebase -i和标记它的提交后edit,你有两个选择.

  1. 使用后git reset HEAD~,单独使用修补程序git add -p选择每个提交中所需的修补程序

  2. 编辑工作副本以删除您不想要的更改; 承认临时国家; 然后撤回下一轮的完整提交.

如果您要拆分大型提交,则选项2非常有用,因为它允许您检查临时版本是否在合并过程中构建并正常运行.这进行如下.

使用rebase -iedit提交后,使用

git reset --soft HEAD~
Run Code Online (Sandbox Code Playgroud)

撤消提交,但将提交的文件保留在索引中.您也可以通过省略--soft来进行混合重置,具体取决于初始提交的最终结果的接近程度.唯一的区别在于您是从开始的所有更改开始还是从未加入所有更改开始.

现在进入并编辑代码.您可以删除更改,删除添加的文件,并执行您想要构建您正在寻找的系列的第一个提交.您还可以构建它,运行它,并确认您拥有一致的源集.

一旦你开心,根据需要暂存/取消暂存文件(我喜欢用git gui它),并通过UI或命令行提交更改

git commit
Run Code Online (Sandbox Code Playgroud)

这是第一次提交.现在,您希望将工作副本还原到分割提交后的状态,以便您可以为下次提交执行更多更改.要查找正在编辑的提交的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)
...
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我正在编辑的提交有sha1 65dfb6a.知道了,我可以在我的工作目录中查看该提交的内容,使用其形式git checkout同时获取提交和文件位置.在这里,我.用作文件位置来替换整个工作副本:

git checkout 65dfb6a .
Run Code Online (Sandbox Code Playgroud)

不要错过最后的点!

这将检查并暂存您正在编辑的提交后的文件,但相对于您之前提交的提交,因此您已提交的任何更改都不会成为提交的一部分.

您可以现在继续并按原样提交以完成拆分,或者再次转发,在进行另一个临时提交之前删除提交的某些部分.

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

git commit --file .git/rebase-merge/message
Run Code Online (Sandbox Code Playgroud)

最后,一旦你做出了所有的改变,

git rebase --continue
Run Code Online (Sandbox Code Playgroud)

将继续并完成rebase操作.

  • 我喜欢你使用原始提交消息的方式。 (3认同)
  • 谢谢!!!这应该是公认的答案.今天本来可以节省我很多时间和痛苦.这是唯一的答案,最终提交的结果将您带到与编辑下的提交相同的状态. (2认同)

小智 18

拆分提交的另一种方法是调用

git rebase -i <branch or commit>
Run Code Online (Sandbox Code Playgroud)

标记您想要拆分的提交edit,然后复制该行这将在变基期间向您呈现两次提交。编辑第一个时,您可以删除第二个中想要的所有内容。完成后,(运行测试,如果适用)git add -Agit commit --amend(在此处调整提交消息)和git rebase --continue。您现在正在进行第二次提交,这只会带来您刚刚从第一次提交中删除的更改。

我认为这比必须小心使用git add -p要容易得多。


小智 17

git rebase --interactive可用于将提交拆分为较小的提交.关于rebaseGit文档对该过程进行了简明扼要的演练 - Splitting Commits:

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

  • 启动交互式rebase git rebase -i <commit>^,其中<commit>包含要分割的提交.实际上,只要包含该提交,任何提交范围都可以.

  • 使用"编辑"操作标记要拆分的提交.

  • 在编辑提交时,执行git reset HEAD^.结果是HEAD被一个重绕,索引也随之而来.但是,工作树保持不变.

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

  • 使用现在适当的提交消息提交now-current索引.

  • 重复最后两步,直到工作树干净.

  • 继续使用rebase git rebase --continue.

如果您不完全确定中间修订版是否一致(它们是编译的,通过测试套件等),那么git stash在每次提交,测试和修改提交时,如果需要修复,您应该使用它来隐藏尚未提交的更改.

  • 总结一下:cmd.exe 或 PowerShell 中的“HEAD^”、cmd.exe 中的“HEAD^^”、PowerShell 中的“HEAD”^``。了解 shell 以及您的特定 shell 的工作原理(即命令如何转换为传递给程序的各个部分)非常有用,这样您就可以在线将命令调整为适合您的特定 shell 的正确字符。(不特定于 Windows。) (2认同)

Gra*_*dma 13

使用最新的提交

如果您只想从现有提交中提取一些内容并保留原始提交,您可以使用

git reset --patch HEAD^
Run Code Online (Sandbox Code Playgroud)

代替git reset HEAD^。该命令允许您仅重置您需要的块。

选择要重置的块后,您将拥有将重置先前提交中的更改的暂存块。现在您更改最后一次提交,从中删除这些更改

git commit --amend --no-edit
Run Code Online (Sandbox Code Playgroud)

并且您有未暂存的块,您可以将其添加到单独的提交中

git add .
git commit -m "new commit"
Run Code Online (Sandbox Code Playgroud)

不使用最新的提交

当然,git rebase --interactive按照上面的建议使用来转到之前的一些提交。

题外话事实:

在 Mercurial 中,他们有——继我想在 git 中看到的hg split第二个功能。hg absorb


man*_*lds 10

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

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

  • 提供更多关于如何处理问题而不仅仅是提供RTFM的背景将会更有帮助. (14认同)

Wil*_*ell 9

在没有交互式变基的情况下,最简单的事情是(可能)从要拆分的分支之前的提交开始创建一个新分支,cherry-pick -n 提交,重置,存储,提交文件移动,重新应用存储并提交更改,然后与前一个分支合并或挑选随后的提交。(然后将以前的分支名称切换为当前的分支名称。)(最好遵循 MBO 的建议并进行交互式变基。)

  • 这比所有“rebase -i”建议要容易得多。不过,我认为由于缺乏任何格式,这并没有引起太多关注。也许你可以回顾一下它,现在你已经有了 126k 点并且可能知道如何去做。;) (2认同)

Mik*_*yer 9

现在,在最新的Windows版TortoiseGit中,您可以轻松完成.

打开rebase对话框,对其进行配置,然后执行以下步骤.

  • 右键单击要拆分的提交,然后选择" Edit"(在pick,squash,delete ...中).
  • 点击" Start"开始变基.
  • 一旦它到达提交分割,检查" Edit/Split"按钮并Amend直接点击" ".提交对话框打开.
    编辑/拆分提交
  • 取消选择要放在单独提交中的文件.
  • 编辑提交消息,然后单击" commit".
  • 在有要提交的文件之前,提交对话框将一次又一次打开.当没有更多文件要提交时,它仍然会询问您是否要再添加一个提交.

非常有帮助,谢谢TortoiseGit!


let*_*man 8

请注意也有git reset --soft HEAD^.它类似于git reset(默认为--mixed)但它保留了索引内容.因此,如果您已添加/删除了文件,则已将它们包含在索引中.

事实证明,在巨型提交的情况下非常有用.


Spa*_*ska 7

必要命令的快速参考,因为我基本上知道该怎么做但总是忘记正确的语法:

git rebase -i <sha1_before_split>
# mark the targeted commit with 'edit'
git reset HEAD^
git add ...
git commit -m "First part"
git add ...
git commit -m "Second part"
git rebase --continue
Run Code Online (Sandbox Code Playgroud)

感谢Emmanuel Bernard 的博客文章


bzu*_*zuo 6

以下是如何在IntelliJ IDEAPyCharmPhpStorm等中拆分一个提交

  1. 在版本控制日志窗口中,选择要拆分的提交,右键单击并选择Interactively Rebase from Here

  2. 将要拆分的标记为edit,单击Start Rebasing

  3. 您应该看到放置了一个黄色标签,这意味着 HEAD 已设置为该提交。右键单击该提交,选择Undo Commit

  4. 现在这些提交返回到暂存区域,然后您可以单独提交它们。提交所有更改后,旧提交将变为非活动状态。