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

Re: [PATCH 1/2] doc: git-push: create PUSH RULES section

From
Julia Evans <julia@jvns.ca>
Date
Sep 18, 2025, 20:48 UTC
Message-ID
<311f6e20-b12c-454a-991f-bcbd66b318bd@app.fastmail.com>
In-Reply-To
<xmqq348kbtbz.fsf@gitster.g>
Show 27 quoted lines
>>  --force::
>> -	Usually, the command refuses to update a remote ref that is
>> -	not an ancestor of the local ref used to overwrite it.
>> -	Also, when `--force-with-lease` option is used, the command refuses
>> -	to update a remote ref whose current value does not match
>> -	what is expected.
>> +	Usually, `git push` will refuse to update a branch that is not an
>> +	ancestor of the local branch or commit being pushed.
>
> I read this as "there are two conditions, and satisifying only one of
> them is sufficient for the push to be allowed.  (1) the local branch
> is a decendant of the remote branch being updated, or (2) the commit
> we push to update the remote branch is a descendant of the remote
> branch being updated".
>
> But of course that is not what you wanted to say.  (1) would mean
>
>     $ git reset origin/foo && git push origin anything:foo
>
> would allow us to push literally anything to overwrite origin's foo
> branch.
>
> I think
>
>     "... not an ancestor of the commit being pushed to update it."
>
> would be a way to avoid such confusion.
I like this, will use that phrasing.
Show 21 quoted lines
>
>> +PUSH RULES
>> +----------
>> +
>> +As a safety feature, the `git push` command only allows certain kinds of
>> +updates to prevent you from accidentally losing data on the remote.
>> +
>> +Because branches and tags are intended to be used differently, the
>> +safety rules for pushing to a branch are different from the rules
>> +for pushing to a tag. In the following rules "update" means any
>> +modifications except deletes. Deletions are always allowed, except when
>> +forbidden by configuration or hooks.
>
> One important operation is omitted.  "update" does not include
> "create" in the following, no?  Obviously since refs/tags/ would
> never take any "update" (unless forced), if "create" were thrown
> into the same category as "update", you cannot push a new tag out.
>
> So, next to "Deletions are always allowed", shouldn't we describe
> what rules apply to creations?  I presume that they are also always
> allowed?
> The first colon might be acceptable (I find it a bit odd, though).
> The second colon is very weird.  ": only" -> ", only" & "allowed:
> the" -> "allowed. The", perhaps?

I agree the second colon is weird, will fix it. (probably ", which means the destination must be an ancestor of the source commit")

Show 38 quoted lines
>
>> +2. If the push destination is a **tag** (`refs/tags/*`): all updates will
>> +   be rejected. The source can be any object
>> +   (since commits, trees and blobs can be tagged).
>
> Again, I might prefer ":" -> ",".  I cannot decide which I prefer
> between "all updates will be rejected" and "by default no updates
> are allowed".  Either should be OK, so let's take what has already
> been written.
>
> The second sentence is not wrong per-se, and I can see that this was
> inherited from the original, but gives me a strange aftertaste. When
> you list object types in the context of "tag" and have only commit,
> tree, and blob, a little voice in the back of my head asks "oh, what
> happend to tags?".  It is made a bit worse with the phrase "can be
> tagged", as it typically means either (1) to create an annotated or
> signed tag object, or (2) to create a ref in refs/tags/ hierarchy
> locally, but usually you do not think of pushing to refs/tags/
> hierarchy as "tagging that object remotely".
>
> I think the untold assumption here is that refs/tags/foo at the
> remote is being updated most of the time from refs/tags/foo we have
> locally, and "any kind of object can be tagged" is trying to say
> that refs/tags/foo we have locally can be an object of any type, as
> the act of creating a ref "refs/tags/foo" and pointing it directly
> at an object is "to create a light-weight tag" for the object.
> Since we can have not just tags but any kind of object locally
> (because any object "can be tagged"), a push can ask object of any
> kind to be pushed to refs/tags/* hierarchy.  But it is an awkward
> concept to explain.
>
> Would side-stepping what exactly "tagging a thing" means, and
> phrasing it like this
>
>     The source is not limited to an annotated or signed tag object,
>     but can be a commit, a tree or even a blob.
>
> work better, I wonder?

I think it depends on whether the reader is more familiar with lightweight tags or annotated tags. The only kind of tag I've ever used personally is a lightweight tag, so the sentence "The source is not limited to an annotated or signed tag object," feels confusing to me ("why would it be limited to an annotated tag? I'm not even totally sure what an annotated tag is!")

But I can see that it would be different if the reader is more familiar with annotated tags. Will think about this.

Maybe we can just say "the source can be any object" to just be clear that there are no restrictions without trying to educate the reader about the nature of Git tags.

Show 31 quoted lines
>> +3. If the push destination is not a branch or tag:
>
> Here, I do understand and support the colon, so I'd equally support
> the first colon of the previous 2 sections for consistency.
>
>> +   * If the source is a tree or blob object, any updates will be rejected
>
> OK, so this is the same rule as the refs/tags/ hierarchy.
>
>> +   * 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.
>> +
>> +You can override these rules by passing `--force` or by adding the
>> +optional leading `+` to a refspec. The only exception to this is that no
>> +amount of forcing will make a branch accept a non-commit object.
>> +
>> +Hooks and configuration can also override or amend these rules,
>> +see e.g. `receive.denyNonFastForwards` and `receive.denyDeletes`
>> +in linkgit:git-config[1] and `pre-receive` and `update` in
>> +linkgit:githooks[5].
>
> Very well written.  refs/heads/ not taking any non-commit, and
> receiver side hooks that may reject a push, are not something
> "--force" can override.  The mention to "the only exception" above
> sounded as if it forgot to mention the latter.
Will mention the receiver side hooks there, I didn't think of that.
> Looking mostly good.  Thanks.
Previous: Junio C HamanoNext: Julia Evans via GitGitGadget
Message 4 of 15 in “doc: git-push: clarify section”
  1. 0/2 doc: git-push: clarify sectionJulia Evans via GitGitGadget, Sep 17, 2025
  2. 1/2 doc: git-push: create PUSH RULES sectionJulia Evans via GitGitGadget, Sep 17, 2025
  3. Junio C HamanoSep 17, 2025
  4. Julia EvansSep 18, 2025
  5. 2/2 doc: git-push: rewrite refspec specificationJulia Evans via GitGitGadget, Sep 17, 2025
  6. brian m. carlsonSep 19, 2025
  7. Jeff KingSep 19, 2025
  8. Julia EvansSep 23, 2025
  9. 0/2 doc: git-push: clarify sectionJulia Evans via GitGitGadget, Sep 23, 2025
  10. 1/2 doc: git-push: create PUSH RULES sectionJulia Evans via GitGitGadget, Sep 23, 2025
  11. 2/2 doc: git-push: rewrite refspec specificationJulia Evans via GitGitGadget, Sep 23, 2025
  12. Junio C HamanoSep 23, 2025
  13. Julia EvansSep 23, 2025
  14. Junio C HamanoSep 23, 2025
  15. Junio C HamanoSep 25, 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.