A remote is just another repository
GitHub is not part of git. A remote is a named URL pointing at another copy of the same repository, and push and fetch copy objects between them. Everything you have learned so far works with no network at all.
git remote add origin https://github.com/you/doors.git
git remote -v
git remote rename origin upstream
git remote remove origin
It is the default name git clone gives the place you cloned from. A fork usually has two: origin (yours) and upstream (the original).
Three kinds of branch
origin/main is not the server. It is a note your repository keeps, updated only when you talk to the server. That single fact explains every "but I pulled ten minutes ago" conversation.
| Name | Is | Moves when |
|---|---|---|
main | your local branch | you commit |
origin/main | your record of where their main was | you fetch or push |
their main | the branch on the server | anyone pushes |
fetch, then merge — which is what pull is
fetch can never break anything: it only adds objects and moves the remote-tracking refs. When you are unsure what is happening on a branch, fetch first and look.
git fetch origin # download commits, move origin/*. Your work is untouched.
git log --oneline --all # look at what arrived
git merge origin/main # now bring it into your branch
git pull # = fetch + merge, in one step
git pull --rebase # = fetch + rebase
push, and the rejection everybody meets
Git refuses because pushing would drop their commits. The fix is never --force: fetch, integrate their work (merge or rebase), then push again. --force on a shared branch deletes someone else's work from the server.
$ git push origin main
! [rejected] main -> main (fetch first)
hint: Updates were rejected because the remote contains work that you do not have locally.
It refuses when the remote has moved since your last fetch — so it overwrites only the history you actually looked at. Plain --force overwrites whatever is there.
Tracking, and the first push of a branch
The "ahead by 2, behind by 1" line in git status is computed against the tracking branch — which is, again, only as fresh as your last fetch.
git push -u origin feature # push, and remember that feature ↔ origin/feature
git push # from then on, this is enough
git status # "Your branch is ahead of 'origin/main' by 2 commits"
git branch -vv # what each branch tracks
git fetch -p # -p prunes remote branches that were deleted