Git push --all vs --mirror

With Git 2.24 (Q4 2019), you won't be able to use git push --all with --mirror.

And the problem is: --all is sometime implied, when you are pushing from a local repository you just cloned with --mirror.
Filippo Valsorda made the unfortunate experience recently:

Ok, git, WTF. This is not in the man pages.

https://pbs.twimg.com/media/ECcR4vaXsAQ4bfm?format=jpg&name=4096x4096

So, fix an earlier regression to "git push --all" which should have been forbidden when the target remote repository is set to be a mirror.

See commit 8e4c8af (02 Sep 2019) by Thomas Gummerer (tgummerer).
(Merged by Junio C Hamano -- gitster -- in commit fe048e4, 30 Sep 2019)

push: disallow --all and refspecs when remote.<name>.mirror is set

Pushes with --all, or refspecs are disallowed when --mirror is given to 'git push', or when 'remote.<name>.mirror' is set in the config of the repository, because they can have surprising effects.
800a4ab ("push: check for errors earlier", 2018-05-16, Git v2.18.0-rc0) refactored this code to do that check earlier, so we can explicitly check for the presence of flags, instead of their side-effects.

However when 'remote.<name>.mirror' is set in the config, the TRANSPORT_PUSH_MIRROR flag would only be set after we calling 'do_push()', so the checks would miss it entirely.

This leads to surprises for users (see above).

Fix this by making sure we set the flag (if appropriate) before checking for compatibility of the various options.

That leads to, with Git 2.29 (Q4 2020), a code cleanup.

See commit 842385b, commit 9dad073, commit 26e28fe, commit 75d3bee, commit 20f4b04, commit 5b9427e, commit 8d2aa8d, commit 424e28f, commit e885a84, commit 185e865 (30 Sep 2020) by Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit 19dd352, 05 Oct 2020)

push: drop unused repo argument to do_push()

Signed-off-by: Jeff King

We stopped using the "repo" argument in 8e4c8af058 ("push: disallow --all and refspecs when remote..mirror is set", 2019-09-02, Git v2.24.0-rc0 -- merge listed in batch #4), which moved the pushremote handling to its caller.


As it says in the documentation:

--all

    Push all branches (i.e. refs under refs/heads/); cannot be used with other <refspec>.

--mirror

    ... specifies that all refs under refs/ (which includes but is not limited to refs/heads/, refs/remotes/, and refs/tags/) be mirrored ...

So a, if not the, key difference is that one means refs/heads/* and one means refs/*. The refs/heads/* names are the branch names. Anything in refs/remotes/ is a remote-tracking name, and anything in refs/tags/ is a tag name. Other notable name-spaces include refs/notes/, refs/replace/, and the singular refs/stash.

The --mirror option goes on to mention:

locally updated refs will be force updated on the remote end, and deleted refs will be removed from the remote end.

Hence --mirror effectively implies both --force and --prune; --all does not. You can, however, add --force and/or --prune to git push --all, if you like.

It is always up to the other Git to decide whether to obey polite requests (those sent without --force) or commands (--force) to make changes to its references.

With deleted local branch, --all doesn't push it and --mirror does.

This is a consequence of the --prune option: telling your Git to use --prune means "ask them to delete names in their name-space(s) that are not in mine".

Tags:

Git

Github

Push