如何压缩两个非连续的提交?


182

我对git中的整个重新定位功能有些陌生。假设我做了以下提交:

A -> B -> C -> D

之后,我意识到其中D包含一个修复程序,该修复程序依赖于中添加的一些新代码A,并且这些提交属于一起。如何壁球AD一起休假BC孤独?

Answers:


256

您可以先运行git rebase --interactiveD并对其重新排序,然后再将D压缩到A中。

Git将打开一个编辑器,您会看到一个类似这样的文件,例如: git rebase --interactive HEAD~4

pick aaaaaaa Commit A
pick bbbbbbb Commit B
pick ccccccc Commit C
pick ddddddd Commit D

# Rebase aaaaaaa..ddddddd onto 1234567 (4 command(s))
#
# 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
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

现在,您更改文件,如下所示:

pick aaaaaaa Commit A
squash ddddddd Commit D
pick bbbbbbb Commit B
pick ccccccc Commit C

git现在将A和D的更改合并到一个提交中,然后将B和C放入。当您不想保留D的提交消息时squash,可以使用fixup关键字而不是。有关更多信息fixup,您可以查阅git rebase文档,或查看该问题有一些不错的答案。


5
最初,我将其读为“将D重置为A,将D压缩为A,然后将B重置为DA”。从答案中尚不清楚这是否可以通过在文本编辑器中对行进行重新排序来完成。
维克多·谢尔琴科

3
如果您的分支机构是本地分支机构,则在重新定基There is no tracking information for the current branch时会出错。在这种情况下,您需要指定要使用的提交数,例如:git rebase -i HEAD~4。看到这个答案
约翰多多

1
我使用交互模式(git rebase -i)多年,我才意识到它可以重新排序。感谢🤟🏻
CalvinChe '19

43

注意:除非您知道后果,否则不应以任何方式更改已推送到另一个存储库的提交

git log --oneline -4

D commit_message_for_D
C commit_message_for_C
B commit_message_for_B
A commit_message_for_A

git rebase --interactive

pick D commit_message_for_D
pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A

类型i(以插入模式放置VIM)

更改列表,使其看起来像这样(您不必删除或包含提交消息)。不要拼错squash

pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A
squash D

键入Esc然后ZZ(保存并退出VIM)

# This is a combination of 2 commits.
# The first commit's message is:

commit_message_for_D

# This is the 2nd commit message:

commit_message_for_A

类型 i

将文本更改为新提交消息的外观。我建议这是对commit A和中更改的描述D

new_commit_message_for_A_and_D

键入Esc然后ZZ

git log --oneline -4

E new_commit_message_for_A_and_D
C commit_message_for_C
B commit_message_for_B

git show E

(You should see a diff showing a combination of changes from A and D)

现在,您已经创建了一个新提交E。提交A并且D不再存在于您的历史记录中,但是并没有消失。您仍然可以在此时恢复它们一段时间git rebase --hard Dgit rebase --hard将破坏任何本地更改!)。


3

对于那些使用SourceTree的用户

确保您尚未推送提交。

  1. 资源库>交互式资源库...
  2. 将D(较新的提交)拖动到A(较旧的提交)的正上方
  3. 确保提交D突出显示
  4. 请点击 Squash with previous

1

交互式rebase可以很好地工作,直到您拥有具有20-30次提交的大型功能分支和/或主分支中的几次合并或/和在分支中提交时解决冲突为止。即使通过历史查找我的提交并替换picksquash此处也没有用。所以我在寻找另一种方式,找到了这篇文章。我进行了更改,以在单独的分支上进行此工作:

git checkout master
git fetch
git pull
git merge branch-name
git reset origin/master
git branch -D branch-name
git checkout -b branch-name
git add --all
#Do some commit
git push -f --set-upstream origin branch-name

在此之前,我收到了大约30次提交的请求请求,其中有2-3次来自主服务器+修复冲突。在此之后,我一次提交就获得了明确的PR。

PS这里是bash 脚本,可以在自动模式下执行此步骤。


这篇文章中的第一个解决方案真的很好,感谢您的链接
Hoody

-1

$ git checkout master

$ git log --oneline

D
C
B
A

$ git rebase --onto HEAD ^^^ HEAD ^

$ git log --oneline

D
A

我想你的意思是--oneline?好像您已删除CB,这不是OP想要的。
bstpierre 2010年

没为我工作。它将我的HEAD和master都移到了A,但没有将D合并到A(git show A),并且D,C和B在我的参考日志中丢失了。不得不git rebase D回来。
Nate
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.