Fix: fatal: not a git repository (or any of the parent directories): .git
Part of: Docker, DevOps & Infrastructure
Quick Answer
How to fix the 'fatal: not a git repository' error in Git by checking your working directory, initializing a repo, recovering a deleted .git folder, and resolving submodule, CI/CD, and IDE path issues.
The Error
You run a Git command — git status, git log, git add, git commit, or anything else — and get:
fatal: not a git repository (or any of the parent directories): .gitYou may also see shorter variations:
fatal: not a git repositoryOr when running a Git command from a script or tool:
fatal: not a git repository (or any of the parent directories): .git
fatal: could not read from remote repository.Some tools and wrappers produce their own messages on top of this error:
Error: fatal: not a git repository (or any of the parent directories): .git
The process exited with code 128.No matter the exact wording, the cause is the same: Git cannot find a .git directory in the current directory or any parent directory above it.
Why This Happens
Every Git repository has a hidden .git directory at its root. This directory contains the entire history of your project — all commits, branches, configuration, hooks, and object data. When you run any git command, Git looks for this .git folder in the current working directory. If it doesn’t find one, it walks up the directory tree checking each parent directory. If it reaches the filesystem root (/ on Linux/macOS, C:\ on Windows) without finding .git, it gives up and prints this error.
This means one of the following is true:
- You’re in the wrong directory. Your terminal’s working directory isn’t inside a Git repository.
- The repository was never initialized. You created a project folder but never ran
git initorgit clone. - The
.gitdirectory was deleted. Something removed the.gitfolder — an overly aggressive cleanup script, a manual deletion, or a file sync tool. - You’re inside a submodule or worktree with a broken link. The
.gitreference file points to a location that no longer exists. - Your CI/CD pipeline or script changed directories before running Git commands.
Fix 1: Navigate to the Correct Directory
The most common cause is simply being in the wrong folder. Check where you are:
pwdThen list the contents to see if .git exists:
ls -la | grep .gitIf there’s no .git directory, you’re not in a Git repository. Navigate to the right one:
cd /path/to/your/project
git statusIf you’re unsure where your repository is, search for it:
# Linux / macOS
find ~ -name ".git" -type d 2>/dev/null
# Windows (Git Bash)
find /c/Users/YourName -name ".git" -type d 2>/dev/nullThis searches your home directory for any .git folders and prints their paths. The parent of each .git directory is a repository root.
Fix 2: Initialize a New Repository with git init
If you created a project folder but never turned it into a Git repository, initialize it:
cd /path/to/your/project
git initThis creates the .git directory and sets up an empty repository. You can now stage and commit files:
git add .
git commit -m "Initial commit"If you also need to connect it to a remote repository on GitHub or GitLab:
git remote add origin https://github.com/username/repo.git
git push -u origin mainIf you run into authentication issues when pushing, see Fix: Permission denied (publickey) for SSH troubleshooting.
Fix 3: Clone the Repository Instead
If the repository already exists on a remote server and you don’t have a local copy yet, clone it rather than manually initializing:
git clone https://github.com/username/repo.git
cd repoThis creates a new directory with the full repository history already set up. Make sure you cd into the cloned directory before running any Git commands — the clone command creates a subdirectory, and your terminal stays in the parent directory after cloning.
A common mistake is running git clone and then immediately running git status without changing into the new directory:
git clone https://github.com/username/repo.git
# Still in the parent directory — this fails:
git status
# fatal: not a git repository (or any of the parent directories): .git
# Fix: cd into the cloned repo first
cd repo
git statusPro Tip: After running
git clone, alwayscdinto the cloned directory before running any Git commands. The clone creates a subdirectory, but your terminal stays in the parent — this is the most common reason developers see “not a git repository” right after cloning.
Fix 4: Recover a Deleted .git Directory
If you accidentally deleted the .git folder, the project files are still there but all Git history is gone locally. Your options depend on whether a remote copy exists.
If you have a remote (GitHub, GitLab, etc.):
The safest approach is to re-clone the repository into a temporary directory and copy the .git folder back:
# Clone the repo into a temp directory
git clone https://github.com/username/repo.git /tmp/repo-recovery
# Copy the .git directory into your project
cp -r /tmp/repo-recovery/.git /path/to/your/project/.git
# Verify
cd /path/to/your/project
git statusgit status will now show any differences between your working files and the last commit on the remote. You can review and commit any local changes you want to keep.
If you have no remote:
Without a remote and without the .git directory, the history is gone. You’ll need to start fresh:
cd /path/to/your/project
git init
git add .
git commit -m "Re-initialize repository after .git deletion"To prevent this in the future, always push your work to a remote, and be careful with commands like rm -rf in your project root. If you had a stash saved before the deletion, that data lived inside .git and is also lost.
Fix 5: Fix Submodule Issues
Git submodules don’t have a full .git directory. Instead, they contain a .git file that points to the parent repository’s .git/modules/ directory. If that reference is broken, you’ll see the “not a git repository” error when working inside the submodule.
Check the .git file inside the submodule:
cat path/to/submodule/.gitIt should contain something like:
gitdir: ../../.git/modules/path/to/submoduleIf that path doesn’t exist or is wrong, the submodule is broken. Fix it by re-initializing submodules from the parent repository:
# From the parent repository root
git submodule deinit -f path/to/submodule
git submodule update --init --recursiveIf the submodule directory is completely missing:
git submodule update --init --recursiveThis re-clones all submodules and sets up the correct .git references.
Fix 6: Handle Bare Repositories
A bare repository is a repository without a working tree — it contains the contents of the .git directory directly, without wrapping them in a .git subfolder. Bare repositories are used on servers and for git push targets. They are not meant for everyday development work.
If you accidentally cloned with --bare:
git clone --bare https://github.com/username/repo.gitYou’ll get a directory that looks like repo.git/ and contains files like HEAD, config, objects/, refs/ — but no working tree and no .git subdirectory. Running git status or git log from outside this directory fails with the “not a git repository” error, and from inside it you’ll get errors about not having a working tree.
To fix this, clone the repository normally (without --bare):
git clone https://github.com/username/repo.git
cd repoIf you need to convert a bare repository to a regular one:
mkdir repo
mv repo.git repo/.git
cd repo
git config --local --bool core.bare false
git checkout mainFix 7: Fix Git Hooks and Aliases Running Outside a Repo
Git hooks and custom aliases sometimes fail with this error because they execute in an unexpected working directory. A hook script might change directories before running Git commands, or a shell alias might reference a path that doesn’t exist.
Hooks:
Check your hook scripts in .git/hooks/. If a hook uses cd to change directories, make sure it changes back or runs Git commands against the correct path:
# In a hook script, always use the repo root
REPO_ROOT=$(git rev-parse --show-toplevel)
cd "$REPO_ROOT"Aliases:
If you have a custom Git alias in ~/.gitconfig that runs a shell command, make sure it doesn’t assume a specific working directory:
[alias]
# Bad: assumes you're in a repo
deploy = !cd /some/other/path && git pull
# Good: saves and restores the directory context
deploy = !git -C /path/to/deploy-repo pullThe -C flag tells Git to run as if it were started in the specified directory. This is useful when your alias or script needs to operate on a different repository.
If you encounter permission issues when running hook scripts, see Fix: bash permission denied for troubleshooting executable permissions.
Fix 8: Fix IDE / Editor Terminal Path
Many developers use the integrated terminal in VS Code, IntelliJ, or other editors. The terminal’s working directory depends on how you opened the editor.
VS Code:
If you opened a single file instead of a folder, the integrated terminal might open in your home directory or a system path — not your project root. Fix this by opening the correct folder:
- File > Open Folder (not “Open File”)
- Select your Git project root
- Open a new terminal (Terminal > New Terminal)
The terminal’s starting directory will match the folder you opened.
You can also verify and change the directory manually:
pwd
cd /path/to/your/project
git statusIntelliJ / WebStorm / other JetBrains IDEs:
The terminal defaults to the project root defined when you opened the project. If you have a multi-module project, the terminal might open in a parent directory that isn’t a Git repo. Use cd to navigate to the correct module, or configure the terminal’s starting directory in Settings > Tools > Terminal > Start directory.
Sublime Text / other editors:
Editors without built-in project concepts may open the terminal in unpredictable directories. Always check pwd before running Git commands.
Fix 9: Fix CI/CD Pipeline Working Directory
CI/CD pipelines in GitHub Actions, GitLab CI, Jenkins, and other systems often run steps in a specific working directory. If a step changes directories or the checkout action didn’t run, Git commands fail.
GitHub Actions:
The actions/checkout step clones your repository into $GITHUB_WORKSPACE. If you skip the checkout step or change directories afterward, Git commands won’t work:
jobs:
build:
runs-on: ubuntu-latest
steps:
# This step is required — without it, there's no repo
- uses: actions/checkout@v4
# Git commands work here because the working directory
# is the repo root
- run: git status
# If you cd somewhere else, git commands may fail
- run: |
cd /tmp
git status # fatal: not a git repositoryIf a step needs to run in a different directory, use the working-directory key to control it explicitly:
- name: Run tests
working-directory: ./packages/my-app
run: npm testIf your GitHub Actions workflow is failing with exit codes, see Fix: GitHub Actions process completed with exit code 1 for common solutions.
GitLab CI:
GitLab CI clones into $CI_PROJECT_DIR. Make sure your scripts don’t navigate out of this directory, or use cd $CI_PROJECT_DIR to return:
build:
script:
- cd $CI_PROJECT_DIR
- git statusJenkins:
Jenkins pipelines use checkout scm to clone the repository. The working directory is usually the Jenkins workspace. If you use dir() or sh 'cd ...' in your pipeline, make sure Git commands run within the checked-out repo:
pipeline {
agent any
stages {
stage('Build') {
steps {
checkout scm
sh 'git status'
}
}
}
}Fix 10: Resolve Windows Path Issues
Windows introduces several path-related problems that can cause this error.
Mapped network drives:
If your repository is on a mapped network drive (e.g., Z:\projects\repo), Git may not recognize it. Use the UNC path instead:
cd //server/share/projects/repo
git statusLong paths:
Windows has a 260-character path limit by default. If your repository is deeply nested, Git may fail to find .git. Enable long paths in Git:
git config --global core.longpaths trueAnd enable long paths in Windows (requires admin privileges and a restart):
reg add "HKLM\SYSTEM\CurrentControlSet\Control\FileSystem" /v LongPathsEnabled /t REG_DWORD /d 1 /fOneDrive / Dropbox / Google Drive sync conflicts:
Cloud sync tools sometimes interfere with the .git directory. They can lock files, rename them with conflict suffixes, or skip syncing certain files. This corrupts the repository structure and can cause Git to not recognize the directory as a repository.
The safest approach is to not store Git repositories inside synced cloud folders. Instead, keep them in a regular local directory and use git push to back up your work to a remote. If you need to move a project out of a synced folder:
mv /c/Users/YourName/OneDrive/project /c/Users/YourName/projects/project
cd /c/Users/YourName/projects/project
git statusWSL vs Windows path mismatches:
If you initialized a repository in Windows but try to access it from WSL (or vice versa), Git may not find the .git directory because of path translation issues. Repositories in WSL should use the Linux filesystem (/home/user/project), and repositories in Windows should use the Windows filesystem (/mnt/c/Users/YourName/project from WSL, or C:\Users\YourName\project from cmd/PowerShell). Mixing the two causes performance issues and sometimes path resolution failures. If you encounter environment variable issues across WSL and Windows, see Fix: environment variable undefined for guidance.
How Other VCS Tools Discover the Repository Root
Git’s “walk up the parent directories looking for .git” rule is a Git-specific convention. Other version-control systems use the same general idea but with their own marker files and edge cases. Knowing the differences is useful when you switch tools or when a tool in your toolchain (like Jujutsu sitting on top of Git) confuses the picture.
Git — .git directory or file:
Git walks up until it finds either a .git directory (normal repo) or a .git file (worktree, submodule, or linked checkout). A .git file is a tiny text file containing gitdir: <path> that points to the real metadata location somewhere else. This is why git status in a submodule succeeds even though there is no full .git directory there. If the gitdir: target is missing or stale, Git reports “not a git repository” even though .git exists.
Jujutsu (jj) — .jj directory, with Git colocation:
Jujutsu stores its operation log and views in a .jj/ directory at the repo root. When jj is used in “Git colocated” mode (the most common setup today), both .jj/ and .git/ live side-by-side and jj keeps them in sync. The discovery rule is the same — walk up until you find .jj/. If you delete .jj/ but leave .git/ intact, jj errors out but plain git keeps working. The error message is different (“There is no jj repo in the current directory or any parent directories”) but the diagnosis is identical to this article: you are outside the repo, the marker was deleted, or the working directory walk hit the filesystem root.
Sapling (Meta’s sl) — .sl directory:
Sapling, the Meta fork of Mercurial used internally and open-sourced, marks its repos with a .sl/ directory. The discovery semantics match Git/jj. Sapling also speaks the Git wire protocol against remote Git repos, but locally the marker is .sl/, not .git/.
Mercurial (hg) — .hg directory:
Mercurial walks up looking for .hg/. The equivalent error is abort: no repository found in '<path>' (.hg not found!). The fix is the same as Fix 1: navigate to the correct directory or run hg init.
Fossil — single-file repository:
Fossil takes a different approach entirely: the repository is a single SQLite database file (commonly repo.fossil) and the working tree is opened against it with fossil open <file>. There is no walk-up discovery — you either have an open checkout (a .fslckout file in the working tree pointing at the database) or you do not. The “wrong directory” failure mode looks like not within an open check-out.
Why submodules and worktrees confuse this:
In Git, a submodule’s .git is a file with gitdir: ../../.git/modules/<name>. A linked worktree (git worktree add) is a directory whose .git is a file with gitdir: <main>/.git/worktrees/<name>. In both cases, the marker is present but it is a pointer — if the target is gone (deleted main repo, deleted modules dir, moved directories without git worktree repair), every command fails with “not a git repository.” If your submodule operations are getting tangled, see Fix: Git submodule update failed for the specific recovery path.
Practical takeaway:
When a tool says “not a repository,” the first thing to check is which marker the tool is looking for — .git, .jj, .sl, .hg, or a .fslckout. Then check whether that marker is a directory, a file pointing somewhere, or absent entirely. The advice in this article is Git-specific, but the diagnostic pattern (where am I, where is the marker, is the marker valid) applies to every VCS.
Still Not Working?
Corrupted .git directory
Sometimes the .git directory exists but is corrupted — missing critical files like HEAD or config. Check if these files exist:
ls -la .git/HEAD .git/config .git/refs .git/objectsIf HEAD or config is missing, Git won’t recognize the directory as a repository even though .git exists. If you have a remote, re-clone and recover using the method in Fix 4. If you don’t, you can try to repair it:
# Recreate a missing HEAD file
echo "ref: refs/heads/main" > .git/HEAD
# Run a filesystem check
git fsck --fullGIT_DIR or GIT_WORK_TREE environment variables
If the GIT_DIR or GIT_WORK_TREE environment variables are set, Git uses those paths instead of searching for .git in the current directory. This can cause confusing behavior when the variables point to the wrong location or a non-existent path.
Check if they’re set:
echo $GIT_DIR
echo $GIT_WORK_TREEIf either one is set and you don’t need it, unset them:
unset GIT_DIR
unset GIT_WORK_TREE
git statusIf these are set in your shell profile (~/.bashrc, ~/.zshrc), remove or fix the lines that set them.
Git not installed or not in PATH
On some minimal systems or containers, Git may not be installed. Verify:
git --versionIf this returns “command not found,” install Git for your platform. On CI/CD containers, make sure the image includes Git or add an installation step.
Filesystem is case-sensitive but the path isn’t matching
On case-sensitive filesystems (common on Linux), /path/to/Project and /path/to/project are different directories. Make sure you’re using the exact casing of your repository path.
# This might fail on case-sensitive systems if the actual directory is "MyProject"
cd /home/user/myprojectReal-world scenario: You mount a Git repository into a Docker container as a volume, and
git statusinside the container fails because the container user (UID 0) does not match the host user (UID 1000) who owns the repo. Git 2.35.2+ blocks this by default as a security measure — fix it withgit config --global --add safe.directory /path/to/repo.
Safe directory restrictions (Git 2.35.2+)
Git 2.35.2 introduced a security fix that prevents Git from operating in repositories owned by a different user. While the error message is usually different (unsafe repository), some configurations can cause Git to report “not a git repository” instead.
If you’re working in a directory owned by another user (common in Docker containers where the repo is mounted as a volume), add it to the safe directories list:
git config --global --add safe.directory /path/to/repoIn Docker, this is a frequent issue because the container user and the host user have different UIDs. Add a wildcard to trust all directories (only do this if you understand the security implications):
git config --global --add safe.directory '*'A Linked Worktree Was Moved Without git worktree repair
If you used git worktree add to create a linked working tree and then moved either the main repo or the worktree to a different path, every command in the moved directory fails with “not a git repository” because the .git file inside the worktree still points at the old absolute path. Recover with:
# Run from the main repo
git worktree list
git worktree repair /path/to/moved/worktreeIf the main repo itself moved, run git worktree repair from the new main location and it updates every linked worktree’s pointer for you. Stale worktree entries can be removed with git worktree remove or by editing .git/worktrees/<name>/gitdir directly.
git rev-parse --show-toplevel Returns Empty Inside a Script
A surprisingly common cause of the error from inside CI scripts and pre-commit hooks is that git rev-parse --show-toplevel is invoked in a directory that is a repo, but a wrapper has already changed GIT_DIR to something invalid. Print the relevant env first:
env | grep -i git
git rev-parse --is-inside-work-tree
git rev-parse --git-dirIf --is-inside-work-tree prints false but you can see .git/ with ls -la, an environment variable (or a git -C invocation in a parent script) is misdirecting Git. Unset GIT_DIR, GIT_WORK_TREE, and GIT_COMMON_DIR, then retry.
Sparse Checkout or Partial Clone Left a Broken State
git clone --filter=blob:none and git sparse-checkout create repos that look incomplete in unfamiliar ways. They are still valid repositories — git status works — but tools that scan the working tree (some IDEs, some CI tools) may misreport them as not-a-git-repo because expected directories are missing. Confirm with:
git rev-parse --is-shallow-repository
git config --get core.sparseCheckout
git sparse-checkout listIf the tool genuinely cannot handle sparse checkouts, run git sparse-checkout disable and re-fetch the full tree.
Related: Fix: Permission denied (publickey) · Fix: git stash pop conflicts · Fix: GitHub Actions exit code 1
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: gh CLI Not Working — Auth Scopes, Multiple Accounts, PR Create Errors, and Enterprise Hosts
How to fix GitHub CLI errors — gh auth login token scopes missing, multiple accounts switching, gh pr create permission denied, GHE host auth, gh repo clone vs git clone, and API rate limits.
Fix: error: failed to push some refs to remote
How to fix Git error 'failed to push some refs' caused by diverged branches, remote changes, protected branches, authentication failures, and pre-push hooks.
Fix: Git remote rejected — file exceeds GitHub's file size limit of 100.00 MB
Resolve the GitHub push error when a file exceeds the 100 MB size limit by removing the large file from history, using Git LFS, or cleaning your repository with BFG Repo Cleaner.
Fix: CONFLICT (content): Merge conflict in file — fix conflicts and then commit the result
How to fix Git merge conflicts during merge, rebase, cherry-pick, and pull — resolve conflict markers, use merge tools, accept theirs or ours, abort, and prevent future conflicts.