我正在尝试将 2 个提交合并为 1 个,所以我遵循了 “squashing commits with rebase” from git ready。
我跑了
git rebase --interactive HEAD~2
在生成的编辑器中,我将 pick
更改为 squash
,然后保存退出,但变基失败并出现错误
没有先前的提交就不能“挤压”
现在我的工作树已经达到这个状态,我很难恢复。
命令 git rebase --interactive HEAD~2
失败并显示:
交互式变基已经开始
git rebase --continue
失败
没有先前的提交就不能“挤压”
概括
错误信息
没有先前的提交就不能“挤压”
意味着您可能试图“向下挤压”。 Git 总是将较新的提交压缩为较旧的提交 或“向上”,如在交互式变基待办事项列表中所见,即前一行的提交。将待办事项列表第一行的命令更改为 squash
将始终产生此错误,因为第一次提交没有任何内容可以压缩。
修复
首先回到你开始的地方
$ git rebase --abort
说你的历史是
$ git log --pretty=oneline
a931ac7c808e2471b22b5bd20f0cad046b1c5d0d c
b76d157d507e819d7511132bdb5a80dd421d854f b
df239176e1a2ffac927d8b496ea00d5488481db5 a
也就是说,a 是第一个提交,然后是 b,最后是 c。提交 c 后,我们决定将 b 和 c 压缩在一起:
(注意:运行 git log
将其输出通过管道传输到寻呼机,在大多数平台上默认为 less
。要退出寻呼机并返回命令提示符,请按 q
键。)
运行 git rebase --interactive HEAD~2
为您提供了一个编辑器
pick b76d157 b
pick a931ac7 c
# Rebase df23917..a931ac7 onto df23917
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
(请注意,与 git log
的输出相比,此待办事项列表的顺序相反。)
将 b 的 pick
更改为 squash
将导致您看到的错误,但如果您通过将待办事项列表更改为
pick b76d157 b
squash a931ac7 c
并保存退出您的编辑器,您将获得另一个编辑器,其内容为
# This is a combination of 2 commits.
# The first commit's message is:
b
# This is the 2nd commit message:
c
当您保存并退出时,编辑文件的内容将成为新组合提交的提交消息:
$ git log --pretty=oneline
18fd73d3ce748f2a58d1b566c03dd9dafe0b6b4f b and c
df239176e1a2ffac927d8b496ea00d5488481db5 a
关于重写历史的注意事项
交互式变基重写历史。尝试推送到包含旧历史的远程会失败,因为它不是快进。
如果您重新定位的分支是一个主题或功能分支您正在自己工作,那没什么大不了的。推送到另一个存储库将需要 --force
选项,或者您可以根据远程存储库的权限,首先删除旧分支,然后推送重新设置的版本。那些可能会破坏工作的命令示例不在此答案的范围内。
在没有非常充分理由(例如泄露密码或其他敏感细节)的情况下,在您与其他人合作的分支上重写已发布的历史记录会迫使您的合作者工作,并且是反社会的,并且会惹恼其他开发人员。 “Recovering From an Upstream Rebase” section in the git rebase
documentation 进行了解释,并强调了这一点。
Rebase(或任何其他形式的重写)其他人基于其工作的分支是一个坏主意:它下游的任何人都被迫手动修复他们的历史记录。本节说明如何从下游的角度进行修复。然而,真正的解决办法是首先避免重新定位上游。 …
如果有多个提交,您可以使用 git rebase -i
将两个提交压缩为一个。
如果您只想合并两个提交,并且它们是“最近的两个”,则可以使用以下命令将两个提交合并为一个:
git reset --soft "HEAD^"
git commit --amend
git reset --soft HEAD~10
,其中 10 是您要合并的提交数。
HEAD
的数量,也可以使用 git reset --soft 47b5c5...
重置为特定的提交,其中 47b5c5...
是提交的 SHA1 ID。
变基:你不需要它:
最常见情况的更简单方法。
在大多数情况下:
实际上,如果您想要的只是简单地将几个最近的提交合并为一个,但不需要 drop
、reword
和其他 rebase 工作。
你可以简单地做:
git reset --soft "HEAD~n"
假设 ~n 是软取消提交的提交数(即 ~1、~2、...)
然后,使用以下命令修改提交消息。
git commit --amend
这与长范围的 squash
和一个 pick
几乎相同。
它适用于 n 次提交,但不仅仅是上面提示的两次提交。
~n
是软取消提交的提交数(即 ~1
、~2
、...)
n
个最后的提交,而是中间的 n
个提交,该怎么办?我可以轻松做到这一点吗?
git rebase -i
就是您需要做的 squash
工作。 @chumakoff
n
个最近的提交合并为一个,首先使用 git reset --soft @~m
,其中 m = n - 1
首先,您应该检查您有多少次提交:
git log
有两种状态:
一是只有两个提交:
例如:
commit A
commit B
(在这种情况下,你不能使用 git rebase 来做)你需要做以下事情。
$ git reset --soft HEAD^1
$ git commit --amend
另一个是有两个以上的提交;你想合并提交 C 和 D。
例如:
commit A
commit B
commit C
commit D
(在这种情况下,你可以使用 git rebase)
git rebase -i B
而不是用“壁球”来做。剩下的变薄很容易。如果您仍然不知道,请阅读http://zerodie.github.io/blog/2012/01/19/git-rebase-i/
git push -f origin master
可能是必要的。
假设您在自己的主题分支中。如果您想将最后 2 个提交合并为一个并看起来像个英雄,请在您进行最后两个提交之前分支提交(使用相对提交名称 HEAD~2 指定)。
git checkout -b temp_branch HEAD~2
然后在这个新分支中压缩提交另一个分支:
git merge branch_with_two_commits --squash
这将带来更改但不会提交它们。所以只要提交它们,你就完成了。
git commit -m "my message"
现在您可以将这个新主题分支合并回您的主分支。
a
和 c
需要合并在一起并保持 b
不变的情况。
git checkout -b combine-last-two-commits "HEAD^2"
) 时,出现错误:fatal: 'HEAD^2' is not a commit and a branch 'combine-last-two-commits' cannot be created from it
你可以取消rebase
git rebase --abort
当你再次运行交互式 rebase 命令时,'squash;提交必须低于列表中的选择提交
$ git rebase --abort
如果要撤消 git rebase,请随时运行此代码
$ git rebase -i HEAD~2
重新应用最后两次提交。上面的命令将打开一个代码编辑器
[最新的提交将在底部]。将最后一次提交更改为 squash(s)。由于 squash 将与先前的提交融合。
然后按 esc 键并输入 :wq 保存并关闭
在 :wq 之后,您将处于活动变基模式
注意:如果没有警告/错误消息,您将获得另一个编辑器,如果出现错误或警告另一个编辑器不会显示,您可以通过运行 $ git rebase --abort
中止如果您看到错误或警告,请继续运行 $ git rebase --continue
您将看到您的 2 提交消息。选择一个或编写自己的提交信息,保存并退出 [:wq]
注意 2:如果您运行 rebase 命令,您可能需要强制将更改推送到远程仓库
$ git push -f
$ git push -f origin master
git push -f origin/master
是缺少其他答案的内容。 +1
我经常使用 git reset --mixed 在您要合并的多个提交之前恢复基本版本,然后我进行新的提交,这样可以让您的提交最新,确保您的版本在您推送到服务器后是 HEAD。
commit ac72a4308ba70cc42aace47509a5e
Author: <me@me.com>
Date: Tue Jun 11 10:23:07 2013 +0500
Added algorithms for Cosine-similarity
commit 77df2a40e53136c7a2d58fd847372
Author: <me@me.com>
Date: Tue Jun 11 13:02:14 2013 -0700
Set stage for similar objects
commit 249cf9392da197573a17c8426c282
Author: Ralph <ralph@me.com>
Date: Thu Jun 13 16:44:12 2013 -0700
Fixed a bug in space world automation
如果我想将 head 两个提交合并为一个,首先我使用:
git reset --mixed 249cf9392da197573a17c8426c282
“249cf9392da197573a17c8426c282”是第三个版本,也是你合并之前的基础版本,之后,我做了一个新的提交:
git add .
git commit -m 'some commit message'
就是这样,希望是每个人的另一种方式。
仅供参考,来自 git reset --help
:
--mixed
Resets the index but not the working tree (i.e., the changed files are
preserved but not marked for commit) and reports what has not been
updated. This is the default action.
由于我几乎将 git cherry-pick
用于所有内容,因此对我来说即使在这里也很自然地这样做。
鉴于我已签出 branchX
并且在它的顶端有两个提交,我想创建一个结合它们的内容的提交,我这样做:
git checkout HEAD^ // Checkout the privious commit
git cherry-pick --no-commit branchX // Cherry pick the content of the second commit
git commit --amend // Create a new commit with their combined content
如果我也想更新 branchX
(我想这是这种方法的缺点),我还必须:
git checkout branchX
git reset --hard <the_new_commit>
如果你有几个提交想要压缩在一起,你可以使用交互式 rebase 方法来完成。 (感谢 Mads 教我这个!)
git rebase 起源/开发 -i
然后,您只需在要压缩的提交前写一个“s”,然后将它们汇总到主提交中
处于 git rebase-interactive 模式(vim)时的专业提示:
导航到您要修改的提交行 (ESC) ciw -(更改内部单词)将更改光标下的整个单词。输入你想做的事情,例如 s 用于挤压 (ESC) wq 写得很清楚,你就完成了。
https://i.stack.imgur.com/xbFAM.gif
然后git push -f
如果您的主分支 git log
如下所示:
commit ac72a4308ba70cc42aace47509a5e
Author: <me@me.com>
Date: Tue Jun 11 10:23:07 2013 +0500
Added algorithms for Cosine-similarity
commit 77df2a40e53136c7a2d58fd847372
Author: <me@me.com>
Date: Tue Jun 11 13:02:14 2013 -0700
Set stage for similar objects
commit 249cf9392da197573a17c8426c282
Author: Ralph <ralph@me.com>
Date: Thu Jun 13 16:44:12 2013 -0700
Fixed a bug in space world automation
并且您想合并前两个提交只需执行以下简单步骤:
首先要安全地检查单独分支中的倒数第二个提交。您可以将分支命名为任何名称。 git checkout 77df2a40e53136c7a2d58fd847372 -b merge-commits 现在,只需将上次提交中的更改挑选到这个新分支中:git cherry-pick -n -x ac72a4308ba70cc42aace47509a5e。 (如果出现任何冲突,请解决冲突)所以现在,您在最后一次提交中的更改存在于您的第二次最后提交中。但是你仍然必须提交,所以首先添加你刚刚挑选的更改,然后执行 git commit --amend。
而已。如果你愿意,你可以在分支“merged-commits”中推送这个合并版本。
此外,您现在可以丢弃 master 分支中的背靠背两次提交。只需将您的主分支更新为:
git checkout master
git reset --hard origin/master (CAUTION: This command will remove any local changes to your master branch)
git pull
在完成所有操作(即压缩提交)后添加到@greg 的答案,如果您执行 git push (原始提交将保留在分支中),而如果您执行 git push -f origin 则提交将被删除。例如,如果您执行 git push,您将提交 B 和提交 C 组合在一起,您将拥有提交 B、提交 C 和提交 BC,但是如果您执行 git push -f origin,您将只有提交 BC
如果您想合并两个最近的提交并仅使用旧提交的消息,您可以使用 expect
自动执行该过程。
我假设:
您正在使用 vi 作为编辑器
你的提交是一行
我用 git version 2.14.3 (Apple Git-98)
进行了测试。
#!/usr/bin/env expect
spawn git rebase -i HEAD~2
# change the second "pick" to "squash"
# down, delete word, insert 's' (for squash), Escape, save and quit
send "jdwis \033:wq\r"
expect "# This is a"
# skip past first commit message (assumed to be one line), delete rest of file
# down 4, delete remaining lines, save and quit
send "4jdG\r:wq\r"
interact
expect
也未描述。
expect
文档的页面的链接。
让我建议你一个更简单的方法,
您可以执行以下操作,而不是深入了解 GIT 的深层概念并为编辑的螃蟹而烦恼;
假设您从 master 创建了一个名为 bug1 的分支。对 bug1 做了 2 次提交。您只用这些更改修改了 2 个文件。
将这两个文件复制到文本编辑器中。收银员。粘贴文件。犯罪。
就这么简单。
git log hashoftheoldcommit
并且它起作用了,但我很想看到一个包含所有这些无法访问的提交的git log --graph
git rebase --interactive HEAD~3
,然后将 c 行移到 a 行下方。将 c 行的pick
更改为s
,然后退出编辑器。