fatal: not a git repository (or any of the parent directories): .git - 10 Causes and Fixes

You ran a Git command and got fatal: not a git repository (or any of the parent directories): .git. That message means exactly one thing: Git walked up from your current directory to the filesystem root and never found a .git entry, so it has no repository to operate on.

This is not a corruption error and not a network error. It is a location error — either you are standing in the wrong directory, or the directory you expected to be a repository never became one, or the .git directory is there but unreadable by the process that asked.

Below: the one-line fix that covers most cases, then 10 distinct causes with a reproduction and a fix for each, a diagnostic checklist for the cases that match none of them, and an explanation of why Git is designed to fail this way. A laptop displaying code on a wooden desk, in a dimly lit workspace.

Errorfatal: not a git repository (or any of the parent directories): .git
Where it happensGit CLI on any OS (Linux, macOS, Windows, WSL), and anything that shells out to git: IDE Git panels, CI runners, Docker builds, npm/husky hooks, deploy scripts.
What it meansGit searched the current directory and every parent directory for a .git entry, found none, and refused to guess which repository you meant.

The Fast Fix

Most of the time you are simply not inside the repository. Check where you are, then move into the repo or create one.

pwd                      # where am I?
git rev-parse --show-toplevel   # prints repo root, or the same fatal error

cd /path/to/your/repo    # case A: you were outside it
git init                 # case B: this folder is genuinely not a repo yet

If git rev-parse --show-toplevel prints a path, you are inside a repository and your original command failed for a different reason — read the error again carefully. If it repeats the fatal message, you need one of the causes below.

What Is Actually Causing It

1. You are in the wrong directory

Reproduce it

cd ~
git status
# fatal: not a git repository (or any of the parent directories): .git

Why it happens — Git resolves the repository from the current working directory upward. Your home directory has no .git, and neither does / above it, so the search ends in failure. A new shell tab, a cd you forgot, or a terminal that opens in $HOME by default all land you here.

The fix

cd ~/projects/my-app
git status
# On branch main

What changed: nothing about the repo — only the directory the command runs from.

Confirm it worked — git rev-parse --show-toplevel prints the repository root path instead of the fatal error.


2. The directory was never initialised as a repository

Reproduce it

mkdir new-project && cd new-project
git add .
# fatal: not a git repository (or any of the parent directories): .git

Why it happens — Creating a folder does not create a repository. Git stores all of its state in a .git directory, and until git init (or a clone) creates it, the folder is an ordinary directory with no history, no index, and no config.

The fix

git init
git add .
git commit -m "Initial commit"

What changed: git init created the .git directory that every later command needs.

Confirm it worked — ls -a lists .git, and git status reports untracked files rather than failing.


3. The clone failed, so the target directory is empty or absent

Reproduce it

git clone https://example.com/does-not-exist.git app
# fatal: repository 'https://example.com/does-not-exist.git/' not found
cd app 2>/dev/null || mkdir app && cd app
git log
# fatal: not a git repository (or any of the parent directories): .git

Why it happens — When a clone aborts — bad URL, auth failure, dropped connection — Git removes or never finishes the target directory. You then cd into a leftover empty folder (or one you created by hand) and every command fails because the clone never wrote .git.

The fix

cd ..
rm -rf app
git clone https://github.com/your-org/your-repo.git app
cd app
git log --oneline -1

What changed: the clone was re-run with a URL that resolves, and the half-made directory was removed first so the clone had a clean target.

Confirm it worked — git log --oneline -1 prints a commit, and git remote -v lists the origin URL.


4. You are one level above the repository root

Reproduce it

git clone https://github.com/your-org/your-repo.git
# Cloning into 'your-repo'...
git status      # still in the parent directory
# fatal: not a git repository (or any of the parent directories): .git

Why it happens — Git's search goes upward only, never downward into subdirectories. The repository exists in ./your-repo, but from the parent directory Git never looks inside it, so it reports failure even though the repo is a few characters away.

The fix

cd your-repo
git status
# On branch main

What changed: you descended into the clone. Alternatively run git -C your-repo status to target it without changing directory.

Confirm it worked — git -C your-repo rev-parse --is-inside-work-tree prints true.


5. The .git directory was deleted or renamed

Reproduce it

cd my-app
mv .git .git_backup
git status
# fatal: not a git repository (or any of the parent directories): .git

Why it happens — An over-eager cleanup, an rm -rf with a glob that matched dotfiles, an archive extracted without hidden files, or a file-sync tool that skips dot-directories all remove the only thing that makes the folder a repository. The working files survive; the history does not.

The fix

mv .git_backup .git     # if it was renamed and still present
git status

# if it is genuinely gone and you have a remote:
cd ..
git clone https://github.com/your-org/my-app.git my-app-restored

What changed: .git is back under its expected name, or the history is recovered from the remote copy.

Confirm it worked — git log --oneline -1 shows your last commit and git status lists your real branch name.


6. The repository is on a filesystem Git refuses to trust (dubious ownership)

Reproduce it

sudo git clone https://github.com/your-org/app.git /srv/app
cd /srv/app
git status
# fatal: detected dubious ownership in repository at '/srv/app'

Why it happens — When the repository is owned by a different user than the one running Git, Git stops before using it. The headline message is the dubious ownership warning, but scripts that then run another Git command in a changed context — or older wrappers that swallow the first error — surface it as the generic not a git repository failure instead.

The fix

sudo chown -R "$USER":"$USER" /srv/app   # preferred: make the owner match

# or, if the ownership is intentional and you accept the risk:
git config --global --add safe.directory /srv/app

What changed: the running user now owns the repository, or that exact path is explicitly marked safe.

Confirm it worked — git -C /srv/app status runs without any fatal message, and git config --global --get-all safe.directory lists the path if you took the second route.


More causes (4 remaining)

7. GIT_DIR points at a path that does not exist

Reproduce it

export GIT_DIR=/tmp/wrong/.git
cd ~/projects/my-app
git status
# fatal: not a git repository: '/tmp/wrong/.git'

Why it happens — GIT_DIR overrides the upward search entirely. Once it is set, Git uses that path and nothing else — so a stale value exported by a shell profile, a CI job, or a previous source makes every command fail no matter which directory you are standing in.

The fix

unset GIT_DIR
unset GIT_WORK_TREE    # same trap, for the working tree side
git status

What changed: the override is gone, so Git resumes its normal search from the current directory.

Confirm it worked — env | grep -i '^GIT_' returns nothing relevant, and git rev-parse --git-dir prints .git.


8. A submodule or worktree .git file points at a missing location

Reproduce it

cd my-app/vendor/lib     # a submodule
cat .git
# gitdir: ../../.git/modules/vendor/lib
rm -rf ../../.git/modules/vendor/lib
git status
# fatal: not a git repository: ../../.git/modules/vendor/lib

Why it happens — In submodules and linked worktrees, .git is a plain file holding a gitdir: pointer rather than a directory. If the target it points to was moved, pruned, or never created, Git follows a dangling pointer and reports no repository.

The fix

cd ../..
git submodule update --init --recursive vendor/lib
cd vendor/lib && git status

# for a stale linked worktree instead:
git worktree repair

What changed: the submodule's real git directory was recreated under .git/modules/, so the gitdir: pointer resolves again.

Confirm it worked — cd vendor/lib && git rev-parse --git-dir prints a path that exists on disk (ls it to confirm).


9. The command runs from a different directory than you think (scripts, hooks, CI)

Reproduce it

# package.json
{ "scripts": { "tag": "cd /tmp && git tag v1.0.0" } }
npm run tag
# fatal: not a git repository (or any of the parent directories): .git

Why it happens — The working directory at the moment Git executes is what matters, not where you typed the command. A cd earlier in the script, a CI step with a custom working-directory, a Makefile recipe in a subshell, or a hook invoked outside the checkout all shift it.

The fix

{ "scripts": { "tag": "git -C \"$PWD\" tag v1.0.0" } }
# or make the script independent of the caller's location
REPO_ROOT="$(cd "$(dirname "$0")" && git rev-parse --show-toplevel)"
git -C "$REPO_ROOT" tag v1.0.0

What changed: the Git command names its repository explicitly with -C instead of inheriting whatever directory the script happened to be in.

Confirm it worked — Add pwd && git rev-parse --show-toplevel as the step immediately before the failing command and read the logged paths.


10. A Docker build or container never received the .git directory

Reproduce it

# .dockerignore
.git
FROM alpine
RUN apk add --no-cache git
COPY . /app
WORKDIR /app
RUN git rev-parse --short HEAD
# fatal: not a git repository (or any of the parent directories): .git

Why it happens — .dockerignore excludes .git from the build context, so COPY . /app copies the source files without it. Mounted volumes cause the same thing when you mount a subdirectory of the repo rather than the root.

The fix

FROM alpine
RUN apk add --no-cache git
ARG GIT_SHA
COPY . /app
WORKDIR /app
RUN echo "$GIT_SHA" > /app/REVISION
docker build --build-arg GIT_SHA="$(git rev-parse --short HEAD)" .

What changed: the commit SHA is computed on the host, where the repository exists, and passed in as a build argument instead of asking Git inside the image. Keeping .git out of the image also keeps it smaller.

Confirm it worked — docker run --rm your-image cat /app/REVISION prints the SHA, and the build no longer runs git inside the container.

Close-up of colorful text on a computer screen, showcasing cybersecurity concepts. ## None of Those? Narrow It Down
  1. Run pwd. If the path is not the repository you expected, the cause is location — cd to the right place and stop here.
  2. Run ls -a and look for .git. A .git directory means a normal repo; a .git file means a submodule or linked worktree (go to step 5); nothing at all means the folder is not a repo (run git init or re-clone).
  3. Run git rev-parse --show-toplevel. A path proves Git can resolve a repository from here, so your original command failed for an unrelated reason. The fatal message again means the search truly fails.
  4. Run env | grep -i '^GIT_'. Any GIT_DIR or GIT_WORK_TREE value overrides the search and must be correct or unset — this rules the environment in or out.
  5. If .git is a file, run cat .git and then ls the path after gitdir:. A missing target means a broken submodule or worktree link; fix it with git submodule update --init or git worktree repair.
  6. Run ls -ld . .git and compare the owner to id -un. A mismatch points at dubious ownership rather than a missing repository.
  7. If the command runs inside a script, hook, CI job, or container, print pwd on the line before it. This separates "the repo is missing" from "the process is somewhere else".
  8. Still failing: run GIT_TRACE=1 git status 2>&1 | head -40. The trace shows the directories Git probed and the final git-dir it settled on, which tells you where the search stopped.

Why This Error Exists At All

Git has no concept of a server session, a project registry, or a "current project" setting. A repository is nothing more than a .git directory sitting in a folder, and the only way the git binary figures out which repository you mean is by looking at your current working directory and walking up the parent chain until it finds one. That walk is the entire discovery mechanism. When it reaches the filesystem root with nothing found, there is no fallback to try — hence fatal, not a warning.

The design is deliberate. Because discovery is purely positional, a repository is self-contained and relocatable: move the folder, rename it, copy it to another machine, and it still works, because nothing outside the folder records its existence. The cost of that property is exactly this error. Git cannot distinguish "you meant the repo two directories down" from "you meant no repo at all", so it refuses to guess rather than operating on something you did not intend. The search is also strictly upward, which is why standing beside a repository feels like a bug but is not one — descending into subdirectories could match several repositories at once, with no principled way to choose.

Once you see it as a resolution failure, the whole family becomes recognisable. GIT_DIR short-circuits the walk, so a stale value breaks every command regardless of location. A submodule's .git file is an indirection in the middle of the walk, so a dangling gitdir: pointer fails the same way. The dubious-ownership refusal is the walk finding a repository and then declining to use it. Containers, CI steps, and npm scripts fail because the process's working directory — not yours — is what seeds the walk. In every case, the question to ask is the same one Git asks: starting from where this process actually is, is there a readable .git above it? Two women working on laptops in a modern, green office space.

Stop It From Coming Back

  • Put the repository and branch in your shell prompt — git-prompt.sh with __git_ps1 for bash/zsh, or Oh My Zsh's git plugin, or starship with its git modules. You see immediately when you are outside a repo.
  • In every script, resolve the root once with REPO_ROOT="$(git rev-parse --show-toplevel)" and pass it to Git as git -C "$REPO_ROOT" ..., so the script never depends on the caller's working directory.
  • Start scripts with set -euo pipefail so a failed cd or a fatal Git call aborts the run instead of letting later commands execute in the wrong directory.
  • In CI, make the repo-dependent steps explicit: set working-directory on the step (GitHub Actions) and keep the checkout action's fetch-depth high enough for the commands you run, rather than assuming the runner's default directory.
  • Compute Git metadata outside containers and inject it — docker build --build-arg GIT_SHA="$(git rev-parse --short HEAD)" — and keep .git in .dockerignore so no image-time command can depend on it.
  • Gate repo-only tooling behind a check: git rev-parse --is-inside-work-tree >/dev/null 2>&1 || { echo "not in a git repo"; exit 1; } gives a readable message instead of a bare fatal.

Related Guides

Errors You Will Probably Hit Next

  • fatal: detected dubious ownership in repository at '/path/to/repo'
  • fatal: your current branch 'main' does not have any commits yet
  • fatal: 'origin' does not appear to be a git repository

This error is a question about location, not about Git's health. Answer pwd, then ls -a for .git, then env | grep GIT_ — in that order — and you will have identified the cause in under thirty seconds. When it happens inside a script, hook, or container, the directory to inspect is the process's, not yours, and printing pwd on the preceding line is faster than any amount of reasoning about it.

댓글

이 블로그의 인기 게시물

TypeError: Cannot read properties of undefined (reading 'map') - 11 Causes and Fixes

psql: FATAL: password authentication failed for user - 10 Causes and Fixes

Error: ERR_MODULE_NOT_FOUND: Cannot find package - 10 Causes and Fixes