Problem

Managing multiple GitHub identities (personal, work, org accounts) requires:

The old setup used includeIf "gitdir:"= blocks in =~/.gitconfig, requiring a new block for each directory where repos lived.

Solution

SSH Config: One file per identity

Instead of a monolithic ~/.ssh/config, use:

~/.ssh/
├── config          # just "Include profiles/*"
└── profiles/
    ├── personal
    ├── work
    ├── org1
    └── org2

Each profile file contains:

Host p1
    HostName ssh.github.com
    Port 443
    User personal-user
    IdentityFile ~/.ssh/id_personal

Git Config: URL rewriting + directory matching

The ~/.gitconfig now has two parts:

URL Rewriting

Maps GitHub usernames to SSH host aliases:

[url "git@p1:personal-user/"]
    insteadOf = "https://github.com/personal-user/"
    insteadOf = "[email protected]:personal-user/"

This allows cloning with regular GitHub URLs while automatically using the correct SSH key.

Identity Selection via Directory Matching

UPDATE 2026-01-06: Changed from URL-based matching to directory-based matching.

Instead of matching by remote URL, match by repository directory:

[includeIf "gitdir:~/dev/personal/"]
    path = ~/.config/git/profiles/personal

[includeIf "gitdir:~/dev/work/"]
    path = ~/.config/git/profiles/work

This approach organizes repositories by profile in the filesystem, making identity selection explicit and reliable.

Profile files live in ~/.config/git/profiles/ and contain only user info:

[user]
    name = "Your Name"
    email = "[email protected]"

Benefits

File Locations

PurposeLocation
SSH profiles~/.ssh/profiles/*
Git profiles~/.config/git/profiles/*
Main git config~/.gitconfig
Main SSH config~/.ssh/config

Repository URL Standard

All repositories should store HTTPS URLs in their .git/config:

[remote "origin"]
    url = https://github.com/username/repo.git

The URL rewriting rules in ~/.gitconfig automatically convert these to SSH at runtime:

$ git remote get-url origin
git@alias:username/repo.git

This means:

Update History

2026-01-06: Changed to Directory-Based Identity Selection

Previous approach: Used hasconfig:remote.*.url:git@alias:*/** to match repositories by remote URL.

Problem discovered: The hasconfig condition checks the URL stored in .git/config before URL rewriting happens. Since repositories store HTTPS URLs (https://github.com/username/...), the pattern looking for SSH aliases (git@alias:*/**) never matched.

Solution: Changed to directory-based matching using gitdir:/path/to/profile/. This approach:

Migration steps:

  1. Changed includeIf conditions from hasconfig:remote.*.url: to gitdir: patterns
  2. Standardized all repository URLs to HTTPS format
  3. Removed local user.name and user.email configs from individual repositories
  4. Organized ~/.gitconfig to group each profile’s URL rewriting and includeIf together

Example =~/.gitconfig= structure:

# Profile configurations - grouped by profile, sorted alphabetically

# profile1 (personal)
[url "git@p1:personal-user/"]
    insteadOf = "https://github.com/personal-user/"
    insteadOf = "[email protected]:personal-user/"
[includeIf "gitdir:~/dev/personal/"]
    path = ~/.config/git/profiles/profile1

# profile2 (work)
[url "git@p2:work-user/"]
    insteadOf = "https://github.com/work-user/"
    insteadOf = "[email protected]:work-user/"
[includeIf "gitdir:~/dev/work/"]
    path = ~/.config/git/profiles/profile2