Easily use multiple git identities on one laptop
When you're a consultant with multiple clients, or fetching personal Git repositories on your work laptop, you might need to switch between multiple identities. You might use your personal handle and email address to commit on your personal projects, and use a work-related email and full name for work repositories.
And if you're in that situation, chances are high that you've used the wrong identity in a repo at least once.
This happened to me again the other day. I have some config files that reuse one multiple laptops, my work one included, and that config comes with a global git config that is set for my personal projects. Long story short, I committed with my personal info on a work repo. That's not a huge issue, but I like to keep these two things separate when possible. So, I wonder, how could I avoid that? And as often lately, I turned to Claude to find a solution.
I ended up creating a zsh hook that warns when entering a git repository without local identity configuration, plus a helper function to quickly apply the right identity.
The hook
The following code hooks on chpwd which allows to detect a change of directory, either with cd or jumper utilities like z/zoxide.
When entering a directory, it first looks to see if that is a git repository, if not, it exits.
Otherwise it looks if an identity is configured in the local git config. If that is the first time we enter the repository for that session it will display a message:
[git-identity] No local identity configured
Run: git-identity-apply <module>We will talk about git-identity-apply shortly after.
It keeps track of which repositories were seen during a session to avoid showing the message each time we cd inside a repo.
Here is the hook script:
# Git identity check on directory change
# Warns when entering a git repo without local identity configuration
# Track warned repos to avoid repeated warnings per session
typeset -gA _git_identity_warned_repos
# Find the dotfiles directory
: ${DOTFILES:="$HOME/.dotfiles"}
# Check git identity and warn if not configured
_git_identity_check() {
# Only proceed if we're inside a git worktree
[[ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" == "true" ]] || return
local git_root
git_root=$(git rev-parse --show-toplevel 2>/dev/null) || return
# Skip if we've already warned about this repo this session
[[ -n "${_git_identity_warned_repos[$git_root]}" ]] && return
# Check if local identity is configured
local local_email local_name
local_email=$(git config --local user.email 2>/dev/null)
local_name=$(git config --local user.name 2>/dev/null)
# Warn if either is missing
if [[ -z "$local_email" || -z "$local_name" ]]; then
_git_identity_warned_repos[$git_root]=1
echo "[git-identity] No local identity configured"
echo " Run: git-identity-apply <module>"
fi
}
# Register the chpwd hook
autoload -Uz add-zsh-hook
add-zsh-hook chpwd _git_identity_check
# Also run on shell startup in case we start in a git repo
_git_identity_checkNow we have an easy way to know that we didn't set a local identity, but we still need to add one. This means two git config commands to run, but I never remember what they are and I mostly want to use the same identities—either my personal one or a client-related one.
My configuration system comes with modules. Module can be anything. I have a module for PHP that install things PHP related, one named "Perso" that installs things that I want on every laptop. I also have one, you guessed it, by company I work for. Using symlinks I can decide on each laptop which module I want to install, run a script, and I keep everything up to date using brew.
So, let's go back to our git identity thing.
In each module where I want a separate identity, I add a file called git-identity that looks like:
git config --local user.email "jane.doe@acme-corp.com"
git config --local user.name "Jane Doe"I made a script, the git-identity-apply you've been waiting to meet, that takes a module name as an argument and configures the repo with the matching identity. To make things even more convenient, you can run the command without parameter, the command lists all modules with an associated identity and lets you select one using fzf. Select one, press enter, and boom, you're ready to go.
# Apply git identity from a module configuration
git-identity-apply() {
local module="$1"
local modules_dir="$DOTFILES/Modules"
# Interactive selection with fzf if no module specified
if [[ -z "$module" ]]; then
local modules=()
for identity_file in "$modules_dir"/*/git-identity(N); do
modules+=("${identity_file:h:t}")
done
if [[ ${#modules[@]} -eq 0 ]]; then
echo "[git-identity] No modules with git-identity found"
return 1
fi
module=$(printf '%s\n' "${modules[@]}" | fzf \
--header="Select git identity" \
--preview="cat $modules_dir/{}/git-identity" \
--preview-window=up:3)
[[ -z "$module" ]] && return 0
fi
# Find the module's git-identity file (case-insensitive match)
local identity_file
for dir in "$modules_dir"/*(N); do
local dir_name="${dir:t}"
if [[ "${(L)dir_name}" == "${(L)module}" ]]; then
identity_file="$dir/git-identity"
break
fi
done
if [[ ! -f "$identity_file" ]]; then
echo "[git-identity] Module '$module' not found or has no git-identity file"
return 1
fi
# Source the identity file to apply the config
source "$identity_file"
# Display the applied identity
local applied_email applied_name
applied_email=$(git config --local user.email 2>/dev/null)
applied_name=$(git config --local user.name 2>/dev/null)
echo "[git-identity] Applied identity from $module"
echo " user.email = $applied_email"
echo " user.name = $applied_name"
# Clear the warned flag for this repo since it's now configured
local git_root
git_root=$(git rev-parse --show-toplevel 2>/dev/null)
if [[ -n "$git_root" ]]; then
unset "_git_identity_warned_repos[$git_root]"
fi
}Using agents to create small tools to make my life easier as a developer is something that I enjoy. I like that I can explain my problem, brainstorm a solution with an agent and have a tool that I wouldn't have created otherwise.
- Improve your automated testing : You will learn how to fix your tests and make them pass from things that slow you down to things that save you time. This is a self-paced video course in French.
- Helping your teams: I help software teams deliver better software sooner. We'll work on technical issues with code, test or architecture, or the process and organization depending on your needs. Book a free call where we'll discuss how things are going on your side and how I can help you.
- Deliver a talk in your organization: I have a few talks that I enjoy presenting, and I can share with your organization(meetup, conference, company, BBL). If you feel that we could work on a new topic together, let's discuss that.
