[!TIP|label: Multiple Accounts]
TYPE ACCOUNTS SSH KEY GITHUB REPOS RANGE work John Doe john@work.com
~/.ssh/work_ed25519
git@github.com:work/*
service account jenkins jenkins@work.com
~/.ssh/jenkins_ed25519
git@github.com:company/*
personal John Doe john@personal.com
~/.ssh/personal_ed25519
git@github.com:john/*
~/.gitconfig.d/accounts/<account>
is used to distinguish accounts and emails~/.gitconfig.d/.gitconfig
is used to distinguish the git remote URLs byincludeIf hasconfig:remote.*.url
~/.ssh/config.d/github
is used to distinguish the ssh key by host-alias
- to check git the git clone debug log via:
GIT_TRACE=true GIT_CURL_VERBOSE=true GIT_TRACE_PACK_ACCESS=true GIT_TRACE_SETUP=true GIT_TRACE_SHALLOW=true \ git clone <repo-url>
- check more details in * iMarslo - troubleshooting
with ssh
gitconfig
# ~/.gitconfig.d/accounts/work
[user]
name = John Doe
email = john@work.com
## -- work -- ##
[url "git@github-work.com:work/"]
insteadOf = git@github.com:work/
[includeIf "hasconfig:remote.*.url:*github.com?work/**"]
path = ~/.gitconfig.d/accounts/work
## -- service account -- ##
[url "git@github-jenkins.com:company/"]
insteadOf = git@github.com:company/
[includeIf "hasconfig:remote.*.url:*github.com?company/**"]
path = ~/.gitconfig.d/accounts/jenkins
## -- work -- ##
[url "git@github-personal.com:john/"]
insteadOf = git@github.com:john/
[includeIf "hasconfig:remote.*.url:*github.com?john/**"]
path = ~/.gitconfig.d/accounts/personal
~/.gitconfig.d/.gitconfig
## -- work -- ##
[url "git@github-work.com:work/"]
insteadOf = git@github.com:work/
[includeIf "hasconfig:remote.*.url:*github.com?work/**"]
path = ~/.gitconfig.d/accounts/work
## -- service account -- ##
[url "git@github-jenkins.com:company/"]
insteadOf = git@github.com:company/
[includeIf "hasconfig:remote.*.url:*github.com?company/**"]
path = ~/.gitconfig.d/accounts/jenkins
## -- work -- ##
[url "git@github-personal.com:john/"]
insteadOf = git@github.com:john/
[includeIf "hasconfig:remote.*.url:*github.com?john/**"]
path = ~/.gitconfig.d/accounts/personal
ssh config
~/.ssh/config.d/github
[!TIP] the HOST name alias should matches to the URL in gitconfig
HOST github-work.com HostName github.com User git IdentityFile ~/.ssh/work_ed25519 IdentitiesOnly yes HOST github-jenkins.com HostName github.com User git IdentityFile ~/.ssh/jenkins_ed25519 IdentitiesOnly yes HOST github-personal.com HostName github.com User git IdentityFile ~/.ssh/personal_ed25519 IdentitiesOnly yes
~/.ssh/config.d/github
HOST github-work.com HostName github.com User git IdentityFile ~/.ssh/work_ed25519 IdentitiesOnly yes HOST github-jenkins.com HostName github.com User git IdentityFile ~/.ssh/jenkins_ed25519 IdentitiesOnly yes HOST github-personal.com HostName github.com User git IdentityFile ~/.ssh/personal_ed25519 IdentitiesOnly yes
~/.ssh/config
[!TIP]
- put the include in the beginning of the
~/.ssh/config
file
- put the include in the beginning of the
with https
[!NOTE]
- prepare PAT for 3 accounts in Personal Access Tokens (classic) or Fine-grained personal access tokens
TOKEN TYPE | GIT OPERATIONS ( clone/push ) |
GIT LFS UPLOAD/DOWNLOAD | GITHUB ACTIONS (WORKFLOW) | semantic-release |
GITHUB API (REST/GRAPHQL) | PRIVATE SUBMODULES | GITHUB CLI (GH ) |
NOTES |
---|---|---|---|---|---|---|---|---|
Fine-grained PAT | ✓ | ✗ Fails (LFS lock verification denied) |
✓ | ✓ | ✓ | ✗ | ⚠️ Partially supported |
Secure and scoped, but limited in CLI/automation contexts like Git LFS |
Classic PAT | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Recommended for CLI tools, LFS, CI/CD, automation, and broad API access |
gitconfig
[credential "https://github.com/work"]
username = john@work.com
[credential "https://github.com/company"]
username = jenkins@work.com
[credential "https://github.com/john"]
username = john@personal.com
# OPTION - force using https for github
[url "https://github.com/"]
insteadOf = git@github.com:
[include]
path = ~/.gitconfig.d/.gitconfig
[credential]
helper = store --file ~/.gitconfig.d/.git-credentials
setup .git-credentials
$ echo -e "protocol=https\nhost=github.com\nusername=john@work.com\npassword=${PAT_WORK}\n" |
git credential-store --file ~/.gitconfig.d/.git-credentials store
$ echo -e "protocol=https\nhost=github.com\nusername=jenkins@work.com\npassword=${PAT_JENKINS}\n" |
git credential-store --file ~/.gitconfig.d/.git-credentials store
$ echo -e "protocol=https\nhost=github.com\nusername=john@personal.com\npassword=${PAT_PERSONAL}\n" |
git credential-store --file ~/.gitconfig.d/.git-credentials store
# to verify/check the credentials
# to check in macOS Keychain
$ printf "protocol=https\nhost=github.com\nusername=john@work.com\n" | git credential-osxkeychain get
# to check in git-credentials file
$ printf "protocol=https\nhost=github.com\nusername=john@work.com\n" | git credential-store --file ~/.gitconfig.d/.git-credentials get
git hook and alias
[!NOTE]
- setup the
~/.git-template
directory as the template directory for git
$ mkdir -p ~/.git-templates/hooks
$ git config --global init.templatedir '~/.git-templates'
$ touch ~/.git-templates/hooks/post-checkout ~/.git-templates/set-git-user.sh
$ chmod +x ~/.git-templates/hooks/post-checkout ~/.git-templates/set-git-user.sh
[!TIP]
~/.git-templates/set-git-user.sh
will be using for bothpost-checkout
hook and git alias
#!/usr/bin/env bash
#=============================================================================
# FileName : set-git-user.sh
# Author : marslo.jiao@gmail.com
# Created : 2025-06-30 21:54:24
# LastChange : 2025-07-01 00:16:22
#=============================================================================
set -euo pipefail
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || {
echo "fatal: not a git repository (or any of the parent directories): .git" >&2
exit 128
}
# shellcheck disable=SC2155
declare -r REMOTE_URL=$( git remote get-url origin 2>/dev/null || git config --get remote.origin.url 2>/dev/null )
[[ -z "${REMOTE_URL}" ]] && { echo "fatal: no remote repository configured for 'origin'" >&2; exit 0; }
declare username=''
declare email=''
case "${REMOTE_URL}" in
*github.com?work/* | *git@github-work.com:* )
username='John Doe'; email='john@work.com' ;;
*github.com?company/* | *git@github-jenkins.com:* )
username='jenkins' ; email='jenkins@work.com' ;;
*github.com?john/* | *git@github-personal.com:* )
username='John Doe'; email='john@personal.com' ;;
esac
# shellcheck disable=SC2015
[[ -n "${username}" && -n "${email}" ]] && {
git config user.name "${username}"
git config user.email "${email}"
printf "\033[0;2;3;37m>> git user set to: \033[0;3;33m%s \033[0;2;3;37m<\033[0;3;36m%s\033[0;2;3;37m>\033[0m\n" "${username}" "${email}"
} || {
printf "\033[0;2;3;37m>> git user not set, using default \033[0;3;33m%s \033[0;2;3;37m<\033[0;3;36m%s\033[0;2;3;37m>\033[0m\n" "$(git config user.name)" "$(git config user.email)"
}
# vim:tabstop=2:softtabstop=2:shiftwidth=2:expandtab:filetype=sh:
bash
#!/usr/bin/env bash
#=============================================================================
# FileName : set-git-user.sh
# Author : marslo.jiao@gmail.com
# Created : 2025-06-30 21:54:24
# LastChange : 2025-07-01 00:16:22
#=============================================================================
set -euo pipefail
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || {
echo "fatal: not a git repository (or any of the parent directories): .git" >&2
exit 128
}
# shellcheck disable=SC2155
declare -r REMOTE_URL=$( git remote get-url origin 2>/dev/null || git config --get remote.origin.url 2>/dev/null )
[[ -z "${REMOTE_URL}" ]] && { echo "fatal: no remote repository configured for 'origin'" >&2; exit 0; }
declare username=''
declare email=''
case "${REMOTE_URL}" in
*github.com?work/* | *git@github-work.com:* )
username='John Doe'; email='john@work.com' ;;
*github.com?company/* | *git@github-jenkins.com:* )
username='jenkins' ; email='jenkins@work.com' ;;
*github.com?john/* | *git@github-personal.com:* )
username='John Doe'; email='john@personal.com' ;;
esac
# shellcheck disable=SC2015
[[ -n "${username}" && -n "${email}" ]] && {
git config user.name "${username}"
git config user.email "${email}"
printf "\033[0;2;3;37m>> git user set to: \033[0;3;33m%s \033[0;2;3;37m<\033[0;3;36m%s\033[0;2;3;37m>\033[0m\n" "${username}" "${email}"
} || {
printf "\033[0;2;3;37m>> git user not set, using default \033[0;3;33m%s \033[0;2;3;37m<\033[0;3;36m%s\033[0;2;3;37m>\033[0m\n" "$(git config user.name)" "$(git config user.email)"
}
# vim:tabstop=2:softtabstop=2:shiftwidth=2:expandtab:filetype=sh:
git hook
[!TIP]
- the
post-checkout
hook will be triggered after agit checkout
command. it will set the git user name and email based on the remote URL automatically- verify by:
# clone a repo $ git clone git@github.com:work/repo.git # verify $ git config --local user.name $ git config --local user.email
#!/usr/bin/env bash
if [[ "$1" = "0000000000000000000000000000000000000000" || "$3" = '1' ]]; then
~/.git-templates/set-git-user.sh
fi
# vim:tabstop=2:softtabstop=2:shiftwidth=2:expandtab:filetype=sh:
git alias
$ git config --global alias.set-user '!~/.git-templates/set-git-user.sh'
# verify
$ cd /path/to/your/repo
$ git set-user