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 a beginner's biggest fears live: "push rejected" and "I overwrote someone's work". Let's break it down so no fear remains.
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 + mergingorigin/maininto your branch. 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.