· 9 min read

Your git identity should follow your GitHub account, not your directory

You push a commit to a repository. You open the pull request on GitHub, and you see it.

Instead of your familiar avatar and a link to your profile, the commit shows your raw name next to a generic grey placeholder. Or worse, you push a commit to a work repository, and it shows up perfectly linked—to your personal account, permanently mixing your work history with your weekend projects.

A GitHub commit list showing three commits: one linked to NjbSyd, one with a grey placeholder avatar and no profile link, and one linked to torvalds.
Three commits, one repository. GitHub decides who gets credit purely from the email address in the commit.

If you maintain multiple GitHub accounts, you have probably run into this. The standard advice for fixing it usually involves setting up SSH aliases or conditional git configurations based on your folder structure.

These solutions work most of the time. But they are solving the wrong problem.

To understand why, we have to look at how git and GitHub handle identity, why the default failure mode is completely silent, and how to tie your git identity to the only thing that actually matters: your authenticated session.

Git and GitHub identity are unrelated systems

Most of the confusion around multiple accounts comes from treating git and GitHub as a single system. They are entirely separate.

Git decides who authored a commit purely by looking at your local configuration. When you create a commit, git reads these two values:

git config user.name    → "Najeeb Said"
git config user.email   → "tcanjb@gmail.com"

Git embeds those strings directly into the commit object. It does not know what GitHub is. It does not verify your email address. You could set your email to linus@kernel.org right now, and git would happily write it into your repository history.

GitHub, on the other hand, makes two distinct checks when you push code:

  1. Authorization: Do you have permission to push to this repository? This is verified using your credential, like a personal access token or an SSH key.
  2. Attribution: Who gets credit for this commit? GitHub determines this by extracting the email address baked into the incoming commit and matching it against the verified email addresses registered in its database.

These two checks are independent. Nothing forces them to match.

Your authorization token can be perfectly valid for your work account while your local git config is still spitting out your personal email address. When you push, GitHub sees a valid token and accepts the code. Then it looks at the email on the commit, matches it to your personal account, and attributes the work there. If the email doesn't match any account, GitHub just displays the raw text with a grey avatar. The commit belongs to nobody.

(Note that this applies exactly the same way to GitHub's noreply addresses. They are just alternate emails registered to an account).

Why the failure is silent

This disconnect creates a uniquely frustrating failure mode.

When you push with the wrong identity, the push succeeds. No warning is printed in your terminal because, from git's perspective, nothing went wrong. If you run git log locally, you see your name, which looks correct at a glance.

The mistake is only visible on GitHub's web interface. You usually find out weeks later, or when a colleague points out that your commits aren't linking to your profile.

Fixing it after the fact is expensive. Because the email address is part of the commit object, changing it requires recalculating the commit hash. You have to rewrite the repository history using a tool like git filter-repo and force-push the result. Doing this on a shared branch breaks every existing clone and open pull request. In practice, nobody actually does this. You just live with the wrong attribution forever.

Why existing solutions fall short

If you search for how to handle multiple accounts, you will find a few standard answers. They all fall short of solving the actual underlying problem.

Setting it manually

You can configure your identity per repository:

git config user.email work@company.com

This works if you remember to run it every single time you clone a repository. You will not remember.

SSH Host aliases

A common approach is configuring ~/.ssh/config to use different keys for different hosts:

Host github-work
    HostName github.com
    IdentityFile ~/.ssh/id_work

You then clone using git@github-work:org/repo.git. This guarantees you are using the right SSH key. It solves authorization. It does absolutely nothing about user.email. You can still push successfully as the wrong author.

Conditional includes (includeIf)

This is the most common recommendation. You tell git to use a specific configuration file if the repository is located in a specific directory:

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

This is a genuine improvement over manual configuration. But it breaks down at the edges.

You might clone a work repository into ~/Downloads to check something quickly. You might keep a personal script inside your work folder. You might use an automated tool that clones into a scratch directory.

The fundamental problem with includeIf is that the directory location is a guess about who you are. The directory correlates with your identity most of the time, which is exactly why it is dangerous. It fails rarely, and when it fails, it fails silently.

Using the GitHub CLI

GitHub's official CLI tool, gh, supports multiple accounts natively. You can authenticate both and switch between them:

$ gh auth status
github.com
  ✓ Logged in to github.com account personal-account (keyring)
  ✓ Logged in to github.com account work-account (keyring)

$ gh auth switch --user work-account
✓ Switched active account for github.com to work-account

This is great for interacting with issues and pull requests. However, gh auth switch only changes which token is active. It does not touch your git configuration. You are now authenticated as your work account, but your commits will still carry your personal email.

The reframe

Identity should be keyed to the account you are authenticated as, not the folder you happen to be standing in.

The gh CLI already knows which account is active. That information is factual, authoritative, and changes at exactly the moment you intend to switch contexts. We just need to wire that state into git.

I wrote a small tool to do exactly this.

How the tool works

The solution is a shell wrapper around the gh binary. Shell functions take precedence over executables on your PATH. When you type gh, you run the function. The function executes the real binary and then checks if you just changed your authentication state.

gh() {
    command gh "$@"
    local rc=$?
    if [ $rc -eq 0 ] && [ "$1" = "auth" ]; then
        case "$2" in
            switch|login|logout) __gh_identity_sync ;;
        esac
    fi
    return $rc
}

A few details matter here. The wrapper only acts on auth switch, login, or logout. Every other gh command passes through untouched. It only syncs your identity if the gh command actually succeeded (rc -eq 0). Finally, it preserves the original exit code. A wrapper that swallows exit codes will eventually break a script or a CI pipeline.

$ gh auth switch --user work-account
✓ Switched active account for github.com to work-account
git identity → Work Name <work@company.com>

When a sync is triggered, the script needs to know who you are. It asks the GitHub API directly:

active=$(command gh api user --jq .login 2>/dev/null)

It queries the API instead of scraping the output of gh auth status. CLI output formats change, but the API endpoint is stable. More importantly, the API provides the exact same source of truth that GitHub will use when evaluating your push.

Storage

Identities are mapped to accounts in a standard git config file located at ~/.gitconfig-gh-identities:

[ghIdentity "work-account"]
    name = Work Name
    email = work@company.com

Using git's native configuration format means there is no custom YAML parser to maintain. We just read and write using git config -f <file>.

If you switch to an account the tool hasn't seen before, it prompts you for a name and email, saves them to this file, and applies them globally.

Logout behavior

If you log out of all accounts, the script does this:

git config --global --unset user.name
git config --global --unset user.email

Leaving a stale identity active after logout would recreate the exact bug we are trying to fix. By clearing the global configuration, the tool ensures that your next commit will fail loudly with git's standard "please tell me who you are" error, rather than silently attributing your work to an inactive account.

Why not build a gh extension?

The GitHub CLI has a solid extension system. You can install extensions and run them as gh my-extension.

However, extensions can only add new subcommands. They cannot intercept or hook into existing commands. Because the goal is to react automatically when a user runs the standard gh auth switch command, a shell wrapper is the only mechanism that has the right structural shape for the job.

The trade-off

If you have read this far, you might have spotted the architectural trade-off.

The includeIf directive is stateless and resolves per-directory. This wrapper creates a stateful, global configuration. There is only one "current" identity active on your machine at any given time.

If your workflow involves having two terminals open side-by-side, actively committing to a personal project in one and a work project in the other, includeIf is a better solution. In that scenario, there is no single correct answer to "who are you right now," so determining identity by directory is the only approach that works.

But for developers who work primarily in one context at a time and switch between them, tying identity to the active authentication token tracks reality much closer.

The good news is that they compose perfectly. You can use this tool to manage your global default identity based on your active GitHub session, while keeping includeIf configurations for specific directories where you want absolute certainty.

Try it out

If you are tired of checking the email address on your commits after a push, you can install the wrapper here:

git-identity-sync on GitHub

It takes a few seconds to source it in your shell profile. The next time you switch accounts, your git identity will follow.