背景
複数のプロジェクトを切り替えて作業していると、会社のリポジトリで個人メールアドレスを使ってコミットしてしまったり、逆に個人リポジトリで会社のアカウントを使ってしまったりすることがあります。
先日もミスしてリモートにプッシュしてしまいました。再発を防ぐために設定を見直しました。
修正方法:プッシュ済みのコミット情報を直す
直近のコミットだけを修正する場合
直近 1 件のコミットだけであれば、git commit --amend で修正できます。
1GIT_COMMITTER_NAME="正しいユーザー名" GIT_COMMITTER_EMAIL="正しいメールアドレス" git commit --amend --author="正しいユーザー名 <正しいメールアドレス>" --no-edit修正後、リモートに強制プッシュします。
1git push --force-with-lease過去の複数コミットをまとめて修正する場合
1. 公式推奨ツール(git-filter-repo)を使う方法
Git 公式では従来の git filter-branch が非推奨となり、高速で安全な git-filter-repo の使用が推奨されています。
Homebrew などでインストールします。
1brew install git-filter-repoリポジトリのルートで以下を実行し、旧アドレスを新アドレスに置き換えます。
1git filter-repo --email-callback '
2return b"正しいメールアドレス@example.com" if email == b"誤った旧メールアドレス@example.com" else email
3' --name-callback '
4return b"正しいユーザー名" if email == b"誤った旧メールアドレス@example.com" else name
5' --force実行後、強制プッシュします。
1git push --force-with-lease2. インタラクティブに修正する方法(git rebase -i)
直近数件のコミットを手動で選びたい場合は、git rebase -i を使います。
1git rebase -i HEAD~5エディタが開いたら、修正したいコミットの pick を edit(または e)に変更して保存します。該当のコミットで停止したら、以下を実行して進めます。
1git commit --amend --author="正しいユーザー名 <正しいメールアドレス>" --no-edit
2git rebase --continueすべて完了したら git push --force-with-lease で反映します。
条件付きインクルード(includeIf)による Git ユーザー情報の自動切り替え
毎回手動で注意するのは限界があるため、ディレクトリ配下ごとに Git ユーザー情報を自動で選択するように設定します。
Git 2.13 以降で利用できる includeIf 機能を使います。
設定手順
たとえば、以下のようなディレクトリ構成を想定します。
- 仕事用プロジェクト:
~/dev/work/ - 個人用プロジェクト:
~/dev/personal/
1. 個別の設定ファイルを作成する
仕事用(~/.gitconfig-work):
1[user]
2 name = 会社でのユーザー名
3 email = 会社でのメールアドレス@work.com個人用(~/.gitconfig-personal):
1[user]
2 name = 個人のユーザー名
3 email = 個人のメールアドレス@personal.com2. 全体設定(~/.gitconfig)に includeIf を追加する
~/.gitconfig の末尾に以下を追記します。
1[includeIf "gitdir:~/dev/work/"]
2 path = ~/.gitconfig-work
3
4[includeIf "gitdir:~/dev/personal/"]
5 path = ~/.gitconfig-personalパスの末尾にスラッシュ / を忘れないように注意してください。
SSH 鍵の管理と双方向リダイレクト設定
SSH 鍵も仕事用と個人用で分ける場合、~/.ssh/config にホスト別名(Host)を定義します。
1Host github.com-work
2 HostName github.com
3 User git
4 IdentityFile ~/.ssh/id_ed25519_work
5 IdentitiesOnly yes
6
7Host github.com-personal
8 HostName github.com
9 User git
10 IdentityFile ~/.ssh/id_ed25519_personal
11 IdentitiesOnly yes次に、それぞれの Git 設定ファイルに insteadOf ルールを追加します。
仕事用(~/.gitconfig-work):
1[user]
2 name = 会社でのユーザー名
3 email = 会社でのメールアドレス@work.com
4
5[url "[email protected]:"]
6 insteadOf = [email protected]:個人用(~/.gitconfig-personal):
1[user]
2 name = 個人のユーザー名
3 email = 個人のメールアドレス@personal.com
4
5[url "[email protected]:"]
6 insteadOf = [email protected]:これで、git clone [email protected]:org/repo.git と標準の URL を指定しても、カレントディレクトリに応じて自動的に github.com-work や github.com-personal に読み替えられ、正しい SSH 鍵が使われます。
課題:GitHub CLI (gh) と AI エージェントでのアカウント混同
Git のコミット名や SSH 鍵の自動切り替えはこれで解決しました。しかし、GitHub CLI (gh) を使って PR を作成したり、Claude Code や Devin などの AI エージェントに操作させたりする際に、新たな問題が起きました。
個人プロジェクトのディレクトリで AI エージェントに PR 作成を指示したところ、gh コマンドが会社のデフォルトアカウントのまま実行され、会社のアカウントで PR が作成されてしまいました。
なぜ gh は includeIf を引き継げないのか
Git の includeIf は git コマンド自体の設定です。一方、gh は独立した CLI ツールであり、特定のホスト(github.com)に対して 1 つの Active アカウントしか保持できません。
gh auth status を実行すると、常に 1 つのアカウントだけが Active account: true になっています。カレントディレクトリを判断してアカウントを自動切替する機能は gh 自体には用意されていません。
プロンプト(指示文)で制御しようとした場合の課題
当初、AI エージェントの設定ファイル(CLAUDE.md や AGENTS.md)に「gh を実行する際は GH_TOKEN="$(gh auth token --user <user>)" gh ... と環境変数を付与すること」という指示を書きました。
しかし、実際に運用してみると以下の問題がありました。
- 人間の手動実行に対応できない:ターミナルで自分が手動で
gh pr createを実行する際にはプロンプト指示が機能しません。 - コンテキストの浪費:すべてのツール実行指示に環境変数付与のルールを含めるため、Context Window のトークンを無駄に消費します。
- 複数ツールの管理コスト:Claude Code、Devin、Codex、Cursor など、利用する AI ツールごとに設定ファイルを用意して同じルールを書き込む必要があり、メンテナンスが煩雑になります。
解決策:Shell Wrapper による自動インターセプト
プロンプトで制御するのではなく、Shell の環境変数レイヤーで透明に処理する方針に切り替えました。
1. ラッパースクリプト(~/bin/gh)の作成
PATH の優先順位が高いディレクトリ(例:~/bin/gh)に、gh と同名のスクリプトを作成します。カレントディレクトリ($PWD)を判定し、適切な GH_TOKEN をセットして本来の gh 二进制ファイルを呼び出します。
1#!/bin/bash
2# カレントディレクトリに応じて GitHub アカウントの Token を自動選択する
3
4# Homebrew でインストールされた本物の gh の絶対パス
5REAL_GH="/opt/homebrew/bin/gh"
6
7case "$PWD/" in
8 *"$HOME/dev/work/"*)
9 TARGET_USER="work-user"
10 ;;
11 *"$HOME/dev/personal/"*)
12 TARGET_USER="personal-user"
13 ;;
14 *)
15 TARGET_USER=""
16 ;;
17esac
18
19if [ -n "$TARGET_USER" ]; then
20 TOKEN="$("$REAL_GH" auth token --user "$TARGET_USER" 2>/dev/null)"
21 if [ -n "$TOKEN" ]; then
22 export GH_TOKEN="$TOKEN"
23 fi
24fi
25
26exec "$REAL_GH" "$@"実行権限を付与します。
1chmod +x ~/bin/gh2. 注意点:Subshell と Zsh の読み込み順序
ラッパースクリプトを設置する際、Zsh の設定ファイル読み込み順序に注意が必要です。
非対話型 Subshell に対応するため ~/.zshenv に設定する
Claude Code や Devin などの AI エージェントは、バックグラウンドのサブプロセス(Subshell)でコマンドを実行します。
Zsh の挙動は以下のとおりです。
~/.zshrc:対話型シェル(ターミナル画面)でのみ読み込まれる。~/.zshenv:対話型・非対話型を問わず、すべての Zsh 起動時に読み込まれる。
~/.zshrc にのみ PATH を設定した場合、手動のターミナル操作ではスクリプトが呼ばれますが、AI エージェントのサブプロセス実行時には通過せず、システム標準の /opt/homebrew/bin/gh がそのまま使われてしまいます。
そのため、~/.zshenv 内で ~/bin を PATH の先頭に設定します。
1# ~/.zshenv で PATH の先頭に ~/bin を指定
2export PATH="$HOME/bin:$HOME/.local/bin:$PATH"Homebrew の brew shellenv による PATH 上書きを防ぐ
~/.zshrc 内で eval "$(/opt/homebrew/bin/brew shellenv)" を呼び出している場合、Homebrew によって /opt/homebrew/bin が PATH の先頭に再挿入されることがあります。
brew shellenv の実行直後に、再度 ~/bin を PATH の先頭に配置します。
1# ~/.zshrc
2eval "$(/opt/homebrew/bin/brew shellenv)"
3
4# ~/bin を再優先化
5export PATH="$HOME/bin:$HOME/.local/bin:$PATH"動作検証
設定後、各環境での挙動を確認します。
-
個人プロジェクトディレクトリでの確認:
1cd ~/dev/personal/my-repo 2gh api user --jq .login 3# 実行結果: personal-user -
仕事用プロジェクトディレクトリでの確認:
1cd ~/dev/work/company-repo 2gh api user --jq .login 3# 実行結果: work-user -
AI エージェントのサブプロセス模擬確認:
1zsh -c "cd ~/dev/personal/my-repo && gh api user --jq .login" 2# 実行結果: personal-user
手動でのターミナル操作でも、AI エージェントによるバックグラウンド実行でも、ディレクトリに応じたアカウント切り替えが自動で行われるようになりました。
まとめ
- コミット履歴の修正:
git commit --amendやgit-filter-repoを利用。 - Git コミット名・SSH 鍵:
~/.gitconfigのincludeIfとinsteadOfによる自動切替。 - GitHub CLI (gh)・AI エージェント:
~/bin/ghラッパースクリプトと~/.zshenvのPATH設定による自動切替。
プロジェクトを適切なディレクトリに配置しておけば、アカウントの取り違えを意識することなく作業を進められます。
CheerChen です。