Corporate Software
How to fix a git push rejected error: a step-by-step guide

Short answer
A git push rejected error happens when the remote branch contains commits your local copy does not have yet. Git deliberately stops the push to avoid losing history and displays a failed to push some refs notification in your console.
The standard fix requires running git pull before attempting your push again. Understanding the root cause behind this safety mechanism helps you pick between a regular pull, a rebase, or a safer force push option when handling everyday code synchronization.
Why a git push rejected error happens
Git expects branches to maintain a predictable, linear progression of commits whenever you synchronize code with an upstream repository. Every time you run a push command, the client evaluates whether your local work sits directly on top of the remote branch tip. Whenever someone else pushes changes first, or a collaborator edits files directly through the GitHub web interface, Git classifies your incoming commits as a non-fast-forward update. The server rejects the transfer instantly so you do not accidentally overwrite commits that exist only on the remote server.
Most rejections stem from standard day-to-day team overlap or routine history cleanup. Multiple developers frequently commit to shared feature branches simultaneously, meaning whoever finishes their network upload first claims the remote reference pointer. Web edits like merged pull requests, updated project readmes, or automated tag generation also add commits you lack locally. Modifying your local commit history with commands such as git rebase, git commit --amend, or hard resets will likewise misalign your branch hashes with the remote server.
Read the full error message
Diagnostic output in your terminal provides clear instructions on how to handle the situation:
! [rejected] main -> main (fetch first)
Error: failed to push some refs to 'https://github.com/user/repo.git'
Hint: Updates were rejected because the remote contains work that you do not have locally.
The critical clue sits right inside the fetch first parenthetical note. Git explicitly reminds you to pull down missing history before sending new commits upstream. Messages warning that the tip of your current branch is behind convey the exact same requirement. Your machine simply needs the latest remote commits before it can calculate a safe merge path forward.
Common git push rejected messages
| Message | Typical cause | First fix |
|---|---|---|
! [rejected] main -> main (fetch first) |
The remote branch has new commits you don’t have | git pull (or git pull --rebase) |
Updates were rejected because the tip of your current branch is behind |
Your local branch has fallen behind the remote | Run git fetch to check the state, then git pull |
Updates were rejected because the remote contains work that you do not have locally |
Someone else pushed before you | Keep history tidy with git pull --rebase |
! [rejected] main -> main (non-fast-forward) |
Local history was changed via rebase/amend and diverged | Assess the situation, then use --force-with-lease |
error: failed to push some refs to '...' (branch mismatch) |
Pushing to the wrong remote branch | Check the tracking branch with git branch -vv |
How to fix a git push rejected error, step by step
Your specific recovery path changes slightly depending on whether you prefer preserving exact merge topologies or maintaining a single line of work. These steps reliably fix push rejections across typical development workflows.
1. Run git pull to bring in the remote changes first
The safest initial reaction involves fetching missing upstream history using a standard git pull command. This operation contacts the remote repository, retrieves all newer commits, and immediately runs a merge against your active branch. If your edits target different files or separate code blocks, Git generates an automatic merge commit and closes the gap seamlessly. Pushing that new merge commit back upstream succeeds without resistance. Whenever conflicting edits overlap in the same files, Git pauses the merge and asks you to reconcile the differences manually, a process explored thoroughly in our guide to resolving a git merge conflict.
2. Use git pull --rebase to keep history linear
Teams that dislike cluttering commit histories with routine merge bubbles usually prefer running git pull --rebase instead. Rebasing fetches the incoming remote commits and replays your local work sequentially on top of that new baseline. The commit log ends up reading as if you wrote your local patches after everyone else had already pushed. Rebasing can still encounter merge conflicts, though you resolve them step by step across individual commits rather than all at once in a single merge bubble. Agreeing on rebase habits early prevents unnecessary push friction across engineering departments.
3. Only reach for force push when you genuinely need to
Using git push --force forces the remote repository to mirror your local commit tree regardless of any diverged history. This means any commits present on the remote branch that are absent on your computer get permanently destroyed. Force pushing remains strictly reserved for private sandbox branches or deliberate history cleanups after squashing experimental commits. Running a destructive force push against primary shared branches like main or master can silently wipe out days of collaborative work done by teammates.
A much safer alternative is using git push –force-with-lease instead of the blunt force flag. This safer command checks whether the remote branch has received unexpected commits from other developers since your last synchronization. If someone else pushed while you were rebasing, the push halts immediately and protects the remote work. Make force-with-lease your standard reflex whenever project circumstances demand history replacement.
Pushing to the wrong branch can trigger a rejection too
A git push rejected error does not always indicate diverging code history. Developers frequently trigger rejections by simply pointing their push target toward the wrong remote branch name. A common example involves attempting to send local commits from main to an older remote repository that still uses master as its primary branch. You can verify your tracking configuration by running git branch -vv in your terminal. When the upstream relationship looks incorrect or missing entirely, re-link the target branch cleanly by issuing git push -u origin <correct-branch-name>.
Push rejected errors in CI/CD pipelines
Push rejections appear just as frequently inside automated deployment systems and continuous integration jobs. Automation steps configured in GitHub Actions or GitLab CI often commit generated documentation, increment build versions, or publish release tags directly back into repository branches. When parallel automated jobs or simultaneous developer pushes alter the target branch while a build runs, the runner encounters a rejection error. You can prevent pipeline failures by configuring the runner to execute git pull --rebase right before its final push or by utilizing pipeline concurrency locks to queue branch writes sequentially.
Reducing this error when working as a team
Encountering a push rejection is simply a routine part of distributed version control rather than a system failure. The genuine danger lies in treating the warning as an annoyance and applying force commands without reading the prompt. Establishing disciplined branch habits across your organization minimizes these interruptions and eliminates unexpected data loss.
Teams work much faster when developers run git fetch before pushing to observe the upstream branch state beforehand. Integrating regular git pull --rebase commands into daily routines keeps long-running feature branches aligned with main project branches without generating unnecessary merge noise. Setting strict repository rules that completely forbid force pushes on primary branches ensures that destructive rewrites remain confined to private developer workstations.
Common mistakes
Many developers develop the risky habit of reaching for --force immediately after seeing their push bounce back. This reflexive behavior clears the terminal error instantly but easily destroys commits introduced by teammates on shared branches. Always review the detailed console logs to determine whether the remote branch contains intentional work before overriding history.
Another frequent problem involves alternating randomly between standard pulls and rebase commands without team alignment. Both methods resolve push rejections, but mixing them haphazardly results in confusing spaghetti commit graphs that make future branch tracking unpredictable. Similarly, failing to verify upstream tracking links with git branch -vv causes engineers to spend hours diagnosing phantom merge conflicts when they simply pushed toward the wrong target branch.
Checklist
- Have you examined the console output to see if the message says fetch first or non-fast-forward?
- Did you run
git fetchto inspect incoming commits before choosing your resolution path? - Has your team formally decided between standard merge commits and linear rebasing?
- Are you using
--force-with-leaserather than raw--forcewhen history rewriting becomes unavoidable? - Did you confirm that your local branch maps correctly to the intended remote name using
git branch -vv?
Next step
Resolving a git push rejected error usually takes less than two minutes once you inspect the incoming commits and select the right synchronization command. The key priority is avoiding unconsidered force pushes that discard shared history across team repositories. If your company wants to build resilient team workflows, implement branch protection strategies, or design automated delivery systems, our group company Web Tasarım Ofisi provides engineering consulting for web infrastructure and custom software projects.
To design an automated development setup tailored to your organization, explore our custom software service or get in touch with us to discuss your technical goals. You can also master everyday branch management by reading our companion guide to resolving a git merge conflict.
Sources
- GitHub Docs, Dealing with non-fast-forward errors, the official explanation of non-fast-forward push errors and recommended fixes
Frequently asked questions
What does a git push rejected error mean?
It means the remote branch you're pushing to contains commits your local copy doesn't have. Git stops the push to avoid silently discarding that work, and asks you to fetch and integrate the remote changes first.
Should I use `git pull` or `git pull --rebase`?
`git pull` fetches the remote changes and creates a merge commit, which branches the history but is the safer default. `git pull --rebase` replays your commits on top of the remote history instead, keeping a single linear history; which one to use is usually a team convention.
What's the difference between `--force` and `--force-with-lease`?
`--force` overwrites the remote branch unconditionally, which can silently delete someone else's commits. `--force-with-lease` only pushes if nobody has pushed to the remote branch since your last fetch, and stops with a warning otherwise.
Can I recover a commit lost after a force push?
If the commit still exists on someone's local machine or in a `git reflog` entry, it can usually be recovered. If nobody has it locally and the reflog has expired, the work may be permanently gone — which is why force pushing to a shared branch is risky.
Why does a git push rejected error show up in CI/CD pipelines?
When an automated workflow commits and pushes back to the repository, a push is rejected if another workflow or developer updated the same branch in the meantime. The usual fix is adding a `git pull --rebase` step right before the push, or serializing concurrent workflows.