Git: Beginner to Advanced
Troubleshooting
Diagnose and recover from difficult repository states.
Lesson 12 of 12
Reading: about 7 min
Lesson + practice: about 15 min
Difficulty: Advanced
On this page5 sections
Diagnose and recover from difficult repository states. Follow the animated example, then use the sections below to review each idea.
Visual walkthrough
Use the player controls or jump menu to inspect each step. The diagram changes as the example moves forward.
The player shows one focused example. The sections below explain the ideas behind each topic.
When Git does something unexpected, the worst move is to start typing fixes from memory. The reliable approach is the same every time: read the state first (git status, git log, git reflog), work out what happened, and only then repair it. Almost nothing in Git is truly lost if you stop and look before acting.
This lesson covers the four situations people most often get stuck in, as in the walkthrough above. Commit IDs will differ in your repository.
Detached HEAD
Normally HEAD points at a branch, and the branch points at a commit. If you check out a commit directly, by ID, by tag or with something like HEAD~1, HEAD points straight at that commit with no branch in between. That is detached HEAD. It is useful for looking at old versions, but commits made there belong to no branch.
How you end up detached
Git prints a long explanation (shortened here), and status confirms the state.
git checkout HEAD~1
git status$ git checkout HEAD~1
Note: switching to 'HEAD~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.
...
HEAD is now at da840bf Add notes
$ git status
HEAD detached at da840bf
nothing to commit, working tree cleanIf you want to keep work you start here, create a branch before or straight after committing: git switch -c try-fix. If you only came to look around, git switch main takes you back.
Leaving with a commit behind
You committed while detached and then switched away. Git warns you, and tells you the ID of the commit you left behind.
git commit -m "Try fix"
git switch main$ git commit -m "Try fix"
[detached HEAD 9757c46] Try fix
1 file changed, 1 insertion(+)
create mode 100644 experiment.txt
$ git switch main
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
9757c46 Try fix
If you want to keep it by creating a new branch, this may be a good time
to do so with:
git branch <new-branch-name> 9757c46
Switched to branch 'main'Lost commits
A commit disappears from git log when no branch points at it any more: after leaving detached HEAD, a reset, an amend, a rebase, or deleting a branch. The commit itself is still in the repository. The reflog records every place HEAD has been, so you can find it and give it a name again.
Find the commit and rescue it
HEAD@{1} shows the Try fix commit you made before switching back to main. Creating a branch at that ID makes it permanent again.
git reflog -4
git branch rescue 9757c46
git log --oneline rescue -2$ git reflog -4
7d3f63c HEAD@{0}: checkout: moving from 9757c469074db02c1aa64776847b8053058ebce6 to main
9757c46 HEAD@{1}: commit: Try fix
da840bf HEAD@{2}: checkout: moving from main to HEAD~1
7d3f63c HEAD@{3}: commit: Update greeting
$ git branch rescue 9757c46
$ git log --oneline rescue -2
9757c46 Try fix
da840bf Add notes| What happened | Where to look |
|---|---|
| Committed in detached HEAD, then switched away | git reflog, the commit before the checkout line |
| git reset moved the branch back | git reflog, the entry before the reset |
| Amended or rebased | git reflog, entries before amend or rebase |
| Deleted a branch | The ID in the “Deleted branch name (was …)” message, or git reflog |
Conflict diagnosis
The Merging lesson shows how to resolve a conflict. When the conflict is confusing, these commands tell you exactly what Git could not combine and why.
Ask Git what it could not combine
UU means both sides changed the file. git ls-files -u lists three stored versions: stage 1 is the common ancestor, stage 2 is your branch (ours), stage 3 is the incoming branch (theirs). git log --merge shows only the commits that touched the conflicted file on each side.
git merge docs
git status --short
git ls-files -u
git log --oneline --merge$ git merge docs
Auto-merging notes.md
CONFLICT (content): Merge conflict in notes.md
Automatic merge failed; fix conflicts and then commit the result.
$ git status --short
UU notes.md
$ git ls-files -u
100644 670a245535fe6316eb2316c1103b1a88bb519334 1 notes.md
100644 b42c605f68e272a73eb437a95e9a4e17ca715fb4 2 notes.md
100644 e5d12073abf9f903ae9989802e5d57a7b92d30df 3 notes.md
$ git log --oneline --merge
5939766 Revise docs
a6069cb Revise introTake one whole side when that is the answer
If one side is simply right, --ours or --theirs replaces the conflicted file with that version. Here the incoming docs version wins. Then stage and commit as usual.
git checkout --theirs notes.md
cat notes.md$ git checkout --theirs notes.md
Updated 1 path from the index
$ cat notes.md
Getting startedLarge files and Git LFS
Every version of every file stays in history, and everyone who clones downloads all of it. Committing large binary files such as videos, datasets or design files makes a repository slow to clone and hard to change. Git LFS (Large File Storage) keeps the large contents on a separate server and stores only a small pointer file in Git.
Track a file type with LFS
Install the Git LFS extension first (it is a separate download). git lfs track writes a rule to .gitattributes, which you commit so the whole team uses it.
git lfs install --local
git lfs track "*.bin"
cat .gitattributes$ git lfs install --local
Updated Git hooks.
Git LFS initialized.
$ git lfs track "*.bin"
Tracking "*.bin"
$ cat .gitattributes
*.bin filter=lfs diff=lfs merge=lfs -textWhat Git actually stores
Your folder still has the real 3 MB file, but the commit contains only a three-line pointer recording the content's hash and size. The 3 MB is uploaded to the LFS server when you push.
git add .gitattributes model.bin && git commit -m "Track model with LFS"
git lfs ls-files
git show HEAD:model.bin$ git add .gitattributes model.bin && git commit -m "Track model with LFS"
[main 5015f3c] Track model with LFS
2 files changed, 4 insertions(+)
create mode 100644 .gitattributes
create mode 100644 model.bin
$ git lfs ls-files
f5063e0db4 * model.bin
$ git show HEAD:model.bin
version https://git-lfs.github.com/spec/v1
oid sha256:f5063e0db4f80c89670145447b00eb69248a34620128b502f5bcb4ec32706c04
size 3072000git status says “HEAD detached at da840bf” and you have just made a commit you want to keep. What should you do?
Reveal answer
Create a branch at the current commit before switching away: git switch -c keep-my-fix. The commit then belongs to a branch and will not be lost.
You deleted a branch with git branch -D and lost a day of work. Where do you look first?
Reveal answer
In the output of the delete command, which printed “Deleted branch name (was ID)”, or in git reflog. Create a new branch at that ID: git branch restored ID.
Your progress
Reveal the check answer or reach the end to complete this lesson.