How to Manage Work & Personal Git Accounts on One Machine

Learn how to configure your local machine to use different Git user details and SSH keys for your work and personal projects. No more mixed identities!

A few years ago, I started to see some odd behaviour in our work git repository. I had made a commit to my fork of a repo and raised a pull request but this was shown as a pull request for my personal GitHub account, not my work one. A quick Google showed me that the issue was the name and email in my git config file and that updating these would solve the issue with future commits. So, I updated with my work details and thought nothing of it.

Fast forward some months, I did a few Adevent of Code challenges and noticed the behaviour the other way, there were commits to my personal git repos using my work GitHub account. Now, I could update my global config to be my personal account but then future commits would come from there and not my work account and I didn’t want to have to keep changing the values before each commit. Faced with the prospect of having to update my config prior to any commit I started to investigate git config to see if there was a way I could have my work and personal accounts configured on the same machine.

For the detail that follows, I have the following setup on my machine. I’ll go into detail on each part so that the setup can be easily copied.

 1~
 2├── .ssh
 3│   ├── config
 4│   ├── personal
 5│   ├── personal.pub
 6│   ├── work
 7│   ├── work.pub
 8│   ├── work_commit_signing
 9│   └── work_commit_signing.pub
10├── Development
11│   └── git
12│       ├── work-repo-01
13│       └── work-repo-02
14├── Personal
15│   └── git
16│       ├── advent-of-code...
17│       └── advent-of-code...
18├── .gitconfig
19├── .gitconfig-personal
20├── .gitconfig-work
21├── .gitmessage-personal
22└── .gitmessage-work  

Git Config

The first thing I needed to do was create two separate configurations, one for personal projects and one for work. As with most developers, I only had one .gitconfig file and it contained only a couple of values, there are my user details and also the content of a message template that gives me a structure to use for each commit.

1[user]
2    name = Andrew Fitzpatrick
3    email = work_email@email.com
4[commit]
5    template = ~/.gitmessage

.gitmessage

I made a copy of .gitmessage and affixed -personal to the end, I also renamed .gitmessage to .gitmessage-work, this gives the flexibility to have two different commit templates. Here is the content of both files:

.gitmessage-work

1[JIRA-nnnnn] <description>
2
3[optional body]
4Why is this change necessary?
5How does it address the issue?
6What side effects does this change have?

.gitmessage-personal

1<type>[optional scope]: <description>
2
3[optional body]
4
5[optional footer(s)]

You’ll see that for personal projects the template is set up to remind me to use the conventional commits structure, whereas my work template reminds me to headline the commit with the JIRA ID and more structured body.

.gitconfig

Unlike the messages, I needed three config files but I followed a similar approach to the one above to make the files I need. Firstly, I copied the .gitconfig and affixed -personal to the end. Then I copied the .gitconfig again and affixed -work to the end. The result was 3 config files .gitconfig, .gitconfig-personal and .gitconfig-work.

These three files allow me a lot of flexibility, not only can I have different identities and point to different templates but I can start to interact with my different profiles in different ways. Here is the content of the files:

.gitconfig-work

1[gpg]
2    format = ssh
3[user]
4    email = work_email@email.com
5    name = Andrew Worden-Fitzpatrick
6    signingkey = /Users/user/.ssh/work_commit_signing
7[commit]
8    template = ~/.gitmessage-work
9    gpgsign = true

.gitconfig-personal

1[user]
2    name = Andrew Fitzpatrick
3    email = personal_email@email.com
4[commit]
5    template = ~/.gitmessage-personal

Immediately you’ll see some obvious differences.

  1. The user section shows my split in identification - I must update my personal name, I’ll do it for the commit of this post :D.
  2. All commits using my work profiles are signed for verification
  3. Each configuration points to different message templates which we set up in the previous step.

These configuration files are very flexible and powerful, I only set a few settings but the git help gives hundreds more.

Finally the content of the original gitconfig file:

1[core]
2    editor = nano
3[includeIf "gitdir/i:~/Personal/git/"]
4    path = ~/.gitconfig-personal
5[includeIf "gitdir/i:~/Development/git/"]
6    path = ~/.gitconfig-work

This is where some of the magic happens. The core section makes it so that nano is used as my message editor regardless of the profile used. I’m not sure how I ended up using Nano, I started using it years ago and like it so I stuck with it.

The two includeIf essentially tell git which config to use depending on where the git command is being run from. If I’m in one of my work repos, which I clone to ~/Development/git, the ~/.gitconfig-work configuration is used. If I’m in one of my personal repos, which I clone to ~/Personal/git, the ~/.gitconfig-personal configuration is used.

So, what happens if I clone a repo to neither of these locations and then try to work with git? Well, git complains that the user.name and user.email are not set, for example:
Committing to git from an unknown location

SSH config

It’s good practice to use ssh when working with GitHub, it ensures the connection is secure whilst also removing the need to authenticate every request as would be needed with a HTTPS connection - this continual sending of a personal access token increases the chances of it being stolen and reused for nefarious purposes. It’s easier to use HTTPS but SSH should be preferred. GitHub has good docs detailing how to communicate over SSH.

In the spirit of wanting to keep my personal and work profiles separate I have two different ssh keys, both are in the ~/.ssh folder on my machine. I have uploaded the personal.pub key to my personal GitHub account and the work.pub key to my work GitHub account. Then I use a config file to define which private key to use, here is the content of the config file:

 1Host github.com
 2  HostName github.com
 3  AddKeysToAgent yes
 4  IdentityFile ~/.ssh/work
 5  IdentitiesOnly yes
 6
 7Host github-andrewfitzy
 8  HostName github.com
 9  AddKeysToAgent yes
10  IdentityFile ~/.ssh/personal
11  IdentitiesOnly yes

The only pain I find with this setup is that I have to remember to alter the URL when cloning a personal repository. For example, instead of cloning using:

1╰─❯ git clone git@github.com:andrewfitzy/2024-advent-of-code.git

I have to update the URL so that it looks like the following:

1╰─❯ git clone git@github-andrewfitzy:andrewfitzy/2024-advent-of-code.git

If I forget to update the URL I can still commit to the local repo but when I try to push I will get an error like:
Committing to git after cloning using an unmodified URL

The same would happen if I was cloning a work repo and accidentally updated the clone URL. This setup ensures I can’t push to a personal or work repo using the incorrect profile.

Conclusion

I’ve been using this setup for a number of years now and it’s been quite frictionless. Yes, I do occasionally forget to modify the URLs when cloning a personal repo but I have the protection of my SSH config to prevent an incorrect push to a work repo.