Reflog answers "where was HEAD five minutes ago," which is why it lets you recover almost any state you have been in recently.
Basic form
git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~3# 4d5e6f7 HEAD@{1}: commit: feat: add login# 8a9b0c1 HEAD@{2}: commit: docs: update README# ...
Each line is the state of HEAD at the moment of an operation. Newer entries appear at the top. Columns: SHA, syntactic ref, reason.
The most common scenario
After an accidental reset:
git reset --hard HEAD~10 # oops, wrong target
git reflog # first line shows the SHA after the reset,
# second line shows the SHA before it
git reset --hard HEAD@{1} # return to the state before the resetOr directly by SHA:
git reset --hard 4d5e6f7 # SHA from reflog
What reflog covers
Reflog records nearly every operation that moves HEAD:
commit,commit --amendcheckout/switchbetween branchesmerge,rebase(a whole series of entries)reset --hard,reset --softpull(moves HEAD via its merge/rebase; a plainfetchdoes not touch HEAD, it only updatesrefs/remotes/*, which have their own separate reflog)- Branch creation/deletion via
branch -d/-D(the last position of that branch)
Each branch also has its own reflog:
git reflog show feature # history of positions for branch feature
git reflog show HEAD # equivalent to git reflog
When reflog will not help
- Uncommitted changes. If a file was only in the working tree or the index and was never committed, reflog cannot see it. Reflog tracks commits only.
- Untracked files deleted with
git clean -fd. Same case. - Other machines. Reflog is local. Your colleague has their own reflog; your actions are not in it.
- Older than the reflog retention period. The default is 90 days
for reachable objects and 30 days for unreachable ones. After that,
git gccleans them up. - After an explicit
git gc --prune=now+git reflog expire. These commands erase history immediately.
Adjusting retention periods
If you work with very long-lived branches or want a longer safety net:
git config --global gc.reflogExpire "180 days"
git config --global gc.reflogExpireUnreachable "90 days"
Or to disable reflog entirely (rarely needed, saves disk space):
git config --global gc.reflogExpire 0
git config --global gc.reflogExpireUnreachable 0
Pitfalls
- HEAD@{N} is not HEAD~N. The first is the N-th entry in the reflog (by time). The second is the N-th ancestor commit (by graph). They are different things.
HEAD@{yesterday},HEAD@{2.hours.ago}are time-based syntax. Handy:git diff HEAD@{1.week.ago} HEAD.- Reflog is stored in
.git/logs/as plain text files. You can read them directly, butgit reflogis more convenient. - On a freshly cloned repository the reflog is empty. History lives in the commits, not in the reflog. Reflog records only local HEAD movements, and a new clone has none yet.