Lesson 10 of 30
origin isn't magic — it's a nickname for a URL. And git pull isn't its own operation; it's fetch, then merge, glued together, which explains most of the confusion around it.
14 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Everything up to now has been local — commits, branches, merges, rebases, all inside one .git folder. A remote is just a name Git gives to a URL of another repository, almost always so you don't have to type git@github.com:you/repo.git every time. origin isn't a reserved keyword; it's simply the conventional name Git assigns automatically when you clone a repo — a nickname pointing at wherever you cloned from. upstream is another common convention, usually added by hand, pointing at the original repo you forked from (as opposed to origin, your own fork).
Once a remote exists, Git can create remote-tracking branches — local bookmarks like origin/main that record where each branch on that remote was, as of your last contact with it. Critically, origin/main only updates when you actually talk to the remote (fetch, pull, push); it does not update on its own just because someone pushed new commits on GitHub. Your local origin/main can be stale relative to the real origin/main for as long as you haven't fetched.
A tracking branch is your local branch (say, main) configured to know which remote-tracking branch it corresponds to (origin/main). That relationship is what lets you type bare git pull or git push with no arguments and have Git know where to send or pull from — it's reading the tracking configuration, not guessing.
The distinction people most often blur: git fetch downloads new commits and objects from the remote and updates your remote-tracking branches (origin/main moves) — but it never touches your actual working branches or working directory. git pull is fetch followed immediately by a merge (or rebase, with --rebase) of the now-updated remote-tracking branch into your current branch. pull is a convenience wrapper around two separate steps, not a distinct third thing.
$ git remote -v
origin git@github.com:adesh/praxis.git (fetch)
origin git@github.com:adesh/praxis.git (push)
Someone else pushes a new commit to main on GitHub. Your local state hasn't heard about it yet:
$ git log --oneline --graph --all
* a1b2c3d (HEAD -> main, origin/main) initial commit
Fetch, and watch only the remote-tracking branch move — HEAD and local main stay put:
$ git fetch origin
remote: Enumerating objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From github.com:adesh/praxis
a1b2c3d..b2c3d4e main -> origin/main
$ git log --oneline --graph --all
* b2c3d4e (origin/main) fix header typo
* a1b2c3d (HEAD -> main) initial commit
origin/main now points at the new commit. Your local main — and your working directory — haven't changed at all. Now bring it in:
$ git merge origin/main
Updating a1b2c3d..b2c3d4e
Fast-forward
header.js | 1 +
$ git log --oneline --graph --all
* b2c3d4e (HEAD -> main, origin/main) fix header typo
* a1b2c3d initial commit
That two-step sequence — git fetch then git merge origin/main — is exactly what git pull does in one command, when main is tracking origin/main.
origin/main can silently go stale. People read origin/main in their log or IDE and assume it reflects GitHub's current state — it only reflects the last time you fetched. Always git fetch before trusting it for anything decision-relevant, like "is my branch behind main."git pull defaults to a merge, not a rebase, unless configured otherwise. This surprises people who expect linear history — set pull.rebase = true (globally via git config --global pull.rebase true) if you want pull to rebase instead of merge by default.git remote add upstream <url> is completely ordinary for contributing to a fork — origin stays your fork, upstream is the project you forked from, and you can fetch/merge from either independently.origin/main is the same kind of object as every other ref covered so far — a name pointing at a commit hash, just namespaced under refs/remotes/ instead of refs/heads/. Nothing new is invented to make remotes work: fetch just writes new commit objects into your object store and updates that one extra category of pointer, and merge does exactly what it always does with any two refs.