Devlog/2025-10-25/4 phút
v1

Sử dụng nhiều tài khoản Git trên cùng một máy.

Cách mình cấu hình SSH và Git config để làm việc với nhiều tài khoản GitHub mà không phải chuyển tay giữa các key và email.

#ssh#git
fig.01 — branches.svg~/.ssh/id_work~/.ssh/id_personal

§1Bối cảnh

Mình đang sử dụng khá nhiều tài khoản git, cả github và gitlab. Cá nhân mình cũng có nhiều profile, và cũng có profile riêng cho công việc ở công ty. Trong quá trình làm việc có một số rắc rối như sau:

  • Mỗi lần chuyển repo là phải remove ssh key để add lại cho đúng
  • Với các project mới cần phải thiết lập lại user.name và user.email cho đúng với tài khoản

Mình muốn mọi thứ tự động: repo nào thì dùng đúng key và đúng tên/email tương ứng, không cần nhớ hay đổi tay nữa.

§2Giải pháp

1. Sử dụng SSH config: host match

SSH hỗ trợ config ssh key theo từng host, nếu chỉ có 2 tài khoản ở hai host khác nhau thì chỉ cần thiết lập host là đáp ứng được yêu cầu:

ini
Match Host github.com
  IdentityFile ~/.ssh/id_rsa_github
  IdentitiesOnly yes

Match Host gitlab.com
  IdentityFile ~/.ssh/id_rsa_gitlab
  IdentitiesOnly yes

2. Sử dụng SSH config: host alias

Trong trường hợp có nhiều tài khoản của cùng một host thì phương án trên không khả thi, mình cần sử dụng phương án mới: host alias.

ini
# Tài khoản cá nhân
Host github-personal
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_rsa_personal
  IdentitiesOnly yes

# Tài khoản công ty
Host github-work
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_rsa_work
  IdentitiesOnly yes

Sau đó mình sẽ clone git về với alias host github-personal, github-work thay vì github.com

bash
# Repo công ty
git clone git@github-work:company/project.git

# Repo cá nhân
git clone git@github-personal:me/my-repo.git

SSH sau đó sẽ tự động chọn ssh key dựa trên host mình đã đặt.

3. Sử dụng SSH config: custom match

Với phương án trên vấn đề được giải quyết ổn thoả, tuy nhiên dần dần mình cảm thấy phiền với việc phải đổi host cho remote url. Mình chuyển qua sử dụng phương án mới:

ini
Match Exec "[[ $(git config --get remote.origin.url) =~ .*[email protected]:my-username.* ]]"
  IdentityFile ~/.ssh/hailerity_rsa
  IdentitiesOnly yes

Trên đây sử dụng Match Exec để thực hiện câu lệnh check xem git remote url khớp với một tài khoản không, nếu khớp sẽ dùng ssh key tương ứng, không khớp sẽ bỏ qua.

Cách này rất linh hoạt, và không cần phải đổi remote url host. Tuy nhiên có một hạn chế với phương án này là không thể áp dụng cho câu lệnh git clone: lúc clone, repo chưa tồn tại nên git config --get remote.origin.url chưa trả về gì. Mục 5 bên dưới giải quyết hạn chế này.

4. Sử dụng Git config để tự chọn user/email

Sau nhiều lần commit project dùng sai user.name và user.email, mình tìm cách thiết lập để tránh sai lầm đó.

Git có hỗ trợ set config file theo điều kiện trong ~/.gitconfig:

ini
[user]
  name = Tram Dev
  email = [email protected]

[includeIf "gitdir:~/Workspaces/personal/**"]
  path = ~/Workspaces/personal/.gitconfig

[includeIf "gitdir:~/Workspaces/work/**"]
  path = ~/Workspaces/work/.gitconfig

Với config trên, các project ở trong ~/Workspaces/personal sẽ sử dụng file gitconfig ở trong thư mục đó, tương tự với ~/Workspaces/work. Như vậy mình chỉ cần viết .gitconfig cho từng thư mục và đặt project vào đúng thư mục là được.

Ví dụ ~/Workspaces/work/.gitconfig:

ini
[user]
  name = Tram Dev
  email = [email protected]

5. Gộp SSH key vào Git config theo thư mục

Mục 4 đã chọn đúng user/email theo thư mục. Git cũng có core.sshCommand để chỉ định lệnh SSH, nên mình đặt luôn SSH key vào cùng file .gitconfig của thư mục đó:

ini
# ~/Workspaces/work/.gitconfig
[user]
  name = Tram Dev
  email = [email protected]

[core]
  sshCommand = ssh -i ~/.ssh/id_ed25519_work -o IdentitiesOnly=yes
ini
# ~/Workspaces/personal/.gitconfig
[user]
  name = Tram Dev
  email = [email protected]

[core]
  sshCommand = ssh -i ~/.ssh/id_ed25519_personal -o IdentitiesOnly=yes

Bây giờ key và email cùng đi theo thư mục. Repo nằm trong ~/Workspaces/work dùng key công ty, repo trong ~/Workspaces/personal dùng key cá nhân, remote url vẫn giữ nguyên [email protected]:.... Cách này thay được cả mục 2 và mục 3.

Điều mình quan tâm nhất là git clone có dùng đúng key không. Với git 2.50, includeIf "gitdir:..." được áp dụng ngay trong lúc clone nếu thư mục đích nằm trong ~/Workspaces/work, nên lệnh clone dùng đúng core.sshCommand của thư mục đó:

bash
cd ~/Workspaces/work
git clone [email protected]:company/project.git

Để chắc chắn trên máy của bạn, bật GIT_TRACE khi clone. Git in ra lệnh SSH thực sự được chạy, trong đó có đường dẫn key:

bash
cd ~/Workspaces/work
GIT_TRACE=1 git clone [email protected]:company/project.git 2>&1 | grep run_command
# trace: run_command: ... ssh -i ~/.ssh/id_ed25519_work -o IdentitiesOnly=yes ... [email protected]

Hoặc kiểm tra repo đã clone đang nhận cấu hình nào:

bash
cd ~/Workspaces/work/project
git config --show-origin core.sshCommand

Nếu dùng phiên bản git cũ hơn mà clone không nhận cấu hình, có thể chỉ định key ngay trong lệnh clone. Option -c cũng được ghi vào config của repo mới, nên các lần pull/push sau vẫn dùng đúng key:

bash
git clone -c core.sshCommand="ssh -i ~/.ssh/id_ed25519_work -o IdentitiesOnly=yes" \
  [email protected]:company/project.git

Nếu tạo key mới, mình dùng Ed25519 thay cho RSA vì key ngắn hơn và được GitHub, GitLab hỗ trợ đầy đủ:

bash
ssh-keygen -t ed25519 -C "[email protected]" -f ~/.ssh/id_ed25519_work
Trạm Dev · 2025-10-25 · v1 · EOF
#ssh#git
$ ls ./devlog4 / 7