How do I squash two non-consecutive commits?

2019-01-16 04:46发布

I'm a bit new to the whole rebasing feature within git. Let's say that I made the following commits:

A -> B -> C -> D

Afterwards, I realize that D contains a fix which depends on some new code added in A, and that these commits belong together. How do I squash A & D together and leave B & C alone?

5条回答
走好不送
2楼-- · 2019-01-16 05:08

$ git checkout master

$ git log --oneline

D
C
B
A

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

$ git log --oneline

D
A
查看更多
我想做一个坏孩纸
3楼-- · 2019-01-16 05:26

For those using SourceTree:

Make sure you haven't already pushed the commits.

  1. Repository > Interactive Rebase...
  2. Drag D (the newer commit) to be directly above A (the older commit)
  3. Make sure commit D is highlighted
  4. Click Squash with previous
查看更多
劳资没心,怎么记你
4楼-- · 2019-01-16 05:28

You can run git rebase --interactive and reorder D before B and squash D into A.

Git will open an editor, and you see a file like this:

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

# Rebase aaaaaaa..ffffdffffdd 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

Now you change the file that it looks like this:

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

And git will now meld the changes of A and D together into one commit, and put B and C afterwards. When you don't want to keep the commit message of D, you can also use the "fix" keyword.

查看更多
狗以群分
5楼-- · 2019-01-16 05:28

Note: You should not change commits that have been pushed to another repo in any way unless you know the consequences.

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

Type i (Put VIM in insert mode)

Change the list to look like this (You don't have to remove or include the commit message). Do not misspell squash!:

pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A
squash D

Type Esc then ZZ (Save and exit 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

Type i

Change the text to what you want the new commit message to look like. I recommend this be a description of the changes in commit A and D:

new_commit_message_for_A_and_D

Type Esc then 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)

You have now created a new commit E. Commits A and D are no longer in your history but are not gone. You can still recover them at this point and for a while by git rebase --hard D (git rebase --hard will destroy any local changes!).

查看更多
神经病院院长
6楼-- · 2019-01-16 05:32

Interactive rebase works well until you have big feature branch with 20-30 commits and/or couple of merges from master or/and fixing conflicts while you was commiting in your branch. Even with finding my commits through history and replacing pick with squash doesn't worked here. So i was looking for another way and found this article. I did my changes to work this on separate branch:

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

Before this I got my pull request with about ~30 commits with 2-3 merges from master + fixing conflicts. And after this I got clear PR with one commit.

查看更多
登录 后发表回答