Skip to content

Fix: error: failed to push some refs to remote

FixDevs · (Updated: )

Part of:  Docker, DevOps & Infrastructure

Quick Answer

How to fix Git error 'failed to push some refs' caused by diverged branches, remote changes, protected branches, authentication failures, and pre-push hooks.

The Error

You run git push and get:

To github.com:user/repo.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'github.com:user/repo.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. Integrate the remote changes (e.g., 'git pull ...') before pushing again.

Or variations:

error: failed to push some refs to 'origin'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart.
error: failed to push some refs to 'github.com:user/repo.git'
hint: Updates were rejected because a pushed branch tip is behind its remote counterpart.
remote: error: GH006: Protected branch update failed for refs/heads/main.
error: failed to push some refs to 'github.com:user/repo.git'

Git refused to push your commits to the remote repository. The remote has changes you do not have locally, or something else is blocking the push.

Why This Happens

When you push, Git checks if your local branch can be fast-forwarded onto the remote branch. If the remote branch has commits that your local branch does not, the push fails because it would overwrite those commits. Git treats this as a safety stop: rather than silently throwing away those remote commits, it refuses the push and asks you to reconcile.

Behind the scenes, every Git push uses a small protocol exchange. Your client tells the server, “I want to update refs/heads/main from commit A to commit B.” The server walks its current ref and confirms that A is the current value. If the server’s main is actually pointing at commit C — because someone else pushed, or because you rewrote history — the precondition fails and the server returns ! [rejected]. The hint text you see is generated by Git’s advice.pushNonFastForward machinery, not the server itself, which is why the wording varies subtly between Git versions.

The “failed to push some refs” line is the umbrella message. The interesting detail is always the inner reason: fetch first, non-fast-forward, GH006, pre-receive hook declined, file size, or permission denied. Read that line carefully before reaching for --force. The wrong fix here can erase a teammate’s work in seconds.

Common causes:

  • Someone else pushed first. A teammate pushed commits to the same branch while you were working.
  • You pushed from another machine. Your own commits from a different computer are on the remote.
  • Branch protection rules. GitHub/GitLab/Bitbucket branch protection blocks direct pushes.
  • Rebased or amended history. You rewrote local history that diverges from the remote.
  • Authentication failure. Your credentials expired or are wrong.
  • Pre-push hook failure. A Git hook script rejected the push.
  • Large file rejection. The remote rejects files exceeding size limits.

Version History That Changes the Failure Mode

The exact wording, defaults, and even safety behavior of git push have changed several times. Knowing your client version (git --version) and your server’s Git version tells you which fix actually applies.

  • Git 2.0 (May 2014) changed push.default from matching to simple. Older clients pushed every matching branch; modern clients only push the current branch. Many “why did my fix branch end up on main?” stories come from pre-2.0 defaults.
  • Git 2.21 (Feb 2019) refined push.default = simple semantics to require that the upstream branch share the same name. If your local branch name does not match the upstream, you get src refspec ... does not match any, which is a related but separate failure.
  • Git 2.27 (Jun 2020) started warning when git pull is run without an explicit pull.rebase setting. That warning is the hint to pick Fix 2 over Fix 1.
  • Git 2.28 (Jul 2020) introduced init.defaultBranch, letting you make main the default name. Older clones still create master, which is why git push origin main sometimes fails with “src refspec main does not match” on legacy local repos.
  • Git 2.30 (Dec 2020) added push.useForceIfIncludes, which makes --force-with-lease even safer by requiring that the lease ref was actually fetched into your local repo. Use it in any 2.30+ workflow.
  • Git 2.34 (Nov 2021) flipped the default merge strategy to ort. This changes how merge commits are produced after git pull, which can affect subsequent fast-forward checks.
  • Git 2.37 (Jun 2022) added branch.autoSetupMerge = simple, which only sets up upstream tracking when the remote and local branch names match. This eliminates a category of “I pushed and nothing went where I expected” bugs.
  • Git 2.41 (Jun 2023) rewrote many of the hints you see in error output to be shorter and more actionable. If your error text looks different from what older Stack Overflow answers show, that is why.
  • GitHub deprecated password authentication on Aug 13, 2021. Any HTTPS push from a client older than that date may need a Personal Access Token even if the local Git binary is current.
  • GitHub raised the warning threshold to 50 MB and the hard limit to 100 MB. Git LFS (initially released Apr 2015) is the supported escape hatch; rewriting history with git filter-repo (Sep 2019, replacing the slower git filter-branch) is the cleanup tool.

If you are stuck on Git < 2.30, upgrade before you trust --force-with-lease on a shared branch.

Fix 1: Pull Before Pushing

The most common fix. Integrate the remote changes first:

git pull origin main

If there are no conflicts, Git merges the remote changes into your branch. Then push:

git push origin main

If git pull creates a merge commit and you prefer a linear history, use git pull --rebase instead (see Fix 2).

If git pull itself fails because you have uncommitted local changes, see Fix: Your local changes would be overwritten by merge.

Fix 2: Pull with Rebase

For a cleaner history without merge commits:

git pull --rebase origin main

This replays your local commits on top of the remote commits. The result is a linear history.

If conflicts arise during rebase:

# Edit the conflicting files
git add <resolved-files>
git rebase --continue

Then push:

git push origin main

To make rebase the default behavior for git pull:

git config --global pull.rebase true

Pro Tip: Use git pull --rebase as your default workflow. It prevents unnecessary merge commits and keeps the branch history clean. Most teams prefer this over merge-based pulls.

Fix 3: Fix Diverged Branches After Rebase or Amend

If you amended a commit or rebased after already pushing, your local and remote histories have diverged. A regular push fails because the histories are incompatible.

Check the divergence:

git log --oneline --graph HEAD origin/main

Option 1 — Force push (if you are the only one working on the branch):

git push --force-with-lease origin main

--force-with-lease is safer than --force. It fails if someone else pushed to the branch since your last fetch, preventing you from overwriting their work.

Warning: Never force push to shared branches (like main or develop) unless you have coordinated with your team. Force push rewrites remote history and can cause data loss for others. For more on rejected pushes, see Fix: git push rejected non-fast-forward.

Option 2 — Reset to remote and re-apply your changes:

If you are not sure what happened and want to start clean:

git fetch origin
git log --oneline origin/main  # See what's on remote
git log --oneline HEAD         # See what's local

Then decide whether to merge, rebase, or cherry-pick your changes.

Fix 4: Fix Protected Branch Errors

If the error includes GH006: Protected branch update failed or similar:

remote: error: GH006: Protected branch update failed for refs/heads/main.
remote: - Changes must be made through a pull request.

The branch has protection rules that block direct pushes. You need to create a pull request instead:

# Create a feature branch
git checkout -b my-feature

# Push the feature branch
git push -u origin my-feature

# Create a PR on GitHub
gh pr create --title "My changes" --body "Description"

Check branch protection settings:

In GitHub: Settings → Branches → Branch protection rules.

Common protections:

  • Require pull request reviews — direct pushes blocked
  • Require status checks — CI must pass before merge
  • Require signed commits — unsigned commits rejected
  • Restrict who can push — only specific users/teams can push

If you are an admin and need to push directly (not recommended):

git push --force-with-lease origin main

Admins can bypass some protections, but this is generally a bad practice.

Fix 5: Fix Authentication Failures

If the error is about authentication:

remote: Invalid username or password.
fatal: Authentication failed for 'https://github.com/user/repo.git'
error: failed to push some refs to 'https://github.com/user/repo.git'

Fix: Update your credentials:

For HTTPS:

# Check your remote URL
git remote -v

# If using HTTPS, update the credential
git credential reject <<EOF
protocol=https
host=github.com
EOF

# Next push will prompt for new credentials
git push origin main

For SSH:

# Test SSH connection
ssh -T [email protected]

# If it fails, check your SSH key
ssh-add -l

If SSH authentication fails entirely, see Fix: git permission denied publickey.

GitHub personal access tokens: If you use HTTPS with GitHub, you need a PAT (Personal Access Token) instead of your password. GitHub stopped accepting passwords in August 2021.

Fix 6: Fix Large File Rejections

GitHub rejects files larger than 100 MB:

remote: error: File large-data.csv is 150.00 MB; this exceeds GitHub's file size limit of 100.00 MB
error: failed to push some refs to 'github.com:user/repo.git'

Fix: Remove the large file from history:

git rm --cached large-data.csv
echo "large-data.csv" >> .gitignore
git commit -m "Remove large file"

If the file was committed in a previous commit, you need to rewrite history:

git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch large-data.csv" \
  --prune-empty -- --all

Or use the faster git-filter-repo:

pip install git-filter-repo
git filter-repo --path large-data.csv --invert-paths

Fix: Use Git LFS for large files:

git lfs install
git lfs track "*.csv"
git add .gitattributes
git add large-data.csv
git commit -m "Track CSV with LFS"
git push origin main

Common Mistake: Removing a large file from the working directory and committing the removal does not remove it from Git history. The file still exists in previous commits and the push still fails. You must use filter-branch or filter-repo to rewrite history.

Fix 7: Fix Pre-Push Hook Failures

If a pre-push hook rejects the push:

error: failed to push some refs to 'origin'

The error output usually includes messages from the hook. Common hook checks:

  • Running tests before push
  • Checking code formatting
  • Validating commit messages
  • Scanning for secrets or credentials

Find the hook:

cat .git/hooks/pre-push

Or for shared hooks configured by the project:

git config core.hooksPath

Fix the underlying issue (failing tests, formatting errors, etc.) rather than bypassing the hook.

Fix 8: Fix Pushing to a New Remote

If the remote does not exist or the URL is wrong:

# Check the remote URL
git remote -v

# Fix the URL
git remote set-url origin [email protected]:user/repo.git

# Or add a remote if none exists
git remote add origin [email protected]:user/repo.git

If the remote repository itself does not exist, create it first (on GitHub, GitLab, etc.) and then push:

git push -u origin main

The -u flag sets up tracking between your local and remote branch.

Still Not Working?

If the error persists after trying the fixes above:

Check if the remote branch was deleted. If someone deleted the remote branch and recreated it, your local tracking is stale:

git fetch --prune origin
git push origin main

Check for required commit signing. Some repositories require GPG-signed commits. Unsigned commits are rejected:

git log --show-signature -1

If your commit is not signed and signing is required:

git config user.signingkey YOUR_KEY_ID
git commit --amend -S  # Re-sign the last commit

Check for repository transfer or rename. If the repository was moved to a new organization or renamed, update your remote URL.

Check disk space on the remote. Self-hosted Git servers (Gitea, GitLab CE) can reject pushes when disk space is low.

Check the server-side hooks. If the remote runs custom server-side hooks (pre-receive, update, post-receive), they might reject your push for reasons not shown in the client error. Check the server logs or contact the repository administrator.

Try pushing a single commit. If pushing many commits fails, try pushing one at a time to identify which commit is causing the issue:

git push origin HEAD~5:main  # Push all but the last 5
git push origin HEAD~4:main  # Then the next one
# Continue until you find the problematic commit

Check whether core.autocrlf rewrote line endings. On Windows clients, an autocrlf = true setting can cause a pre-receive hook that validates file hashes to reject every push. Inspect the rejection text for CRLF or mixed line endings, set core.autocrlf = input, and re-commit the affected files.

Check whether the upstream is set to a deleted ref. After a teammate runs git push --delete origin feature-x, your local origin/feature-x still exists until you run git fetch --prune. A subsequent push targets a ref that no longer exists, returning failed to push some refs with a quietly different inner cause. Always run git fetch --prune before debugging.

Check whether your IDE is using a separate Git binary. VS Code, JetBrains IDEs, and SourceTree often bundle their own Git executable. If the bundled version is older than 2.30, --force-with-lease may behave differently than from the terminal. Run git --version inside the IDE’s integrated terminal and compare it with git --version in your shell.

Check the network proxy and HTTP/2 settings. Corporate proxies sometimes terminate the smart HTTP push midway, producing a generic failed to push some refs. Set git config --global http.version HTTP/1.1 and retry. If it works, you have a proxy compatibility issue rather than a Git issue.

If you end up in merge conflict during the pull, see Fix: git merge conflict for resolution steps.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles