# Branches, refs and HEAD

> A branch is a file holding one commit hash; HEAD says which branch you’re on.

Concept · How Git works · https://levelcode.org/concepts/git-refs

A **branch** isn't a copy of your code, and it doesn't contain commits. It's a **ref**: a name that points to one commit hash.

```bash
$ cat .git/refs/heads/main
9f2c1e7a4b…
```

Commits point to their parents, so one commit hash is enough to reach a branch's whole history. That's why branches in Git are nearly free. Creating one writes 41 bytes. (Git may also keep refs together in `.git/packed-refs`, or in the newer reftable format, so the file isn't always there. `git rev-parse main` always gives you the hash.)

## Kinds of refs

| Ref | Example | Moves? |
|---|---|---|
| Local branch | `refs/heads/feature-x` | Yes, forward to each new commit you make on it |
| Remote-tracking branch | `refs/remotes/origin/main` | Only when you `fetch` or `pull` |
| Tag | `refs/tags/v1.4.0` | No, it's fixed to one commit |

`origin/main` is your repository's **last known position** of `main` on the server. It doesn't update until you fetch.

## HEAD

**HEAD** is the ref that says where you are. Normally it points to a branch, not directly to a commit:

```bash
$ cat .git/HEAD
ref: refs/heads/main
```

When you commit, Git creates the commit with the current HEAD commit as its parent, then **moves the branch HEAD points to** forward. HEAD comes along with it.

## Detached HEAD

If you check out a commit, tag or remote branch directly (`git switch --detach v1.4.0`), HEAD holds a commit hash instead of a branch name:

```text
HEAD → 41ab…          (detached)
main → 9f2c…
```

You can look around and even commit, but no branch moves to follow those commits. When you switch away, they're left with no name pointing to them. They aren't deleted right away, and you can find them in the [reflog](https://levelcode.org/concepts/reflog). To keep them, create a branch where you are: `git switch -c keep-this`.

## Moving refs

- `git commit`, `merge` and `pull` move the current branch **forward**. See [fast-forward vs merge commits](https://levelcode.org/concepts/fast-forward-merge).
- `git reset <commit>` moves the current branch **anywhere**, including backwards. See [the three areas](https://levelcode.org/concepts/git-three-areas).
- `git branch -f name <commit>` moves another branch.
- `git push` asks the remote to move **its** ref. A push that would move it backwards or sideways is rejected unless you force it.

Everything else in Git's history is the [object store](https://levelcode.org/concepts/git-object-model). Refs are the only part that changes.
