Posted in Coding
Mixing work and private life always brings its problems. That is also true for open source software, and for being a member of the communities on Github. In my case, I have a number of repositories on Github that I would classify as personal; they are not linked to my work at ThoughtWorks. Some of these are significant projects in their own right.
At the same time, an increasing number of clients I work with at ThoughtWorks keep their source code on Github. And to be specific: they use github.com itself, not the on-premise Github Enterprise version. It never felt right to use my regular Github account in such cases, and I experienced real issues because of it. For example, one client regularly ran Gitrob, my membership in their repositories was a link to my non-work repositories, which Gitrob also scanned. Due to the way it works Gitrob has a tendency to report false positives, needlessly causing concern and extra work for the client team.
So, to avoid such issues I created another Github account purely for work. Easy as that? Turns out there is a significant problem; a problem that now has simple solution, but one that is difficult to find.
Commit vs push
A committer in a Git repository and an account on Github are different concepts. To be honest, at first I didn’t realise that they are totally distinct and, consequently, I never thought about their relationship. In a nutshell:
- A committer is relevant for git’s commit subcommand, which commits changes to a repository that may or may not end up on Github.
- An account is relevant for git’s push subcommand, which pushes changes to a repository, normally the origin, on Github
Separating two identities for committing is straight-forward: the user name and email address that are recorded with a commit can be configured on the global level and on a repository level, for example using a command like this:
The information is used by the
git command when making the commit. It will be displayed on Github, too, once the commits have arrived there.
When pushing locally committed changes to Github, most commonly SSH is used. This obviously needs some authentication, but how does Github decide whether or not to allow a push?
My Github account is associated with an email address and there is a good chance that the same email address is recorded in the commits that I’m trying to push. Similarly, I might have used a user name that matches my Github account name. Neither of this is relevant, though, because in a distributed version control system the repository I hold locally can contain commits from any number of people, and I shoud be allowed to push all those to the origin, provided I have access. And, in addition, it would be a terrible authentication mechanism because anyone can put any string they wish into those fields.
Git and SSH identities
For an account one or more (public) SSH keys can be stored. When a push via SSH occurs and it can be authenticated using one of the keys then the push is allowed, provided, of course, that the Github account has been given write access to the repository.
Now that still left one question open, or at least it did for me: How does Github know which keys to check? How does it know it needs to look for keys in my account? Maybe that was a naive view. When the push occurs, Github has no concept of who I am. It only sees an SSH connection that offers key-based authentication. So, what Github actually does is a reverse lookup. It takes the key and checks which account this key belongs to. From that it can derive whether the push should be authorised.
This behaviour explains why it is not possible to associate the same SSH key with multiple Github accounts. And it makes clear what we have to do to use multiple Github accounts from the same Unix account on a developer machine, namely use different SSH identities when pushing. With plain
ssh selecting identities is achieved with the
-i option, but, unfortunately, the
git command doesn’t have a comparable option.
In the past the standard way to solve this issue was to use the SSH config and define different aliases for github.com, as described in this answer on Stackoverflow for example. Later, an environment variable was added to
git that made it possible to specify the full SSH command, which in turn provided a way to add the
-i option. It still required us to remember to set the variable in a given shell, based on which repository we wanted to push to.
Only now did I learn that from Git 2.10 onwards there is a repository-level configuration option to set the SSH command, as described in this answer. And, yes, this works on Mac OS, too, e.g.
This option has completely solved the problem for me in, what I think, is the most convenient way possible.