gitcommitrebasegit-commitgit-amend

'git commit --amend' in detached HEAD state


I understand that the correct way of amending an old Git commit is to use rebase --interactive, but just to get clear on the concepts, I would like to understand what happens when I do

When I do this, instead of amending the commit, it branches a new commit off of the PARENT of that same commit.

Is this just Git's way of telling me that I cannot amend a commit that already has children commits?


Solution

  • In Git, once a commit is created, it's set in stone; you cannot change it. All you can do—by amending it, cherry-picking it, etc.—is create a new commit that "resembles" it.

    I understand your confusion: "amend" is a bit of a misnomer; it's somewhat misleading, as it suggests modifying something in place. In Git, amending a commit actually consists in creating a brand new commit that has the same parent(s) as the original commit.

    As an example, let's assume that, after running git checkout B, you're in the following situation:

    enter image description here

    (Your HEAD is detached, but that's beside the point.) Whether or not you make and stage changes, running git commit --amend will put you in this situation:

    enter image description here

    Commit D may be very, very similar to B; in particular, it may have exactly the same patch, exactly the same commit message as B, etc.. However, (commit, author) timestamps will usually differ (unless you can amend a commit under a second!), which means the SHA-1 of D will differ from that of B; and if two commits don't have the same SHA, they're not the same commit.

    When we say B is a parent commit of C, we mean commit C references commit B by its SHA. However, commit C has no way of knowing anything about the SHA of commit D, because commit D was created after C. Therefore, D cannot be C's parent. That's why commit D goes off on a tangent and doesn't have any descendants.


    If you want to land in the following state,

    enter image description here

    where B' is even only slightly different from B, you should use git rebase -i, not git commit --amend.