Every Git operation moves data between three areas: the working tree (your files), the index (next commit's draft), and the repository (committed history under .git). Naming what moves where is the single most useful mental model.
A remote-tracking branch is a local ref that mirrors a branch on a remote. They live under refs/remotes/<remote>/ and are updated by git fetch (and operations that imply fetch, like git pull and git clone). You do not commit directly to them.
Normally HEAD points at a branch ref, which in turn points at a commit. In detached HEAD, HEAD points directly at a commit. Detached HEAD is not broken; it is a deliberate mode that is useful for inspecting history and dangerous for committing without thinking.
The reflog records every change to HEAD and to each branch tip on your local machine. It is local, per-clone, and not pushed. Almost any "lost" commit can be rescued through it.
Git has two fundamentally different merge outcomes: fast-forward, where one branch's tip is simply moved, and three-way, where a new commit with two parents is created. The choice depends on whether the branches have diverged.
Fast-forward
If main's tip is an ancestor of feature, no real merge is needed. Git just moves main forward:
A---B---C feature
/
main
# after git merge feature
A---B---C main, feature
In Git, a branch is just a movable reference to a commit. Creating a branch is creating a 41-byte file. Compared to centralized systems where branches are full directory copies, Git's model makes branching cheap enough to use casually.
What a branch actually is
cat .git/refs/heads/main
# a1b2c3d4e5f6...
git rev-parse main
That hash is the tip of the branch. The "branch" itself is the chain of commits reachable from the tip via parent pointers.
The index is a binary file at .git/index that mirrors a tree object: a sorted list of paths with mode, hash, and stat information. It is the staging area for the next commit and a cache that lets Git skip rehashing unchanged files.
Objects are immutable and named by hash. References are the mutable, human-readable names that point at them. Every branch, tag, remote-tracking branch, and HEAD is a ref.
Where refs live
Refs are files (or entries in the packed-refs file) under .git/refs/:
Git names every object by the cryptographic hash of its content. This is content-addressed storage: the name is the content's fingerprint. The default hash is SHA-1 (160 bits, 40 hex chars). SHA-256 support exists experimentally since Git 2.29.
How a hash is computed
Git prepends a header of the form <type> <size>\0 to the raw content, then hashes:
Git's repository is, at its heart, a content-addressed object store. There are exactly four object types: blob, tree, commit, and tag. Every operation eventually touches them. Understanding these four types demystifies most of Git.
Blobs
A blob stores file contents, nothing more. No filename, no permissions, no history. Two files with identical contents share one blob, regardless of where they live in the tree.