First, what's actually happening?
That error means your local branch is behind the remote branch. Someone else (or you on another machine) pushed commits to the same branch, and Git refuses to overwrite them. It's a safety feature—Git isn't trying to be a jerk, it's protecting you from clobbering someone else's work.
I've seen this bite people in all sorts of places. Last month a client's dev team hit it right before a demo because two devs were pushing straight to main without pulling first. Total mess, but the fix is the same every time.
Fix 1: The 30-second fix (when you have nothing new to lose)
If you don't have any local commits that exist only on your machine—or you don't care about them—just pull the remote changes and then push again.
git pull origin main
That will fetch the remote commits and merge them into your local branch. If there are no conflicts, Git will auto-merge, and you can push right away:
git push origin main
If you're on a different branch name, swap main for whatever you're pushing to. This works when your local branch has zero commits that the remote doesn't have. If you do have local commits, this will create a merge commit—which is fine, but it can clutter history if you're doing this a lot.
Fix 2: The 5-minute fix (when you have local commits and want clean history)
This is the one I use 90% of the time. Instead of merging, you rebase your local commits on top of the remote ones. That keeps the history linear—no silly merge commits.
git pull --rebase origin main
Git will replay your local commits on top of the remote ones. If there are no conflicts, you're done. Just push:
git push origin main
If you hit a conflict during rebase, Git will stop and tell you which file(s) need attention. Open them, fix the conflicts (look for the <<<<<<< markers), then:
git add .
git rebase --continue
Repeat if there are more conflicts. When it finishes, push. This is the cleanest way to sync your branch and get your commits in.
Fix 3: The advanced fix (15+ minutes, when you need to force-push responsibly)
Sometimes you've rewritten history on your local branch—like you did a git rebase -i to squash commits—and the remote history no longer matches. In that case, a normal push won't work, and you need a force push. But don't just use git push --force; that can nuke someone else's commits if they pushed in the meantime.
Use --force-with-lease instead. It checks that the remote branch hasn't changed since you last fetched. If it has, the push is rejected, and you know someone else added commits.
git push --force-with-lease origin main
Only use this if you're absolutely sure your local branch has the history you want and the remote branch is junk (like a bad merge you want to undo). I've had to do this after a botched merge left the remote in a broken state—it's a lifesaver then.
Which fix should you pick?
Start with Fix 1 if you're in a hurry and don't have local commits you care about. If you have commits, skip straight to Fix 2. Reserve Fix 3 for when you've rewritten history and know the remote is safe to overwrite.
One more tip: if you're working in a shared branch like main or develop, always pull with --rebase before pushing. It avoids this whole mess 99% of the time. I tell all my clients to set it as the default:
git config --global pull.rebase true
That one line saves you from that error more than anything else I know.
And if you're still stuck after these, check you're pushing to the right remote and branch name—typos happen, and git remote -v shows you what's where.