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
includeIfgit config includeIf can switch identity automatically — but it has three holes that bite you exactly when it matters:
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.
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.
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.
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 globally npm install -g gitsignet # …or run without installing npx gitsignet doctor
# 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.
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 /. With strict: true, a commit to any remote that matches no rule is blocked.
| 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.