gitsignet

A FlossyPurse Studios tool

Never commit as the wrong you again.

A git identity guard that blocks the commit when your author identity doesn't match the repo. includeIf can set an identity — gitsignet is what stops a wrong one.

npm version MIT license CI passing
Identity verified
$ 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 set your identity automatically — by directory, and since git 2.36 by remote URL too (hasconfig:remote.*.url:...). What it can't do is stop a commit under the wrong identity.

It only sets, never blocks

Typo a glob, or set user.email by hand in a fresh clone, and includeIf will not stop you from committing. gitsignet's pre-commit hook fails the commit when the identity is wrong.

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.

user.useConfigOnly isn't enough

Git's own user.useConfigOnly refuses to commit with no identity configured at all — but it says nothing about an identity that IS configured and just wrong for this remote. gitsignet checks against a rule, not just presence.

Zero-config doctor

gitsignet doctor works before any config exists — it shows the identity you're about to commit as and the parsed remote. Once a rule exists, it also names the matching rule and whether you match it, and gitsignet fix applies the identity that rule expects.

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 where you've run gitsignet install 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. Without one, a repo with no origin at all still blocks a placeholder identity — an email at a reserved example or test domain such as ci@example.com, which no person commits as. With strict: true, a commit to any remote that matches no rule is blocked.

Commands

CommandWhat it does
gitsignet doctorExplain 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 checkThe guard. Exits non-zero on a mismatch or strict violation. --hook stays quiet on success.
gitsignet fixApply the identity the matching rule expects (sets user.name/user.email). --global writes global config. Refuses when no rule matches.
gitsignet installAdd the guard to this repo's pre-commit hook (honours core.hooksPath). Idempotent; preserves an existing hook.
gitsignet uninstallRemove the guard from the pre-commit hook.
gitsignet initWrite 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.