GitHubの複数アカウントの使い分け|ディレクトリで自動に分ける

github 複数 アカウント 使い分けで検索した人がつまずくのは、アカウントを2つ作る手順ではない。作ったあとに、いま手元のターミナルがどちらのアカウントとして振る舞っているのかが分からなくなるところで止まる。会社のリポジトリに個人の名前でコミットが載っていた、というかたちで後から気づく。ここでは、切り替えを人の記憶に任せない形に寄せる。境界の決め方、ディレクトリ単位で自動的に切り替える設定、リモートのURLの分け方、そして取り違えを後から見つける手順までを順に並べる。

手で切り替える設計をやめる、という前提

複数アカウントの記事は「切り替えコマンド」の紹介で終わりがちだが、実務で効くのは逆の考え方になる。切り替える操作を増やすほど、忘れる機会が増える。

前提として、切り替わる場所は3つに分かれている。ブラウザで見ているGitHubの画面、手元のGitがコミットに書き込む名前とメールアドレス、そしてリモートに押すときの認証だ。この3つは互いに独立していて、片方だけ切り替えた状態が普通に成立する。画面では会社のアカウントを開いているのに、コミットの著者は個人のメールアドレスのまま、という組み合わせが起きるのはこのためになる。

だから、使い分けの設計で最初に決めるのは操作ではなく置き場所になる。どのディレクトリにあるリポジトリはどちらのアカウントとして扱うのか、という境界を1本引く。境界をディレクトリで引けば、Gitは自分がどこにいるかを知っているので、人が覚えておく必要がなくなる。

実際の置き方は2つの親ディレクトリを作るだけで足りる。仕事のリポジトリを置く場所と、個人のリポジトリを置く場所を分ける。クローンする前にどちらに置くかを決める、という手順がそのまま境界の維持になる。ここが曖昧なまま設定を足しても、設定が効かないリポジトリが混ざって、かえって見分けがつかなくなる。

境界をどこに引くかは、リポジトリの数ではなく所有者で決めるほうが崩れにくい。会社の組織に属するリポジトリと、個人のアカウントに属するリポジトリは、フォークの関係があっても所有者が違う。所有者で分けておけば、後から参加する組織が増えても親ディレクトリを1つ足すだけで済む。逆に「案件ごと」「言語ごと」で分けると、同じ案件に両方のアカウントが関わった時点で境界が消える。

詰まるのはGitHubの画面ではなく手元のGit

GitHub側の切り替えは、公式に用意されている。アカウントスイッチャーに複数のアカウントを追加しておけば、画面の右上から行き先を変えられる。追加したセッションはそのパソコンとブラウザに残る形なので、別のパソコンで同じことをするには、そちらでも追加する操作が必要になる。外部のリンクからGitHubに入ったときは、どのアカウントで開くかを先に尋ねられる。

一方、手元のGitはこの切り替えを一切知らない。コミットに載る名前とメールアドレスは、Gitの設定ファイルから読まれる。グローバルの設定だけを置いている状態だと、どのリポジトリでも同じ名前が使われる。会社のリポジトリに個人のメールアドレスが残るのは、この一点が原因になる。

さらに、押すときの認証も別勘定になる。SSHで押すなら、どの鍵が使われるかはSSHの設定で決まる。HTTPSで押すなら、保存されている認証情報が使われる。GitHubのドキュメントは、複数アカウントを1台で扱う場合の手段として、HTTPSなら認証情報をリポジトリのURL単位で分けること、SSHなら鍵を分けてホストの別名を作ることを挙げている。

つまり、直す対象は3つある。名前とメールアドレス、鍵か認証情報、そしてリモートのURLだ。このうち最初の2つはディレクトリ単位で自動化でき、3つ目はクローンの時点で決まる。

ディレクトリ単位で自動的に切り替える

Gitには、設定ファイルを条件付きで読み込む仕組みがある。includeIf という指定で、条件に合ったときだけ別の設定ファイルを取り込める。

条件に使えるキーワードは複数あるが、使い分けで使うのは gitdir になる。公式のドキュメントは、この条件がリポジトリの .git ディレクトリの位置とパターンの一致で判定されると説明している。パターンの末尾を / で終えると ** が補われ、そのディレクトリとその中身のすべてに一致する。つまり、仕事用の親ディレクトリを1つ指定すれば、その下に何個リポジトリを作っても条件が効く。

書き方は、グローバルの設定に条件付きの取り込みを2行足して、取り込まれる側の小さなファイルに名前とメールアドレスを書くだけになる。

; ~/.gitconfig
[includeIf "gitdir:~/work/"]
  path = ~/.gitconfig-work
[includeIf "gitdir:~/personal/"]
  path = ~/.gitconfig-personal
; ~/.gitconfig-work
[user]
  name = Your Name
  email = [email protected]

この形にすると、~/work/ の下に入った瞬間に会社の名前とメールアドレスが有効になり、~/personal/ に移れば個人のものに戻る。切り替えのコマンドは要らない。効いているかどうかは、リポジトリの中で git config user.email を実行して返ってくる値で確かめられる。

ほかに onbranch でブランチ名から判定する形と、hasconfig:remote.*.url でリモートのURLから判定する形もある。後者は、置き場所を分けずにリモートの向き先で分けたい場合に使える。ただし、判定のために設定ファイル全体が先に走査されるため、取り込まれる側のファイルにリモートのURLを書くことは許されていない。

この設定を入れる前に、グローバルの設定から名前とメールアドレスを抜いておくと安全度が上がる。グローバルに既定値が残っていると、境界の外にあるリポジトリで黙ってその値が使われる。抜いておけば、境界の外でコミットしようとした時点でGitが名前を求めてくるため、置き場所を間違えたことがその場で分かる。設定を足すより、既定値を消すほうが効く場面になる。

リモートのURLをホスト名で分ける

名前とメールアドレスが直っても、押す先の認証が混ざっていると失敗する。ここで起きる混乱を、複数アカウントの解説記事は次のように書いている。

しかし、複数アカウントを使用している場合に[email protected]としてしまうと、「どっちの GitHub アカウントですか?」となってしまいます。 出典: zenn.dev

解き方は、SSHの設定でホストの別名を作ることになる。~/.ssh/config に、実体は github.com を向きながら名前だけ違うホストを用意して、それぞれに別の鍵を割り当てる。

Host github-work
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_work
  IdentitiesOnly yes

このあとは、クローンするときのURLを git@github-work:owner/repo.git の形にするだけで、どの鍵が使われるかが確定する。IdentitiesOnly yes を付けておくのは、付けないとSSHが手元の鍵を順番に試してしまい、意図しない鍵で認証が通る場合があるためになる。

クローンし直さずに既存のリポジトリを移したい場合は、git remote set-url origin git@github-work:owner/repo.git でURLを差し替えるだけで足りる。作業中の変更やブランチはそのまま残る。差し替えたあとに一度だけ取得を試して、認証が通ることを確かめておく。

1回だけ別の鍵で操作したいなら、設定を足さずに GIT_SSH_COMMAND に鍵のパスを渡す形でも通る。GitHubのドキュメントもこの手を挙げている。既存のリモートURLを書き換えたくない場合は、URLの置き換え設定を使って、特定のオーナー宛てだけ別名のホストに向ける方法もある。

HTTPSで押すなら認証情報をパス単位にする

SSHを使わず、個人アクセストークンでHTTPS経由で押している場合は、別の設定が要る。既定では、認証情報はホスト名の単位で保存される。github.comに対して1つのトークンが記憶されるので、2つのアカウントを行き来すると片方が上書きされる形になる。

GitHubのドキュメントが挙げている対処は、認証情報をURLのパスまで含めて保存させる指定になる。

git config --global credential.https://github.com.useHttpPath true

これを入れると、リポジトリのURLごとに別のトークンを記憶できる。初回だけ、それぞれのリポジトリでトークンを聞かれる形になる。

トークンを使う場合の注意が1つある。トークンには有効期限と権限の範囲があり、片方のアカウントのトークンが切れたときに、もう片方のトークンが代わりに使われて拒否される形になりやすい。拒否の理由は「そのリポジトリが見つからない」という表示で返ってくることがあるため、権限の問題だと気づきにくい。アカウントごとに期限をそろえておくと、切れた時期から原因を絞れる。

なお、GitHub CLI は認証をGitとは別に持っている。ターミナルでの操作をCLI経由で行っているなら、gh auth switch で使うアカウントを切り替える。Gitの設定を直してもCLIの認証は変わらないため、この層も別に確かめる必要がある。

取り違えを後から見つける手順

設定を足した直後は正しく動いていても、新しいリポジトリを境界の外にクローンした瞬間に崩れる。壊れたことに気づく仕組みを先に用意しておく。

確かめる順番は決まっている。

  • リポジトリの中で git config user.email を実行し、返ってくるアドレスが想定どおりか見る
  • git log --format='%an <%ae>' -5 で直近のコミットの著者を並べ、混ざっていないか見る
  • git remote -v でリモートのURLを見て、ホスト名が別名になっているか見る
  • ssh -T git@github-work のように別名でつないで、どのアカウントとして認識されるか見る

すでに押してしまった分の著者は、履歴を書き換えないと直らない。共同で使っているブランチでは書き換えを避け、以降のコミットから正しくする判断のほうが安全になる。押す前に気づけるようにするには、コミット前のフックで user.email が想定のドメインかを見て、違えば止める形が手早い。

二重になっているのはGitHubだけではない

ここまでの設定で、ターミナル側の使い分けは人の記憶から外れる。それでも切り替えの感覚が残るなら、原因はGitHubの外にある。

GitHubを2つ持っている人は、たいていSlackのワークスペースも2つあり、Notionのログインも2つあり、Googleのアカウントも2つある。ブラウザのプロファイルは1つにつき1サービス1セッションしか保持できないので、片方を開くと片方が落ちる。Gitの設定をいくら整えても、この層は動かない。

この層を扱う道具は、Webアプリごとに窓とセッションの置き場所を分ける作りになっている。アプリごとに独立したワークスペースを持たせることで、同じサービスの2つのアカウントを同時に開いたままにできる。何ができるかはできることにまとまっていて、仕事の単位で窓をまとめる考え方はワークスペースの説明が短い。

GitHubを扱う人に関係が深いのは、ブラウザの中にターミナルが入っている点になる。リポジトリの画面を見ながら手元のコマンドを打てるので、窓を行き来する回数がそのまま減る。詳しくはターミナルにある。対応しているサービスの一覧は使えるアプリで確かめられる。

順番として、まずGitの設定をディレクトリ単位に直す。それで残った切り替えだけが、窓の側の課題になる。

よくある質問

仕事用と個人用で、アカウントは2つ作るべきですか?

置き場所と権限を分けたい場合は2つに分ける形が扱いやすい。ただしGitHubは1つのアカウントに複数のメールアドレスを紐づけられるため、組織への招待だけで足りるなら1つに寄せる判断もある。2つにするなら、リポジトリを置くディレクトリを先に分けておくと設定が単純になる。

コミットの著者を間違えたまま押してしまいました。直せますか?

自分だけが使っているブランチなら履歴を書き換えて直せる。共同で使っているブランチでは、書き換えると他の人の手元と食い違うため避けたほうが安全になる。以降のコミットから正しい設定にして、必要なら該当のコミットに補足を残す形が現実的になる。

ディレクトリごとの設定が効いているか、どう確かめますか?

そのリポジトリの中で git config user.email を実行し、返ってくるアドレスを見る。想定と違う場合は、includeIf のパターンとリポジトリの実際のパスが一致していないことが多い。パターンの末尾に / を付けると、そのディレクトリ以下のすべてに効く形になる。

同じSSHの鍵を2つのアカウントで使い回せますか?

GitHubは同じ公開鍵を複数のアカウントに登録できない作りになっている。アカウントごとに鍵を作り、SSHの設定でホストの別名と対にして割り当てる。別名を使ったURLでクローンしておけば、どの鍵で押すかがリポジトリごとに固定される。

記事一覧へ戻る