Search code examples
gitgit-pushgit-remotegit-tag

How can I delete any of those remote tags in one git command?


Context

I have a continuous integration setup where we trigger a deploy by adding a tag patch, minor, or major. In each case, my release script will trigger on these tags, generate an appropriate new version number and release the library.

Problem

Once the library is released, I want to make sure none of these tags remains on the remote. I know I could do the following:

git push origin :patch; git push origin :minor ; git push origin :major;

But this is inefficient as it takes three connections instead of one.

I tried to go with git push origin --delete patch minor major, but if the three tags are not found the command doesn't delete any:

git push origin --delete minor major patch
error: unable to delete 'patch': remote ref does not exist
error: failed to push some refs to '[email protected]:algolia/algoliasearch-client-android.git'
# You can see none was deleted, as if I try again on the existing ones:
[algoliasearch-client-android] git push origin --delete minor major      
To github.com:algolia/algoliasearch-client-android.git
 - [deleted]         minor
 - [deleted]         major

I think this is specific to git push/remote tags, as locally I can do:

git tag --delete minor major patch
Deleted tag 'minor' (was db08908)
Deleted tag 'major' (was db08908)
error: tag 'patch' not found.

Is there a way to delete any of several remote tags in one command?


Solution

  • First, although not directly relevant to the answer, it's important to remember that Git does not have remote tags. Git just has tags. What you are doing with git push is having your Git call up some other Git, and then asking that other Git to change some of its references.

    The other Git's references are not remote at all. They're just references! If they start with refs/tags/, they are the specific kind of reference that is a tag name. (All fully qualified names start with refs/; the next few characters after refs/ determines what kind of reference this is.)

    Doing git push origin --delete a b c has your Git ask their Git to delete unqualified references: your Git does not know if these are branch names, tag names, or something else entirely. So your Git and their Git have to coordinate and figure this out. If they don't have one of them, your Git says to you that your two Gits could not agree as to what these all were, and does none of them.

    But you can give fully-qualified reference names: git push origin --delete refs/tags/a refs/tags/b refs/tags/c. Now your Git is sure what you mean to ask them, so it just asks them to delete their refs/tags/a, their refs/tags/b, and their refs/tags/c. Their Git runs these requests through their own verification—i.e., are you allowed to do that?—and the default answer is always "yes". Their Git will reply with "OK, deleted" or "I don't know that reference", and you'll be fine with either of those answers.

    You can do this with the other syntax noted in jsageryd's answer a different syntax:

    git push origin :refs/tags/a :refs/tags/b :refs/tags/c
    

    for instance; the syntax used is not really important, unless you want to push some create or update requests along with some delete requests (--delete applies to all the references you supply). The --atomic flag, if you use it, is relevant, but the default is actually --no-atomic.