Locally, Git is self-sufficient, but the team synchronizes through a remote repository — a shared copy of the project on a server (GitHub, GitLab, Bitbucket). Three commands — push, pull, fetch — are all the traffic between you and the server. This is also where the two biggest fears live: "push rejected" and "I overwrote someone's work". Let's break it down so no fear remains.
A teammate pushed a commit: the server has it, your copy of origin/main does not — Git never goes to the network on its own, so the copy goes stale. That is why the push is rejected: you are asking the server to replace its history with yours, and yours has no such commit. fetch refreshes only the copy and leaves your files alone; pull also brings that commit into your branch — and now the push goes through.
origin and tracking branches
On git clone, the server's address is saved under the name origin — it is just an alias for the URL (there can be several servers, but usually there is one). A local branch tracks the server branch of the same name: main knows about origin/main, and git status reports the divergence: "your branch is behind by 2 commits".
An important detail: origin/main is a local copy of the server's state, taken at the last synchronization. Between synchronizations it goes stale — Git does not go to the network on its own.
git push -u origin fix-cart # first push of a new branch: -u links it to the server branch
git push # after that, just push
fetch and pull: look or take
git fetch— download what is new from the server without touching your files: only theorigin/*copies are updated. After that you can calmly look at what your colleagues brought:git log main..origin/main.git pull= fetch + merging the tracked branch (formainthat isorigin/main) into yours. The files are updated immediately.
Pull is the everyday routine, fetch is the careful option: for when you want to see others' changes first and decide afterwards. A useful setting, git pull --rebase (or pull.rebase=true in the config), replays your local commits on top of the fresh server ones instead of creating small "Merge branch main" merge commits — the history is noticeably cleaner; why this works is covered in the rebase article.
"Push rejected": what is happening and what to do
! [rejected] main -> main (fetch first)
This is not breakage but protection: the server has commits that you don't (a colleague pushed first). Git refuses to overwrite them with your version of history. The standard remedy:
git pull # take the others' work and combine (a conflict is possible — resolved as usual)
git push # now it goes through
What you should not do is git push --force to a shared branch: it overwrites the server's history with yours, and other people's commits disappear. Force-push is legitimate in one case: your personal branch whose history you deliberately rewrote before review (and preferably as --force-with-lease — it refuses to overwrite what you haven't seen). On main/develop, force-push is usually forbidden by server settings — and rightly so.
The daily cycle
A typical day with a remote repository:
git switch main && git pull # morning: a fresh main
git switch -c feature-export # a branch for the task
# ...work, commits...
git push -u origin feature-export # send the branch
# then — pull request, review, and merging on the server
git switch main && git pull # after the merge: update
git branch -d feature-export # tidy up the branch
The single habit that makes all of this smooth: start from a fresh main and push your branch early, without hoarding a week of local commits. An early push is also a backup: a burned-out laptop does not take your work with it.
In short
- origin is an alias for the server;
origin/mainis a local snapshot of the server's state that goes stale between synchronizations. - fetch downloads without touching files, pull = fetch + merge.
pull --rebasekeeps history clean. - "Push rejected" = the server has something new: pull, then push. It is protection, not an error.
- Force-push — only to your own branch, and preferably
--force-with-lease; to shared branches — never. - The rhythm: from a fresh main, a branch per task, an early push.
What to read next
- Pull requests and code review — what happens to the branch on the server.
- Branches and merging — the previous topic in the series.
- Undo and recovery — if something did go wrong after all.
- Branching models — how a team agrees on where and when to push.