Git may be able to recover code that disappeared from your current branch, but recovery begins with inspection. A reflog records local movements of references such as HEAD; it does not guarantee that every file or commit still exists, and it is not a substitute for a remote or tested backup.
The safe pattern is: protect the repository, inspect history, create a recovery reference, compare the candidate, and restore only after verification.
Protect the repository first
If this is a real incident, make a copy of the repository before experimenting. Work from the copy or from a separate clone. Do not begin with reset --hard, clean, garbage collection, reflog deletion, or a force-push.
Record the current state:
git status
git branch --show-current
git log --oneline --decorate -n 10
Save the output in your incident log after removing private paths or repository details that should not be shared. These commands inspect the repository; they do not restore anything.
Understand what may still exist
There are several different places to look:
| Location | What it may contain | Important limitation |
|---|---|---|
| Working tree | Unsaved or currently present files | A later save may have overwritten the useful state |
| Commit history | Committed versions | The needed work may never have been committed |
| Reflog | Recent local movements of a branch or HEAD |
It is local and entries may eventually expire |
| Remote repository | Pushed commits and branches | It may not contain the latest local work |
| Editor local history | Saved file contents in the editor | Settings and retention limits vary |
| Backup | Files captured by the backup system | Coverage and restore access must be verified |
The Git reflog documentation describes reflogs as records of when reference tips were updated locally. The Git user manual also describes reflogs as a way to find commits after a branch was moved. This is different from ordinary Git history, which is shared through commits and references.
Practice in a disposable repository
Do not learn recovery on your only course repository. Create a small practice repository:
mkdir git-recovery-practice
cd git-recovery-practice
git init
printf 'first version\n' > notes.md
git add notes.md
git commit -m "Add first version"
printf 'second version\n' > notes.md
git commit -am "Add second version"
git log --oneline
The exact initial-branch message may differ by Git version and configuration. The important result is two commits with a known file.
Now identify the earlier commit from git log, and move the practice branch back:
git reset --hard HEAD~1
This command is intentionally destructive to the practice repository’s current working tree. Never run it on real coursework merely because it appears in a tutorial. The exercise gives us a controlled incident to inspect.
Inspect the reflog
Run:
git reflog --date=local
Look for the entry showing the branch or HEAD before the reset. You may see a reference such as HEAD@{1}. Treat that name as a pointer for inspection, not as proof that it is the version you want.
For the disposable exercise, an illustrative reflog might look like this:
9f3a2c1 HEAD@{0}: reset: moving to HEAD~1
2b7e410 HEAD@{1}: commit: Add second version
9f3a2c1 HEAD@{2}: commit: Add first version
The identifiers and times will differ on your machine. The useful clue is the action: the branch moved back to the first commit, while the reflog still names the second commit as the previous HEAD. Inspect it without changing the branch:
git show --stat HEAD@{1}
git show HEAD@{1}:notes.md
If the file path contains spaces or shell-special characters, quote it. If the candidate looks correct, create a new branch at that commit rather than moving the current branch again:
git switch -c recovered-notes HEAD@{1}
You now have a named recovery reference. Compare it with the current state before deciding whether to copy a file, merge work, or return to the original branch.
Recover a file into a separate directory
When you need one file rather than the whole branch, write the candidate to a new location:
git show HEAD@{1}:notes.md > recovered-notes.md
Open and inspect recovered-notes.md. Check the expected headings, code, dates, and other meaningful content. Do not overwrite the original until you know which version is correct and have another copy of the current state.
What recovery looks like in a student incident
Jordan runs git reset --hard HEAD~1 after an assignment test fails. The command returns successfully, but the branch now lacks a function Jordan wrote earlier. Jordan does not immediately reset again. A repository copy is preserved, the current branch and commit are recorded, and git reflog --date=local shows the earlier HEAD.
Jordan uses git show to inspect the candidate commit and exports only the missing file into a new recovery directory. The recovered file contains the expected function, but that is not enough: Jordan compares the diff, runs the assignment’s permitted tests from the recovery copy, and checks that no later edits were silently discarded. Only after that comparison does Jordan decide whether to copy the function into the working branch or create a separate branch for instructor review.
If the reflog candidate is incomplete, Jordan records that result and checks the remote, editor history, exported submission, and backup. The result is “this local candidate did not contain the expected function,” not “the work never existed.” That wording keeps the next search grounded in evidence.
Know when the evidence is not enough
Recovery becomes less predictable when the candidate is not a complete, readable project. Stop and record the result instead of escalating commands just because the deadline is close.
| Finding | What it tells you | Next safe action |
|---|---|---|
| The reflog points to a commit, but the file is absent | That commit may predate the file, use another path, or never contain the missing edit | Inspect the commit tree and search other refs, editor history, and backups |
| The candidate contains the file, but the project does not run | The code may depend on later files, configuration, or an environment that was not recovered | Preserve the candidate, compare the diff, and rebuild only in a separate copy |
| The reflog has no useful candidate | Local reference movement did not preserve the needed state, or the relevant entry has expired | Check a remote, exported submission, tested backup, or authorized copy; then communicate the deadline risk |
| Git reports object or repository errors | The repository may need specialized recovery, and repeated writes could complicate it | Stop experiments, keep the repository copy, and seek qualified help |
Do not turn git fsck into a guessing game. It can report unreachable objects, but a dangling object is not automatically the correct assignment. If you inspect one, record its identifier, export it separately, and compare its contents with the incident log before treating it as a candidate. Recovery is complete only when the version is both present and verified for the work you need to submit.
The Git fsck documentation describes git fsck as a consistency and reachability check and documents unreachable or dangling objects. It can help identify objects that are no longer referenced, but it cannot decide which object represents your assignment. Treat it as an advanced diagnostic step after the basic history and backup checks, not as a magic restore command.
If Git does not contain the work
Git cannot recover edits that were never recorded in Git. Search the editor’s local history, a tested backup, an exported submission, or an authorized teammate copy. For example, Visual Studio Code documents a Timeline view that can include local file history and a “Local History: Find Entry to Restore” action. Its retention and file-size settings still apply, so do not assume every save exists.
If the work is missing from all known sources, record that fact as the result of the search, not as a claim that the data is mathematically unrecoverable. Move to the communication step while preserving the evidence.
Common mistakes
- Running
reset --hardbefore copying the repository. - Confusing the reflog with a shared backup.
- Recovering a commit without checking its contents.
- Force-pushing a recovery branch over a remote branch.
- Running
git fsck --lost-foundand treating every dangling object as the answer. - Assuming an editor history or backup has unlimited retention.
Do this now
Complete the disposable practice exercise, then write one sentence explaining the difference between normal commit history and a local reflog. Verify the recovered file by comparing its contents with the known second version.
Log what you learned
Record the repository’s starting state, the reference you inspected, the recovery copy you created, and the evidence that made the candidate version trustworthy. If this was a real incident, record which recovery sources remain untested.