In Git, what does the -u flag do in git push -u origin main?
answer
- It is short for something longer
- Writes configuration, not data
- Two keys land in .git/config
- branch.<name>.remote and branch.<name>.merge
- Same as git branch --set-upstream-to
basics
~20 s-u (--set-upstream) records origin/main as the upstream of the local branch main in the repository's own config, so later bare git push, git pull and git status know which remote branch to use and compare against.
solid answer
~40 s`-u` is short for `--set-upstream`. Besides doing the push, it writes two keys into the local `.git/config`: `branch.main.remote = origin` and `branch.main.merge = refs/heads/main`. That pairing is what Git calls the branch's **upstream**. Once it exists, `git push` and `git pull` with no arguments know where to go, `git status` can say "ahead of 'origin/main' by 2 commits", and the shorthand `@{u}` (or `@{upstream}`) resolves to `origin/main` in any revision expression. The upstream is purely local configuration — nothing about it is stored on the server, and each clone configures its own. If the branch already exists you can set the same thing without pushing, using `git branch -u origin/main`.
code
console · 10 lines$ git push -u origin main
Branch 'main' set up to track remote branch 'main' from 'origin'.
$ git config --get branch.main.remote
origin
$ git config --get branch.main.merge
refs/heads/main
$ git rev-parse --abbrev-ref @{u}
origin/maingo deeper
Recall that -u is --set-upstream and that after using it once, plain git push and git pull work for that branch. Be able to name git branch -u as the way to set it afterwards.
Explain the mechanics: it writes branch.<name>.remote and branch.<name>.merge into the local config, which is what makes @{u} resolve and lets git status compute ahead/behind.
Show that the upstream is per-clone local state, so it can never explain a teammate's behaviour, and know the interaction with push.default when local and remote branch names differ.
Discuss defaults you would standardise for a team — push.autoSetupRemote or a documented push.default — so contributors never hit the no-upstream error, and the tradeoff of implicit versus explicit push targets.
## What "upstream" means Every local branch in Git can optionally be associated with one branch on one remote. That association is called the branch's **upstream** (also "tracking" configuration). It answers two everyday questions for Git: where does a bare `git push` send this branch, and what does a bare `git pull` fetch and integrate? It also gives `git status` something to compare your branch against. ## Where the upstream is stored The upstream is two lines of local configuration, nothing more: - `branch.<name>.remote` — the remote's name, usually `origin`. - `branch.<name>.merge` — the *full ref name on the remote*, e.g. `refs/heads/main`. Both live in `.git/config` inside your clone. There is no server-side record of who tracks what; a teammate cloning the same repository configures upstreams independently. This matters when reasoning about surprises: nothing a colleague does can change your upstream, and changing yours changes nothing for anyone else. ## What -u actually does `git push -u origin main` performs the push and, if it succeeds, writes those two config keys. It is exactly equivalent to running the push and then `git branch --set-upstream-to=origin/main main`. The flag is idempotent — running it again just rewrites the same values — and it is only needed once per branch, not on every push. `--set-upstream` is the long form. ## What you get once an upstream exists - `git push` and `git pull` with no arguments resolve to the upstream instead of erroring with "There is no tracking information for the current branch" / "The current branch main has no upstream branch". - `git status` reports divergence: "Your branch is ahead of 'origin/main' by 2 commits", or "have diverged". The short form `git status -sb` prints `## main...origin/main [ahead 2]`. - The revision shorthand `@{u}` / `@{upstream}` resolves to the upstream's remote-tracking ref, so `git log @{u}..HEAD` lists exactly what you have not pushed. A related shorthand `@{push}` resolves to where a push *would* go, which differs from `@{u}` only in triangular setups where you fetch from one remote and push to another. - `git branch -vv` annotates each branch with its upstream and ahead/behind counts. ## Upstream names need not match The local branch and its upstream can have different names: `git push -u origin feature:review/feature` sets the upstream of local `feature` to `origin/review/feature`. This is legal but worth knowing, because the default `push.default = simple` behaviour deliberately refuses a bare `git push` when the names differ, to protect you from pushing to a differently named branch by accident. ## Setting, changing, and removing it later - `git branch -u origin/main` (long form `--set-upstream-to`) sets the upstream of the current branch without pushing; add a branch name to target another branch. - `git branch --unset-upstream` removes it. - Creating a branch from a remote-tracking branch usually sets it for you: `git switch -c feature origin/feature`, or the DWIM form `git switch feature` when only one remote has a branch by that name. This is governed by `branch.autoSetupMerge`, and can be forced either way with `--track` / `--no-track`. - Recent Git versions add a `push.autoSetupRemote` setting which makes a first bare `git push` behave as if `-u` had been given, so the flag is no longer needed by hand. ## Common confusions The upstream is *not* the remote itself — `git remote add` registers a remote (a URL), while `-u` links one branch to one branch on it. It is also not the "upstream" remote of a fork workflow, where people conventionally name the original repository's remote `upstream`; that is a remote name that happens to collide with the term. Finally, `-u` transfers no extra data: the push sends the same objects with or without it.
- How would you set or change a branch's upstream without pushing?`git branch -u origin/main` (long form `--set-upstream-to=origin/main`) sets the upstream of the current branch; append a branch name to target a different one. `git branch --unset-upstream` removes it. Both only rewrite `branch.<name>.remote` and `branch.<name>.merge` in the local config — no network access and no objects transferred.
- What stops working if a branch has no upstream at all?A bare `git pull` fails with "There is no tracking information for the current branch", and under the default `push.default = simple` a bare `git push` fails with "The current branch has no upstream branch". `git status` prints no ahead/behind line, and `@{u}` fails to resolve, so `git log @{u}..HEAD` errors. Explicit `git push origin main` still works fine.
- Must a local branch and its upstream share a name?No. `git push -u origin feature:review/feature` sets local `feature` to track `origin/review/feature`. Git allows it, but with the default `push.default = simple` a later bare `git push` refuses when the names differ, precisely so you notice. Use `push.default = upstream` if mismatched names are intentional in your setup.
saying these in an interview costs you the question
- Thinks -u uploads extra data or metadata to the server
- Believes the upstream link is stored on the remote
- Assumes -u must be repeated on every push
- Confuses the upstream setting with the 'upstream' remote of a fork
- Thinks local branch and upstream must share a name