Remove local git tags that are no longer on the remote repository

GitGit Tag

Git Problem Overview


We use tags in git as part of our deployment process. From time to time, we want to clean up these tags by removing them from our remote repository.

This is pretty straightforward. One user deletes the local tag and the remote tag in one set of commands. We have a little shell script that combines both steps.

The 2nd (3rd, 4th,...) user now has local tags that are no longer reflected on the remote.

I am looking for a command similar to git remote prune origin which cleans up locally tracking branches for which the remote branch has been deleted.

Alternatively, a simple command to list remote tags could be used to compare to the local tags returned via git tag -l.

Git Solutions


Solution 1 - Git

This is great question, I'd been wondering the same thing.

I didn't want to write a script so sought a different solution. The key is discovering that you can delete a tag locally, then use git fetch to "get it back" from the remote server. If the tag doesn't exist on the remote, then it will remain deleted.

Thus you need to type two lines in order:

git tag -l | xargs git tag -d
git fetch --tags

These:

  1. Delete all tags from the local repo. FWIW, xargs places each tag output by "tag -l" onto the command line for "tag -d". Without this, git won't delete anything because it doesn't read stdin (silly git).

  2. Fetch all active tags from the remote repo.

This even works a treat on Windows.

Solution 2 - Git

From Git v1.7.8 to v1.8.5.6, you can use this:

git fetch <remote> --prune --tags
Update

This doesn't work on newer versions of git (starting with v1.9.0) because of commit e66ef7ae6f31f2. I don't really want to delete it though since it did work for some people.

As suggested by "Chad Juliano", with all Git version since v1.7.8, you can use the following command:

git fetch --prune <remote> +refs/tags/*:refs/tags/*

You may need to enclose the tags part with quotes (on Windows for example) to avoid wildcard expansion:

git fetch --prune <remote> "+refs/tags/*:refs/tags/*"

Solution 3 - Git

If you only want those tags which exist on the remote, simply delete all your local tags:

$ git tag -d $(git tag)

And then fetch all the remote tags:

$ git fetch --tags

Solution 4 - Git

Looks like recentish versions of Git (I'm on git v2.20) allow one to simply say

git fetch --prune --prune-tags

Much cleaner!

https://git-scm.com/docs/git-fetch#_pruning

You can also configure git to always prune tags when fetching:

git config fetch.pruneTags true

If you only want to prune tags when fetching from a specific remote, you can use the remote.<remote>.pruneTags option. For example, to always prune tags when fetching from origin but not other remotes,

git config remote.origin.pruneTags true

Solution 5 - Git

All versions of Git since v1.7.8 understand git fetch with a refspec, whereas since v1.9.0 the --tags option overrides the --prune option. For a general purpose solution, try this:

$ git --version
git version 2.1.3

$ git fetch --prune origin "+refs/tags/*:refs/tags/*"
From ssh://xxx
 x [deleted]         (none)     -> rel_test

For further reading on how the "--tags" with "--prune" behavior changed in Git v1.9.0, see: https://github.com/git/git/commit/e66ef7ae6f31f246dead62f574cc2acb75fd001c

Solution 6 - Git

Good question. :) I don't have a complete answer...

That said, you can get a list of remote tags via git ls-remote. To list the tags in the repository referenced by origin, you'd run:

git ls-remote --tags origin

That returns a list of hashes and friendly tag names, like:

94bf6de8315d9a7b22385e86e1f5add9183bcb3c        refs/tags/v0.1.3
cc047da6604bdd9a0e5ecbba3375ba6f09eed09d        refs/tags/v0.1.4
...
2f2e45bedf67dedb8d1dc0d02612345ee5c893f2        refs/tags/v0.5.4

You could certainly put together a bash script to compare the tags generated by this list with the tags you have locally. Take a look at git show-ref --tags, which generates the tag names in the same form as git ls-remote).


As an aside, git show-ref has an option that does the opposite of what you'd like. The following command would list all the tags on the remote branch that you don't have locally:

git ls-remote --tags origin | git show-ref --tags --exclude-existing

Solution 7 - Git

In new git versions (like v2.26.2 or higher) you could use --prune-tags

> -P
> --prune-tags
> Before fetching, remove any local tags that no longer exist on the remote if --prune is enabled. This option should be used more carefully, unlike --prune it will remove any local references (local tags) that have been created. This option is a shorthand for providing the explicit tag refspec along with --prune, see the discussion about that in its documentation.

So you would need to run:

git fetch origin --prune --prune-tags

Solution 8 - Git

Git natively supports cleanup of local tags

git fetch --tags --prune-tags

This command pulls in the latest tags and removes all deleted tags

Solution 9 - Git

I know I'm late to the party, but now there's a quick answer to this:

git fetch --prune --prune-tags # or just git fetch -p -P

Yes, it's now an option to fetch.

If you don't want to fetch, and just prune:

git remote prune origin

Solution 10 - Git

this is a good method:

git tag -l | xargs git tag -d && git fetch -t

Source: demisx.GitHub.io

Solution 11 - Git

Updated @2021/05

enter image description here

Pass $REPO parameter to custom script.

The content of sync_git_tags.sh

#!/bin/sh

# cd to $REPO directory
cd $1
pwd

# sync remote tags
git tag -l | xargs git tag -d && git fetch -t

Old

> ps: updated @2021/05, git fetch --prune --prune-tags origin not working in my MacOS.

I add the command to SourceTree as a Custom Action on my MacOS.
Setting Custom Actions by Sourcetree -> Preferences... -> Custom Actions


Script to run have to be the git path.

I use git fetch --prune --prune-tags origin to sync tags from remote to local.

enter image description here enter image description here

Solution 12 - Git

Show the difference between local and remote tags:

diff <(git tag | sort) <( git ls-remote --tags origin | cut -f2 | grep -v '\^' | sed 's#refs/tags/##' | sort)
  • git tag gives the list of local tags
  • git ls-remote --tags gives the list of full paths to remote tags
  • cut -f2 | grep -v '\^' | sed 's#refs/tags/##' parses out just the tag name from list of remote tag paths
  • Finally we sort each of the two lists and diff them

The lines starting with "< " are your local tags that are no longer in the remote repo. If they are few, you can remove them manually one by one, if they are many, you do more grep-ing and piping to automate it.

Solution 13 - Git

Just added a git sync-local-tags command to pivotal_git_scripts Gem fork on GitHub:

https://github.com/kigster/git_scripts

Install the gem, then run "git sync-local-tags" in your repository to delete the local tags that do not exist on the remote.

Alternatively you can just install this script below and call it "git-sync-local-tags":


#!/usr/bin/env ruby

# Delete tags from the local Git repository, which are not found on 
# a remote origin
#
# Usage: git sync-local-tags [-n]
#        if -n is passed, just print the tag to be deleted, but do not 
#        actually delete it.
#
# Author: Konstantin Gredeskoul (http://tektastic.com)
#
#######################################################################

class TagSynchronizer
  def self.local_tags
    `git show-ref --tags | awk '{print $2}'`.split(/\n/)
  end

  def self.remote_tags
    `git ls-remote --tags origin | awk '{print $2}'`.split(/\n/)
  end

  def self.orphaned_tags
    self.local_tags - self.remote_tags
  end

  def self.remove_unused_tags(print_only = false)
    self.orphaned_tags.each do |ref|
      tag = ref.gsub /refs\/tags\//, ''
      puts "deleting local tag #{tag}"
      `git tag -d #{tag}` unless print_only
    end
  end
end

unless File.exists?(".git")
  puts "This doesn't look like a git repository."
  exit 1
end

print_only = ARGV.include?("-n")
TagSynchronizer.remove_unused_tags(print_only)

Solution 14 - Git

The same answer as @Richard W but for Windows (PowerShell)

git tag | ForEach-Object -Process { git tag -d $_ }
git fetch -t

Solution 15 - Git

How about this - drop all local tags and then re-fetch? Considering your repo might contain submodules:

git submodule foreach --recursive  'git tag | xargs git tag -d'
(alternatively, "for i in `find .git  -type d -name '*tags*'`; do rm -f $i/*;  done")
git fetch -t
git submodule foreach --recursive git fetch -t

Solution 16 - Git

TortoiseGit can compare tags now.

Left log is on remote, right is at local.

enter image description here

Using the Compare tags feature of Sync dialog:

enter image description here

Also see TortoiseGit issue 2973

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionkENDView Question on Stackoverflow
Solution 1 - GitRichard WView Answer on Stackoverflow
Solution 2 - GitloganfsmythView Answer on Stackoverflow
Solution 3 - GitnewmangtView Answer on Stackoverflow
Solution 4 - GitNicholas CareyView Answer on Stackoverflow
Solution 5 - GitChad JulianoView Answer on Stackoverflow
Solution 6 - GitMike WestView Answer on Stackoverflow
Solution 7 - GitEla DuteView Answer on Stackoverflow
Solution 8 - GitNirav ShahView Answer on Stackoverflow
Solution 9 - GitjokerView Answer on Stackoverflow
Solution 10 - GitimjoseangelView Answer on Stackoverflow
Solution 11 - GitAechoLiuView Answer on Stackoverflow
Solution 12 - GitdotstarajView Answer on Stackoverflow
Solution 13 - GitKonstantin GredeskoulView Answer on Stackoverflow
Solution 14 - GitDanonView Answer on Stackoverflow
Solution 15 - Gittetzu0View Answer on Stackoverflow
Solution 16 - GitYue Lin HoView Answer on Stackoverflow