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

From: Ævar Arnfjörð Bjarmason <[email-masked]>
Date: Tue, 16 Jan 2018 01:38:52 +0100

On Mon, Jan 15 2018, Michael Giuffrida jotted:

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