push uploads, push rejection protects history
git push origin feature/x uploads commits on your local branch feature/x to the remote's feature/x. The first push of a new branch needs -u to set tracking: git push -u origin feature/x. After that, plain git push from feature/x defaults to the right place. Tracking turns the branch into a single line of effort that flows in both directions.
Push rejection is Git refusing to overwrite history that exists on the server but not in your local copy. The error is clear: "Updates were rejected because the remote contains work that you do not have locally." Your job is not to bypass the rejection — it is to integrate the remote work first. git fetch, then either rebase your branch onto the new tip (git rebase origin/feature/x) or merge it (git merge origin/feature/x), then push again.
Force push is the escape hatch you should never reach for casually. git push --force overwrites the remote's branch with your local history, even if you would lose commits. The catastrophic version: someone else pushed work you did not have, and force-push wipes their commits from the server. The safer alternative — and the only one any senior engineer should use — is git push --force-with-lease, which refuses to push if the remote tip moved since your last fetch. Same effect when nothing surprising happened, refused operation when it did.
Two patterns are worth muscle memory. Set up tracking on first push: git push -u origin <branch>. Force-push rebased history with the safety net: git push --force-with-lease. Configure git config --global push.default current so git push with no arguments always pushes the current branch to its tracked remote, removing one more category of accidental wrong-branch pushes.