Guide
Use repo-local identity policy with .gitrole
Use .gitrole to declare the preferred Git identity role for a repository and the small set of roles that are allowed there.
A shared repository doesn't say which Git identity belongs there, so a personal role can look fine until the history is full of the wrong author. A .gitrole file in the repo root names the role you normally want and the short list of roles that are still allowed. It doesn't switch you, install a hook, or block a commit. If you haven't saved a role yet, start with Use the right Git identity for this repo.
Pin a repo to one role
When one saved role is the one this repo should use, pin it:
gitrole pin company-main
That writes .gitrole and prints:
pinned role company-main
file .gitrole
default company-main
allowed company-main
The file on disk is:
{
"version": 1,
"defaultRole": "company-main",
"allowedRoles": [
"company-main"
]
}
Check it:
gitrole resolve
company-main
If resolve prints that name, you can stop. defaultRole has to be in allowedRoles. Pin puts the same role in both, so you don't have a preferred role that the file then rejects.
If .gitrole already exists, pin exits 1 and leaves the file alone. A second run can't merge or widen the list by accident:
error: .gitrole already exists in this repo; gitrole pin will not overwrite or merge existing repo policy
How to read status
When .gitrole exists, gitrole status and gitrole doctor add the policy on top of the commit, remote, and SSH checks. Allowed-but-not-default stays aligned. The role is on the list, so it isn't a warning, and the default is still visible so you can see it isn't the preferred one.
This is gitrole status when the file prefers company-main, also allows maintainer-personal, and the global identity matches maintainer-personal:
maintainer-personal aligned
commit Maintainer Name <maintainer@personal.example>
push maintainer via github.com-personal
scope global
policy allowed role maintainer-personal (default: company-main)
The same repo on one line is policy=ok and overall=aligned. policy=ok on gitrole status --short means the effective role is defaultRole or is listed in allowedRoles. It does not mean the role is the default:
role=maintainer-personal scope=global override=false commit=ok remote=ok auth=ok policy=ok overall=aligned
gitrole doctor uses a different word for that case. The policy check is info, not warn, because info doesn't select overall=warning:
info policy effective role maintainer-personal is allowed here, but repo defaultRole is company-main
On status --short, policy=warn and exit 2 only when the effective role is outside allowedRoles. policy=na means there is no .gitrole file. A missing file doesn't make status or doctor fail. resolve does fail when the file is absent, because that command's only job is to read it.
Surprises
defaultRole and every allowedRoles entry use the same role-name rules as a saved role: lowercase letters, numbers, -, and _. company-main and agent_bot are valid. client acme, Work, Client, and no-role are not. no-role is reserved because status --short writes it when no saved role matches.
An invalid name doesn't warn and continue. These commands exit 1, write the error to stderr, and print nothing on stdout, including gitrole status --short. The line isn't written until the policy loads, so a bad name can't show up as a role= value:
gitrole resolvegitrole resolve --jsongitrole statusgitrole status --shortgitrole doctorgitrole doctor --json
What it doesn't do
The file doesn't switch roles, install a hook, or block git commit. It tells check commit, status, and doctor whether the current role is the one this repo asked for. If an agent should read that before it commits, continue with Use gitrole as an identity preflight for agents and automation.
File format
Edit the file when more than one role is valid. This one prefers company-main and also allows maintainer-personal. gitrole resolve --json prints the same object:
{
"version": 1,
"defaultRole": "company-main",
"allowedRoles": [
"company-main",
"maintainer-personal"
]
}
| Field | Meaning |
|---|---|
version | Schema version. Currently 1. |
defaultRole | The role that normally belongs in this repo. It also has to appear in allowedRoles. |
allowedRoles | Roles that are still valid here. |
On gitrole doctor --json, that evaluation is repoPolicy.status: default, allowed, or notAllowed. default and allowed are policy=ok on status --short. notAllowed is policy=warn. The exit codes and the exact failure text are in Machine Readable Contracts.
Invalid names
A defaultRole of client acme looks like this on stderr:
error: repo policy file .gitrole is invalid: defaultRole invalid role name "client acme"; use lowercase letters, numbers, "-" or "_"
An invalid allowed role is named on that field. With defaultRole set to work and Client in allowedRoles:
error: repo policy file .gitrole is invalid: allowedRoles invalid role name "Client"; use lowercase letters, numbers, "-" or "_"
A missing .gitrole file is not this failure. resolve still exits 1 when the file is absent. status and doctor keep working, with policy=na on status --short.