Last modified: September 13, 2025
This article is written in: πΊπΈ
HEAD
is Gitβs pointer to the snapshot youβre currently working onβthe bookmark of your checkout. Most of the time, HEAD
points to the tip of a branch (like master
or main
). When you commit, HEAD
(and that branch) advance to the new commit.
HEAD
is a tiny text file: .git/HEAD
. Running cat .git/HEAD
might show ref: refs/heads/master
, meaning HEAD
points to the branch named master
. If it shows a raw commit hash instead, HEAD
is detached (explained below).git switch
or git checkout
) moves HEAD
to point at the target branch. Knowing where HEAD
points makes branching, merging, and navigating history far more predictable.
Time β (commits leftβright)
ββββββββββββββ Merge βββββββββββββββ
main ββββββΌββββββββββββββββββββββββββββββββββββββββββββΆ
A1 A2 M1 A3 A4 (branch tip: main)
\ ^ \
\ β \
feature/login βββββββββββββββ ββββββββββΆ
F1 F2 F3 M2 F4 (HEAD -> feature/login)
hotfix/urgent ββββββββββββββββββββββββββββββββ
H1 H2
Tags/Remotes:
(origin/main) at A3 (v1.2) tag at M1
Legend:
β = commit node
A*, F*, H* = commit short IDs (on main, feature/login, hotfix/urgent)
M* = merge commits
HEAD -> <branch> means your working tree points to that branchβs tip
HEAD -> feature/login
(on the feature/login row) tells you your current checkout is at F4. New commits will extend feature/login.git switch main
(or git checkout main
), HEAD
moves to A4, and the indicator becomes HEAD -> main
.A detached HEAD is when HEAD
points directly to a commit instead of to a branch tipβusually after checking out a specific commit hash or a tag.
HEAD
elsewhere because nothing points to them.git switch -c my-work
) or merge/cherry-pick it into an existing branch. (You can often recover recent detached commits with git reflog
if you move away accidentally.)This is what it looks like when you check out a specific commit:
main ββββββββββββββββββΆ
A1 A2 A3 A4
(HEAD) ----------------^
(checked out at commit A3, no branch name)
Here HEAD
points to A3 itself, not a branch. New commits would form an orphaned line unless you first make a branch, e.g. git switch -c temp-work
.
If you commit (F) while HEAD
is detached at commit C, history forks:
A <-- B <-- C (HEAD) <-- F
\
D <-- E (master)
Commit F is not on master
. If you switch back to master
, thereβs no branch reference to F unless you merge, cherry-pick, or create a branch pointing to it. If left unattached, F can be garbage-collected once unreachable.
You enter a detached HEAD state by checking out a commit hash or tag. For example:
git switch b4d373k8990g2b5de30a37bn843b2f51fks2b40
Or equivalently:
git checkout b4d373k8990g2b5de30a37bn843b2f51fks2b40
HEAD
now points to that exact commit rather than a branch tip. Git will say:
Note: switching to 'b4d373k8990g2b5de30a37bn843b2f51fks2b40'.
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.
Thatβs your cue: commits made now live off to the side until you attach them to a branch (e.g., git switch -c keep-this
).
Sometimes you realize youβve made valuable commits while detached and want to keep them. The safe fix is to create a branch at the commit youβre on and then switch to it.
git branch new_branch
Then switch to it:
git switch new_branch
Now new_branch
contains all commits you made while HEAD
was detached, and youβre no longer detachedβHEAD
points to the branch tip again. Your work is preserved and anchored to new_branch
.
Before:
A <-- B <-- C (HEAD) <-- F
After creating and switching to new_branch:
A <-- B <-- C <-- F (new_branch, HEAD)
F is no longer βfloatingβ; itβs anchored to the new_branch branch.
Tip: You can do this in one step with git switch -c new_branch
. Verify where you are with git status
or git branch --show-current
.
Itβs cleaner to avoid a detached HEAD
unless youβre only looking. If you intend to make changes, create a branch at that commit first so new work has a name and a home.
Example Scenario: You want to check out an older commit to debug or reproduce behavior.
I. Create a branch named old_version
at that commit:
git branch old_version b4d373k8990g2b5de30a37bn843b2f51fks2b40
II. Switch to old_version
:
git switch old_version
Now, any commits you make stay on old_version
βno work drifts unreferenced.
Tip: One-step variant: git switch -c old_version b4d373k8990g2b5de30a37bn843b2f51fks2b40
.
When youβre done exploring a detached commit, just jump back to a normal branch (like master
):
git switch master
or
git checkout master
Youβll be right where master
last pointed. If you made commits while detached and havenβt put them on a branch yet, do so before switchingβor recover them later via git reflog
and then create a branch to keep them.
Youβve made useful commits while detached and want them on master
. The trick is to merge the commit that contains your work (usually the latest one on that detached line) into master
.
I. Switch to master
(the branch you want to bring changes into):
git switch master
(Or git checkout master
.)
Tip: If your default branch is main
, substitute accordingly.
II. Merge the detached commit (use the tip commitβs hash):
git merge b4d373c8990a2b5de30a37bf843b2f51f5c2b400
This creates a merge commit tying your detached line into master
. Youβre effectively stitching that βloose threadβ back into the main history. If Git reports conflicts:
git status # see which files need attention
# resolve conflicts in your editor
git add <file_with_conflicts>
git merge --continue # or: git commit
Now your detached changes are part of master
, recorded with a merge commit.
(master) (HEAD detached)
| |
D <-- E C <-- F
\ /
------ Merge Commit --
Result: a single continuous history where Fβs changes are integrated into master.
Notes
git cherry-pick <hash>
instead.