Fatal: Not a Git Repository? The 60-Second Fix

Fatal: Not a Git Repository? The 60-Second Fix

The four real causes of fatal: not a git repository, ranked plus the one command that tells you which one you have before you touch anything.

HUMANby @mrsaynothing28d7 min read

The fastest way to lose twenty minutes is typing git pull in the folder above your repo. Git answers fatal: not a git repository (or any of the parent directories): .git, and most answers online reply with git init — which is wrong for three of the four real causes.

The 60-second triage:

git rev-parse --show-toplevel || ls -a

--show-toplevel prints your repository root when one exists above you: wrong directory, case closed, cd there. When it fails too, the same line lists the folder's contents: no .git in that list means there was never a repo here to find. That single command separates the two causes behind most occurrences. The rest of this post is the smaller cases.

The 60-second triage

git rev-parse --show-toplevel || ls -a prints a root fails — no repo found wrong directory — done cd "$(git rev-parse --show-toplevel)" what does ls -a show? the answer decides everything no .git at all ZIP / copied files → re-clone .git is a one-line file git worktree repair broken in every repo? env | grep GIT_DIR → unset inside vendor/libfoo/? submodule update --init brand new? git init, once five causes, one command
fig 1 — the five causes behind one error. Start at the triage command; click your branch.

Match your symptom to a row — the fix is the third column.

all wrong dir no .git GIT_DIR submodule worktree
Symptom Likely cause Fix
Error in one project, ls -a shows no .git ZIP download / copied files git clone again
Error from a subfolder, root works Standing outside the tree cd "$(git rev-parse --show-toplevel)"
Error in every directory, even ones that worked GIT_DIR exported unset GIT_DIR, purge it from the shell profile
Error inside a submodule folder Submodule not initialized git submodule update --init --recursive
.git is a file, error persists Dangling worktree/submodule pointer git worktree repair, else re-clone

verify: git rev-parse --show-toplevel → the repo root path, no error

Why does Git say "fatal: not a git repository"?

Git finds a repository by walking up from the current directory, hunting for a .git entry, and stops at the first one. That walk breaks in exactly four ways: you stand outside the tree, the .git folder is gone, an environment variable redirects the search, or .git exists but points at nothing. Stack Overflow's 2022 Developer Survey put Git on roughly 94% of developer machines, so the error is a shared rite of passage. Learn the four cases once and they stop costing time.

(If your repo is fine and you actually came here to undo a commit, Undo Anything in Git collects every repair path on this site in one place — the long-form walk-through is git undo last commit.)

Case 1: you are in the wrong directory

The common one. Two shapes:

  1. You cd'd into a subfolder (docs/, src/api/) and the shell habit from another project says the repo root is here. Git walks up, finds nothing, quits.
  2. You cloned a repo and expected the files here, but git clone always creates a subdirectory named after the repo. The checkout sits one level below where you're standing.
git rev-parse --show-toplevel   # prints the root when one exists
cd "$(git rev-parse --show-toplevel)"  # jump there in one step

The second command is the permanent fix for the muscle-memory problem: it works from any depth inside the repo.

Case 2: there is no .git — the folder never was a repo

ls -a shows no .git. Usually one of:

  • A GitHub ZIP download. Code → Download ZIP ships the files only: no .git, no history, no remote. GitHub's own docs confirm the ZIP archive contains the source snapshot alone.
  • Files copied in from a machine or another project, without the hidden folder.
  • A cleanup tool deleted .git as "hidden junk".

Fix: re-clone to recover the history.

cd ..
git clone https://github.com/<user>/<repo>.git

git init here starts a fresh repository; nothing is recovered. And the copied-files mess usually drags untracked strays with it; git remove untracked files covers the cleanup once the clone is in place.

git init doesn't repair a repo — it creates an empty one where you stand.

Case 3: GIT_DIR is set, and breaks every repo

The tell: the error follows you into directories where Git worked an hour ago. An exported GIT_DIR tells Git to look at one fixed path instead of discovering the repo, so every "ordinary" directory becomes "not a repository".

env | grep GIT_DIR     # GIT_DIR=/some/old/path
unset GIT_DIR

If it comes back every morning, a line in ~/.bashrc, ~/.zshrc, or a CI wrapper exports it.

CI systems set GIT_DIR on purpose

Inside a pipeline, GIT_DIR is correct and deliberate — the runner points Git at the checkout it prepared. Unsetting it there breaks the build; the landmine is only when that line leaks into your interactive shell profile. Fix the shell, leave the CI alone.

verify: env | grep GIT_DIR → empty

Case 4: the submodule was never initialized

You cd into vendor/libfoo/, and Git says the directory is not a repository. It isn't: until initialized, a submodule directory is an empty folder with an entry in .gitmodules and nothing checked out.

cat .gitmodules        # lists the submodule paths
git submodule update --init --recursive

Run that from the repo root, not inside the submodule. The same walk-up rule applies, and the empty submodule has no root to walk to.

verify: git submodule status → no leading dash on the path (a dash = still uninitialized)

Case 5: .git is a file, and its target is gone

Worktrees and submodules make .git a one-line file: gitdir: /path/to/real/gitdir. When the real gitdir is deleted (a cleaned .git/modules, a moved drive), the pointer dangles and the walk-up finds a .git that answers nothing:

$ ls -la .git
-rw-r--r-- 1 you you 35 Sep 28 10:12 .git     # a file, not a directory
$ cat .git
gitdir: ../.git/modules/libfoo
git worktree repair    # re-links worktrees whose paths changed

If worktree repair can't find the target, the gitdir is gone for good. Re-clone. The history lives in the parent repo's .git/modules/<name> for submodules; check it exists before assuming the worst.

verify: cat .git → the gitdir: path exists on disk

So is git init ever the right fix?

Once: when ls -a shows no .git and you intend to start a brand-new repository here. Run it, and the next command is git add, not git log.

Everywhere else it's a trap with a signature. git init one folder too deep builds a nested repo with zero commits, and your next git log answers fatal: your current branch 'main' does not have any commits yet. That error means you just created a repository where you stood, and the real one is one level up. The cleanup is surgical:

cd the-nested-folder
rm -rf .git    # removes only the accidental repo, files untouched

Double-check the prompt before that rm: it must run inside the nested folder, never at the project root. Then cd back to the real root and confirm with git rev-parse --show-toplevel that Git finds what it was always supposed to find.

verify: git log --oneline -1 → a commit, not the no-commits-yet error