Git’s Terminology, Decoded
After 15 years of daily Git use, it’s easy to forget how bewildering its vocabulary can be for newcomers. I asked folks on Mastodon which Git terms they find most confusing, and the responses covered nearly every core feature. Here’s a practical guide to the most confusing bits.
HEAD vs. heads
Internally, Git stores branches in a directory called .git/refs/heads. A “head” is essentially a branch pointer—the most recent commit on that branch. The official glossary distinguishes between a branch (all commits on it) and a head (just the latest commit), but they’re two views of the same concept.
HEAD (all caps) is special: it points to your current branch, stored in .git/HEAD. This capitalization distinction—a head is a branch, HEAD is the current branch—might be Git’s weirdest naming choice. But the exceptions to “HEAD is the current branch” are where things get tricky.
Detached HEAD State
$ git checkout v0.1
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
[...]
Normally, HEAD points to a branch like main, and new commits land on that branch. But HEAD can also point directly to a commit ID—say, when you check out a tag. Git calls this “detached HEAD state.”
In this state, several operations break down:
git pullfails, since there’s no current branch to update.git pushonly works with special syntax.git commit,git merge,git rebase, andgit cherry-pickstill run, but they create “orphaned” commits not connected to any branch, making them hard to find later.
To escape, either create a new branch or switch to an existing one.
“Ours” vs. “Theirs” in Merge and Rebase
When resolving conflicts, git checkout --ours file.txt picks one side—but which side is “ours” depends on the operation:
- Merge: The current branch is “ours”; the branch being merged in is “theirs.”
- Rebase: It’s reversed—the current branch is “theirs,” and the base branch you’re rebasing onto is “ours.”
$ git checkout merge-into-ours # current branch is "ours"
$ git merge from-theirs # branch we're merging in is "theirs"
$ git checkout theirs # current branch is "theirs"
$ git rebase ours # branch we're rebasing onto is "ours"
This inversion happens because git rebase main works by repeatedly merging your current branch’s commits into a copy of main. Tools like VSCode mirror this confusion by labeling them “current change” and “incoming change.”
“Up to Date with ‘origin/main’” Is a Lie (Sort Of)
When git status says your branch is “up to date with origin/main,” it doesn’t mean your branch matches the remote right now. It means it’s up to date with what origin/main looked like the last time you ran git fetch or git pull—possibly days ago. Git could show “as of your last fetch 5 days ago” since the reflog stores that timestamp, but it doesn’t. This gives a false sense of security if you’ve forgotten to sync.
HEAD^, HEAD~, and Parent Syntax
For most commits, HEAD^ and HEAD~ are identical—both mean “one commit ago.” The difference matters with merge commits, which have multiple parents:
HEAD^is the first parent;HEAD^2is the second,HEAD^3the third, and so on.HEAD~3means the parent’s parent’s parent—a linear walk back.HEAD^3is the third parent of a merge commit, which may be a completely different commit thanHEAD~3.
In a merge context, HEAD^ is “ours,” and HEAD^2 is “theirs.”
The Two-Dot and Three-Dot Ranges
A - B main
\
C - D test
With git log, the difference is clear:
main..testshows commits ontestnot onmain(C and D in the diagram).main...testshows commits on both sides (B, C, and D).
But git diff interprets the same syntax differently: git diff test..main compares the two branch tips directly (B vs. D), while git diff test...main diffs from their common ancestor (A vs. D). Two commands, two meanings for the same symbols—the documentation doesn’t make this easy.
Fast-Forward, Explained
$ git status
On branch main
Your branch is behind 'origin/main' by 2 commits, and can be fast-forwarded.
(use "git pull" to update your local branch)
When git status says your branch “can be fast-forwarded,” it means the history looks like this—your local branch simply lacks the remote’s newer commits:
main: A - B - C
origin/main: A - B - C - D - E
A - B - C - D - E (origin/main)
|
main
No divergence means no conflicts are possible; a fast-forward simply appends those commits. After git pull, your history becomes:
main: A - B - C - D - E
origin/main: A - B - C - D - E
If your local branch has its own unique commit, fast-forwarding is impossible:
A - B - C - X (main)
|
- - D - E (origin/main)
You’ll then see a different status message:
$ git status
Your branch and 'origin/main' have diverged,
and have 1 and 2 different commits each, respectively.
References and Symbolic References
“Reference” is an umbrella term covering at least three things:
- Branches and tags like
mainorv0.2. HEAD, the current branch pointer.- Expressions like
HEAD^^^, which Git resolves to a commit ID (technically “revision parameters”).
“Symbolic reference” usually refers only to HEAD, which holds a central role—most core commands depend on its value.
Refspecs and Tree-ish
Remote configuration in .git/config includes refspec entries like +refs/heads/main:refs/remotes/origin/main. Most users never touch these defaults, and the syntax remains opaque.
The git checkout man page refers to a “tree-ish” argument, which can mean:
- A commit ID (e.g.,
182cd3f) - A reference to one (
main,HEAD^^,v0.3.2) - A subdirectory inside a commit (
main:./docs)
In practice, “tree-ish” can simply mean “commit or reference to a commit.”
Index, Staged, Cached
These three terms all point to the same file: .git/index, which holds staged changes. Flags like git diff --cached, git rm --cached, and git diff --staged all operate on this staging area.
There are quirks: --index and --cached generally don’t mean the same thing, and the index file tracks untracked files for performance even though you don’t think of untracked files as “staged.”
Reset vs. Revert vs. Restore
These three commands overlap, and their man pages don’t help:
git reset --hardandgit restore .alone can appear to do the same thing (discard working tree changes).git revert COMMIT: Creates a new commit that undoesCOMMIT(deleting lines it added). Safe for shared branches.git reset --hard COMMIT: Moves your current branch back toCOMMIT, erasing everything after. Destructive.git restore --source=COMMIT PATH: RestoresPATHfromCOMMITwithout touching history or other files.
The nouns differ—current HEAD, some commits, working tree files—but the verb meanings aren’t obvious.
The Many Meanings of “Track”
Git uses “track” three different ways:
- Untracked files: not managed by Git, won’t be committed.
- Remote-tracking branch (e.g.,
origin/main): a local reference storing what the remote’smainpointed to at last fetch. - Branch tracking a remote:
mainis configured in.git/configsogit pullandgit pushwork.
The confusing part: a local branch tracking a remote and a remote-tracking branch are different objects. You can commit to main; you cannot commit to origin/main—it only updates via fetch or pull.
Checkout and Reflog
git checkout does two unrelated jobs: switching branches and discarding unstaged changes to files. Modern Git splits these into git switch and git restore, though the original remains due to muscle memory. Argument ordering (e.g., git checkout main file.txt) and the optional -- separator can still trip up long-time users.
The reflog (“reference log”) records everything a reference has ever pointed to. It can rescue you from catastrophic mistakes like deleting a branch, but its interface is difficult to navigate comfortably.
Merge, Rebase, and Cherry-Pick
git merge: creates a single new commit combining two branches.git rebase: copies commits from your current branch onto a target, one at a time.git cherry-pick: similar to rebase but with different syntax—rebase copies from your branch, while cherry-pick brings commits to it.
The Onto Flag
git rebase --onto main otherbranch mybranch moves a specific range of commits (those between otherbranch and mybranch) onto a new base. The three-branch syntax is hard to remember, but it solves niche workflows where you need to relocate only part of a branch’s history.
“Commit”: Verb and Noun
Unlike in databases where “commit” is only a verb, Git uses it as both: “commit often” and “the most recent commit.” A commit can also be conceptualized three ways—as a snapshot, a diff, or a history—and different commands treat it differently (git show shows a diff, git log builds history from it, git restore takes a snapshot view). Git’s terminology doesn’t clarify which interpretation applies.
More Terms Worth Knowing
Additional confusing terms that come up frequently:
- Pickaxe: likely referring to
git log -Sandgit log -G, which search through prior diffs. - Blob, tree: internal object types that often surprise newcomers.
- Push vs. pull: pull is fetch plus merge; they’re not opposites.
- Origin, upstream, downstream: directionality concepts that aren’t intuitive.
- Porcelain and plumbing: the distinction between user-friendly and internal commands.
If forced to pick the three most confusing terms, it would be hard to argue with these: the head/HEAD distinction, the difference between a “remote-tracking branch” and a “branch that tracks a remote,” and how “index,” “staged,” and “cached” all mean the same thing.



