gitprofile
A Go CLI tool for managing multiple git identities — work, personal, open-source — each with its own SSH key, commit-signing setup, and directory routing. The right identity and key activate automatically based on which folder a repo lives in.
How it works
Two problems underlie every multi-account git setup:
- Git identity —
user.name, user.email, and the commit-signing key. Git owns this.
- SSH key routing — which private key authenticates a
push/pull/clone. SSH owns this, and most setups silently break here.
gitprofile solves both using git's includeIf directive. It maintains a single managed block in ~/.gitconfig:
# >>> gitprofile managed >>>
[includeIf "gitdir:~/work/"]
path = ~/.config/gitprofile/profiles/work.gitconfig
[includeIf "gitdir:~/personal/"]
path = ~/.config/gitprofile/profiles/personal.gitconfig
# <<< gitprofile managed <<<
Each profile's fragment carries identity, SSH command, and optional signing config:
# ~/.config/gitprofile/profiles/work.gitconfig
[user]
name = Jane Dev
email = jane@company.com
signingkey = ~/.ssh/id_work.pub
[core]
sshCommand = "ssh -i ~/.ssh/id_work -o IdentitiesOnly=yes"
[commit]
gpgsign = true
[gpg]
format = ssh
Any repo cloned under ~/work/ automatically gets the right name, email, signing key, and SSH key — with zero per-repo commands. IdentitiesOnly=yes ensures SSH never authenticates with the wrong account.
Installation
git clone <this-repo>
cd gitprofile
go build -o gitprofile .
mv gitprofile /usr/local/bin/
Requires Go 1.21+.
Commands
gitprofile add <name>
Interactive wizard: collects identity, host, SSH key (generate new or adopt existing), owned directories, and signing preference. Writes the profile to ~/.config/gitprofile/config.yaml, generates a gitconfig fragment, and updates the managed block in ~/.gitconfig.
gitprofile add work
# Full Name: Jane Dev
# Email: jane@company.com
# Host (github.com/gitlab.com...): github.com
# Generate new SSH key? (y/n) [y]: y
# Generated new SSH key at ~/.ssh/id_work
# Enable SSH signing for commits? (y/n) [y]: y
# Directories this profile owns? (comma-separated): ~/work,~/company
# Profile created and installed!
Then add the public key to your git host:
gitprofile key show work
gitprofile list
Lists all configured profiles with their owned directories.
gitprofile list
# - work: Jane Dev <jane@company.com> (dirs: [~/work ~/company])
# - personal: Jane <jane@personal.dev> (dirs: [~/personal])
gitprofile current
Shows which profile applies to the current directory and why.
cd ~/work/myrepo
gitprofile current
# Profile: work (Jane Dev <jane@company.com>)
# Reason: in directory ~/work (owned by profile)
gitprofile use <name>
Applies a profile as a local override in the current repo's .git/config. Use this for repos that live outside any owned directory.
cd /tmp/some-repo
gitprofile use work
# Profile config applied locally to this repo.
gitprofile clone <url> [--as <name>]
Clones a repo using a profile's SSH key from the first byte. When --as is given, the clone runs with GIT_SSH_COMMAND set and lands inside the profile's first owned directory as a subdirectory.
gitprofile clone git@github.com:company/service.git --as work
# Clones into ~/work/service with the work SSH key
gitprofile key show <name>
Prints the public key to paste into GitHub / GitLab / Bitbucket.
gitprofile key show work
# Public key for work:
# ssh-ed25519 AAAA...
gitprofile key path <name>
Prints the private and public key file paths.
gitprofile key path work
# Private: ~/.ssh/id_work
# Public: ~/.ssh/id_work.pub
gitprofile edit <name>
Interactively update any field of an existing profile. Re-renders the gitconfig fragment and managed block.
gitprofile edit work
gitprofile remove <name>
Removes the profile, its gitconfig fragment, and its includeIf entry. The SSH key files are left untouched.
gitprofile remove work
# Profile removed.
gitprofile doctor
Diagnoses common issues:
- Duplicate directory ownership across profiles
- Missing or invalid SSH keys (permissions, parsability)
- Missing gitconfig fragments
- Live SSH connectivity test against each profile's host (
ssh -T git@github.com)
- Missing managed block in
~/.gitconfig
gitprofile doctor
# Testing ssh -T github.com using key ~/.ssh/id_work ...
# Hi janeDev! You've successfully authenticated...
# [OK] All checks passed.
Files written
| Path |
Purpose |
~/.config/gitprofile/config.yaml |
Profile registry |
~/.config/gitprofile/profiles/<name>.gitconfig |
Per-profile gitconfig fragment |
~/.gitconfig |
Managed includeIf block appended / replaced |
~/.gitconfig.gitprofile.bak |
Backup created before each ~/.gitconfig edit |
~/.ssh/id_<name> |
Generated ed25519 private key (0600) |
~/.ssh/id_<name>.pub |
Generated ed25519 public key (0644) |
Safety
- Managed block — all edits to
~/.gitconfig live inside clearly-marked >>> gitprofile managed >>> sentinels. The tool only touches its own section; everything else is preserved.
- Backup —
~/.gitconfig.gitprofile.bak is written before any modification.
- Key permissions — private keys are created with
0600; the key directory is set to 0700. Existing keys are permission-fixed on adoption.
- No clobbering —
add refuses to overwrite an existing key file.
IdentitiesOnly=yes — injected into every sshCommand so SSH never falls back to the SSH agent and authenticates as the wrong account.
Pending / roadmap
signing package — allowed_signers file management and host paste hints for signing-key registration
hosts package — host-specific guidance (GitHub / GitLab / Bitbucket key registration URLs)
--force flag on add to overwrite an existing key
--global mode to set a catch-all default profile
- Shell completion (
cobra provides this for free)
- Windows support (path handling and SSH command quoting)
Development
go test ./... # run all tests
go build -buildvcs=false . # build binary
Tests cover config round-trips (internal/config), gitconfig fragment rendering and managed-block rewriting (internal/gitconfig), and SSH key generation and validation (internal/sshkey).
License
MIT