Quoting the git merge
documentation, --squash
means:
Produce the working tree and index state as if a real merge happened (except for the merge information), but do not actually make a commit or move the HEAD
, nor record $GIT_DIR/MERGE_HEAD
to cause the next git commit
command to create a merge commit. This allows you to create a single commit on top of the current branch whose effect is the same as merging another branch (or more in case of an octopus).
In other words, after you git commit
the result, you just have an ordinary commit, not a merge commit. The tree associated with this ordinary commit—what you get in the work-directory if you check it out—matches the tree you would have gotten by doing a merge, but no actual merge is recorded.
Given the setup you described, you had this before running git merge --squash B
:
...- o <-- A
\
o - o <-- B
as your commit graph. After git checkout A; git merge --squash B; git commit -m squash-B
you now have:
...- o ----- * <-- HEAD=A
\
o - o <-- B
Note that there is no commit-graph connection between the latest commit *
and either of the two commits that are exclusively on branch B
. The tree for commit *
is the same as the one you would have gotten by doing a regular merge, but the commit graph is different: a real merge would have looked like this:
...- o ----- *
\ /
o - o
(I left out the branch labels here to emphasize that this is not what you actually have).
The dot-dot notation A..B
asks for "commits reachable from B
, minus commits reachable from A
". The graph looks like this (same as a few above, just repeated here for clarity):
...- o ----- * <-- HEAD=A
\
o - o <-- B
The two o
commits along the lower section are reachable from B
, along with the upper o
and any earlier commits; but the upper o
and any earlier commits are reachable from A
by starting at the commit marked *
and working backwards, so these are excluded. That leaves just the two o
commits: the one at the tip of branch B
, and the one before it.
Note that if you had done a real merge, A..B
would be empty as all commits reachable from the tip of branch B
would also be reachable from the tip of branch A
. (And, by contrast, B..A
would be just commit *
since that commit would be reachable from A
but not from B
; but this is true with the no-actual-merge squash-only history as well.)