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

16

686

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.

Carson answered 3/12, 2009 at 17:1 Comment(2)
I proposed a new feature in git to support pruning stale tags: thread.gmane.org/gmane.comp.version-control.git/168833Vaudevillian
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 to this other question.Applique
S
84

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
Shows answered 4/12, 2009 at 15:43 Comment(2)
The side note about tags not present locally can be expanded to check more remotes: git remote | xargs -L 1 git ls-remote --tags | git show-ref --tags --exclude-existingRuthenium
git supports --prune-tags. Uncertain what version this was introduced. git-scm.com/docs/git-fetch#git-fetch---prune-tagsSnakemouth
R
1358

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.

Retrench answered 21/3, 2011 at 2:42 Comment(12)
as noted in a separate answer, this deletes ALL local tags, and ones not in the remote repo obviously won't be re-createdCatcher
Does the first command require an xargs extension or something? I get this when executing that command in windows cmd: 'xargs' is not recognized as an internal or external command, operable program or batch file.Coiffeur
@Web_Designer: I assume you are working with Windows here. You need to use the real git-bash or have the git installation directory with the xargs executable in your path.Passade
This might not work for everybody. You should do git fetch --tags to be on the safe side.Timpani
I had to go git tag -l | %{git tag -d $_} to get this working in PowerShell. Not sure about anyone else.Desai
For pure batch: for /F %%i in ('call git tag -l') do git tag -d %%iTurbosupercharger
One word of warning on this-- we have a very slow pipe to our remote repo, and this caused a huge data download on the git fetch --tags: took over 30 minutes, and I think redownloaded the entire repo. Be advised, YMMV.Exhalant
git for-each-ref --format='delete %(refname)' refs/tags | git update-ref --stdinVoussoir
or git tag -d $(git tag -l)Wheat
For powershell, you can also use: git tag -l | ForEach-Object {git tag -d $_}Subrogation
The xargs is not exists as @Coiffeur mentioned. It doesn't come with Git for Windows.Ellissa
What xargs does is for each output line of the command before the pipe, it execute the command in front of it with the output line as an additional argument. So on windows you can just copy paste the output of git tag -l in notepad and and prefix each line with git tag -d then run all those lines.Batavia
T
333

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/*"

NOTE: in all these cases would be likely be "origin" or whichever remote you might usually reference.

Tungstate answered 1/5, 2013 at 0:39 Comment(13)
This is explicitly documented as not doing what you say it would in git documentation v1.9.4. It would seem like a very good interface to do it though.. Can you clarify "recent version"Whilom
I refer to the documentation that is packaged with Git for Windows 1.9.4-preview20140611 (and i suspect all previous versions). I access said documentation with "git fetch --help" [quote]Tags are not subject to pruning if they are fetched only because of the default tag auto-following or due to a --tags option.[/quote]Whilom
github.com/git/git/blob/master/Documentation/fetch-options.txt Look at --prune. It appears to be still documented as I say in version 2.1rc0. (at least if the github.com/Git repository is the "official" one, which I suppose it is.Whilom
It doesn't do what the OP needs (tested with git v2.1.1).Heinous
git fetch --prune <remote> +refs/tags/*:refs/tags/* didn't work in ZSH however it works in BASHFormicary
On Windows you also don't need ' quotes, so git fetch --prune origin +refs/tags/*:refs/tags/* works fine.Puddle
@Formicary That's just because zsh expands * but if you single quote that it should be fine.Phonics
On Windows, this worked for me with double quotes but NOT with single quotes. So, this worked: git fetch --prune origin "+refs/tags/*:refs/tags/*"Broeker
git --prune --tags stopped working in precisely version v1.9.0 because of commit e66ef7ae6f31. The Chad Juliano approach works as far back as v1.7.8, but no farther back (I tested v1.7.7 --- no luck!).Voussoir
@v01pe - there is now a git shortcut --prune-tags available since git 2.17.0 described in the documentation under the PRUNING section: git-scm.com/docs/git-fetch/2.17.0 From the document: The --prune-tags option is equivalent to having refs/tags/*:refs/tags/* declared in the refspecs of the remote. Equivalents: git fetch origin --prune --prune-tags OR git fetch origin --prune 'refs/tags/*:refs/tags/*' OR git fetch <url of origin> --prune --prune-tags OR git fetch <url of origin> --prune 'refs/tags/*:refs/tags/*'Mayst
git fetch origin --prune --prune-tags prune both remote tracking branches and tags. checked in git 2.18 version.Hood
In case someone may curious, the <remote> must be replace with Git URL. For example: https://github.com/authorName/repoName.git.Ellissa
No it doesn't need to be replaced, the list of remotes and their aliases available in the repo can be checked by git remote --verbose.Frodeen
F
325

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
Facetious answered 21/1, 2019 at 20:56 Comment(3)
Can be written as git fetch -pPMassingill
Note that this will prune local branches too edit I'm on 2.19.2.windows.1 thoughMission
There might be a bug in newer gits. My 2.39.2 didn't want to prune with --prune-tags although usage text suggests it. I had to enabled the mentioned option fetch.pruneTags to successfully delete a stale tag. O_oVolans
C
201

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
Cacography answered 9/10, 2014 at 15:40 Comment(2)
@ocroquette, I'm not sure how it's nicer than xargs. If you have more tags than ARG_MAX, or similar limitations, this won't work. Unlikely, but possible, and that's why xargs is great.Advisee
git config --global alias.prune-tags '!git tag -d $(git tag) && git fetch --tags' Obligatory alias command. Enjoy. :-)Judi
B
86

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

Beatabeaten answered 2/12, 2014 at 16:42 Comment(3)
Replaced origin with upstream and git corrected my local tags based on the upstream; next git push origin :<deleted-tag-name> updated my GitHub fork, removing the deleted tag.Itu
At least with git 2.18.0 one can also use this syntax: git fetch --prune --prune-tags originLights
Starting with git 2.17.0 - the --prune-tags option was included and described in the PRUNING section in detail with equivalent commands in the following document: git-scm.com/docs/git-fetch/2.17.0 git fetch origin --prune --prune-tags OR git fetch origin --prune 'refs/tags/*:refs/tags/*' OR git fetch <url of origin> --prune --prune-tags OR git fetch <url of origin> --prune 'refs/tags/*:refs/tags/*' Mayst
S
84

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
Shows answered 4/12, 2009 at 15:43 Comment(2)
The side note about tags not present locally can be expanded to check more remotes: git remote | xargs -L 1 git ls-remote --tags | git show-ref --tags --exclude-existingRuthenium
git supports --prune-tags. Uncertain what version this was introduced. git-scm.com/docs/git-fetch#git-fetch---prune-tagsSnakemouth
W
44

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
Way answered 19/5, 2020 at 5:36 Comment(1)
As a note: I find that both flags must be specified: --prune --prune-tags. --prune-tags in isolation has no effectCondillac
E
15

Git natively supports cleanup of local tags

git fetch --tags --prune-tags

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

Exserviceman answered 6/4, 2018 at 19:32 Comment(2)
This removes "any local tags that no longer exist on the remote if --prune is enabled". So it should be git fetch --tags --prune --prune-tags. This would also prune other refereces, but AFAIK there's no way to only prune tags. Anyway, the command in this answer did not work for me. (git version 2.32.0)Penalty
This did not work for me. I had to prune away branches as well via git fetch --prune --prune-tags. My git version is 2.37.3. No sure if git fetch --tags --prune --prune-tags will skip pruning branches?Entebbe
C
12

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
Cloutman answered 22/4, 2020 at 15:36 Comment(0)
K
7

this is a good method:

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

Source: demisx.GitHub.io

Kashakashden answered 19/2, 2018 at 7:10 Comment(0)
P
6

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

git tag | ForEach-Object -Process { git tag -d $_ }
git fetch -t
Papist answered 6/4, 2019 at 18:37 Comment(0)
T
5

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

Thicken answered 8/11, 2019 at 3:41 Comment(0)
A
4

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.

Aufmann answered 15/10, 2014 at 19:4 Comment(3)
The complete command to delete all remote tags not present locally would then be: diff <(git tag | sort) <( git ls-remote --tags origin | cut -f2 | grep -v '\^' | sed 's#refs/tags/##' | sort) | grep ">" | cut -c3- | xargs -I{} git push origin :refs/tags/{}Axolotl
If you need to make such a diff and displaying the commit hash at the same time: diff <(git show-ref --tags | grep -v '{}' | awk '{print $1 " " $2}') <(git ls-remote --tags origin | grep -v '{}' | awk '{print $1 " " $2}')Topaz
This comparison was exactly what I was looking for, thank you. The only thing I'm confused about is that it also outputs a couple of lines which don't start with an arrow <, but a number followed by a comma and then what looks like the first three characters of a commit hash (?), e.g. 7,8d4...Noodlehead
P
3

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)
Priceless answered 14/12, 2010 at 20:57 Comment(0)
L
1

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
Lox answered 9/9, 2014 at 19:34 Comment(0)
F
1

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

Flagman answered 14/7, 2017 at 9:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.