Image by suju from Pixabay

I have several public repos and corporate repos that I work with daily. I needed a way to have git use the different configurations based on where my git repository was from.

For example, corporate repositories should use my corporate email and corporate username. Public GitHub repositories should use my personal email and name.

You could do this by setting the username and email in each repository:

git config user.name "My Name"

git config user.email "my@email.com"

But this is very error prone. For one, I’ve already setup my global username and email to be my corporate one, since I do most of my work there. Second, this requires that I remember whenever I clone a repo to override the global config if I need to.

What I ended up with is a set of tooling to have context specific configuration and context specific verification in case something goes wrong.

Step 1.) Move GitHub Clones to New Folder

Previously I had all my repos, corporate and public, under one folder:

~/git

Now I needed two separate folders. One for work and one for play.

I settled on:

~/git : work repositories

~/github : public github repositories

After creating the new folder I needed to move all the public repos to the new github folder.

I created a simple shell script to move all repos that had the remote origin set as github to my new github folder.

#!/usr/bin/env bash for d in */; do

REMOTE=$(cd $d; git config --get remote.origin.url)

if [[ $REMOTE == *"github.com"* ]]

then

mv $d ~/github/.

fi

done

Step 2.) Setup Context Specific Git Config

As of Git 2.13 added a new feature to the global .gitconfig file syntax: includeIf

The includeIf allows you to do conditional configuration logic. As of right now it only supports conditional logic on file paths, hence the need to move repositories around.

I created two sub git configurations. One for each environment and I placed it within their respective folders. I then setup the global .gitconfig to conditionally include them:

[includeIf "gitdir:~/git/"]

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

path = ~/github/.gitconfig

Finally, I setup the specific git configurations for each environment:

Corporate:

[user]

name = userid

email = corporate@email.com

[core]

hooksPath = ~/git/.git-hooks

Personal Github:

[user]

name = My Name

email = personal@email.com

[core]

hooksPath = ~/github/.git-hooks

You’ll notice I setup the user name and email in each. I also setup a specific git-hooks for each. We’ll get to this in a bit.

You can still configure lots of your other truly global configurations in the original ~/.gitconfig like editor, color, aliases etc.

Further Reading:

Step 3.) Setup Environment Specific Git-hooks

Configuration is only as good as the verification for it. As such I felt it necessary to setup a client side git-hook to check if the commit being created was configured correctly for the environment.

I do this I created a file in each environment’s git-hooks folder:

pre-commit

#!/bin/bash

#

# An example hook script to verify what is about to be committed.

# Called by "git commit" with no arguments. The hook should

# exit with non-zero status after issuing an appropriate message if

# it wants to stop the commit.

#

# To enable this hook, rename this file to "pre-commit". if git rev-parse --verify HEAD >/dev/null 2>&1

then

against=HEAD

else

# Initial commit: diff against an empty tree object

against=4b825dc642cb6eb9a060e54bf8d69288fbee4904

fi # Redirect output to stderr.

exec 1>&2 REMOTE=`git config --get remote.origin.url`

USERNAME=`git config --get user.name`

EMAIL=`git config --get user.email` checkEmailUsername() {

if [[ "$EMAIL" != "$1" ]]

then

echo "Invalid email: $EMAIL"

echo "git config user.email $1"

exit 1

fi if [[ "$USERNAME" != "$2" ]]

then

echo "Invalid username: $USERNAME"

echo "git config user.name \"$2\""

exit 1

fi

} if [[ $REMOTE == *"git.corporateRepo.com"* ]]

then

checkEmailUsername myCorporate@email.com corporateUsername

fi

then

checkEmailUsername

fi if [[ $REMOTE == *"github.com"* ]]thencheckEmailUsername m yPersonal@email.com "Erik Englund"fi

Ensure the file is executable.

This will cause any commit to abort if the username or email is incorrect for the given remote. This avoids nasty rebases later if something is wrong.

Why two git-hooks scripts if they are the same?

Yes, you could get away with one. But for me my corporation already had several client side git-hooks to enforce things like commit messages and case insensitive file names. Therefore, I needed to have two separate git-hook folders. I can then enforce the corporate standards on the corporate repositories and do whatever I want in the public repositories.

Further Reading:

Conclusion

Now all I have to remember to do is clone my corporate repositories in one location and my public GitHub repositories in another. It isn’t full proof, but it’s pretty close.