In a forked repository, what do the Git remotes named origin and upstream conventionally point to?
answer
- Two remotes, one workflow
- Only one of them accepts your pushes
- Naming convention, not a Git feature
- refs/remotes/origin/* vs refs/remotes/upstream/*
- git remote add upstream <url>
basics
~10 sBy convention origin is your own fork, the copy you can push to, and upstream is the original project you forked from and normally only fetch from. Git gives neither name any special meaning.
solid answer
~40 sWhen you contribute through a fork you work with two remotes. `origin` is the clone URL of **your** copy — you fetch from it and you are the only one pushing to it. `upstream` is the original repository; you fetch from it to learn what the maintainers have merged, and you usually have no push rights there at all. Only `origin` is semi-special: `git clone` creates a remote with that name automatically. `upstream` is pure convention — you add it yourself with `git remote add upstream <url>`, and you could call it `canonical` or `mainline` with no behavioural difference. Each remote gets its own set of remote-tracking refs, so `origin/main` and `upstream/main` are two independent pointers that can be at completely different commits.
code
console · 8 lines$ git clone https://example.org/you/project.git
$ cd project
$ git remote add upstream https://example.org/project/project.git
$ git remote -v
origin https://example.org/you/project.git (fetch)
origin https://example.org/you/project.git (push)
upstream https://example.org/project/project.git (fetch)
upstream https://example.org/project/project.git (push)go deeper
Memorise the pair: origin is your fork and you push there, upstream is the original project and you only fetch from it. Be able to type git remote add upstream <url> and read git remote -v output.
Explain that these are ordinary remotes with per-remote tracking refs under refs/remotes/, and that fetching one leaves the other's refs untouched. Distinguish the remote named upstream from a branch's upstream branch.
Show why the split matters operationally: your fork's copy of main is stale by default, so upstream/main is the only trustworthy reference for what the project contains. Mention guards against accidental pushes to upstream.
Frame it as an access-control shape: contributors need a writable publishing target plus a read-only source of truth. Be ready to discuss when a fork model beats granting branch access directly in one shared repository.
## What a fork is, from Git's point of view Git has no `fork` command and no concept of a fork. A fork is a **server-side copy** of a repository made by the hosting service; once it exists it is just another repository with its own URL. Everything a fork-based workflow does is ordinary Git: you clone one repository and add a second remote pointing at another. That is why the whole workflow reduces to a naming convention over `git remote`. ## The two-remote convention - **`origin`** — your fork. You have write access, so this is where your topic branches live and where you push. `git clone <your-fork-url>` creates this remote for you; the name `origin` is simply the default `git clone` uses. - **`upstream`** — the repository you forked from. You fetch from it to see new work by the maintainers. You typically cannot push here; if you try, the server rejects the push. Nothing in Git enforces this. `upstream` has no built-in meaning whatsoever — you create it yourself: ``` git remote add upstream https://example.org/project.git git remote -v ``` ## Careful: two different meanings of "upstream" Git uses the word *upstream* for a second, unrelated idea: a branch's **upstream branch**, the tracking relationship you query with `@{u}` and set with `git branch -u`. A branch's upstream branch is usually `origin/<branch>` even in a fork workflow, and has nothing to do with a remote that happens to be *named* `upstream`. Interviewers like this distinction because candidates who have only copied commands off a wiki conflate the two. ## Remote-tracking refs are per remote Each remote gets its own namespace of remote-tracking refs under `refs/remotes/`: - `refs/remotes/origin/*` — what your fork looked like at your last fetch of `origin` - `refs/remotes/upstream/*` — what the original project looked like at your last fetch of `upstream` These are updated **only** by fetching that particular remote. `git fetch upstream` moves `upstream/main` and leaves `origin/main` exactly where it was. That is the single most common source of confusion in fork workflows: after fetching upstream, `git status` on your local `main` may still report it as up to date, because it is compared against its upstream branch `origin/main`, which nobody has touched. It also explains why a plain `git pull` in a fork never picks up new maintainer work: `pull` contacts the remote your branch tracks, which is your own fork. ## Why the split exists at all The fork model is what you use when you do not have write access to the project you want to change. Publishing your branch needs a repository you *can* write to, and reviewers need a URL they can fetch from — your fork provides both. The original repository remains the single source of truth about what has actually been accepted, so you keep a live read-only connection to it rather than trusting your fork's copy of `main`, which goes stale the moment it is created and never updates by itself. ## Practical setup ``` git clone https://example.org/you/project.git # creates origin = your fork cd project git remote add upstream https://example.org/project/project.git git fetch upstream ``` From here, `upstream/main` is your reference point for "what the project actually contains", and `origin` is your publishing target. Some people additionally guard against accidents by pointing upstream's push URL at a bogus value with `git remote set-url --push`, so a stray `git push upstream` fails locally instead of at the server. ## What interviewers listen for They want to hear that these are just two remotes, that the names are conventions, that each carries its own remote-tracking refs, and that fetching one does not update the other. A candidate who says "origin is my fork, upstream is the real repo, and my local main tracks origin so I have to fetch upstream explicitly" has demonstrated the whole model in one sentence.
- Does `git fetch upstream` change what `origin/main` points to?No. Remote-tracking refs are per remote: `git fetch upstream` updates refs under `refs/remotes/upstream/`, and `origin/main` only moves when you fetch `origin` (or push to it). Right after fetching upstream, `upstream/main` and `origin/main` can be hundreds of commits apart, which is exactly the drift you then have to reconcile.
- Why does a plain `git pull` in a fork never bring in new maintainer commits?Because `pull` contacts the remote your current branch tracks, which in a fork clone is `origin` — your own copy. Your fork's `main` only changes when you push to it, so pulling from it returns nothing new. You must fetch `upstream` explicitly, or name it: `git pull upstream main`.
- Is the name `upstream` required for the second remote?No. Only `origin` has any default status, and that is just the name `git clone` picks. The second remote can be called anything — `mainline`, `canonical`, the project's name — and Git behaves identically. `upstream` is simply the convention most projects' contributing docs use, so it is worth following.
Your fork is your personal photocopy of a shared document that you are allowed to scribble on; upstream is the master copy in the library, which you may read but not write.
saying these in an interview costs you the question
- Thinking Git has a built-in fork command or fork mode
- Believing origin/main updates when you fetch upstream
- Confusing the remote named upstream with a branch's upstream branch
- Assuming git pull in a fork brings in maintainer commits
- Expecting to push directly to the upstream remote