How do you make Git use a work email under ~/work and a personal one everywhere else?
answer
- one machine, two identities
- the setting should follow the directory
- config can pull in another config
- condition on the repository's location
- includeIf with a gitdir pattern
basics
~20 sUse a conditional include in your global config: an includeIf section with a gitdir condition pointing at ~/work/ pulls in a second config file whose user.email is the work address, so every repository under that directory gets the work identity automatically.
solid answer
~50 sIn `~/.gitconfig`, add a section such as `[includeIf "gitdir:~/work/"]` with `path = ~/.gitconfig-work`, and put the work `[user]` block in that file. Git evaluates the condition against the path of the repository's `.git` directory; a trailing slash means "this directory and everything under it". Because the include is spliced in at the point where it appears, ordinary precedence still applies — settings after it in the same file would win, so put the conditional include *after* your defaults. Verify inside a repository with `git config --show-origin --get user.email`, which names the file the value came from. The strongest version of this pattern also sets `user.useConfigOnly = true` and leaves no global `user.email` at all: Git then refuses to commit rather than inventing an identity in a directory you forgot to cover. Conditions on the current branch (`onbranch:`) exist too, and `gitdir/i:` matches case-insensitively.
code
ini · 8 lines[user]
name = Ada Lovelace
useConfigOnly = true
[includeIf "gitdir:~/work/"]
path = ~/.gitconfig-work
[includeIf "gitdir:~/personal/"]
path = ~/.gitconfig-personalgo deeper
Recall that Git can set a different user.email per repository, and that a conditional include in your global config can do it automatically for everything under a directory.
Explain the includeIf gitdir condition, the trailing-slash meaning, and that included content is spliced in at the directive's position so ordinary precedence still decides the winner.
Demonstrate the hardened setup: no global email plus user.useConfigOnly so uncovered repositories fail loudly, and verification with git config --show-origin rather than trusting the file text.
Own the risk framing: wrong-identity commits are cheap to prevent and expensive to rewrite, so decide whether machine layout, provisioning, or a mandatory per-clone step is the right control for your organisation.
## The problem One machine, two identities. Committing with a personal address to a work repository — or worse, a work address to a public personal project — is embarrassing, hard to notice, and permanent once pushed, because the author email is part of the commit object. Setting `user.email` per repository works but relies on remembering it every single time you clone. Conditional includes make the machine remember instead. ## The mechanism Git's config format supports including other files. The unconditional form is `[include] path = <file>`, which splices the named file in at that point. The conditional form is `[includeIf "<condition>"] path = <file>`, which does the same only when the condition holds. The most useful condition is `gitdir:`, matched against the path of the repository's `.git` directory: - `gitdir:~/work/` — the leading `~/` is expanded to your home directory, and the trailing `/` means the pattern also matches everything beneath, so every repository under `~/work` matches. - `gitdir/i:` is the same with case-insensitive matching, which matters on case-insensitive filesystems. - `onbranch:<pattern>` matches when the current branch name matches the pattern — useful for settings tied to a branch rather than a location. Included paths are relative to the config file containing the directive, and `~/` expansion works, so keeping `~/.gitconfig-work` next to `~/.gitconfig` is conventional. ## Ordering is precedence An include is not a special layer. Its contents are read *at the position of the directive*, exactly as if pasted there. So a `user.email` defined **after** the `includeIf` in the same file overrides the included one, and a repository's own `.git/config` overrides both because local scope is read after global. Put your defaults at the top of the file and the conditional includes below them, and the intuition "the more specific one wins" holds. ## Making mistakes loud The pattern's weakness is repositories that fall outside every condition: they silently get whatever default you left in place. Two settings harden it: - **Remove the global `user.email` entirely** and set `user.useConfigOnly = true`. With that boolean on, Git will not fall back to guessing an identity from the username and hostname; it errors out and tells you to configure one. A directory you forgot to cover now fails at commit time instead of producing a commit with the wrong address. - Keep `user.name` global if it does not vary, and let only the email differ. Some people additionally use different signing keys or SSH commands per identity, which the same included file can carry. ## Verifying it works Do not trust it because the file looks right. Inside a repository under `~/work`, run `git config --show-origin --get user.email`: the output names the file that supplied the value, which will be `~/.gitconfig-work` when the include fired. Do the same in a repository outside that tree and confirm you get the personal address — or, if you removed the global default, that Git says no identity is configured. `git config --list --show-origin --show-scope` gives the whole picture when something is off. A nuance worth knowing: the condition is evaluated against the repository Git has actually resolved, so it depends on where you run the command. Outside any repository there is no `gitdir` to match and the include does not fire. ## Repairing commits made with the wrong identity Conditional includes do not retroactively fix anything. A commit already made with the wrong email keeps it, because the author field is part of the hashed commit object. Fixing the most recent one is `git commit --amend --reset-author` after correcting the configuration; fixing a range means rewriting that history, with all the usual consequences for anyone who has already fetched it. That asymmetry — cheap to prevent, expensive to repair — is the argument for setting this up on day one of a new machine. ## When per-repository config is still the right answer Conditional includes are a convenience for a *layout* you control. If your repositories are not organised by identity on disk, a per-repository `git config user.email` at clone time is clearer than a pile of conditions, and `user.useConfigOnly = true` still gives you the safety net that forces you to make the choice.
- Why does the position of the includeIf inside ~/.gitconfig matter?Because an include is spliced in where it appears rather than forming a separate layer. Anything defined after it in the same file overrides the included values. Put your defaults first and the conditional includes below, so the more specific identity wins.
- What does user.useConfigOnly add to this setup?With it set to true and no global `user.email`, Git refuses to invent an identity from the username and hostname. A repository outside every condition then fails at commit time with a clear error instead of silently recording an address you did not intend.
- How do you confirm the include actually fired?Run `git config --show-origin --get user.email` inside a repository under the matched directory. The output prefixes the value with the file it came from, so seeing the work config file's path proves the condition matched — far more reliable than inspecting the config text.
- Does fixing the configuration correct commits you already made with the wrong address?No. The author email is part of the commit object, so existing commits keep it. `git commit --amend --reset-author` fixes only the tip; anything further back requires rewriting history, which changes every affected hash and disrupts anyone who has already fetched it.
saying these in an interview costs you the question
- Thinks an include creates a higher-priority layer than the surrounding file
- Puts the conditional include above the defaults and wonders why it loses
- Believes the fix retroactively corrects earlier commits
- Relies on remembering git config per clone as equally safe
- Assumes the condition matches the working directory rather than the repository location