Thread (94 messages) flat view 94 messages, 4 authors, 2018-02-09

Re: [BUG] git remote prune removes local tags, depending on fetch config

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2018-01-16 11:14:25

On Tue, Jan 16 2018, Michael Giuffrida jotted:
Just to confirm, you're talking about the behavior of removing *all*
tags that aren't found on the remote? (Not just tags that used to be
on some remote, but have since been removed from that remote.) So,
with your proposed workflow, you would never create your own tags
locally, without pushing them to the remote before running `git fetch`
-- otherwise they would simply be deleted.

It doesn't seem like a useful feature -- you wouldn't expect `git
fetch --prune` to remove your local branches that you were developing
on, right?
Yes, I realize this isn't everyone's cup of tea, but it's very useful in
our in-house use-case for git.

We have repos that have tags pushed to them on every deployment, thus
the cumulative set of tags is in the hundreds of thousands.

There's a pruning script that prunes both branches and tags from the
server (and archives them to an archive repo that has every ref ever).

Then we need to push these deletions back to client checkouts. For
branches that's easy, just set fetch.prune=true. For tags I need to
chase down checkouts and do:

    git -C {} config --replace-all remote.origin.fetch "refs/tags/*:refs/tags/*" "^refs/tags/*:refs/tags/*$"

It would be easier to just set fetch.{prune,pruneTags}=true in
/etc/gitconfig, this is on e.g. shared development staging boxes where
you might have a manually cloned checkout in your ~.

Of course it'll wipe away any manually created tag you have, *ideally*
we'd have something like the refs/remotes/ namespace for tags, but that
ship has sailed.

But meanwhile tag pruning is an OK compromise, 99% of our users will
never even think to create a tag (and if they created one manually they
couldn't push it), the advanced users that would know what sort of repo
they're editing and can out-out of the behavior by editing their local
config.
On Mon, Jan 15, 2018 at 4:38 PM, Ævar Arnfjörð Bjarmason
[off-list ref] wrote:
quoted
On Mon, Jan 15 2018, Michael Giuffrida jotted:
quoted
`git remote prune <name>` should "delete all stale remote-tracking
branches under <name>". I was surprised to discover, after some
troubleshooting, that it also deletes *all* local tags that don't
exist on the remote, if the following refspec is included in the
remote's fetch config:

    +refs/tags/*:refs/tags/*

So, if `remote.origin.fetch` is configured to fetch all tags from the
remote, any tags I create locally will be deleted when running `git
remote prune origin`. This is not intuitive [1], nor is is it
explained in the docs [2]. Is this behavior obvious to someone with a
better understanding of Git internals?

I did find a better way to automatically fetch tags (using tagopt
instead of adding the fetch refspec). However, the refspec doesn't
seem "wrong" in itself; in particular, `git fetch --tags` used to be
considered equivalent to specifying the refspec
"refs/tags/*:refs/tags/*" -- implying that this is a sensible refspec
[3]. So I wouldn't expect it to "break" the behavior of another
command.

[1] https://stackoverflow.com/q/34687657/1327867
[2] https://git-scm.com/docs/git-remote.html#git-remote-empruneem
[3] https://github.com/git/git/commit/c5a84e92a2fe9e8748e32341c344d7a6c0f52a50
These docs are really confusing, but it is working as intended, and
really should be re-documented.

The `git remote prune` subcommand just ends up piggy-backing on
git-fetch, whose behavior is explained here:
https://git-scm.com/docs/git-fetch.html#git-fetch---prune

It's worked this way since at least v1.8.5.6, maybe at some distant
point in the past it only did this for branches when invoked via
git-remote as the documentation says.

RELATED:

I've actually had the reverse problem with this. I want some way to turn
this behavior on without explicitly hacking the refspec, so I can do it
globally in /etc/gitconfig or in ~/.gitconfig without screwing with the
config of each checkout on certain machines.

You can set fetch.prune=true, but that only prunes the branches, you
need to inject remote.origin.fetch into each checkout, unless I've
missed some way of doing this.

I wanted to add fetch.pruneTags that would make it as if you had
refs/tags/*:refs/tags/* in the fetch spec, but I haven't hacked that up
yet, if anyone can see any inherent issue with that plan I'd like to
know about it.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help