When using multiple GitHub accounts, the process of selecting the correct Personal Access Token (PAT) for git push or git pull over HTTPS can be a bit cumbersome.
Specifically, when dealing with repositories from multiple owners, such as:
https://github.com/aont/foo.git
https://github.com/another-user/bar.git
Git Credential Manager or gh auth git-credential might default to the currently active account, leading to errors like:
remote: Permission to owner/repo.git denied to other-user.
To address this, the owner portion of the remote URL is interpreted as the GitHub CLI account name, and a credential helper is embedded directly into the .gitconfig file. This helper retrieves the PAT using:
gh auth token --user OWNER
and provides it to Git.
This eliminates the need for external scripts.
Prerequisites
First, ensure that you are logged into your GitHub accounts using gh:
gh auth login
If you have multiple accounts registered, you can verify them with:
gh auth status
With this method, if a repository URL is:
https://github.com/aont/foo.git
the command:
gh auth token --user aont
will be executed.
Therefore, the basic assumption is:
repository owner = gh registered account name
.gitconfig
The configuration should be as follows:
[credential]
useHttpPath = true
[credential "https://github.com/"]
helper =
helper = "!f() { \
[ \"$1\" = get ] || exit 0; \
account=''; \
while IFS='=' read -r k v; do \
[ \"$k\" = path ] && account=${v%%/*}; \
done; \
[ -n \"$account\" ] || exit 0; \
token=$(gh auth token --user \"$account\") || exit 0; \
printf 'username=%s\\npassword=%s\\n' \"$account\" \"$token\"; \
}; f"
[credential "https://gist.github.com/"]
helper =
helper = "!f() { \
[ \"$1\" = get ] || exit 0; \
account=''; \
while IFS='=' read -r k v; do \
[ \"$k\" = path ] && account=${v%%/*}; \
done; \
[ -n \"$account\" ] || exit 0; \
token=$(gh auth token --user \"$account\") || exit 0; \
printf 'username=%s\\npassword=%s\\n' \"$account\" \"$token\"; \
}; f"
There are three key points:
credential.useHttpPath = true is Important
Typically, Git's credential helper receives only:
protocol=https
host=github.com
However, in this case, we need:
aont/foo.git
the path.
Therefore, set:
[credential]
useHttpPath = true
This causes the path to be passed to the credential helper's standard input, such as:
protocol=https
host=github.com
path=aont/foo.git
Use the Beginning of the Path as the Account
Inside the helper, the first element of the path is extracted using:
[ "$k" = path ] && account=${v%%/*}
For example, if:
path=aont/foo.git
then:
account=aont
After that, the corresponding PAT for that account is retrieved from the GitHub CLI using:
token=$(gh auth token --user "$account")
Therefore, when accessing:
https://github.com/aont/foo.git
the command:
gh auth token --user aont
is automatically executed.
Conversely, for:
https://github.com/another-user/bar.git
the command:
gh auth token --user another-user
is executed.
There is no need to run gh auth switch every time.
Reset Existing Credential Helpers with helper =
Another important point is:
helper =
On Git for Windows, for example, Git Credential Manager or gh auth git-credential might already be configured.
If another helper returns the credentials first, this helper might not be executed.
As a result, you might encounter an error like:
remote: Permission to foo/bar.git denied to wrong-account.
Therefore, use:
helper =
helper = "!f() { ... }; f"
The first empty helper = resets any previously configured credential helpers, and then only this helper is registered.
This is crucial when dealing with multiple GitHub accounts.
Use the Same Mechanism for Gists
The same credential helper can be configured for Gists.
However, the standard Gist clone URL is:
https://gist.github.com/GIST_ID.git
and the username cannot be determined from the URL.
Therefore, in this setup, the Gist remote URL is intentionally formatted as:
https://gist.github.com/USER/GIST_ID.git
For example, if:
https://gist.github.com/aont/0123456789abcdef.git
then, from the credential helper's perspective:
path=aont/0123456789abcdef.git
so, the command:
gh auth token --user aont
is automatically used.
This allows you to treat GitHub repositories and Gists with the same rules.
github.com/USER/REPO
gist.github.com/USER/GIST
↓
USER is extracted
↓
gh auth token --user USER
Verification
You can check which credentials Git is actually retrieving using git credential fill.
For example, run:
printf '%s\n' \
'protocol=https' \
'host=github.com' \
'path=aont/foo.git' \
'' |
git credential fill
The expected output is:
protocol=https
host=github.com
username=aont
password=...
If the username is a different account, you can check if another credential helper is still present using:
git config --show-origin --get-all credential.helper
Why Not Use gh auth git-credential Directly?
The GitHub CLI has a standard command:
gh auth git-credential
This is sufficient for normal single-account usage.
However, when using multiple accounts with the same host (github.com), you might want to explicitly control which gh account is used based on the repository owner.
This helper uses a very simple rule:
remote URL
↓
owner is extracted
↓
gh auth token --user owner
so you don't need to be aware of which account is currently active in gh.
Advantages of This Configuration
This method does not save the PAT itself in the .gitconfig file.
The PAT is retrieved each time it is needed using:
gh auth token --user ACCOUNT
Therefore, the configuration only saves the rule for which account to use.
Additionally, you do not need to run:
gh auth switch
or set:
git config credential.username ...
for each repository.
The remote URL itself becomes the credential routing information.
Summary
When using multiple GitHub accounts over HTTPS, using the account portion of:
github.com/<account>/<repo>
directly for selecting the gh account can simplify operations.
The mechanism is:
Git remote URL
↓
credential.useHttpPath
↓
path=owner/repo.git
↓
owner is extracted
↓
gh auth token --user owner
↓
username/password is returned to Git
If you have multiple personal accounts and the relationship:
repository owner = GitHub account
holds true, this method is quite easy to use.
For organization-owned repositories where:
owner != account used for authentication
you will need separate mappings, but this configuration is sufficient for primarily personal accounts.
GitHub複数アカウント運用で、repo ownerから自動的にghのPATを選ぶ
GitHubで複数アカウントを使っていると、HTTPSでのgit pushやgit pull時に「どのアカウントのPATを使うか」が地味に面倒です。
特に、
https://github.com/aont/foo.git
https://github.com/another-user/bar.git
のように複数ownerのrepositoryを扱っていると、Git Credential Managerやgh auth git-credentialが現在のアクティブアカウントを使ってしまい、
remote: Permission to owner/repo.git denied to other-user.
のようなエラーになることがあります。
そこで、remote URLのowner部分をそのままGitHub CLIのアカウント名として解釈し、
gh auth token --user OWNER
からPATを取り出してGitに返すcredential helperを.gitconfigへ直接埋め込みます。
外部スクリプトも不要です。
前提
まず、利用するGitHubアカウントはあらかじめghにログインしておきます。
gh auth login
複数アカウントを登録している場合は、
gh auth status
で確認できます。
この方法では、たとえばrepository URLが
https://github.com/aont/foo.git
なら、
gh auth token --user aont
を実行します。
したがって、基本的には
repository owner = ghに登録したaccount名
という運用を前提にします。
.gitconfig
設定は次のようにします。
[credential]
useHttpPath = true
[credential "https://github.com/"]
helper =
helper = "!f() { \
[ \"$1\" = get ] || exit 0; \
account=''; \
while IFS='=' read -r k v; do \
[ \"$k\" = path ] && account=${v%%/*}; \
done; \
[ -n \"$account\" ] || exit 0; \
token=$(gh auth token --user \"$account\") || exit 0; \
printf 'username=%s\\npassword=%s\\n' \"$account\" \"$token\"; \
}; f"
[credential "https://gist.github.com/"]
helper =
helper = "!f() { \
[ \"$1\" = get ] || exit 0; \
account=''; \
while IFS='=' read -r k v; do \
[ \"$k\" = path ] && account=${v%%/*}; \
done; \
[ -n \"$account\" ] || exit 0; \
token=$(gh auth token --user \"$account\") || exit 0; \
printf 'username=%s\\npassword=%s\\n' \"$account\" \"$token\"; \
}; f"
ポイントは3つあります。
credential.useHttpPath = true が重要
通常、Gitのcredential helperには
protocol=https
host=github.com
程度しか渡されません。
しかし今回必要なのは、
aont/foo.git
というpathです。
そこで、
[credential]
useHttpPath = true
を設定します。
これによってcredential helperの標準入力に、
protocol=https
host=github.com
path=aont/foo.git
のようにpathも渡されます。
pathの先頭をaccountとして使う
helper内では、
[ "$k" = path ] && account=${v%%/*}
として、pathの最初の要素を取得しています。
たとえば、
path=aont/foo.git
なら、
account=aont
になります。
その後、
token=$(gh auth token --user "$account")
で、そのアカウントに対応するPATをGitHub CLIから取得します。
つまり、
https://github.com/aont/foo.git
へのアクセス時は自動的に、
gh auth token --user aont
相当になります。
一方、
https://github.com/another-user/bar.git
なら、
gh auth token --user another-user
になります。
gh auth switchを毎回実行する必要はありません。
helper = で既存credential helperをリセットする
もう一つ重要なのが、
helper =
です。
Git for Windowsなどでは、すでにGit Credential Managerやgh auth git-credentialなどが設定されている場合があります。
たとえば別のhelperが先にcredentialを返すと、今回のhelperまで処理が回ってきません。
その結果、
remote: Permission to foo/bar.git denied to wrong-account.
のようになります。
そこで、
helper =
helper = "!f() { ... }; f"
とします。
最初の空のhelper =で、それ以前に設定されていたcredential helperをリセットし、その後に今回のhelperだけを登録しています。
複数GitHubアカウントを扱う場合には、この部分がかなり重要です。
Gistにも同じ仕組みを使う
Gistにも同じcredential helperを設定できます。
ただし通常のGist clone URLは、
https://gist.github.com/GIST_ID.git
となっていて、URLからユーザー名を判断できません。
そこで、この運用ではGistのremote URLを意図的に、
https://gist.github.com/USER/GIST_ID.git
という形式にします。
たとえば、
https://gist.github.com/aont/0123456789abcdef.git
なら、credential helperから見ると、
path=aont/0123456789abcdef.git
となるので、
gh auth token --user aont
を自動的に使えます。
つまりGitHub repositoryとGistを同じルールで扱えます。
github.com/USER/REPO
gist.github.com/USER/GIST
↓
USERを抽出
↓
gh auth token --user USER
動作確認
Gitが実際にどのcredentialを取得するかは、git credential fillで確認できます。
たとえば、
printf '%s\n' \
'protocol=https' \
'host=github.com' \
'path=aont/foo.git' \
'' |
git credential fill
を実行します。
期待する出力は、
protocol=https
host=github.com
username=aont
password=...
です。
ここでusernameが別アカウントになっている場合は、
git config --show-origin --get-all credential.helper
で、別のcredential helperが残っていないか確認するとよいです。
なぜgh auth git-credentialをそのまま使わないのか
GitHub CLIには標準で、
gh auth git-credential
があります。
通常の単一アカウント運用ならこれで十分です。
ただ、複数アカウントを同一ホストgithub.comで使っている場合、「repository ownerに応じてどのgh accountを使うか」を明示的に制御したくなります。
今回のhelperでは、
remote URL
↓
ownerを抽出
↓
gh auth token --user owner
という非常に単純な規則にしているため、現在どのアカウントがghでactiveになっているかを意識する必要がありません。
この構成の利点
この方法だとPATそのものを.gitconfigに保存しません。
PATは必要になるたびに、
gh auth token --user ACCOUNT
から取得します。
そのため、設定として保存されるのは「どのアカウントを使うか」というルールだけです。
また、repositoryごとに、
gh auth switch
したり、
git config credential.username ...
を設定したりする必要もありません。
remote URLそのものがcredential routingの情報になります。
まとめ
複数GitHubアカウントをHTTPSで使う場合、
github.com/<account>/<repo>
のaccount部分をそのままghのaccount選択に使うと、かなりシンプルに運用できます。
仕組みとしては、
Git remote URL
↓
credential.useHttpPath
↓
path=owner/repo.git
↓
ownerを抽出
↓
gh auth token --user owner
↓
username/passwordとしてGitへ返す
だけです。
個人アカウントを複数使っていて、
repository owner = GitHub account
という関係が成立している環境なら、かなり扱いやすい方法だと思います。
Organization配下のrepositoryなどで、
owner != 認証に使うaccount
となる場合だけは別途マッピングが必要ですが、個人アカウント中心ならまずこの構成で十分です。
This article was originally published by DEV Community and written by vast cow.
Read original article on DEV Community