Deleting branches or tags in git

When deleting stuff in Git, for instance, branches, or tags, we often come across the solution whereby we do something like the following:

git push origin :unwanted-branch

or perhaps:

git push origin :refs/tags/unwanted-tag

I wanted to quickly explain why the colon is there, and it has to do with what Git calls the Refspec.

The Refspec.

Essentially, the refspec is used to configure which remote resource (e.g. branch, tag, etc.) should be used, and what the referencing local resource (again, e.g. branch, tag, etc.) should be called. In other words, it fully specifies the local to remote mapping. Specifically, the format of a refspec is:

[+]source-resource:destination-resource

where the optional + at the front turns off fast-forward merging – similar to --no-ff.

Therefore, when a simple command (that we easily understand) such as the following is issued:

$ git remote add origin https://git-server.com/repository.git

What really happens is that the following fetch refspec gets created:

fetch = +refs/heads/*:refs/remotes/origin/*

This tells Git to fetch every resource under refs/heads/ (the source), and store them under refs/remotes/origin/ (the destination). Because this is a fetch, the source is the remote server, and the destination is the local machine. An easy way of reading this is “fetch from refs/heads/* (source) to refs/remotes/origin/* (destination).”

To concretize this, let’s look at another example. Consider the simple command:

git push origin master

This automatically generates a push refspec of:

push = refs/heads/master:refs/heads/master

This basically means “push from refs/heads/master (source) to refs/heads/master (destination).” However, if we wanted to be explicit, we could have issued, instead:

git push origin master:refs/heads/special/master

Which would create a push refspec of:

push = refs/heads/master:refs/heads/master

And this simply means “push from refs/heads/master (source) to refs/heads/special/master (destination).”

Back to deleting stuff.

Now that we’ve understood the refspec a bit more, let’s again look at the delete commands that we often find:

git push origin :unwanted-branch
git push origin :refs/tags/unwanted-tag

It should make sense now.

Let’s consider the branch deletion example. When we want to delete a branch, the idea is to push nothing to the remote. To specify nothing, we omit the source part of the refspec entirely. Hence, it is simply :unwanted-branch. This pushing of nothing will trigger a delete of the branch at the remote end.

Similarly, Git stores tags under refs/tags. Hence, to delete a tag, we do the same thing, and specify :refs/tags/unwanted-tag. This also triggers a delete of the tag at the remote end.