2019-12-28
|~7 min read
|1286 words
I recently made a small contribution to a VS Code 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 stephen@personal.tld
, but if I’m working on a project at work, it should be stephen@work.tld
.
Not only is it possible, there are multiple ways to achieve it:
.gitconfig
in each directory with desired configurationAt 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
. The .gitconfig
will represent the --global
git config.
In our global .gitconfig
we have:
[user]
name = Stephen
email = stephen@personal.tld
That means that by default commits will look something like this by default:
$ git log
commit bbed084e8e9135deaa808613aab2a1fb2e21289f
Author: Stephen <stephen@personal.tld>
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 stephen@personal.tld
, commits are tied to stephen@work.tld
.
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
│ │ └── .git
│ │ └── config
└── .gitconfig
Now, in the ~/work/work-project-1/.gitconfig
we can define a new user email:
[user]
email = stephen@work.tld
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 "stephen@work.tld"
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
user.email=stephen@personal.com
...
user.email=stephen@work.tld
(END)
Notice two things:
So, how are duplicates resolved? For a simple rule of thumb, I’m going with my cascading analogy to CSS, i.e., the latest relevant rule wins. 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 <stephen@work.tld>
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 "stephen-alt@work.tld"
$ git config --list
user.name=Stephen
user.email=stephen@personal.tld
...
user.email=stephen-alt@work.tld
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 stephen@personal.tld
(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 ~/work/.git/config
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.
First, we’ll cd
into work-project-n
to see our config file:
$ pwd
/Users/stephen/work/work-project-n
$ git config --list
user.name=Stephen
user.email=stephen-alt@work.tld
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 includeif
line.
Let’s repeat our test for work-project-n
that we did with work-project-1
:
$ 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 <stephen-alt@work.tld>
Date: Thu Dec 12 10:20:12 2019 -0500
Initial Commit
Et voilá!
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!
.gitconfig
is a reasonable choice) and then reference it in the includeIf
section of the global config file. The reason I used .git/config
was simply because I wanted to use the git
API 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.Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!