在不创建分支以及在新分支上进行大量工作的情况下,是否有可能在将提交保存到本地仓库后将单个提交分成几个不同的提交?
git rebase -i
可以实现这一功能。
首先,从一个干净的工作目录开始:执行git status
应该不会显示待处理的修改、删除或添加。
现在,您需要决定要拆分哪个提交。
要拆分最近的提交,请执行以下操作:
$ git reset HEAD~
现在按照通常的方式逐个提交碎片,根据需要进行多次提交。
这需要进行 变基(rebase),也就是重写历史。为了指定正确的提交,你有几个选择:
如果它是三个提交之前,那么
$ git rebase -i HEAD~3
这里的3
是指回退了多少个提交。
如果它在你要计算的范围之外,那么
$ git rebase -i 123abcd~
其中123abcd
是你想要拆分的提交的SHA1值。
如果你在一个不同的分支(比如特性分支),想要合并到 master
分支:
$ git rebase -i master
当你进入rebase编辑界面时,找到你想要拆分的提交。在那一行的开头,用edit
(e
简写)替换pick
。保存缓冲区并退出。现在,rebase就会停在你想要编辑的提交之后。然后进行以下操作:
$ git reset HEAD~
按照通常的方式逐个提交代码片段,根据需求生成尽可能多的提交记录。
最后。
$ git rebase --continue
git add -p
命令来部分添加文件,如果需要只提交部分内容的差异,则可以在命令中加入 e
选项进行编辑。如果你想要保留一些工作但又不想包含在当前的提交中,那么 git stash
命令也非常有用。 - Craig Ringergit rebase -i HEAD^3
命令单独重新排序。这样,如果拆分出现问题,你就不必撤销太多的工作。 - David M. Lloydgit add -p
更好的是 git add -i
,但似乎不太为人所知。 - Phillippgit reset HEAD~
后,最新提交到 HEAD 中的文件将会保留在磁盘上,它们并不会丢失。我的翻译是否符合您的要求呢? - Wayne Conrad来自 git-rebase 手册(SPLITTING COMMITS 章节)
在交互模式下,您可以使用“edit”操作标记提交。然而,这并不一定意味着 git rebase 期望此编辑的结果恰好是一个提交。实际上,您可以撤销该提交,也可以添加其他提交。这可用于将提交拆分为两个:
使用命令
git rebase -i <commit>^
开始一个交互式 rebase,其中<commit>
是您要拆分的提交。实际上,只要包含该提交的任何提交范围都可以。使用“edit”操作标记要拆分的提交。
当编辑该提交时,执行
git reset HEAD^
命令。其效果是将 HEAD 向后移动一个提交,索引跟随移动,但工作树保持不变。现在将您想要包含在第一个提交中的更改添加到索引中。您可以使用
git add
(可能是交互式的)或git gui
(或两者都使用)来完成此操作。以适当的提交消息提交当前索引。
重复最后两个步骤,直到您的工作树保持干净。
使用
git rebase --continue
继续 rebase 过程。
~
代替^
。 - Kevin Kuszykgit show
查看提交,或者如果你忘记了或更喜欢这样做:稍后通过“引用日志(reflog)”返回到它。直到两周后或其他时间垃圾回收将其清除之前,其中任何内容都不会真正被“丢失”。 - underscore_d~
和^
是不同的东西。你仍然想要使用插入符号^
,因此你需要根据你的Shell适当地对其进行转义。在PowerShell中,它是HEAD`^
。在cmd.exe中,你可以将其加倍来进行转义,像这样:HEAD^^
。在大多数(全部?)的Shell中,你可以用引号括起来,如:"HEAD^"。 - AndrewFgit commit --reuse-message=abcd123
命令。它的短选项是 -C
。 - j0057之前的回答已经讲解了如何使用git rebase -i
来编辑要拆分的提交,并将其分成不同的部分提交。
当你想要将更改拆分为单独的文件时,这种方法很有效,但你需要知道更多细节。
在到达要拆分的提交后,使用rebase -i
并标记其为edit
后,你有两个选择:
使用git reset HEAD~
,逐个检查补丁,并使用git add -p
选择每个提交中所需的补丁。
编辑工作副本以删除不想要的更改;提交中间状态;然后为下一轮拉取完整提交。
如果你正在拆分一个大型提交,则选项2很有用,因为它可以让你检查中间版本是否能够顺利构建和运行。具体步骤如下:
在使用rebase -i
并进行edit
操作后,执行以下操作:
git reset --soft HEAD~
要撤销提交但保留索引中的已提交文件,您可以进行软重置(使用--soft选项)。如果您的初始提交离最终结果还很远,则可以省略--soft进行混合重置。唯一的区别是您是从所有更改都已暂存开始还是从它们全部未暂存开始。
现在进入编辑代码阶段。您可以删除更改,删除添加的文件,并做任何您想要构建所寻找的系列的第一个提交的操作。您还可以构建并运行它,并确认您拥有一组一致的源代码。
一旦您满意了,根据需要对文件进行暂存/取消暂存(我喜欢使用git gui
),然后通过UI或命令行提交更改。
git commit
第一个提交已完成。现在您想将工作副本恢复到分割后的提交之后的状态,以便可以将更多的更改用于下一次提交。要找到您正在编辑的提交的sha1,请使用git status
。在状态的前几行中,您将看到当前正在执行的变基命令,在其中可以找到原始提交的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 65dfb6a .
别忘了句号!
这将检出和暂存文件,它们与你正在编辑的提交之后的文件相同,但相对于你之前所做的提交,因此你已经提交的任何更改都不会成为该提交的一部分。
现在,您可以直接提交它以完成分割,或者再次循环,删除某些部分的提交,然后再进行临时提交。
如果您想要为一个或多个提交重用原始提交消息,则可以直接从rebase的工作文件中使用它:
git commit --file .git/rebase-merge/message
最后,一旦您提交了所有更改,
git rebase --continue
会继续进行并完成变基操作。
git checkout *正在编辑的Sha* .
时,它总是显示从*不在Git日志中的某个Sha*更新了0个路径
,并且没有任何更改。 - Noumenon使用 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
将第一个 pick
改为 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
让我们执行这个变更,将“第三个”添加到文件 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
reflog
,也不需要将所有内容都回滚。 git rebase --abort
就可以完成。 - Victor Sergienkorebase --abort
,在一个单独的分支上进行变基更容易。这样,如果你需要切换任务,你可以轻松地切换回非变基版本。 - undefinedgit rebase --interactive
可以用来将一个提交拆分成多个小提交。 在 Git 文档中关于rebase的简明介绍中有这个过程的详细步骤 - 分割提交:
^
是命令行中的转义字符:它应该重复一次。例如,使用git reset HEAD^^
而不是git reset HEAD^
。 - Frédéric^
两次会将当前HEAD上面的两个提交重置。 - Farway"HEAD^"
,在 cmd.exe 中使用 HEAD^^
,在 PowerShell 中使用 HEAD`^
。了解 shell(以及您特定的 shell)如何工作(即命令如何变成传递给程序的单个部分),这对于将在线命令转换为特定 shell 的正确字符非常有用。(不特定于 Windows。) - AndrewF现在在Windows上最新的TortoiseGit中你可以非常容易地做到这一点。
打开rebase对话框,配置它,然后按照以下步骤进行操作。
编辑
”(在pick、squash、delete之间)。 开始
”开始rebasing。 编辑/拆分
”按钮并直接点击“Amend
”。提交对话框将打开。commit
”。 非常有帮助,谢谢TortoiseGit!
一个必要命令的快速参考,因为我基本知道该做什么但总是会忘记正确的语法:
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
感谢Emmanuel Bernard的博客文章 《如何使用Git将提交分成两个》。
git rebase -i <branch or commit>
将您想要拆分的提交标记为edit
,然后复制该行。这将在rebase期间呈现出两次提交。在编辑第一个提交时,删除您希望在第二个提交中拥有的所有内容。完成后(如果适用)运行测试,git add -A
,git commit --amend
(在此处调整提交消息)和git rebase --continue
。现在您已经处于第二个提交上,它刚刚引入了您刚刚从第一个提交中删除的更改。
我认为这比仔细使用git add -p
要容易得多。
git reset --soft HEAD^
命令。它类似于git reset
(默认为--mixed
),但保留索引内容。因此,如果您已添加/删除文件,则已经在索引中了。
事实证明,在处理大型提交时非常有用。
git rebase -i
。手册中正好有你想要的内容:
git autorebase split COMMIT_ID
命令来拆分提交。 - Jérôme Pouillergit reset HEAD~
、git stash
,接着使用git cherry-pick
来挑选压缩中的第一个提交,最后再执行git stash pop
。虽然我的cherry-pick情况比较特殊,但是git stash
和git stash pop
对于其他情况也非常方便。 - SOFe