This error occurs when you try to push to a branch that is currently checked out in a non-bare remote repository. Git prevents this to avoid making the remote working directory inconsistent. The fix involves either converting the remote to a bare repository or pushing to a different branch.
When you push changes to a Git repository, the receiving repository must update its branch references. If the remote repository is a "non-bare" repository (one with a working directory and checked-out files), Git refuses to update the branch that is currently checked out there. This protection exists because updating a checked-out branch would cause the remote's working directory and index to become out of sync with the branch HEAD. Anyone working in that remote repository would suddenly find their files in an inconsistent state, potentially losing uncommitted work. This error typically appears in two scenarios: 1. **Development server setups** - Pushing to a repository on a server where you've manually cloned and checked out files 2. **Peer-to-peer workflows** - Pushing directly between developer machines rather than through a central bare repository The error message specifically mentions `refs/heads/main` (or `master`) because that's the branch being rejected. You can push to any other branch of the same repository without issues—the restriction only applies to the currently checked-out branch.
First, check if the remote repository is bare or non-bare. SSH into the remote server and run:
cd /path/to/repo
git rev-parse --is-bare-repositoryIf it returns false, the repository is non-bare and has a working directory. This is the root cause of the error.
You can also check by looking for a .git folder:
- Non-bare repository: Has a .git subdirectory and checked-out files
- Bare repository: Contains Git data directly (objects, refs, HEAD) without a working directory
If this repository is meant to be a central server that receives pushes, convert it to bare. On the remote server:
cd /path/to/repo
git config --bool core.bare trueThen remove the working directory files (keep them backed up if needed):
# First, back up if you have uncommitted work
cp -r . ../repo-backup
# Remove working directory files, keeping .git
rm -rf !(*.git)
# Or move everything from .git to the repo root
mv .git/* .
rm -rf .gitFor a cleaner approach, create a fresh bare clone:
cd ..
git clone --bare repo repo.git
# Then update your remote URL to point to repo.gitIf you need to keep the non-bare repository (e.g., for deployment), switch the remote to a different branch. SSH into the remote and run:
cd /path/to/repo
git checkout -b temp-branch
# Or checkout an existing branch
git checkout developNow you can push to main because it's no longer checked out. After pushing, you can switch back:
git checkout mainThis is a temporary workaround—consider using a deployment branch or proper CI/CD instead.
Git 2.3.0 introduced a safer way to push to checked-out branches. On the remote repository, run:
git config receive.denyCurrentBranch updateInsteadThis tells Git to update both the branch AND the working directory when receiving a push. The working directory will be updated to match the pushed content if it's clean.
Important caveats:
- This only works if the remote working directory is clean (no uncommitted changes)
- If there are uncommitted changes, the push will still be rejected
- This is safe for deployment scenarios but not for repositories with active development
If you can't modify the remote configuration, push to a different branch:
git push origin main:deploy-stagingThen SSH into the remote and merge:
cd /path/to/repo
git merge deploy-stagingThis is useful when you don't have admin access to change the remote's Git configuration.
When setting up a new remote repository that will receive pushes, always create it as bare:
git init --bare /path/to/repo.gitOr clone an existing repository as bare:
git clone --bare https://github.com/user/repo.gitBare repositories:
- Have no working directory or checked-out files
- Can receive pushes to any branch without restrictions
- Are the standard for Git servers (GitHub, GitLab use bare repos)
- Convention: name them with .git suffix (e.g., myproject.git)
### Understanding Bare vs Non-Bare Repositories
| Feature | Non-Bare | Bare |
|---------|----------|------|
| Working directory | Yes | No |
| .git folder | Subdirectory | Root level |
| Can checkout branches | Yes | No |
| Can receive pushes | Limited | Any branch |
| Use case | Development | Server/central |
### The receive.denyCurrentBranch Options
| Value | Behavior |
|-------|----------|
| refuse (default) | Reject pushes to checked-out branch |
| warn | Allow push but print warning |
| ignore | Allow push silently (dangerous!) |
| updateInstead | Allow push and update working directory |
Never use `ignore` - it leaves the working directory completely out of sync with HEAD, causing confusion and potential data loss.
### Deployment Workflows
For deployment scenarios where you want to push and have files automatically update, consider these alternatives:
1. Git hooks: Use a post-receive hook in a bare repo to checkout files elsewhere:
#!/bin/bash
GIT_WORK_TREE=/var/www/mysite git checkout -f2. CI/CD pipelines: Push to a bare repo, trigger a deployment script
3. Pull-based deployment: Have the server pull changes instead of receiving pushes
### Why This Error Exists
Git's design philosophy is that pushing is for synchronizing bare repositories. Non-bare repositories should use git pull to receive changes. This model avoids:
- Overwriting uncommitted work
- Index/working directory desynchronization
- Confusion about which changes are local vs received
warning: BOM detected in file, this may cause issues
UTF-8 Byte Order Mark (BOM) detected in file
fatal: Server does not support --shallow-exclude
Server does not support --shallow-exclude
warning: filtering out blobs larger than limit
Git partial clone filtering large blobs warning
fatal: Server does not support --shallow-since
Server does not support --shallow-since in Git
kex_exchange_identification: Connection closed by remote host
Connection closed by remote host when connecting to Git server