Never commit as the wrong you again.
A git identity guard that blocks the commit when your author identity doesn't match the repo — with rules keyed off the remote, not the directory.
$ git commit -m "fix auth bug" ✗ gitsignet: wrong identity for this remote — commit blocked remote : github.com/acme-corp/widgets rule : github.com/acme-* expected: Work Me <me@acme.com> current : Personal Me <me@personal.dev> fix : run `gitsignet fix` to apply the expected identity $ gitsignet fix ✓ applied the expected identity (local git config): user.email → me@acme.com
Why git already has includeIf
git config includeIf can switch identity automatically — but it has three holes that bite you exactly when it matters:
It keys off the directory
Clone a work repo into ~/personal/ by mistake and includeIf silently picks the wrong identity. gitsignet keys off the remote URL — the repo is what it is no matter where it lives on disk.
It's silent
You only learn the identity was wrong after the commit is pushed with your personal email on a work repo. gitsignet is loud: doctor tells you up front, and the guard fails the commit.
It never blocks
A fresh clone with no user.email set will happily author commits. includeIf won't stop it. gitsignet's pre-commit hook exits non-zero on a mismatch or a missing identity.
Zero-config doctor
Run gitsignet doctor anywhere and it explains the identity you're about to commit as, the parsed remote, the matching rule, and whether it's OK — before you've written a single config line.
Install
# install globally npm install -g gitsignet # …or run without installing npx gitsignet doctor
Quick start
# 1. See who you're about to commit as, and why gitsignet doctor # 2. Write a global config with your identities + rules gitsignet init --global # 3. Turn on the pre-commit guard in a repo gitsignet install
Keep one global config with all your identities and you never think about it again — every repo you clone gets checked against its own remote.
Configuration
A .gitsignet.json in the repo overrides a global config at ~/.config/gitsignet/config.json. Rules match top-to-bottom; first match wins.
{
"strict": false,
"profiles": {
"work": { "name": "Work Me", "email": "me@acme.com" },
"personal": { "name": "Personal Me", "email": "me@personal.dev" }
},
"rules": [
{ "remote": "github.com/acme-*", "profile": "work" },
{ "remote": "github.com/my-username", "profile": "personal" },
{ "remote": "gitlab.com/**", "name": "Personal Me", "email": "me@personal.dev" }
]
}
remote is a glob matched against host/owner/repo, host/owner, and host. * matches within one path segment; ** crosses /. Use the literal "(none)" to match a local-only repo that has no origin remote. With strict: true, a commit to any remote that matches no rule is blocked.
Commands
| Command | What it does |
|---|---|
gitsignet doctor | Explain the identity you're about to commit as, the parsed remote, the matching rule, and whether it's OK. Warns when a later rule is shadowed by an earlier broad one. Never fails a commit. |
gitsignet check | The guard. Exits non-zero on a mismatch or strict violation. --hook stays quiet on success. |
gitsignet fix | Apply the identity the matching rule expects (sets user.name/user.email). --global writes global config. Refuses when no rule matches. |
gitsignet install | Add the guard to this repo's pre-commit hook (honours core.hooksPath). Idempotent; preserves an existing hook. |
gitsignet uninstall | Remove the guard from the pre-commit hook. |
gitsignet init | Write a sample config. --global writes it to your XDG config dir. |
The installed hook calls gitsignet if it's on PATH, else falls back to npx --no-install gitsignet — so it works whether the tool is installed globally or as a dev dependency. Running only npx gitsignet is not enough to arm the hook: neither probe can see the transient npx cache, so the guard falls open. Install a resolvable binary (npm i -g gitsignet or npm i -D gitsignet); gitsignet install warns loudly when it can't find one.