87

Is there a way to keep my local git tags in lockstep with a remote's tags? That is -- not only get new tags when created (as usual, when fetch-ing/pull-ing), but also prune tags no longer on a remote and also delete existing tags when someone else git push -f's a tag. I know I can git fetch remotename followed by git remote prune remotename to achieve similar behaviour for branches.

mlb
  • 1,222
  • 1
  • 9
  • 12
  • Looks a bit like http://stackoverflow.com/questions/1841341/remove-local-tags-that-are-no-longer-on-the-remote-repository – VonC May 08 '12 at 01:06
  • 2
    Note: with Git 2.17 (Q2 2018), a simple `git config fetch.pruneTags true` will make your `git fetch` do what you want! See [my answer below](https://stackoverflow.com/a/49215190/6309). – VonC Mar 10 '18 at 23:47

8 Answers8

90

...also prune tags no longer on a remote

git fetch gets with Git 2.17 (Q2 2018) an handy short-hand for getting rid of stale tags that are locally held.

See commit 6317972, commit 97716d2, commit e249ce0, commit 627a129, commit d0e0747, commit 2c72ed7, commit e1790f9, commit 59caf52, commit 82f34e0, commit 6fb23f5, commit ca3065e, commit bf16ab7, commit eca142d, commit 750d0da, commit 0711883, commit ce3ab21, commit aa59e0e (09 Feb 2018) by Ævar Arnfjörð Bjarmason (avar).
(Merged by Junio C Hamano -- gitster -- in commit c1a7902, 06 Mar 2018)

fetch: add a --prune-tags option and fetch.pruneTags config

Add a --prune-tags option to git-fetch, along with fetch.pruneTags config option and a -P shorthand (-p is --prune).
This allows for doing any of:

git fetch -p -P
git fetch --prune --prune-tags
git fetch -p -P origin
git fetch --prune --prune-tags origin

Or simply:

git config fetch.prune true &&
git config fetch.pruneTags true &&
git fetch

Instead of the much more verbose:

git fetch --prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'

Before this feature it was painful to support the use-case of pulling from a repo which is having both its branches and tags deleted regularly, and have our local references to reflect upstream.

At work we create deployment tags in the repo for each rollout, and there's lots of those, so they're archived within weeks for performance reasons.

Without this change it's hard to centrally configure such repos in /etc/gitconfig (on servers that are only used for working with them). You need to set fetch.prune=true globally, and then for each repo:

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

Now I can simply set fetch.pruneTags=true in /etc/gitconfig as well, and users running "git pull" will automatically get the pruning semantics I want.


Update Apr. 2021, Git for Windows 2.30.1 and GitHub Desktop 2.8

It works if you have set prune and pruneTags options:

cd C:\path\to\local\repo
git config fetch.prune true
git config fetch.pruneTags true

Then click on Fetch origin in GitHub Deskop: the logs will show:

2021-04-28T20:25:21.244Z - info: [ui] Executing fetch: 
  git -c credential.helper= -c protocol.version=2 fetch --progress --prune origin (took 2.986s)

... and any local tag not present in the remote will be gone!

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 4
    Color me impressed! This a hell of a follow up over the years since I originally asked. – mlb Mar 13 '18 at 08:50
  • @mlb Thank you. That feature is not yet released, but should be part of Git 2.17 scheduled for release at the very beginning of April 2018. – VonC Mar 13 '18 at 09:06
  • @realtebo What Git version are you using? – VonC Apr 11 '18 at 08:21
  • git version 2.13.2.windows.1 on windows 10, both using powershell and git bash for windows – realtebo Apr 11 '18 at 08:22
  • 1
    @realtebo Well... the non-verbose form has only been implemented in 2.17, released last week ;) https://github.com/git-for-windows/git/releases/tag/v2.17.0.windows.1 – VonC Apr 11 '18 at 08:27
  • I'll wait, because git for windows is still at 2.16. thanks for fast reply. – realtebo Apr 11 '18 at 08:53
  • @realtebo No Git for Windows is at 2.17, not 2.16. Just unzip the https://github.com/git-for-windows/git/releases/download/v2.17.0.windows.1/PortableGit-2.17.0-64-bit.7z.exe anywhere you want, set a simplified PATH for testing (as in https://stackoverflow.com/a/47688159/6309) and you are good to go. – VonC Apr 11 '18 at 08:56
  • None of this worked (2.28, windows). Produced no errors, but failed to remove tags that didn't exist on remote. [Only this worked](https://stackoverflow.com/a/10644209/616460). – Jason C Apr 28 '21 at 19:13
  • @JasonC OK, strange. It does work just fine on my repos. – VonC Apr 28 '21 at 19:16
  • @VonC This particular repo is primarily managed with GitHub Desktop, and tags are primarily managed with the GitHub web interface (via the releases/tags UI); could be a quirk related to that. Not sure. – Jason C Apr 28 '21 at 19:17
  • 1
    @JasonC Indeed. I will update my GitHub Desktop to the latest version (2.8: https://github.blog/changelog/2021-04-28-github-desktop-2-8-includes-expanding-diffs-hiding-whitespace-and-repository-aliases/) and test it out. – VonC Apr 28 '21 at 19:19
  • @JasonC I have updated GitHub Desktop, made some test, and reported my findings in the (now edited) answer. In short: it just works (TM). – VonC Apr 28 '21 at 20:30
49

The following worked for me:

git fetch --prune --tags
Dominik Ehrenberg
  • 1,533
  • 1
  • 15
  • 12
  • 1
    This is exactly what I needed. A bit more straightforward than the accepted answer, in my opinion. – Joe Aug 14 '13 at 19:10
  • The same works for branches aswell if you leave out the --tags – Dominik Ehrenberg Sep 12 '13 at 09:25
  • Don't know why I hadn't thought of this originally. Much simpler and easier to understand. – mlb Jan 09 '14 at 21:49
  • 13
    I don't understand why this is the accepted answer : the git documentation (for V1.9.4) explicitly says that this will NOT work. – Félix Cantournet Jul 25 '14 at 12:15
  • 2
    No, you cannot delete any local tags by just doing ´git fetch -p -t´ – Christoph Feb 03 '15 at 09:05
  • I have git version 2.12.0 on my Mac and yet --prune still does work – Dominik Ehrenberg Mar 17 '17 at 08:12
  • @DominikEhrenberg I find that it doesn't delete tags locally that have already been deleted in the remote. It only fetches new tags at best. Maybe this is the reason (from man git fetch): `Tags are not subject to pruning if they are fetched only because of the default tag auto-following or due to a --tags option`. I'm on git 2.7.4 – R.D. May 11 '17 at 01:39
  • Works like a charm) – pratt Nov 15 '17 at 13:00
  • Didn't work (2.28, windows). Produced no errors, but failed to remove tags that didn't exist on remote. [Only this worked](https://stackoverflow.com/a/10644209/616460). – Jason C Apr 28 '21 at 19:14
21

A bit of research has shown that git has no way to tell the difference between local or foreign tags (all tags go to .git/refs/tags/). Therefore, it is not possible to determine the difference between locally created tags and prune-able remote tags. The options, are then reduced to: having an ever growing set of tags, or only the tags that are on the server.

git push --tags origin && \
git tag | xargs -n1 git tag -d && \
git fetch --tags

Drop the first line for the latter behaviour, and could be potentially git alias'd for frequent usage.

An alternative would be to create a branch (as they can be identified as local/remote) at a tag point and are never write to it again. Then using remotename/branchname as a tag to checkout would keep tags in sync (in addition to git fetch and git remote prune remotename).

Either way is a hack, and the "right" answer is to stop changing tags all the time.

mlb
  • 1,222
  • 1
  • 9
  • 12
  • It's also possible to supply all tags inline to `git tag -d`, thus `git tag | xargs git tag -d && git fetch --tags` worked for me. – solstice333 Jan 13 '17 at 23:43
15

Another solution which actually works for me:

git tag -l | xargs git tag -d && git fetch -t
Ron
  • 22,128
  • 31
  • 108
  • 206
13

use these command to sync tags(delete all local then fetch all remote)

git tag -d $(git tag) # delete all local tags
git fetch --all # fetch all remote to local
xxy
  • 1,058
  • 8
  • 14
  • This is exactly the information I needed. Worked like a champ. – Rod Hartzell Feb 04 '20 at 20:59
  • 1
    This one worked when the other did not. I had been getting this error `! [rejected] 7.34 -> 7.34 (would clobber existing tag)` And none of the other methods worked. Thank you!!! – Intervalia Jun 10 '20 at 19:46
8

git push --tags will push your local tags up to the server. By default, git fetch (the first half of git pull or git pull --rebase) will pull tags, but you can specify -t or --tags to pull all of them.

I'm not sure how to prune remotely deleted tags, but the fetch should pull down any force-updated tags.

LJHarb
  • 32,486
  • 2
  • 32
  • 38
5

disclaimer this uses git internals (some may argue that the filesystem is a git interface, but that's for another day :D)

# Blow away all local tags, this will remove any that are tagged locally
# but are not on the remote
rm .git/refs/tags/*

# Download all the tags from the remote
git fetch --tags
anthony sottile
  • 61,815
  • 15
  • 148
  • 207
  • This won't always work -- sometimes tags are stored in .git/info/refs and not in .git/refs/tags/* I am still researching the interplay between those two locations, but simply clearing .git/refs/tags/* is certainly not a foolproof way to clear tags. – Phil Whittington Jan 27 '22 at 23:04
  • @PhilWhittington I haven't found a situation where it doesn't work -- and it doesn't sound like you have either? – anthony sottile Jan 28 '22 at 02:33
0

Here's an alternative solution:

git fetch -p +refs/tags/*:refs/tags/*

From the git fetch doc:

-p --prune

Before fetching, remove any remote-tracking references that no longer exist on the remote. Tags are not subject to pruning if they are fetched only because of the default tag auto-following or due to a --tags option. However, if tags are fetched due to an explicit refspec (either on the command line or in the remote configuration, for example if the remote was cloned with the --mirror option), then they are also subject to pruning.

solstice333
  • 3,399
  • 1
  • 31
  • 28