Git for Multi-Organization Development

Michael Pellaton
Netcetera Tech Blog
5 min readJan 3, 2018

--

In this article we first discuss the challenges of using Git for multiple organizations from one computer and then a possible solution.

After having committed to a GitHub repository with an author email address definitively not meant to be used for open source contributions and consequently having spent more time using «git filter-branch» than he feels comfortable with, the author sought to find a technical solution for his own shortcomings. This article documents and shares the results from this quest.

The Standard Git Setup

The standard Git setup documentation be it from Git [1] or GitHub [2] globally defines the user name and email with the two following commands:

git config --global user.name "Michael Pellaton"
git config --global user.email michael.pellaton@somedomain.tld

To access the upstream Git repositories using SSH, these sources tell you to create a default SSH key if none exists and register the corresponding private key with the upstream repository manager like GitHub or Bitbucket

Challenges

This standard setup works well as long as you work only with repositories from a single organization. This no longer works as soon as you have to deal with repositories from different organizations imposing different policies like:

  • Use work email to commit to work repos only and use a private email for all open source stuff.
  • As contractor use an email of the client for commits to his repositories.
  • Use the login for work repos and use your real name for open source repos.
  • Have different incompatible SSH key requirements.
  • Having to use autocrlf=true for one client and false for another.

The Old Solution

Local Configurations
The simplest solution would be to have a global configuration for the most common case and then add a local configuration overriding these values. The problem with this approach is that doing the local configuration after every single clone is cumbersome and might sometimes be forgotten. Besides that, if there is a change in the policy of an organization, the local configurations of each cloned repository needs to be updated.

Identity Switching Scripts
The idea behind this solution is to have a shell script that manipulates the global Git configuration for every organization you work for. All you have to do is run the appropriate script. The advantage of this approach is that all organization dependent stuff is centralized in one place and cloning an additional repository or changing a value when needed is simple. However, the disadvantage of the forgetful developer remains — and believe me, removing inappropriate email addresses and user names from a GitHub repository with git filter-branch and force pushes [3] is no fun neither.

However, when I first met the challenges of working for multiple organizations simultaneously back in 2012, identity switching scripts were the best solution. For completeness, here’s an example of such a script:

identity-github.sh:#!/bin/bash
git config --global user.name "Michael Pellaton"
git config --global user.email michael.pellaton@somedomain.tld
git config --global commit.template ~/.gitmessage.github
git config --global core.autocrlf true
export GIT_SSH_COMMAND="ssh -i ~/.ssh/id_rsa.github"
echo "Git identity switched to: GitHub"
git config user.name
git config user.email

New Git Features to the Rescue

Conditional Includes
Quite recently, in May 2017, Git introduced «conditional includes» with the 2.13 release [4]. Whenever Git reads the .gitconfig file — which is basically for every command — it evaluates the condition and decides whether to include another configuration file or not. Configuration variables in this additional file take precedence over values already defined in upstream configurations up to the point of the include (as if the included values where inlined at the point of the inclusion.

The basic form of a conditional include is includeIf.<condition>.path and currently the following two conditions are supported [5]:

  • gitdir:PATTERN matches the location of the repository against the glob pattern specified. If it matches, the include condition is met.
  • gitdir/i: same as gitdir but with case insensitive matching

In the following example, .gitconfig.foo is only included if the current git repository lies withing ~/foo/:

[includeIf "gitdir:~/foo/"]
path = .gitconfig.foo

With this new feature we can get rid of the identity switching scripts by simply checking out the repositories of every organization we work for in a separate subdirectory. A conditional include statement then automatically applies the appropriate configuration for each git operation. The days of forgetting to manually switch Git identities or to configure every repository after cloning are gone \o/.

The core.sshCommand Configuration Variable
The 2.10 release of Git (September 2016) introduced the configuration variable core.sshCommand [6]. The command specified is executed instead of the standard ssh in fetch and push operations.

The value the environment variable GIT_SSH_COMMAND takes precedence over core.sshCommand and we therefore have to make sure it is no longer set before using the latter in a conditionally imported configuration.

The New Solution

The main Git configuration file now contains all the common and fallback settings and it includes additional configuration files depending on the checkout location of the repository:

~/.gitconfig:# common and fallback configurations
[user]
name = Michael Pellaton
email = private@email.tld
[core]
sshCommand = ssh -i ~/.ssh/id_rsa.private
[color]
ui = auto
# conditional configurations for github repos
[includeIf “gitdir:~/git/github/”]
path = .gitconfig.github
# conditional configuration for work repos
[includeIf “gitdir:~/git/work/”]
path = .gitconfig.work

The conditional include file applied to all repos cloned under ~/git/github with the overrides for user name, email and the SSH key to use:

~/.gitconfig.github:[user]
email = public@email.tld
[core]
sshCommand = ssh -i ~/.ssh/id_rsa.github

And to finish up, the conditional include file applied to all repos cloned under ~/git/work the work settings:

~/.gitconfig.work:[user]
name = some.weird.work.username
email = work@email.tld
[core]
sshCommand = ssh -i ~/.ssh/id_rsa.work

Conclusion

In the hectic of the daily business as software developers we try to keep ahead of the evolving frameworks tools and technologies. In this steady stream of new technology to understand, judge and adapt, small new features in the very fundamental tools of our work are often overlooked. Despite being small the effect on our work quality or effectiveness can be considerable.

Externally driven events like the basic installation of a (new) personal computer are good occasions to scrutinize the setup of our development tools and find better solutions for challenges we considered to be «solved» last time.

--

--