How do I squash two non-consecutive commits?

前端 未结 5 2163
执笔经年
执笔经年 2020-11-30 17:16

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

Afterward

相关标签:
5条回答
  • 2020-11-30 17:56

    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, ex: git rebase --interactive HEAD~4

    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, instead of squash, you would use the fixup keyword. For more on fixup, you can consult the git rebase docs, or check out this question which has some good answers.

    0 讨论(0)
  • 2020-11-30 18:03

    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!).

    0 讨论(0)
  • 2020-11-30 18:04

    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
    0 讨论(0)
  • 2020-11-30 18:07

    $ git checkout master

    $ git log --oneline

    D
    C
    B
    A
    

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

    $ git log --oneline

    D
    A
    
    0 讨论(0)
  • 2020-11-30 18:08

    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.

    P.S. here is bash script to do this steps in automode.

    0 讨论(0)
提交回复
热议问题