git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 4/4] doc: git-push: rewrite refspec specification

From
D. Ben Knoble <ben.knoble@gmail.com>
Date
Aug 28, 2025, 19:28 UTC
Message-ID
<CALnO6CCZD+DQSG9OMbAia02Nhpseuko95RVRYZ6-YJpvfsAWog@mail.gmail.com>
In-Reply-To
<72114133aafc94354c8b7f3c8ac65456421ce34b.1756240823.git.gitgitgadget@gmail.com>
Thanks for the docs updates! A few nits below, but this looks nice to me :)

On Tue, Aug 26, 2025 at 4:40 PM Julia Evans via GitGitGadget <gitgitgadget@gmail.com> wrote:

Show 14 quoted lines
>
> From: Julia Evans <julia@jvns.ca>
>
> - Originally it said that a refspec was `+<src>:<dst>`, but then later
>   contradicted itself by saying that the `:<dst>` is optional.
>   Mention that `:<dst>` is optional much earlier.
> - Put the complex sets of rules about different refspec forms
>   in lists instead of in long paragraphs of prose
> - Add examples for the various types of refspecs
>   (negative, deletion, pattern, etc)
> - Previously `*` and `^` were not mentioned, mention them
> - Explain what `+` does earlier
> - Remove "might be added in the future" (it's a given that software
>   might change in the future)
Excellent ideas!
Show 13 quoted lines
> diff --git a/Documentation/git-push.adoc b/Documentation/git-push.adoc
> index 0232195515c9..78d433c60c51 100644
> --- a/Documentation/git-push.adoc
> +++ b/Documentation/git-push.adoc
> @@ -57,77 +57,74 @@ OPTIONS[[OPTIONS]]
> +The format for a refspec is [+]<src>[:<dst>], for example `main`,
> +`main:other`, or `HEAD^:refs/heads/main`.
> ++
> +The `<src>` is often the name of the local branch to push, but it can be
> +any arbitrary "SHA-1 expression" (see linkgit:gitrevisions[7]).
> ++
> +The `<dst>` determines what to update on the remote side. It must be the
> +name of a branch, tag, or other ref, not an arbitrary expression.

A welcome (to me) simplification from the original paragraph, especially if we don't lose the original content but rearrange it better :)

> +`:<dst>` is optional.
Here…
> ++
> +`+` is optional and does the same thing as `--force`.
> ++
…and here, I find it odd to start sentences with punctuation if we can avoid it.
Show 20 quoted lines
> +You can write a refspec using the fully expanded form (for
> +example `main:refs/heads/main`) which specifies the exact source
> +and destination, or with a shorter form (for example `main` or
> +`main:other`). Here are the rules for how refspecs are expanded,
> +as well as various other special refspec forms:
> ++
> + 1. `<src>` without a `:<dst>` means to update the same ref as the
> +       `<src>`, unless the `remote.<repository>.push` configuration specifies a
> +       different <dst>. For example, if `main` is a branch, then the refspec
> +    `main` expands to `main:refs/heads/main`.
> + 2. If <dst> unambiguously refers to a ref on the <repository> remote,
> +    then expand it to that ref. For example, if `v1.0` is a tag on the
> +    remote, then `HEAD:v1.0` expands to `HEAD:refs/tags/v1.0`.
> + 3. If <src> resolves to a ref starting with refs/heads/ or refs/tags/,
> +    then prepend that to <dst>. For example, if `main` is a branch, then
> +    `main:other` expands to `main:refs/heads/other`
> + 4. The special refspec `:` (or `+:` to allow non-fast-forward updates)
> +    directs Git to push "matching" branches: for every branch that exists on
> +    the local side, the remote side is updated if a branch of the same name
> +    already exists on the remote side.

I'm not 100% sure this belongs as an item in an ordered list here, since it implies (structurally) something about the order of possible expansions tried. But the introduction to the list does say "rules [and] special refspec forms"… Hm. Maybe it's worth splitting the special ones out? idk.

I see Junio mentioned something similar.
Show 5 quoted lines
> + 5. `tag <tag>` expands to `refs/tags/<tag>:refs/tags/<tag>`.
> + 6. <src> may contain a * to indicate a simple pattern match.
> +    This works like a glob that matches any ref matching the pattern.
> +    There must be only one * in both the <src> and <dst>.
> +    It will map refs to the destination by replacing the * with the
Should src/dst/* have backticks here? I'm not sure.
> +    contents matched from the source. For example, `refs/heads/*:refs/heads/*`
> +    will push all branches.
> + 7. A refspec starting with ^ is a negative refspec.
Ditto the "^"
Show 7 quoted lines
> +    This specifies refs to exclude. A ref will be considered to
> +    match if it matches at least one positive refspec, and does not
> +    match any negative refspec. Negative refspecs can be pattern refspecs.
> +    They must only contain a <src>.
> +    Fully spelled out hex object names are also not supported.
> +    For example, `git push origin 'refs/heads/*' '^refs/heads/dev-*'`
> +    will push all branches except for those starting with `dev-`

I learned something new today! This isn't in the manual I have for 2.48.1 or 2.51.x locally. Thanks! [Junio mentions it's on the fetch side, which I see now]

> + 8. If `<src>` is empty, it deletes the <dst> ref from the remote
Backticks for dst ;)
> +    repository. For example, `git push origin :dev` will
> +    delete the `dev` branch.
> +    Deletions are always accepted without a leading `+` in the
> +    refspec (or `--force`), except when forbidden by configuration or hooks.

Maybe "except when forbidden on the remote by…" ? (This came from the original and does not need tweaked in this series, though.)

Show 7 quoted lines
> +    See `receive.denyDeletes` in linkgit:git-config[1] and `pre-receive` and
> +    `update` in linkgit:githooks[5].
> + 9. If the refspec can't be expanded unambiguously, error
> +    out with an error indicating what was
> +    tried, and depending on the `advice.pushUnqualifiedRefname`
> +    configuration (see linkgit:git-config[1]) suggest what refs/
> +    namespace you may have wanted to push to.
Wrapping looks strange to me here.
Show 8 quoted lines
> +
> ++
> +Not all updates are allowed: it depends on what kind of destination
> +you're pushing to. In the following rules "update" means any
> +modifications except deletes, which as noted above are treated differently.
> ++
> +All of these rules
> +can be overridden by adding the optional leading `+` to a refspec
Ditto.
Show 34 quoted lines
>  (or using `--force` command line option). The only exception to this
>  is that no amount of forcing will make the `refs/heads/*` namespace
>  accept a non-commit object. Hooks and configuration can also override
> @@ -135,18 +132,21 @@ or amend these rules, see e.g. `receive.denyNonFastForwards` in
>  linkgit:git-config[1] and `pre-receive` and `update` in
>  linkgit:githooks[5].
>  +
> -Pushing an empty <src> allows you to delete the <dst> ref from the
> -remote repository. Deletions are always accepted without a leading `+`
> -in the refspec (or `--force`), except when forbidden by configuration
> -or hooks. See `receive.denyDeletes` in linkgit:git-config[1] and
> -`pre-receive` and `update` in linkgit:githooks[5].
> -+
> -The special refspec `:` (or `+:` to allow non-fast-forward updates)
> -directs Git to push "matching" branches: for every branch that exists on
> -the local side, the remote side is updated if a branch of the same name
> -already exists on the remote side.
> -+
> -`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
> +1. If the destination is a **branch** (`refs/heads/*`): the source must
> +   be a commit object, and only fast-forward updates are allowed.
> +2. If the destination is a **tag** (`refs/tags/*`):  the source can
> +   be any object (as commits, trees and blobs can be tagged), and any
> +   updates to them will be rejected.
> +3. For destinations outside of `refs/{tags,heads}/*`:
> +   * If the source is a tree or blob object, any updates will be rejected
> +   * If the source is a tag or commit object, any fast-forward update
> +     is allowed, even in cases where what's being fast-forwarded is not a
> +     commit, but a tag object which happens to point to a new commit which
> +     is a fast-forward of the commit the last tag (or commit) it's
> +     replacing. Replacing a tag with an entirely different tag is also
> +     allowed, if it points to the same commit, as well as pushing a peeled
> +     tag, i.e. pushing the commit that existing tag object points to, or a
> +     new tag object which an existing commit points to.
I didn't close-read this bit, but it seems reasonable.
-- 
D. Ben Knoble
Previous: Julia EvansNext: Julia Evans via GitGitGadget
Message 17 of 87 in “doc: git-push: clarify DESCRIPTION section & refspec definition”
  1. 0/4 doc: git-push: clarify DESCRIPTION section & refspec definitionJulia Evans via GitGitGadget, Aug 26, 2025
  2. 1/4 doc: git-push: update introJulia Evans via GitGitGadget, Aug 26, 2025
  3. D. Ben KnobleAug 28, 2025
  4. Junio C HamanoAug 28, 2025
  5. Kristoffer HaugsbakkAug 29, 2025
  6. Julia EvansAug 28, 2025
  7. D. Ben KnobleAug 28, 2025
  8. 2/4 doc: git-push: clarify "where to push"Julia Evans via GitGitGadget, Aug 26, 2025
  9. Junio C HamanoAug 27, 2025
  10. 3/4 doc: git-push: clarify "what to push"Julia Evans via GitGitGadget, Aug 26, 2025
  11. Junio C HamanoAug 26, 2025
  12. Julia EvansAug 27, 2025
  13. D. Ben KnobleAug 28, 2025
  14. 4/4 doc: git-push: rewrite refspec specificationJulia Evans via GitGitGadget, Aug 26, 2025
  15. Junio C HamanoAug 26, 2025
  16. Julia EvansAug 27, 2025
  17. D. Ben KnobleAug 28, 2025
  18. 0/4 doc: git-push: clarify DESCRIPTION section & refspec definitionJulia Evans via GitGitGadget, Sep 12, 2025
  19. 1/4 doc: git-push: clarify introJulia Evans via GitGitGadget, Sep 12, 2025
  20. Junio C HamanoSep 12, 2025
  21. Julia EvansSep 15, 2025
  22. Junio C HamanoSep 16, 2025
  23. Julia EvansSep 16, 2025
  24. Ben KnobleSep 16, 2025
  25. Junio C HamanoSep 16, 2025
  26. Junio C HamanoSep 17, 2025
  27. Julia EvansSep 18, 2025
  28. 2/4 doc: add an UPSTREAM BRANCHES section to pull/push/fetchJulia Evans via GitGitGadget, Sep 12, 2025
  29. Junio C HamanoSep 12, 2025
  30. Julia EvansSep 15, 2025
  31. Junio C HamanoSep 15, 2025
  32. Julia EvansSep 15, 2025
  33. Junio C HamanoSep 16, 2025
  34. Junio C HamanoSep 16, 2025
  35. Junio C HamanoSep 16, 2025
  36. Julia EvansSep 18, 2025
  37. 3/4 doc: git-push: clarify "where to push"Julia Evans via GitGitGadget, Sep 12, 2025
  38. Junio C HamanoSep 12, 2025
  39. Junio C HamanoSep 12, 2025
  40. Julia EvansSep 15, 2025
  41. 4/4 doc: git-push: clarify "what to push"Julia Evans via GitGitGadget, Sep 12, 2025
  42. 0/4 doc: git-push: clarify DESCRIPTION sectionJulia Evans via GitGitGadget, Sep 23, 2025
  43. 1/4 doc: git-push: clarify introJulia Evans via GitGitGadget, Sep 23, 2025
  44. 2/4 doc: add an UPSTREAM BRANCHES section to pull/push/fetchJulia Evans via GitGitGadget, Sep 23, 2025
  45. Junio C HamanoSep 24, 2025
  46. Julia EvansSep 30, 2025
  47. 3/4 doc: git-push: clarify "where to push"Julia Evans via GitGitGadget, Sep 23, 2025
  48. 4/4 doc: git-push: clarify "what to push"Julia Evans via GitGitGadget, Sep 23, 2025
  49. Junio C HamanoSep 24, 2025
  50. Julia EvansSep 25, 2025
  51. Junio C HamanoSep 25, 2025
  52. Julia EvansSep 25, 2025
  53. Junio C HamanoSep 26, 2025
  54. Junio C HamanoSep 26, 2025
  55. Julia EvansSep 26, 2025
  56. Junio C HamanoSep 26, 2025
  57. Julia EvansSep 26, 2025
  58. Junio C HamanoSep 26, 2025
  59. D. Ben KnobleSep 28, 2025
  60. D. Ben KnobleSep 23, 2025
  61. 0/5 doc: git-push: clarify DESCRIPTION sectionJulia Evans via GitGitGadget, Sep 30, 2025
  62. 1/5 doc: git-push: clarify introJulia Evans via GitGitGadget, Sep 30, 2025
  63. 2/5 doc: add an UPSTREAM BRANCHES section to pull/push/fetchJulia Evans via GitGitGadget, Sep 30, 2025
  64. Junio C HamanoSep 30, 2025
  65. Julia EvansOct 3, 2025
  66. Junio C HamanoOct 3, 2025
  67. Jean-Noël AVILAOct 1, 2025
  68. Julia EvansOct 3, 2025
  69. 3/5 doc: git-push: clarify "where to push"Julia Evans via GitGitGadget, Sep 30, 2025
  70. 4/5 doc: git-push: clarify "what to push"Julia Evans via GitGitGadget, Sep 30, 2025
  71. Junio C HamanoSep 30, 2025
  72. Jean-Noël AVILAOct 1, 2025
  73. 5/5 doc: git-push: Add explanation of `git push origin main`Julia Evans via GitGitGadget, Sep 30, 2025
  74. D. Ben KnobleOct 1, 2025
  75. Julia EvansOct 3, 2025
  76. D. Ben KnobleOct 1, 2025
  77. 0/5 doc: git-push: clarify DESCRIPTION sectionJulia Evans via GitGitGadget, Oct 6, 2025
  78. 1/5 doc: git-push: clarify introJulia Evans via GitGitGadget, Oct 6, 2025
  79. 2/5 doc: add an UPSTREAM BRANCHES section to pull/push/fetchJulia Evans via GitGitGadget, Oct 6, 2025
  80. Kristoffer HaugsbakkOct 7, 2025
  81. Julia EvansOct 7, 2025
  82. D. Ben KnobleOct 7, 2025
  83. 3/5 doc: git-push: clarify "where to push"Julia Evans via GitGitGadget, Oct 6, 2025
  84. 4/5 doc: git-push: clarify "what to push"Julia Evans via GitGitGadget, Oct 6, 2025
  85. 5/5 doc: git-push: Add explanation of `git push origin main`Julia Evans via GitGitGadget, Oct 6, 2025
  86. D. Ben KnobleOct 6, 2025
  87. Junio C HamanoOct 6, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.