Search code examples
gitgit-rebase

git rebase -i HEAD~N returns more than N commits


Here is my git command:

git rebase -i HEAD~2

I would expect to see the 2 most recent commits but I see this instead:

enter image description here

I also want to mention that while I have been attempting to squash certain commits, I consistently running into merge conflicts. I'm getting the feeling that these issues might be related.

I'm a bit of a git noob so any help would be much appreciated.

git log --graph --all --oneline --decorate: enter image description here

Commit Chain:

enter image description here


Solution

  • Merge commits have more than one parent commit. ~ indicates the first parent of a commit. That is the left hand side of your graph. ~ only follows the first parents. HEAD~2 asks for the first grandparent.

    ~ is a good way to move through history skipping over merged branches.

    * HEAD
    |\
    | *
    | *
    | *
    | *
    * | HEAD~1
    |/
    * HEAD~2
    |\
    | *
    | *
    |/
    * HEAD~3
    

    git rebase -i HEAD~2 is really git rebase -i HEAD~2..HEAD so you're going to get all the commits reachable from HEAD excluding all the commits reachable from HEAD~2. That means HEAD, HEAD~1, and all the commits in the side branch.

    ^ is for selecting parents. HEAD^ is the first parent, same as HEAD~. HEAD^2 selects the second parent, which is the first commit in the branch.

    ^ and ~ can be chained. If you want just two commits on the side branch, that's git rebase -i HEAD^2~2. HEAD^2 selects the second parent of HEAD. So HEAD^2~2 is the first grandparent of the second parent of HEAD. It's easier with an illustration.

    * HEAD
    |\
    | * HEAD^2
    | * HEAD^2~ also HEAD^2^
    | * HEAD^2~2 also HEAD^2^^
    | * HEAD^2~3 also HEAD^2^^^
    * | HEAD~1 also HEAD^1
    |/
    * HEAD~2 also HEAD^1~
    |\
    | *
    | *
    |/
    * HEAD~3
    

    Confused? That's ok. Rather than messing with all this, it's often easier to just rebase back to the branch you merged off of and leave the extra commits as pick.

    git rebase -i main
    

    See gitrevisions for more.