Rants from the Ballmer Peak rss feed

The tyranny of git-flow defaults

UPDATE: The suggestion to remove the git master branch is very bad. It is better to keep it and change git-flow's develop for a stable branch.

I'm a proponent of developing software in branches, and more particularly, having a stable or production branch which tracks public software releases for the purpose of performing hotfixes whenever necessary without interrupting the normal development cycle. One nice tool which helps enforce this style of development from the command line is git-flow. It's just a bunch of scripts which avoids typing repetitive commands. If you prefer using a GUI, the nice folks from Atlassian, creators of Bitbucket, also have released SourceTree, a git and Mercurial client with smart branching support for git-flow.

But the defaults for git-flow and any git host like GitHub clash. By default git, and any git hosting, is going to create empty repositories with a default branch named master. When you initialize git-flow on such a repository, it will ask a set of questions and suggest defaults:

$ git-flow init
No branches exist yet. Base branches must be created now.
Branch name for production releases: [master]
Branch name for "next release" development: [develop]

How to name your supporting branch prefixes?
Feature branches? [feature/]
Release branches? [release/]
Hotfix branches? [hotfix/]
Support branches? [support/]
Version tag prefix? []

This is bad. Everybody by default will make changes on the master branch, but that's not what you want! You actually want other people to make changes based off the develop branch. This is what I call the tyranny of the default, which I first heard from Steve Gibson on the Security Now podcast. The idea behind this tyranny is simple: most people don't touch the settings of their software.

And therefore, if you are a git-flow user, you are likely using the wrong default for collaboration. It is the wrong default because anybody forking your project and sending pull requests will do so against the master branch. Fortunately, GitHub allows one to change the default branch of any repository. You can do this at any time: from that moment on, any user cloning the repository will get the new default branch, and

Should master die in a fire?

The question is, should you keep using master for the stable even after changing GitHub default branch? Many users are unaware of sophisticated branch development, so they may anyway try to look at the master branch ignoring project documentation (which is likely to be missing anyway…). Also, since master is a default, it is likely there is software out there which has hardcoded values which may fail. Discovering these and communicating them to the developers is a nice touch.

For these reasons it is best if you avoid having a master branch at all. What would you call it? releases is too close to the release branches temporarily created by git-flow. official sounds weird. Maybe stable, which indicates to people that the branch is safe and won't crash and burn.

The rest of this post is a guide to perform the necessary changes to follow this convention of having a stable branch and a develop branch which is the default receiver of pull requests. Unfortunately git doesn't track branch renames, so if you already have a repository and plan to go on a renaming spree, you will hear a lot of complaints. On the other hand, git tells you if you try to pull from a branch which has disappeared, so at least people will notice, unlike a lot of RSS feeds which don't age well…

Cases for repository owners

Starting from scratch

The ideal situation, create your project locally, then upload to GitHub. It's the ideal case because nobody will suffer the change:

$ mkdir secretharem
$ cd secretharem
$ git init
$ git-flow init
[answer with 'stable' instead of the default 'mater']

The git-flow init command will create each branch with an initial commit. Now you can go to GitHub and create an empty repository, then we upload the branches (note we specify both stable and develop):

$ git remote add origin git@github.com:user/secretharem.git
$ git push -u origin stable develop

Interestingly at this point GitHub will have picked develop as the default branch for the project, likely due to ASCII sorting. But it won't hurt if you go to https://github.com/user/secretharem/settings and verify that the default branch is set to develop.

This is the ideal setup because your repository starts with the correct configuration, and any future forks on GitHub will use that information for pull requests against the develop branch.

Moving an existing repo to git-flow

Usually you will have a repository with the master branch and no more. Before initializing git-flow you should rename the master branch:

$ git clone git@github.com:user/worldneedsmorexml.git
$ cd worldneedsmorexml
$ git checkout -b develop
$ git checkout -b stable
$ git-flow init
[answer with 'stable' first, 'develop' later]
$ git push --set-upstream origin develop stable

From the lonely master branch we create first the aliases develop and stable because otherwise git-flow complaints that they don't exist. After the branches have been pushed to GitHub, go to https://github.com/user/worldneedsmorexml/settings and change the default branch from master to develop. If you don't this, trying to delete the master branch will fail because you can't remove from GitHub the default branch. After that, deletion is easy:

$ git branch -d master
$ git push origin :master

The syntax for removing branches is that, pushing the branch with a colon before its name.

Renaming a git-flow master branch to stable

If you have a repository using git-flow and want to rename master to stable, first go to GitHub's settings and change the default branch to develop which you will likely have not done yet. Then:

$ cd ilovekpop
$ git checkout develop
$ git branch -m master stable
$ git push --set-upstream origin stable
$ git push origin :master
[now edit .git/config with your text editor]

Since git-flow is already initialized locally, it will be tracking the old master branch. Open .git/config and rename that to stable. After that everything should keep working as usual.

Cases for people with a cloned repository

Starting from scratch

Not hard, you do a git clone and the default GitHub branch (develop) gets checked out.

Existing clone after branch rename

Users with existing checkouts will get the following message when they try to pull from the deleted branch:

Your configuration specifies to merge with the ref 'master'
from the remote, but no such ref was fetched.

This means that the branch has disappeared. And hopefully the user noticed during the previous git pull that new branches were created. The user can then check out one of the new branches and delete master:

$ git checkout develop|stable
$ git branch -d master

Cases for users with forks

Well, this is interesting. If I recall correctly, in the good old days of GitHub each forked repository had a button on the website which allowed you to merge upstream changes clicking on it. This seems to have been gone and replaced with instructions to perform those changes manually from the command line (so much for GUIs, eh?). And likely for good reason: it rarely worked, and for popular repositories with many forks it surely taxed their servers, because it would look for changes not only in the upstream repository but also other forks (remember, git is a distributed version control system, so there's no real upstream or server). In fact, I remember having to refresh that page several times due to the amount of time it took to calculate changes to merge.

Well, presuming you have configured an upstream source like their instructions suggest, you can update your master branch to follow either of the new ones with simple local commands, then delete your master:

$ git checkout -b develop
$ git merge upstream/develop
$ git push --set-upstream origin develop
$ git branch -d master
$ git push origin :master
remote: error: refusing to delete the current branch: refs/heads/master
To git@github.com:forkuser/healthyspam.git
 ! [remote rejected] master (deletion of the current branch prohibited)
 error: failed to push some refs to 'git@github.com:forkuser/healthyspam.git'
$

Ah, indeed. Remember, you can't remove GitHub's default branch. You first need to go to your own fork on GitHub, change the repository settings to the recently pushed develop branch, and then you can remove your old master. Replace the commands with stable if you would prefer to track that instead.

One strange feature of git is that after a remote branch has been deleted, you will likely still see it if you try to list it:

$ git fetch upstream
remote: Counting objects: 8, done.
remote: Total 6 (delta 1), reused 6 (delta 1)
Unpacking objects: 100% (6/6), done.
From github.com:remoteuser/healthyspam
 * [new branch]      develop    -> upstream/develop
 * [new branch]      stable     -> upstream/stable
$ git branch -va
* master                   a8e1d54 Initial commit
  remotes/origin/HEAD      -> origin/master
  remotes/origin/master    a8e1d54 Initial commit
  remotes/upstream/develop 280e777 Develop
  remotes/upstream/master  a8e1d54 Initial commit
  remotes/upstream/stable  8cdc31d Stable

You only need to run a command to prune the local cache:

$ git remote prune upstream
Pruning upstream
URL: git@github.com:remoteuser/healthyspam.git
 * [pruned] upstream/master

Now you are clean and properly updated with the upstream branches.

But users get now the develop branch by default!

Indeed, if you have changed GitHub's default branch to develop and a user clones a repository, by default he gets that single development branch. And that's what you want, really. Git is not a software distribution platform (cue complaints from people downloading huge repository histories), it's for developers. If you are in the situation of having multiple branches, one of them for releases, that's because you are doing public releases. Your users willing to get a stable version will get those, or will follow your documentation (or their intuition) to check out the stable branch.

UPDATE: The suggestion to remove the git master branch is very bad. It is better to keep it and change git-flow's develop for a stable branch.


See the article index or browse articles by tags: programming , bureaucracy , git , tools .
Published on: 17/01/2014 23:39. Last update: 15/02/2014 08:26. rss feed
Copyright 2016 by Grzegorz Adam Hankiewicz.
Generated with ipsum genera. Look at the source code.