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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxx> wrote:
>
> From: Julia Evans <julia@xxxxxxx>
>
> - 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!

> 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.

> +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.

> + 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 "^"

> +    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.)

> +    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.

> +
> ++
> +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.

>  (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





[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux