skip to content

In Git, what does git push origin HEAD:refs/heads/release do?

level: middleimportance: should knowfreq 40%

answer

  1. Two halves separated by a colon
  2. Left half is local, right half is remote
  3. Names on the two sides need not match
  4. HEAD works even when detached
  5. Full ref name needed for a new destination

basics

~10 s

It pushes the commit your HEAD points at to the branch release on origin, creating it if absent, regardless of what your local branch is called. HEAD is the source, refs/heads/release the destination ref.

solid answer

~50 s

A push refspec is `<src>:<dst>`. The source is any local revision — a branch name, `HEAD`, a tag, even a raw commit ID — and the destination is the ref name to update on the remote. So this command sends whatever `HEAD` resolves to and points `origin`'s `release` branch at it, with no requirement that a local branch named `release` exists. Spelling the destination in full as `refs/heads/release` matters when the ref does not exist yet: Git refuses an unqualified new destination it cannot classify, with "unable to push to unqualified destination". The refspec form is also how you push a branch under a different remote name, push from a detached HEAD, or push a branch you do not have checked out (`git push origin feature:feature`). None of this changes the fast-forward rule: the update is still rejected if it would drop commits.

code

bash · 5 lines
bash
git push origin HEAD:refs/heads/release   # publish current commit as 'release'
git push origin feature:review/feature    # different name on the remote
git push origin feature:feature           # branch you have not checked out
git push origin :feature                  # empty source deletes
git push --dry-run origin HEAD:refs/heads/release

go deeper

for a junior

Recall that a push can name a different branch on the remote using the colon form, and that HEAD on the left means the commit you are currently on.

for a middle

Explain source and destination halves, why a brand-new destination needs the full refs/heads/ name, and that the empty-source form deletes.

for a senior

Use it deliberately for publishing under another name, pushing an older commit, or pushing from a detached HEAD, and know that --atomic is what keeps a multi-ref push from half-applying.

for a principal

Treat push as a ref-update protocol when designing release plumbing: encode the mapping in remote.<name>.push, require atomic multi-ref updates where refs must move together, and rely on receiver-side rules rather than client discipline.

## The refspec model Everything `git push` does is expressed as one or more refspecs of the form `<src>:<dst>`, optionally prefixed with `+`. - **`src`** — anything that resolves locally to a commit: a branch (`feature`), `HEAD`, a tag, `HEAD~2`, a full ref name, or a raw object ID. - **`dst`** — the name of the ref to update **on the remote**. Not a local ref, and not required to exist beforehand. - **`+`** — a leading plus marks that refspec as forced, the per-refspec equivalent of forcing the whole push. When you run a bare `git push`, Git constructs the refspec for you using `push.default` and the branch's upstream. Writing the refspec by hand is simply doing that job explicitly. ## Reading the example `git push origin HEAD:refs/heads/release` resolves `HEAD` to a commit — the tip of whatever branch you are on, or the commit itself if you are detached — and asks `origin` to set `refs/heads/release` to it. Two consequences follow: - **Local and remote names are decoupled.** Your branch might be `wip/mine`; the remote branch is `release`. This is how you publish work under a review-friendly name, or promote a build. - **It works from a detached HEAD.** There is no local branch to name, so the bare push forms would fail; the refspec form does not care. ## Why the full ref name If `release` already exists on the remote, `git push origin HEAD:release` works: Git matches the short name against existing remote refs. If it does **not** exist, Git cannot tell whether you mean a branch, a tag, or something else, and errors with a message about an unqualified destination that neither matches an existing remote ref nor begins with `refs/`. Writing `refs/heads/release` removes the ambiguity. The same applies to creating tags: `refs/tags/v1.0`. ## Other useful shapes - `git push origin feature` — shorthand where source and destination are the same name; Git expands it. - `git push origin feature:feature` — push a branch you do not have checked out. - `git push origin HEAD` — push the current branch to a same-named remote branch. - `git push origin :feature` — empty source: delete the remote branch. - `git push origin +feature:feature` — forced update of that one refspec. - `git push origin HEAD~3:refs/heads/stable` — publish an older commit as a branch tip, useful when you want to expose only part of your local work. ## Multiple refspecs and atomicity Several refspecs can be given in one command: `git push origin main:main release:release`. By default the receiving side evaluates each update independently, so one can succeed while another is rejected — leaving the remote in a half-updated state. `git push --atomic` asks the receiver to apply all of them or none, which matters when two refs must move together, for example a branch and the tag naming the same commit. It requires support on the receiving side; where that is unavailable the push fails rather than silently degrading. ## Configuration that stores a refspec Rather than typing the same mapping repeatedly, `remote.<name>.push` stores default push refspecs for that remote, consulted when a bare `git push <remote>` is run. This is how a permanently renamed publishing target is set up once instead of per command. ## Guardrails still apply A hand-written refspec is not a bypass. The destination update is still subject to the fast-forward rule unless forced, still subject to `receive.denyDeletes` and `receive.denyNonFastForwards` on the receiving repository, and still visible to `pre-receive` and `update` hooks, which see the old value, new value, and ref name of every proposed update. Use `git push --dry-run` (`-n`) to see exactly which refs a refspec would touch before touching them — invaluable when the mapping is non-obvious. ## The interview point Most people only ever type `git push origin branchname` and never learn that it is sugar over a refspec. Being able to explain source, destination, the empty-source deletion, the leading `+`, and why new destinations need a full ref name shows you understand push as a ref-update protocol rather than as a file upload.

  • Why does git push origin HEAD:release sometimes fail while HEAD:refs/heads/release works?
    When `release` does not yet exist on the remote, an unqualified destination is ambiguous — Git cannot tell whether you mean a branch or a tag — so it refuses with an "unqualified destination" error. Fully qualifying it as `refs/heads/release` (or `refs/tags/...` for a tag) states the intent. Once the ref exists, the short name matches it and works.
  • What does git push --atomic add when you push several refspecs at once?
    It asks the receiving repository to apply all the requested ref updates or none of them. Without it each update is evaluated independently, so one branch can move while another is rejected, leaving the remote half-updated. It depends on support at the receiving end; where that support is absent, the push fails outright rather than silently falling back.
  • How do you avoid retyping a non-obvious mapping for every push?
    Store it in `remote.<name>.push`, which holds default push refspecs used when you run a bare `git push <remote>`. That way a permanently renamed publishing target is configured once. Before relying on any refspec, `git push --dry-run` shows exactly which remote refs it would update.

saying these in an interview costs you the question

  • Thinks the destination must be an existing local branch
  • Believes a refspec bypasses the fast-forward check
  • Reads the colon form as source and target files
  • Assumes local and remote branch names must match
  • Cannot explain why a new destination needs refs/heads/

context