It's important to realize that in Git, branches are to some extent an illusion. Git isn't about branches. Git is about commits.
When you use git merge
—or GitHub's clicky buttons to achieve a merge—what you get is, in general, a new commit. This new commit does not—and literally can not—change any existing commits at all.1 It is a new commit, so it has a new, never-seen-before hash ID.
Meanwhile, branch names themselves just commit object hash IDs. To put it more precisely, each name holds one hash ID. This commit is the last commit that is considered "on the branch". Other, earlier commits can be on this same branch as well, and any one commit can be—and many are—on many branches at the same time.
The process of making a new commit, in Git, is done either "on a branch"—in a situation in which the special name HEAD
contains a branch name—or in "detached HEAD mode", where the special name HEAD
contains a raw hash ID. If done "on a branch", the last step of creating a new commit is for Git to write the new commit's hash ID into the current branch name, so that this branch name now identifies the new commit. Since no other branch name is updated at this time, the new commit is only on this branch.
This applies to git merge
too.2 So if you are on branch B
and run git merge A
, the new commit's hash ID is written to the name B
. This means that branch B
has the new commit as its final commit. The new commit, however, has not just the usual one parent, but instead has two: the first parent is the usual first parent, while the second parent is the tip commit of branch A
. So now, all the commits that were only on branch A
before, are now on both branches. But the name A
is not updated, so the new merge commit is not (yet) present on branch A
.
It is now possible to move the name A
such that the merge is on both branches—but you must do that as a separate step, if you want to do that.
1This is due to a fundamental property of all Git objects: a Git object's ID is a cryptographic hash of its content. If you take the content out of some existing object, change it, and put it back into Git, what you get is a new and different object with a new and different hash ID. The old object is still there, under its old hash ID, unchanged.
2This paragraph describes true merges as performed by git merge --no-ff
or git merge
that requires a true merge. With command line Git, git merge --ff-only
or git merge
that otherwise allows a fast-forward operation does not make a new commit after all.
When using GitHub to merge (but not for "rebase and merge" or "squash and merge"), the GitHub interface makes a true merge even if a fast-forward operation is possible.