fatal: not a git repository, and how to get out of it
fatal: not a git repository means git found no .git folder here or in any parent. Four causes, and the popular fix that quietly makes things worse.

fatal: not a git repository (or any of the parent directories): .git
Git commands only work inside a repository, and a repository is any folder containing a hidden .git directory. This message means git looked in your current folder, then every folder above it, and never found one.
The message tells you exactly what it did. The parenthetical is not filler: git really does search upward, which is why the command works from deep inside a project and fails one folder outside it.
Everything here was run against real repositories on git 2.50.0.
What git is looking for
.git is a normal folder that happens to start with a dot, so most file managers hide it. It holds every commit, branch and setting. The project files you can see are just the current checkout. Delete .git and you have an ordinary folder of files with no history.
Because git searches upward, this works from anywhere inside a project:
cd myproject/src/components/deep
git rev-parse --show-toplevel
# /path/to/myproject
git rev-parse --show-toplevel prints the repository root, or fails with the same fatal message. It is the fastest way to answer "am I in a repository, and which one".
Four causes, in the order worth checking
1. You are in the wrong directory. By far the most common. You opened a terminal, it started in your home folder, and you never moved.
pwd # where am I
ls -a # is there a .git here
If .git is not listed and pwd is not your project, cd into the project. That is the whole fix, and it is the right one far more often than any of the others.
2. The project was never a repository. Downloaded as a ZIP, created by a scaffolding tool that does not initialise git, or simply never set up. Here git init is correct:
cd myproject
git init
3. The .git folder was deleted. A cleanup script, an over-broad delete, or copying the files without hidden files. The message is identical to case 1, which is why checking pwd first matters. If the project exists on a remote, cloning it again is safer than rebuilding by hand, because a clone brings the full history back:
git clone <url> myproject-fresh
4. You are above the repository, not inside it. Your project is at ~/code/myproject and you are in ~/code. Git searches upward, never downward, so a repository in a subfolder is invisible from the parent.
The fix that makes it worse
The advice you will find most often is "just run git init". In cases 1 and 4 that is actively harmful, and it is worth seeing what it does.
Start with a real project that has history:
cd realproj
git log --oneline
# 78c7620 real work
Now step up one level and run git init there, which is what happens when you are in the wrong directory and follow the popular advice:
cd ..
git init
git status --short
# ?? realproj/
Look at that output. The new repository does not see your commit. It sees the entire project as a single untracked folder, ?? realproj/. Ask it for history and it says so plainly:
git log --oneline
# fatal: your current branch 'master' does not have any commits yet
Nothing was destroyed, and the inner repository still has all its commits. But you now have a repository inside a repository, the error message has disappeared, and the thing that replaced it is a confusing state where git status and git log describe a project you did not mean to create.
So the rule is: run pwd before you run git init. If the folder you are in is not the project you mean to version, git init is the wrong command.
If you have already done it, the cleanup is to delete the accidental repository, taking care to name the right one:
cd .. # the folder where you ran git init by mistake
rm -rf .git # removes only the empty outer repository
Check with ls -a first and make sure you are not standing in the project itself. Running git init inside a repository that already exists is harmless by comparison, as git says outright:
Reinitialized existing Git repository in /path/to/myproject/.git/
What is actually inside .git
Worth a look once, because it makes the error stop feeling mysterious. A freshly initialised repository contains exactly this:
HEAD config description hooks info objects refs
objects holds the content of every commit. refs holds the branches and tags. HEAD records which branch you are on, and config holds the settings including your remotes. That is the entire repository. Copying those seven entries copies the history; deleting them deletes it.
One exception catches people who check for the folder programmatically. Inside a submodule, .git is a file, not a directory. It contains a single line pointing at the real repository data stored elsewhere:
file vendored/.git
# vendored/.git: ASCII text
So a check like ls -d .git/ or [ -d .git ] reports "not a repository" for a perfectly valid submodule. Use git rev-parse --is-inside-work-tree instead, which answers the question git's own way and works in every case.
When it happens in a script or CI
The same error in an automated pipeline usually has a different cause than the same error at your keyboard, because nobody is standing in the wrong folder. Three that recur:
A cd that did not apply. Each step in many CI systems runs in its own shell, so a directory change in one step is gone by the next. The fix is to change directory inside the same step, or set the working directory in the step's configuration.
A checkout that has not happened yet. A step that runs git before the repository is cloned finds no .git because there genuinely is not one yet. Ordering, not configuration.
A Docker build that copied the files but not the history. COPY . . respects .dockerignore, and .git is in most default ignore files for good reasons: it is often the largest thing in the project. Any build step that runs git describe or reads the commit hash will fail. Pass the value in as a build argument instead of expecting the history to be there.
In all three the fix is the same shape: give the command a repository, or stop asking it for one.
The other error with almost the same words
This one is a different problem, and the wording is close enough to send people down the wrong path:
fatal: 'origin' does not appear to be a git repository
fatal: Could not read from remote repository.
Here you are inside a repository. Git is telling you that the remote it was asked to contact is not a repository, or does not exist. The causes are a remote that was never added, a typo in the name, or a URL pointing somewhere wrong.
git remote -v
With no output, no remote is configured, and you add one:
git remote add origin https://github.com/user/repo.git
If a remote is listed but the URL is wrong, correct it rather than adding a second:
git remote set-url origin https://github.com/user/repo.git
The distinction to hold on to: not a git repository (or any of the parent directories) is about where you are. does not appear to be a git repository is about where you are pushing to.
Avoiding it entirely
Make your shell tell you. Most modern prompts can display the current branch when you are inside a repository, and show nothing when you are not. That turns this error into something you can see before you type a command, and it costs one line of shell configuration.
There is a second habit worth building, which is to check rather than assume when a command surprises you:
git rev-parse --is-inside-work-tree
That answers true inside a repository and fails with the fatal message outside one. It is the same question the error is answering, asked deliberately instead of by accident, and it works correctly in the submodule case where looking for a .git folder does not.
Failing that, two keystrokes are enough. git status at the start of a session confirms both that you are in a repository and which branch you are on, and it is the same habit that stops you committing to the wrong branch or undoing the wrong commit.
Want to build the habit somewhere it cannot cost you anything? Work through Git basics in a real repository in the browser.
More from the blog

git cherry-pick: move one commit anywhere
Cherry-pick copies a commit onto your current branch. It creates a new commit with a new hash, so the original stays where it was.
Read more
CORS error: what the browser is actually blocking
A CORS error is the browser refusing a response the server already returned with a 200. Here is that proved with two real origins, and the fixes.
Read moreReady to write some code?
Put this into practice - start your first free lesson. No setup, no credit card.