keepcalculator

06 Numbers and Powers

HEAD After a Squash Merge: Trace the Parent First

Card No. 06·17795 · Updated · Standard formula, shown below

After a squash merge, HEAD points to the current commit; it does not automatically point to the original feature-branch tip or preserve that branch’s individual commits. Before blaming Git for an unexpected history, inspect the commit’s parents and the branch or reference you are viewing: a squash merge normally creates one new commit with a single parent, unlike a conventional merge commit.

What a squash merge changes

A squash merge takes the changes introduced by a series of commits and records them together as one commit on the destination branch. For example, a feature branch might contain commits A, B, and C, while the destination branch is at P. Squashing those changes produces a new commit S on the destination branch. In the usual case, S has P as its sole parent; A, B, and C are not parents of S and are not made part of the destination branch’s ancestry merely because their changes appear in S.

The distinction is between content and history. The resulting files may contain the combined effect of A, B, and C, but Git’s commit graph records S as a new commit. A normal merge commit, by contrast, usually has two parents: the previous destination tip and the feature-branch tip. That second parent connects the feature commits to the destination branch’s ancestry.

Trace HEAD and its parent

HEAD is Git’s name for the commit currently checked out, either directly or through a branch reference. Start by checking where you are and what commit HEAD identifies:

  • git status shows the current branch, or indicates detached HEAD, and reports uncommitted changes.
  • git branch --show-current prints the current branch name when HEAD is attached to a branch. It prints nothing in detached-HEAD state.
  • git log -1 --oneline --decorate shows the current commit with any visible branch or tag labels.
  • git show -s --format='%H%n%P%n%s' HEAD prints HEAD’s full commit ID, its parent ID or IDs, and its subject.

In that last command, %P lists the parent commit IDs. One parent is typical for an ordinary commit and for a squash-created commit; two or more parents are the characteristic shape of a merge commit. A root commit has no parent. These are useful clues, but parent count alone does not prove what operation created a commit: history can be rewritten, and a commit’s message is editable.

How to inspect the squash commit

To see the commit and its change relative to its parent, run git show --stat HEAD. For the full patch, use git show HEAD. If HEAD is not the commit you meant to inspect, substitute a branch name, tag, or commit ID, such as git show --stat origin/main. The command displays the selected commit’s recorded change against its first parent; it does not reconstruct the feature branch’s original sequence of commits.

To compare a suspected squash commit with the destination branch’s previous tip, inspect the commit’s parent ID, then compare those two commits. For example:

  • git show -s --format='%H %P %s' HEAD identifies HEAD and its parent.
  • git diff HEAD^ HEAD shows the change recorded by HEAD relative to its first parent.
  • git log --oneline --graph --decorate --all gives a compact view of commits reachable from the refs Git currently knows about.

HEAD^ means the first parent of HEAD. For commits with multiple parents, HEAD^2 means the second parent. A squash commit usually has no second parent, so asking for HEAD^2 will fail. This is not evidence that Git lost a parent; it is consistent with the single-parent structure of a squash commit.

Why the feature commits may still appear

Squashing does not necessarily erase the original commits from the repository immediately. The feature branch may still point to them, and other branches, tags, or reflogs may also retain references. Use git branch -a --contains <commit> to find local or remote-tracking branches that contain a commit, and git log --all --oneline --decorate to inspect commits reachable from known refs.

If no reference points to the old commits, they may not appear in ordinary branch history even if Git still has their objects temporarily. Git’s reflog records recent movements of local references and HEAD; git reflog can help locate a branch tip or commit after a checkout, reset, or branch update. Reflogs are local records, not a permanent, shared history, and entries can expire or be removed. Do not assume they will always be available as a recovery mechanism.

Common causes of confusing output

  • Looking at the wrong reference: HEAD may be on a different branch, or detached at a commit. Check status and the decorated log before interpreting the graph.
  • Expecting the original commits to be ancestors: the squash commit records the combined changes, not the feature branch’s commit chain as its parents.
  • Confusing a remote-tracking branch with a live remote view: names such as origin/main are local references updated by fetch. Run git fetch before relying on them to reflect the remote’s latest state.
  • Assuming a subject proves the operation: messages such as “squash merge” are conventions, not structural proof. Inspect parent IDs and the diff.
  • Expecting a clean working tree to describe commit history: git status reports working-tree and staging changes, while git log and git show explain committed history.

Squash, merge, and rebase compared

A standard merge preserves the existing branch commits and usually adds a two-parent merge commit. A squash merge creates a new, single-parent commit containing the combined patch, so the original feature commits are not connected as ancestors through that commit. A rebase replays commits onto a different base, generally creating new commit IDs for the replayed commits; it does not combine them into one commit unless a separate squashing operation is used.

These approaches can yield similar file contents while producing different graphs. That matters when checking ancestry, deciding whether a branch is already merged, reviewing changes, or later integrating the same work again. Because a squash commit does not record the feature tip as a parent, Git cannot infer the original branch relationship from that commit alone. Teams may therefore delete the feature branch after integration or use consistent branch and review practices to make the relationship easier to follow.

Frequently Asked Questions

Does a squash merge change where HEAD points?
It can, if the operation checks out or updates the destination branch, but “squash merge” by itself does not define your current checkout. Check git status and the decorated log to see what HEAD currently identifies.

How can I tell whether HEAD is a squash commit?
Inspect its parents and patch with git show -s --format='%H %P %s' HEAD and git show --stat HEAD. A single parent and a combined change are consistent with a squash commit, but the graph alone cannot establish which command or hosting workflow created it.

Why does git log not show my feature commits?
They may not be ancestors of the squash commit. Check other branches and tags with git log --all or search for references containing a known commit; a local reflog may also show a recently moved branch tip.

Can I use HEAD~2 to find the feature branch’s second-to-last commit?
No. HEAD~2 follows the first-parent chain two steps back from HEAD. It does not search for commits that were combined into a squash patch or recover the old feature-branch sequence.

Is a missing second parent a Git error?
No. A squash-created commit normally has one parent. A second parent is expected for a typical merge commit, not for every commit that incorporates changes from another branch.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →