When I first started writing the specification for and developing Librarygame in late 2011 I had only been using git and GitHub for about a year. Although I was pretty new to source control I had read Vincent Driessen’s fantastic write up on A successful Git branching model. It’s a superb read and something I still refer back to, and recommend to new git users, today. I became very comfortable using branches for features, hotfixes and releases but I was a bit confused how this could work with multiple forked repositories of the same application.
Skip the story: show me the solution
To give a little bit of background; Librarygame, a web application devised and owned by my company Running in the Halls, is a gamification platform for libraries. It takes a user’s activity in the library (after they have signed up and agreed) and awards them points and achievements depending on how often/when they have visited, the number of books/type of books they borrow and if they return books on time. It does a fair bit more than that, but that’s the basics.
The problem with creating a heavily customisable product
When development of Librarygame began we had a single customer, The University of Huddersfield. We spent a lot of time talking to the developers at their library about how the system could work and figured things out as we went along. When the first version was completed in early 2012 it was a version tailored and specific to The University of Huddersfield.
It wasn’t until we had two more customers, The University of Glasgow and The University of Manchester, that there became an issue with how I was using git and GitHub. The problem was that I had created a core application but it was very specific to how it was being used in one library. Each different library came with different systems that Librarygame would need to hook into, as well as other considerations. Some of these included the following:
- different authentication layers/SSO solutions
- different book information API(s)
- different types of trackable activities
- different branding and styles
The question I began asking myself was how can I maintain a core application as well as the different modules and methods required for each university? My ideal situation would be to have a central repository for the core Librarygame application, with individual repositories for each university. I just had to figure out how to keep everything in sync and organised. When I thought about it, this was just the same as forking someone else’s public repository but instead of making changes and sending pull requests I just need to do the occasional
git pull to sync any changes. This should be easy.
Problem: you can’t fork your own repository on GitHub
Forking someone else’s public repository on GitHub is really simple, you just view the repository and hit fork. Unfortunately you can’t fork your own repository. But there is an easy way to get around this with a few git commands.
How to [kind of] fork your own repository on GitHub
I’ll go through each step and include the various git commands required to achieve the setup:
1. Set up the core application git and GitHub repository
First of all you need your core application repository. For the purpose of this article I will use an example repository I have created on my own account
2. Create x number of GitHub repositories for each customer version
The next step is to create new GitHub repositories for each separate customer version of the application and set them up locally. I have created another repository on my own account to illustrate this
In the case of Librarygame we now have four customer repositories, in addition to the core Librarygame application. One for each of our customers and their unique version of Librarygame.
3. Add an upstream remote to the new repositories
Next we need to add a remote to each customer version repository that references the core application repository—the repository from step 1. This is our link between the core application and the customer forked application.
To do this you will need to open terminal (Mac) and change directory to the customer application and add the remote with the following command:
git remote add upstream https://github.com/samcroft/example-core-application.git
Note: I am using the example repository I created on my own GitHub account in step 1.
4. Fetch the core application branches
Before we can merge commits from the core application into the customer repository we need to fetch the branches from the upstream remote we just added in the previous step. This is easily done with the following command:
git fetch upstream
This downloads all of the upstream commits and branches from the core application repository. This effectively means that you have two groups of branches that you can
- the customer repository branches (master, development, feature-x etc)
- the upstream core application repository branches, prefixed with upstream (master, development, release-v1.1, hotfix-1.0.1 etc)
Note: I have used some example branch names here.
checkout these branches by prefxing the branch name with
upstream/. For example, if you wanted to
checkout the development branch of the core application you would run the following command:
git checkout upstream/development
5. merge the upstream/branch into the customer branch
The final step is to merge the required branch into the desired customer branch. This is exactly the same as merging branches but with the addition of prefixing the source branch with
upstream/. Firstly, make sure you are working on the desired branch you would like to merge into (e.g. master):
git checkout master
Next you just need to merge the source branch (e.g. master):
git merge upstream/master
This will merge all of the commits from the core application master branch into the customer application master branch. You may have to resolve some merge conflicts of course.
At this point your commit is complete and you can sync with your GitHub repository.
To merge future changes to the core application with the customer version you just need to run through steps 4 and 5;
At this point you have effectively forked your own repository and can then begin working on any customisations in the customer repository, while at any time pulling in any changes from the core application.
Dealing with the message: “Please enter a commit message to explain why this merge is necessary,especially if it merges an updated upstream into a topic branch”
The first time I went through the above steps I was shown this message and output that looked like git had encountered an error. It was asking me to type a commit message for the merge, but it wouldn’t let me type anything. This is because git had launched into its default editor, Vim. I’m not a Vim user so this had me puzzled for a minute or two. To enter a commit message with the Vim editor you need to use some [interesting] commands:
- press i (to start INSERT mode)
- enter the commit message, or leave it with the default merge message
- press esc (to exit INSERT mode)
- type :wq and press enter (to Write the current file and Quit Vim)
This method now allows me to maintain the core Librarygame application and create separate forked repositories for each customer university version. The end result is:
- a single repository that I can use for the core Librarygame application
- multiple repositories for each customer university version of Librarygame
- a relationship between each of the university repositories and the core Librarygame repository
- the ability to pull from the core Librarygame repository and merge into the customer repository as and when required