# Multiple GitHub Accounts on One Machine in Two Commands

Most developers end up with more than one GitHub account: one for work, one for personal projects, maybe one for a client. Git can handle this, but doing it by hand takes a few separate pieces of setup:

1.  a separate SSH key for each account
    
2.  a `Host` alias for each key in `~/.ssh/config`
    
3.  each public key added to the right GitHub account
    
4.  a commit name and email that switch depending on which folder you are in
    
5.  remotes that use the right alias
    

This guide does all of that with [ghprofile](https://github.com/Tessie475/ghprofile), a small open source CLI, in two commands. `ghprofile add` declares an account, and `ghprofile apply` sets it up and confirms the key reaches the right account.

## Contents

*   [What you need](#what-you-need)
    
*   [Install](#install)
    
*   [Step 1: Add your main account](#step-1-add-your-main-account)
    
*   [Step 2: Apply](#step-2-apply)
    
*   [Step 3: Add your other accounts](#step-3-add-your-other-accounts)
    
*   [Step 4: Clone with the right account](#step-4-clone-with-the-right-account)
    
*   [Existing repositories](#existing-repositories)
    
*   [Already have a key for one of the accounts?](#already-have-a-key-for-one-of-the-accounts)
    
*   [Checking on it later](#checking-on-it-later)
    
*   [What it writes](#what-it-writes)
    
*   [Undoing it](#undoing-it)
    
*   [FAQ](#faq)
    

## What you need

*   `git`
    
*   `ssh-keygen`, which ships with macOS, most Linux distributions, and Git for Windows
    
*   a GitHub account for each identity
    

## Install

**macOS and Linux** with Homebrew:

```bash
brew install Tessie475/tap/ghprofile
```

**macOS and Linux** without Homebrew:

```bash
curl -sSfL https://raw.githubusercontent.com/Tessie475/ghprofile/main/scripts/install.sh | sh
```

**Windows:** paste this link into your browser to download the zip:

```plaintext
https://github.com/Tessie475/ghprofile/releases/download/v0.2.7/ghprofile_0.2.7_windows_amd64.zip
```

Unpack it and put `ghprofile.exe` on your `PATH`.

**With Go:**

```bash
go install github.com/Tessie475/ghprofile/cmd/ghprofile@latest
```

Check it worked:

```bash
ghprofile version
```

## Step 1: Add your main account

Set up one account at a time: add it, apply it, then move on to the next. You can add every account first and apply once, but one at a time is the smoother experience, because each run only asks you about one account. Step 3 explains why that matters.

Each account gets a short name of your choosing. The name also becomes the SSH alias, so `personal` becomes `github-personal`.

Start with the account you use most and mark it as the default:

```bash
ghprofile add personal --email you@gmail.com --default
```

The default account's email is used for commits everywhere, except in folders you assign to another account later.

`add` asks for your commit name and uses your current git name as the suggestion. Run `ghprofile add` with no flags and it asks for everything instead.

## Step 2: Apply

Preview first if you like. This changes nothing:

```bash
ghprofile apply -dry-run
```

```plaintext
create directory   ~/.ssh
generate key       ~/.ssh/id_ed25519_personal
write              ~/.config/ghprofile/gitconfig-personal
write              ~/.ssh/config
write              ~/.gitconfig

5 change(s) not applied (dry run)
```

Then run it for real:

```bash
ghprofile apply
```

Three things happen.

**It generates a key.** `ssh-keygen` asks for a passphrase, as it does when you follow GitHub's own instructions. Setting one is recommended. Press Enter twice to skip it. On macOS the passphrase is saved to your keychain, so you are only asked once.

**It writes your SSH and git config.** Anything already in those files stays as it was, and a backup is saved before each file is changed.

**It helps you add the key to GitHub.** It copies the public key to your clipboard, opens GitHub's SSH keys page, and waits:

```plaintext
sign the browser in as you@gmail.com, the "personal" account, then paste the key, save it,
and press Enter:
```

Check that the browser is signed in to the account it names before you paste. Adding a key to the wrong account is the most common mistake in this setup, and it's easy to do when more than one account is signed in.

After you press Enter, ghprofile connects to GitHub and reads back which account the key belongs to:

```plaintext
authenticated as your-username
```

If it names the wrong account, the key went to the wrong place. Remove it from that account and run `ghprofile upload personal` to try again.

## Step 3: Add your other accounts

Now add the second account, and tell it which folder it belongs to:

```bash
ghprofile add work --email you@company.com --dir ~/work/
```

Inside `~/work/`, commits now use `you@company.com`. Everywhere else they still use your default email.

Before you apply, switch the browser to your work account. Then apply again:

```bash
ghprofile apply
```

Your first account is already done, so this run only covers the new one:

```plaintext
generate key       ~/.ssh/id_ed25519_work
write              ~/.config/ghprofile/gitconfig-work
write              ~/.ssh/config
write              ~/.gitconfig
```

This is why one account at a time is worth it. Each run opens the browser for a single account, so you only have to make sure you are signed in to that one account. If you add both and apply once, ghprofile walks through them back to back, and it's easy to paste the second key while the browser is still signed in to the first account.

Repeat this step for any further accounts. To see everything you've declared:

```bash
ghprofile show
```

```plaintext
NAME      ALIAS            HOST        IDENTITY         DIRECTORIES  DEFAULT
personal  github-personal  github.com  you@gmail.com    -            yes
work      github-work      github.com  you@company.com  ~/work/
```

## Step 4: Clone with the right account

For your other accounts, use the alias in place of `github.com`:

```bash
git clone git@github-work:company/project.git
```

Clone work repositories inside `~/work/` so they also pick up the work email. To confirm, run this inside the repo:

```bash
git config user.email
```

It should print `you@company.com`.

Use the alias for your default account too, `git@github-personal:`. A plain `git@github.com:` URL is not reliable unless you set up the option in the next section.

## Existing repositories

A plain `git@github.com:` URL doesn't match any alias, so SSH doesn't know which of your keys to use and offers whatever it can find, which is often the wrong account's key or none at all. The result depends on your machine:

*   on macOS it often works, because the system keeps your keys loaded in the SSH agent. It can still pick the wrong account, or stop working after a restart.
    
*   on Windows and Linux it usually fails with `Permission denied (publickey)`, because nothing is loaded and ghprofile's keys don't use the file names SSH looks for by default.
    

You have two ways to deal with this.

**Option 1: keep** `github.com` **for your default account.** Tell ghprofile that plain `github.com` URLs belong to your default account:

```bash
ghprofile default personal -claim-host
```

```bash
ghprofile apply
```

Plain `git@github.com:` URLs then always use your default account's key, on every system, and repositories you already have for that account keep working as they are. The command explains the trade-off and asks before changing anything: plain URLs can then only reach your default account, so your other accounts must use their alias. `ghprofile default personal -no-claim-host` reverses it.

**Option 2: move everything to aliases.** `fix-remote` rewrites existing repositories to the right alias, based on which folder they are in.

Either way, existing repositories for your other accounts need their alias, and `fix-remote` is how you move them.

Preview:

```bash
ghprofile fix-remote -all
```

Apply:

```bash
ghprofile fix-remote -all -write
```

## Already have a key for one of the accounts?

Run `ghprofile keys`, which connects to GitHub with each key in `~/.ssh` and reports the account it reaches. This matters if you already have a key for one of your accounts, because you don't need to replace a key that already works.

```bash
ghprofile keys
```

```plaintext
KEY                       HOST        ACCOUNT             PROFILE
~/.ssh/id_ed25519         github.com  your-personal-user  -
~/.ssh/old_deploy_key     github.com  no access           -
```

A key's file name and comment don't tell you this, since they are whatever was typed when the key was made. Once you know which key belongs to which account, point the profile at it:

```bash
ghprofile add personal --email you@gmail.com --default -key ~/.ssh/id_ed25519
```

`apply` then uses that key and skips generating a new one.

## Checking on it later

Run `ghprofile verify`, which connects to GitHub with each profile and reports the account it reaches. Two other commands help too:

| Command | What it does |
| --- | --- |
| `ghprofile verify` | Connects to GitHub and reports which account each profile reaches |
| `ghprofile check` | Looks for problems in your files, without going online |
| `ghprofile plan` | Shows anything that has drifted from your profiles |

`ghprofile apply` is safe to run again at any time. When nothing has changed, it changes nothing.

## What it writes

ghprofile changes three things: your `~/.ssh/config`, your `~/.gitconfig`, and one small identity file per account in `~/.config/ghprofile/`. It also creates a key in `~/.ssh` for any account that doesn't have one.

Paths below are shortened to `~`. In the real files they are written out in full.

In `~/.ssh/config`, one block per account:

```plaintext
# BEGIN ghprofile:work
Host github-work
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_work
  IdentitiesOnly yes
  AddKeysToAgent yes
  UseKeychain yes
# END ghprofile:work
```

`IdentitiesOnly yes` is what keeps the accounts apart. Without it, SSH offers every key it has, and GitHub accepts the first one that matches any account.

In `~/.gitconfig`, an include for the default identity and a conditional include for each folder:

```plaintext
[includeIf "gitdir:~/work/"]
	path = ~/.config/ghprofile/gitconfig-work
```

Everything is inside `# BEGIN ghprofile` / `# END ghprofile` markers. ghprofile only ever changes what is inside its own markers, so you can keep editing the rest of these files as usual.

## Undoing it

Run `ghprofile remove <name>`, then `ghprofile apply`. This removes that account's config and leaves its key in place:

```bash
ghprofile remove work
```

```bash
ghprofile apply
```

Every file ghprofile changed also has a timestamped backup next to it, named `<file>.ghprofile-backup-<time>`.

## FAQ

### Can I use two GitHub accounts on one computer?

Yes. Each account needs its own SSH key, because GitHub won't let the same key be added to two accounts. An SSH host alias picks the key, and git's `includeIf` picks the commit email based on the folder.

### Why are my commits showing the wrong email?

Git uses one global `user.email` unless something switches it per folder. Run `git config user.email` inside the repository to see which email it uses, and give the account a folder with `ghprofile add <name> --dir <folder>`. Commits you already made keep their old email.

### Does this work on Windows?

Yes. ghprofile runs on Windows with Git for Windows installed, as well as on macOS and Linux. On Windows a plain `git@github.com:` URL fails unless you set up `-claim-host`, as explained [above](#existing-repositories).

### Do I need to replace my existing SSH key?

No. Run `ghprofile keys` to see which account your key reaches, then point the profile at it with `-key`. ghprofile only generates a key when the profile's key file doesn't exist.

## Wrapping up

That's the full setup: add an account, apply, repeat. The source is on [GitHub](https://github.com/Tessie475/ghprofile). Issues and pull requests are welcome.
