gitsignet
v0.2.0
Published
Git identity guard — block commits made under the wrong author, with remote-aware rules and a zero-config doctor.
Maintainers
Readme
gitsignet
Website & docs: gitsignet.dev
Stop committing to work repos as your personal self (and vice-versa).
gitsignet is a git identity guard: it blocks a commit made under the wrong
author, using rules keyed off the repo's remote — so the right identity is
enforced no matter where you cloned the repo.
$ git commit -m "wip"
✗ gitsignet: wrong identity for this remote — commit blocked
remote : github.com/acme-corp/widgets
rule : github.com/acme-*
expected: Work Me <[email protected]>
current : Personal Me <[email protected]>
fix : run `gitsignet fix` to apply the expected identityThen run gitsignet fix and the right user.name/user.email for that remote
are applied for you — no retyping, no copy-paste.
Why
git config includeIf can switch identity — but it keys off the directory
the repo lives in, it is silent, and it never stops a bad commit. Clone a work
repo into the wrong folder, or forget to set user.email in a fresh clone, and
you will happily author commits as the wrong person. You find out when the commit
is already pushed with your personal email on a work repo (or your work email on
an open-source PR).
gitsignet keys rules off the remote instead of the directory, and it runs
as a pre-commit hook that fails the commit when the identity is wrong.
Install
npm install -g gitsignetOr run it without installing:
npx gitsignet doctorQuick start
# 1. See what identity you're about to commit as, and why:
gitsignet doctor
# 2. Create a config (writes .gitsignet.json in the repo, or --global):
gitsignet init --global
# 3. Edit the config, then enable the pre-commit guard in a repo:
gitsignet installConfiguration
A .gitsignet.json in the repo overrides a global config at
$XDG_CONFIG_HOME/gitsignet/config.json (~/.config/gitsignet/config.json).
Keep one global config with all your identities and rules, and you never
have to think about it again.
{
"strict": false,
"profiles": {
"work": { "name": "Work Me", "email": "[email protected]" },
"personal": { "name": "Personal Me", "email": "[email protected]" }
},
"rules": [
{ "remote": "github.com/acme-*", "profile": "work" },
{ "remote": "github.com/my-username", "profile": "personal" },
{ "remote": "gitlab.com/**", "name": "Personal Me", "email": "[email protected]" }
]
}profiles— named identities you can reuse across rules.rules— matched top-to-bottom; the first match wins. Each rule points at aprofile, or gives an inlinename/email.remote— a glob matched againsthost/owner/repo,host/owner, andhost.*matches within one path segment;**crosses/. Sogithub.com/acme-*matches a whole org,github.com/acme/widgetsa single repo. Use the literal"(none)"to match a repo that has nooriginremote at all (a local-only repo). This lets you guard the identity of remoteless repos — for example{ "remote": "(none)", "profile": "personal" }— which no host/owner glob can ever match.strict— whentrue, a commit to a remote that matches no rule is blocked. Off by default (unmatched remotes are allowed).
Commands
| Command | What it does |
| --- | --- |
| gitsignet doctor [--json] | 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 [--hook] [--json] | The guard. Exit non-zero on a mismatch / strict violation, or when the identity could not be verified (git missing or failing, unreadable config). --hook stays quiet on success. --json prints the result as JSON. |
| gitsignet fix [--global] | 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 gitsignet 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 [--global] | Write a sample config. |
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. If gitsignet can't be found at all (for example on a
fresh clone where nobody installed it), the hook fails open: it prints a
one-line notice and lets the commit proceed rather than hard-blocking it.
⚠ The hook needs a real install —
npx gitsignet ...is not enough. The pre-commit hook resolves the binary withcommand -v gitsignetandnpx --no-install gitsignet; neither can see packages that only live in the transientnpxdownload cache. If you only ever run gitsignet vianpx gitsignet, the hook will fall open (commits pass unchecked). To actually arm the guard, install a resolvable binary:npm i -g gitsignet # global — works in every repo npm i -D gitsignet # per-project dev dependency
gitsignet installnow detects this and prints a loud warning if the hook it just wrote cannot resolve gitsignet.
Exit codes
check and doctor exit 1 on a blocking condition (wrong identity, missing
identity, or a strict-mode violation) and 0 otherwise. Unknown commands exit 2.
check also exits 1 when it could not verify the identity: git is not on
PATH, git fails for a reason other than "not a git repository" (for example
"dubious ownership"), or the config file is not valid JSON. A guard that cannot
check must not let the commit through silently. Outside a git repository,
check --hook is still a no-op.
How identity is resolved
gitsignet mirrors git's own precedence: GIT_AUTHOR_NAME/GIT_AUTHOR_EMAIL
environment variables override git config user.name/user.email. That's the
identity your next commit will actually carry — which is exactly what gets checked.
License
MIT © flossy-studio
