2021-02-05
Limiting what branches I track from an upstream Git repository
I track a number of upstream Git repositories for software we use or that I'm interested in, where by 'track' I mean that I keep a local copy and update it periodically. I've been growing more and more unhappy with how noisy this process has been getting, so recently I did some work on making this tracking quieter. Sadly this left me with one remaining pain point, the repository for Grafana.
Grafana's repository is unusual because the Grafana developers work
in a profusion of short lived feature branches that appear in the
main repository. When I do a 'git pull
' of the Grafana repository
after only a few days, I get a shower of newly created branches and
another shower of recently deleted remote branches. As far as I can see, 'git fetch
' has
no particularly good way to suppress this output through a .git/config
option; the most you can do is run 'git fetch
' entirely quietly.
In order to deal with this, I've now switched to tracking only a limited range of upstream branches. Ideally I would only need to track one upstream branch, the main one, but in practice the Grafana developers create a separate branch for each significant release and I also want to track those branches, just in case. Because of this need to track additional branches combined with Grafana's branch naming practices, the result is a little messy and imperfect.
The normal Git refspec for an upstream 'origin' repo is, in .git/config format:
[remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/*
(The plus sign is a bit magical but is probably okay here.)
To track only a limited subset of branches, we need to change this to one or more 'fetch =' lines that are more specific. Fortunately, you can have multiple 'fetch =' lines; unfortunately, Git's support for wildcard matching here is relatively limited, so we can only do so much given Grafana's branch naming scheme. What I use is:
[remote "origin"] fetch = +refs/heads/master:refs/remotes/origin/master fetch = +refs/heads/v*:refs/remotes/origin/v*
Grafana's version branches are called things like 'v7.4.x', but it also has a few other branches that start with 'v' and we can't use a wildcard match of 'v[1-9]*'. I could list out all of the current major versions that I care about, but that would leave me having to manually change things every so often (such as when 'v8.0.x' is created, which the Grafana people are working on). For now I've decided to accept a few extra branches (currently four); if more unwanted 'v*' branches show up, I may change my mind.
(More recent versions of Git than any version I have access to allows for negative refspecs in 'git fetch', and even documents wildcards a bit in the git-fetch manpage.)
This change of 'fetch =' lines limits what will be pulled in the
future, but it doesn't do anything to prune the unwanted feature
branches I currently have (and I'm not certain git will still notice
deleted upstream branches, since I've probably told it to not look
up information on them). To get rid of them, we need to manually
delete the local 'origin/<...>' branches using 'git branch -d -r
origin/<...>
'. I generated a list of branches to remove by starting
with 'git branch -r
' and then filtering out remote branches I
didn't want to remove, then used a big shell for
loop to do the
work.
Possibly there are better Git ways to do this. I am somewhat of a brute force person when it comes to Git, because I don't know the ins and outs; when I want to do something new, I make a strategic expedition into the manpages and Internet searches, looking for a relatively obvious answer.
PS: Grafana could make my life easier here by putting all of their release branches in a specific namespace, like 'release/v7.4.x'. Then I could have a fetch spec of 'refs/heads/release/*' and it wouldn't match any other feature branches, because of course you wouldn't put feature branches in release/.