Let the System Remember: Multiple Git Accounts

Shannon Cartier Lucy: Woman with Shoestrings

Shannon Cartier Lucy: Woman with Shoestrings

Summary

Spent an hour reverse-engineering my own Git multi-account setup. 🙃 It turns out the "hacky" approach (SSH host aliases with fake hostnames) works better than the "correct" one. Why? Loud failure beats silent failure, and state beats process. Every "just remember to..." is a bug.

I spent an hour this morning trying to figure out how my Git setup works. I configured it two years ago and hadn't thought about it since. That's a sign it works.

The setup exists because I have two GitHub accounts. When I push code, Git needs the right SSH key and the right identity. Wrong key and the push fails. Wrong identity and my personal email ends up in my employer's commit history.

Most people solve this with SSH host aliases, an approach from a gist circulating since 2017. You clone personal repos with git@github-personal instead of git@github.com, and the SSH config maps each alias to a different key. Looks hacky. Works.

A commenter complained: "why fake hostnames when you could set an environment variable?" GIT_SSH_COMMAND="ssh -i ~/.ssh/personal-key" git push keeps the "correct" URL.

Sounds reasonable until, inevitably, you forget. With host aliases, using the wrong one gets you a permission denied error. The push fails, you fix it, you move on. With environment variables, forgetting to set one means the push succeeds with the wrong identity. You've leaked your personal email into a work repo and you won't know until someone else notices.

That difference is why I stick with host aliases. A permission error stops you on the spot. Leaking the wrong identity doesn't stop you at all, which is the failure mode you actually have to worry about.

The reason host aliases win is that they encode your choice in the repo's configuration. Clone with github-personal, and every operation after that uses the right key. Environment variables ask you to make the right choice every single time.

It's the same reasoning behind reaching for a database constraint before an application-level check. When a decision has to persist, encode it in the setup instead of relying on yourself to redo it each time.

For attribution (user.name and user.email), git-autoconfig prompts you to pick an identity when you open an unconfigured repo. A fallback.

Maybe even better is Git's conditional includes, where the directory structure encodes the decision:

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

Clone into the right folder. Everything else follows.

Clone into the wrong folder and you'll notice on first commit, because the author is wrong. Fix it by moving the repo to the right directory, or run git config user.email "right@email.com" to override locally. The conditional include sets a default you can always override.

You can go further with Git's url.insteadOf directive, which rewrites URLs automatically. Clone with git@github.com and Git silently converts it to the right host alias based on directory. Too much magic for me. I'd rather be explicit at clone time than let the system guess from a folder path. If you trust your directory discipline, it's one less thing to remember.

The setup I described has stayed invisible for two years, and on the day it finally matters it will fail as a loud permission error I can't miss. When I catch myself repeatedly remembering to do some small manual step, I take that as a sign the setup is wrong and worth fixing, so the reminder stops being necessary.