I am trying to delete git tags that are older than X months
We have release tags that we need to retain, they are all tagged release-*
where * = date
I know how to delete a singular tag git push origin :refs/tags/<tagName>
So I extrapolated to get all the remote tags to delete them
git ls-remote --tags origin | xargs git push origin :$1
To skip the release tags I was planning on using egrep -v
making the command
git ls-remote --tags origin | egrep -v "(^\*|release*)" | xargs git push origin :$1
But I still haven't figured out how to do it by date.
Locally I can order things by date like so git for-each-ref --sort=taggerdate --format '%(refname)' refs/tags | egrep -v "(^\*|release*)"
but that does not help me with remote tags.
If it helps I don't mind deleted or not deleting local tags in order to delete the remote ones.
Finally we use gitlab if it provides a better way of cleanup?
Regardless this needs to be something that can be run like a script through Jenkins to help with our git cleanup needs.
Update
Since we have thousand of git tags I realized that xargs is going to be rather slow.
I believe the way to delete will more likely be something like
git push origin $(< git tag | <sorting_by_date + exclude release> \
| sed -e 's/^/:/' | paste -sd " ")
That way the command will basically be appending :refs/tags/tag1 :refs/tags/tag2
into a single command instead of doing a unique delete for every tag and contacting the remote.
After working on this for the past couple of days I figured out a solution that works quite well.
First collect all the git tags by chronological order, I excluded release*
tags
git for-each-ref --sort=taggerdate --format '%(refname:short) %(taggerdate:short)' refs/tags | egrep -v "(^\*|release*)"
This will give the following output
master_7 2017-12-05
master_8 2017-12-05
master_9 2017-12-07
master_10 2017-12-08
master_11 2017-12-08
update_framework_1 2017-12-12
master_12 2017-12-12
master_13 2017-12-13
So it's the name of the tag separated by a space and then the date in YYYY-MM-DD
format. If you want a full date or something specific update taggerdate:short
with something appropriate.
I then took the output and processed it line by line looking at the date and comparing it with my cut off date.
This allowed me to generate a list of tags to delete.
Then I run the deletions by looping through the tag list and performing the following command
git push origin :refs/tags/<tag1> :refs/tags/<tag2>
I played it safe and run the command every 50 tags, but git didn't seem to have any problems with this.
Finally after all the deletions are complete I run
git fetch --prune origin +refs/tags/*:refs/tags/*
which deletes all local tags that do not exist in the remote.
And there you have it all tags are cleaned up.
You'll need to be pretty specific about your source for a date stamp, as there are multiple candidates:
Of these, only the first is immediately available via git ls-remote
: to get the rest, you must have the objects available locally, so that you can read them and extract their embedded (encoded-via-text) date stamps.
You can fetch tags and their target objects using git fetch --tags origin
, which is mostly the same as running, e.g., `git fetch origin '+refs/tags/:refs/tags/'. But if you want to make sure that your repository does not pick up their tags as your tags, you might want to fetch these into a separate name-space, such as:
git fetch origin '+refs/tags/*:refs/rtags/origin/*'
so that whatever their (origin's) repository has tagged as refs/tags/v12.34
, for instance, now appears in your repository under the name refs/rtags/origin/v12.34
. You can now read the name: v12.34
, under the refs/rtags/origin/
space.
You can then use the full name to read the tagged Git object. If the tagged object is an annotated tag, you can extract its metadata, including its date. If the tagged object is a commit, you can read its metadata.
If the tagged object is an annotated tag, you can read its object
metadata and continue reading objects ("peeling" the tag) until you find the final object; or you can have Git do this for you, using the name^{commit}
or name^{}
syntax. Note that the name^{commit}
syntax directs Git to peel the tag until it reaches a commit, or else exit with an error, so that if the annotated tag eventually points to something else—such as a blob object—you catch this error. The name^{}
syntax directs Git to peel the tag until it reaches any non-annotated-tag object, i.e., commit, tree, or blob.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With