From: "Julia Evans via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: "brian m. carlson" <sandals@crustytoothpaste.net>,
Jeff King <peff@peff.net>, Julia Evans <julia@jvns.ca>,
Julia Evans <julia@jvns.ca>
Subject: [PATCH v2 1/2] doc: git-push: create PUSH RULES section
Date: Tue, 23 Sep 2025 18:10:48 +0000 [thread overview]
Message-ID: <8be0554d02e147ddf8a033e8bb7b30417bf4d501.1758651049.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.1973.v2.git.1758651049.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 | 94 ++++++++++++++++++-------------------
1 file changed, 45 insertions(+), 49 deletions(-)
diff --git a/Documentation/git-push.adoc b/Documentation/git-push.adoc
index d1978650d6..4faf915f94 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 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,45 @@ 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 deletions and creations. Deletions and creations
+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, which means 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.
+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 exceptions are that no
+amount of forcing will make a branch accept a non-commit object,
+and forcing won't make the remote repository accept a push that it's
+configured to deny.
+
+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-23 18:10 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 ` [PATCH 1/2] doc: git-push: create PUSH RULES section Julia Evans via GitGitGadget
2025-09-17 22:35 ` 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 ` Julia Evans via GitGitGadget [this message]
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=8be0554d02e147ddf8a033e8bb7b30417bf4d501.1758651049.git.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=julia@jvns.ca \
--cc=peff@peff.net \
--cc=sandals@crustytoothpaste.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.