Open source is brilliant, there’s no denying that, and GitHub has been instrumental in open source’s recent success. I’m a keen open-sourcerer myself, and I have a number of projects on GitHub. However, as great as sharing code is, we often want to keep some projects to ourselves. To this end, GitHub created private repositories which act like any other Git repository, only, well, private!
A slightly less common issue, and one I’ve come up against myself, is the desire to only keep certain parts of a codebase private. A great example would be my site, CSS Wizardry; I want the code to be open source so that people can poke through and learn from it, but I want to keep any draft blog posts private until they are ready to go live. Thankfully, there is a very simple solution to this particular problem: using multiple remotes.
Before we begin, it’s worth noting that you can actually build a GitHub Pages site from a private repo. You can keep the entire source private, but still have GitHub build and display a full Pages/Jekyll site. I do this with csswizardry.net. This post will deal with the more specific problem of keeping only certain parts of the codebase (branches) private, and expose parts of it as either an open source project, or a built GitHub Pages site.
N.B. This post requires some basic Git knowledge.
Adding your public remote
Let’s assume you’re starting from scratch and you currently have no repos set up for your project. (If you do already have your public repo set up, skip to the “Adding your private remote” section.)
So, we have a clean slate: nothing has been set up yet, we’re doing all of that now. On GitHub, create two repositories. For the sake of this article we shall call them site.com and private.site.com. Make the site.com repo public, and the private.site.com repo private (you will need a paid GitHub account).
On your machine, create the site.com directory, in which your project will live. Do your initial work in there, commit some stuff — whatever you need to do. Now we need to link this local Git repo on your machine with the public repo (remote) on GitHub. We should all be used to this:
$ git remote add origin email@example.com:[user]/site.com.git
Here we are simply telling Git to add a remote called
origin which lives at
firstname.lastname@example.org:[user]/site.com.git. Simple stuff. Now we need to push our current branch (which will be
master, unless you’ve explicitly changed it) to that remote:
$ git push -u origin master
Here we are telling Git to push our
master branch to a corresponding
master branch on the remote called
origin, which we just added. The
-u sets upstream tracking, which basically tells Git to always shuttle code on this branch between the local
master branch and the
master branch on the
origin remote. Without upstream tracking, you would have to tell Git where to push code to (and pull it from) every time you ran the
pull commands. This sets up a permanent bond, if you like.
This is really simple stuff, stuff that you will probably have done a hundred times before as a Git user. Now to set up our private remote.
Adding your private remote
We’ve set up our public, open source repository on GitHub, and linked that to the repository on our machine. All of this code will be publicly viewable on GitHub.com. (Remember, GitHub is just a host of regular Git repositories, which also puts a nice GUI around it all.) We want to add the ability to keep certain parts of the codebase private. What we do now is add another remote repository to the same local repository. We have two repos on GitHub (site.com and private.site.com), but only one repository (and, therefore, one directory) on our machine. Two GitHub repos, and one local one.
In your local repo, check out a new branch. For the sake of this article we shall call the branch
dev. This branch might contain work in progress, or draft blog posts, or anything you don’t want to be made publicly viewable on GitHub.com. The contents of this branch will, in a moment, live in our private repository.
$ git checkout -b dev
We have now made a new branch called
dev off the branch we were on last (
master, unless you renamed it).
Now we need to add our private remote (private.site.com) so that, in a second, we can send this branch to that remote:
$ git remote add private email@example.com:[user]/private.site.com.git
Like before, we are just telling Git to add a new remote to this repo, only this time we’ve called it
private and it lives at
firstname.lastname@example.org:[user]/private.site.com.git. We now have one local repo on our machine which has two remote repositories associated with it.
Now we need to tell our
dev branch to push to our
$ git push -u private dev
Here, as before, we are pushing some code to a repo. We are saying that we want to push the
dev branch to the
private remote, and, once again, we’ve set up upstream tracking. This means that, by default, the
dev branch will only push and pull to and from the
private remote (unless you ever explicitly state otherwise).
Now you have two branches (
dev respectively) that push to two remotes (
private respectively) which are public and private respectively.
Any work we do on the
master branch will push and pull to and from our publicly viewable remote, and any code on the
dev branch will push and pull from our private, hidden remote.
Adding more branches
So far we’ve only looked at two branches pushing to two remotes, but this workflow can grow as much or as little as you’d like. Of course, you’d never do all your work in only two branches, so you might want to push any number of them to either your public or private remotes. Let’s imagine we want to create a branch to try something out real quickly:
$ git checkout -b test
Now, when we come to push this branch, we can choose which remote we send it to:
$ git push -u private test
This pushes the new
test branch to our
private remote (again, setting the persistent tracking with
You can have as many or as few remotes or branches as you like.
Combining the two
Let’s say you’ve been working on a new feature in private for a few days, and you’ve kept that on the
private remote. You’ve now finalised the addition and want to move it into your public repo. This is just a simple
merge. Check out your
$ git checkout master
Then merge in the branch that contained the feature:
$ git merge dev
master contains the commits that were made on
dev and, once you’ve pushed
master to its remote, those commits will be viewable publicly on GitHub:
$ git push
Note that we can just run
$ git push on the
master branch as we’d previously set up our upstream tracking (
So far this has covered working on just one machine; we had two GitHub remotes and one local repository. Let’s say you’ve got yourself a new Mac (yay!) and you want to clone an existing project:
$ git clone email@example.com:[user]/site.com.git
This will not clone any information about the remotes you had set up on the previous machine. Here you have a fresh clone of the public project and you will need to add the private remote to it again, as above.
If you’d like to see me blitz through all that in one go, check the showterm recording.
The beauty of this is that we can still share our code, but we don’t have to develop quite so openly all of the time. Building a framework with a killer new feature? Keep it in a private branch until it’s ready for merge. Have a blog post in a Jekyll site that you’re not ready to make live? Keep it in a private
drafts branch. Working on a new feature for your personal site? Tuck it away until it’s finished. Need a staging area for a Pages-powered site? Make a
staging remote with its own custom domain.
All this boils down to, really, is the fact that you can bring multiple remotes together into one local codebase on your machine. What you do with them is entirely up to you!