From: "Julia Evans via GitGitGadget" <gitgitgadget@gmail•com>
To: git@vger•kernel.org
Cc: Julia Evans <julia@jvns•ca>, Julia Evans <julia@jvns•ca>
Subject: [PATCH 1/2] doc: git-push: create PUSH RULES section
Date: Wed, 17 Sep 2025 21:33:34 +0000 [thread overview]
Message-ID: <2f2dc22c47530445bce50f1bdef9630b046677bd.1758144815.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.1973.git.1758144815.gitgitgadget@gmail.com>
From: Julia Evans <julia@jvns•ca>
Right now the rules for when a `git push` is allowed are buried at the
bottom of the description of `<refspec>`. Put them in their own section
so that we can reference them from `--force` and give some context for
why they exist.
Having the "PUSH RULES" section also lets us be a little bit more
specific with the rule in `--force`: we can just focus on the rule
for pushing for a branch (which is likely the one that's most relevant)
and leave the details about what happens when you push to a tag or a ref
that isn't a branch to the later section.
Signed-off-by: Julia Evans <julia@jvns•ca>
---
Documentation/git-push.adoc | 93 ++++++++++++++++++-------------------
1 file changed, 44 insertions(+), 49 deletions(-)
diff --git a/Documentation/git-push.adoc b/Documentation/git-push.adoc
index d1978650d6..193016e291 100644
--- a/Documentation/git-push.adoc
+++ b/Documentation/git-push.adoc
@@ -91,48 +91,6 @@ is ambiguous.
configuration (see linkgit:git-config[1]) suggest what refs/
namespace you may have wanted to push to.
---
-+
-The object referenced by <src> is used to update the <dst> reference
-on the remote side. Whether this is allowed depends on where in
-`refs/*` the <dst> reference lives as described in detail below, in
-those sections "update" means any modifications except deletes, which
-as noted after the next few sections are treated differently.
-+
-The `refs/heads/*` namespace will only accept commit objects, and
-updates only if they can be fast-forwarded.
-+
-The `refs/tags/*` namespace will accept any kind of object (as
-commits, trees and blobs can be tagged), and any updates to them will
-be rejected.
-+
-It's possible to push any type of object to any namespace outside of
-`refs/{tags,heads}/*`. In the case of tags and commits, these will be
-treated as if they were the commits inside `refs/heads/*` for the
-purposes of whether the update is allowed.
-+
-I.e. a fast-forward of commits and tags outside `refs/{tags,heads}/*`
-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.
-+
-Tree and blob objects outside of `refs/{tags,heads}/*` will be treated
-the same way as if they were inside `refs/tags/*`, any update of them
-will be rejected.
-+
-All of the rules described above about what's not allowed as an update
-can be overridden by adding an the optional leading `+` to a refspec
-(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
-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
@@ -145,6 +103,7 @@ 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>`.
+Not all updates are allowed: see PUSH RULES below for the details.
--all::
--branches::
@@ -332,14 +291,12 @@ allowing a forced update.
-f::
--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.
+
-This flag disables these checks, and can cause the remote repository
-to lose commits; use it with care.
+This flag disables that check, the other safety checks in PUSH RULES
+below, and the checks in --force-with-lease. It can cause the remote
+repository to lose commits; use it with care.
+
Note that `--force` applies to all the refs that are pushed, hence
using it with `push.default` set to `matching` or with multiple push
@@ -508,6 +465,44 @@ reason::
refs, no explanation is needed. For a failed ref, the reason for
failure is described.
+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.
+
+1. If the push destination is a **branch** (`refs/heads/*`): only
+ fast-forward updates are allowed: the destination must be an ancestor
+ of the source commit. The source must be a commit.
+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).
+3. If the push destination is not a branch or tag:
+ * 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.
+
+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].
+
NOTE ABOUT FAST-FORWARDS
------------------------
--
gitgitgadget
next prev parent reply other threads:[~2025-09-17 21:33 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-17 21:33 [PATCH 0/2] doc: git-push: clarify section Julia Evans via GitGitGadget
2025-09-17 21:33 ` Julia Evans via GitGitGadget [this message]
2025-09-17 22:35 ` [PATCH 1/2] doc: git-push: create PUSH RULES section Junio C Hamano
2025-09-18 20:48 ` Julia Evans
2025-09-17 21:33 ` [PATCH 2/2] doc: git-push: rewrite refspec specification Julia Evans via GitGitGadget
2025-09-19 0:39 ` [PATCH 0/2] doc: git-push: clarify section brian m. carlson
2025-09-19 4:25 ` Jeff King
2025-09-23 18:08 ` Julia Evans
2025-09-23 18:10 ` [PATCH v2 " Julia Evans via GitGitGadget
2025-09-23 18:10 ` [PATCH v2 1/2] doc: git-push: create PUSH RULES section Julia Evans via GitGitGadget
2025-09-23 18:10 ` [PATCH v2 2/2] doc: git-push: rewrite refspec specification Julia Evans via GitGitGadget
2025-09-23 21:54 ` [PATCH v2 0/2] doc: git-push: clarify section Junio C Hamano
2025-09-23 22:10 ` Julia Evans
2025-09-23 23:09 ` Junio C Hamano
2025-09-25 15:59 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=2f2dc22c47530445bce50f1bdef9630b046677bd.1758144815.git.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail$(echo .)com \
--cc=git@vger$(echo .)kernel.org \
--cc=julia@jvns$(echo .)ca \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox