December 28, 2019
I recently made a small contribution to a VSCode extension on Github. In addition to evaluating the code, the maintainer, @roblourens, went above and beyond to ensure that I was credited with the contribution.
It turns out I wouldn’t have been because my git config on the machine I made the commit had a different email from the one attached to my Github account.
The easiest way to fix this (and what I did for the sake of expediency) is to simply add the missing email to my Github account so that those contributions are attached to my profile.
In the process of reading about setting up your commit email address and adding emails to your Github account, however, I also wondered if it was possible to have different emails configured locally based on the repository I’m working in.
For example, if I’m working on a personal project, I want the commit to be from
firstname.lastname@example.org, but if I’m working on a project at work, it should be
Not only is it possible, there are multiple ways to achieve it:
.gitconfigin each directory with desired configuration
At the most basic level, these strategies work because of the specificity rules for Git. Similar to CSS, the “closest” configuration to the code is the one that will be applied.
What does all of this mean? To begin with, let’s imagine a baseline configuration - a foundation on which we’ll build for the remainder of the post. For purposes of simplicity, imagine. the following home directory:
. ├── personal │ ├── personal-project-1 │ ├── ... │ └── personal-project-n ├── work │ ├── work-project-1 │ ├── ... │ └── work-project-n └── .gitconfig
There are two directories which house our personal and work projects and a
.gitconfig will represent the
--global git config.
In our global
.gitconfig we have:
[user] name = Stephen email = email@example.com
That means that by default commits will look something like this by default:
$ git log commit bbed084e8e9135deaa808613aab2a1fb2e21289f Author: Stephen <firstname.lastname@example.org> Date: Fri Dec 6 11:37:23 2019 -0600 <git commit message>
Okay, now that we have the basics out of the way, let’s talk about the different strategies for specifying when a commit is work related so that instead of attributing it to
email@example.com, commits are tied to
The easiest way to create an ad hoc change to your git config is to create a new
.gitconfig for the project.
. ├── personal │ ├── personal-project-1 │ ├── ... │ └── personal-project-n ├── work │ ├── work-project-1 │ │ └── .git │ │ └── config │ ├── ... │ └── work-project-n └── .gitconfig
Now, in the
~/work/work-project-1/.gitconfig we can define a new user email:
[user] email = firstname.lastname@example.org
A prerequisite step si that the
work-project-1 has initialized git:
$ git init
This creates a
.git directory with a
config file within it.
Now that that’s done, we can add the new email:
$ git config --add user.email "email@example.com"
To verify that our new email is working as expected, first confirm that the setting was set correctly.
The easiest way is to list out the git config for that directory.
$ git config --list user.name=Stephen firstname.lastname@example.org ... email@example.com (END)
Notice two things:
So, how are duplicates resolved? For a simple rule of thumb, I’m going with my specificity analogy to CSS, but for the specific answer (since there are more options than just global and local, refer to the docs).
How can we be confident that this works? Examples!
$ pwd /Users/stephen/work/work-project-1 $ touch test.js $ git add test.js $ git commit -m "Initial Commit" $ git log commit c30cc8aafd6e824048406ab2bd771cebf99bef38 (HEAD -> master) Author: Stephen <firstname.lastname@example.org> Date: Thu Dec 12 10:20:12 2019 -0500 Initial Commit
Perfect! Attribution is given to the work email.
At this point, we’ve established how we can update the configuration for git at a project level, but what happens if we have a lot of projects? It’s pretty onerous to have to update the config for each new project.
This is where the Conditional Includes options of Git comes in!1
Since we’re now going to set the config for all projects within the work directory, we need to set that up.
Start by initializing git in the parent directory2 and adding the new email to the config.
$ pwd /Users/stephen/work/ $ git init $ git config --add user.email "email@example.com" $ git config --list user.name=Stephen firstname.lastname@example.org ... email@example.com
Quick reminder - by initializing git in
~/work we now have the following structure:
. ├── personal │ ├── personal-project-1 │ ├── ... │ └── personal-project-n ├── work │ ├── work-project-1 │ │ └── .git │ │ └── config │ ├── ... │ ├── work-project-n │ └── .git │ ├── ... │ └── config └── .gitconfig
Notice that at this point, if we tried to make a commit in
work-project-n, it would still be attributed to
firstname.lastname@example.org (the email in the global config). This is because even though there’s a “closer” config than the global one, it’s unaware of it.
We need to tell git that for all directories within work, we should look at the file
For this step, I manually edited the
~/.gitconfig to include the following section:
[includeIf "gitdir:~/work/"] path = ~/work/.git/config
That should be it! Let’s test to confirm.
By repeating our process from the local config changes we can see our changes in action.
work-project-n to see our config file:
$ pwd /Users/stephen/work/work-project-n $ git config --list user.name=Stephen email@example.com includeif.gitdir:~/work/.path=~/work/.git/config
There’s only one email! This makes sense because we never set an email for the config in
work-project-n, but, we also have the
Let’s repeat our test for
work-project-n that we did with
$ pwd /Users/stephen/work/work-project-n $ touch test.js $ git add test.js $ git commit -m "Initial Commit" $ git log commit 390e985c22b07273ee92b9750b589f8a79db64ae (HEAD -> master) Author: Stephen <firstname.lastname@example.org> Date: Thu Dec 12 10:20:12 2019 -0500 Initial Commit
There are any number of settings that you may want to specify at a directory or project level. Hopefully this post demonstrated a few ways you might tacklet that from the ad hoc to the group and global level!
.gitconfigis a reasonable choice) and then reference it in the
includeIfsection of the global config file. The reason I used
.git/configwas simply because I wanted to use the
gitAPI to modify the user. When I repeated the process a second time, I opted out of initializing git for the parent repo since I didn’t intend to actually track changes.
Thanks for reading! My name's Stephen Weiss. I live in Chicago with my wife, Kate, and dog, Finn.
Click here to see the archives of my weeks in review and sign up yourself!