# gitprofiles
`gitprofiles` is CLI for managing multiple Git identities with local enforcement.
The intended workflow is:
- first, define profiles once
- then, set one profile per repository
Commits/pushes are blocked when profile state is missing or mismatched, although, there are some limitations. Please read below.
## Compatibility
The only supported platforms are:
- Linux/POSIX shell environments
The program may or may not work on other platforms. Support for them is not planned either.
## Install
You must have [cargo](https://doc.rust-lang.org/cargo) installed on your system.
```bash
cargo install gitprofiles
```
## Quickstart
1. Initialize global integration:
```bash
gitprofiles init
```
2. Create a profile (interactive):
```bash
gitprofiles create
```
3. In each repository, select a profile:
```bash
gitprofiles set-profile work
```
4. Validate repository state:
```bash
gitprofiles check
```
PROTIP: `gitp` is an alias for invoking this program, instead of `gitprofiles`.
## Commands
- `gitprofiles init`
- `gitprofiles create`
- `gitprofiles set-profile <name>`
- `gitprofiles check`
- `gitprofiles list`
- `gitprofiles remove <name>`
## How Enforcement Works
The way gitprofiles was designed is straightforward. It does not try to wrap Git or replace its behavior, but instead, it adds a guardrail at the two moments where identity mistakes usually matter most. They're during, 1: creating commits, and, 2: pushing changes.
When you run `gitprofiles init`, the tool creates two hooks under `~/.config/gitprofiles/hooks`: `prepare-commit-msg` and `pre-push`. Both hooks call `gitprofiles check`.
`gitprofiles check` is the policy gate. It reads your repo's local `gitprofiles.profile`, `user.name`, and `user.email`, then compares that state against the profile definition in `~/.config/gitprofiles/profiles.toml`. If the profile marker is missing, if the profile name is not found in the TOML file, or if local name/email drift from that profile, the operation is rejected.
During `pre-push`, Git also sends the list of refs that are about to be pushed. `gitprofiles check` reads that payload and inspects pushed tags. If a pushed tag is an annotated tag object, gitprofiles validates its embedded `tagger` identity against your selected profile. If they do not match, the push is blocked.
So practically, once you run `gitprofiles set-profile <name>` in a repo, the checks keep that repo locked to the specified profile, unless you intentionally change something.
## Limitations
1. This program cannot block local annotated-tag creation (Git has no client hook for that), but it does block pushes that would publish annotated tags whose embedded `tagger` identity does not match the selected profile.
2. This program is just a client-side enforcement, which is great enough. However, local hooks can be bypassed if things were invoked in ways that skip Git.
3. The initialization command will refuse when `core.hooksPath` is already set globally, and it will also refuse if managed hook targets already exist. This is so the program does not overwrite an existing hook setup. A hook integration mode that can coexist with pre-existing hooks (chaining instead of refusing by default) is planned.
## Troubleshooting
```
refusing to initialize: global core.hooksPath is already set
```
The program detected an existing hook setup and intentionally refused to overwrite it. That is currently a hard stop by design. You need to clear or migrate that existing hooks path manually before running `gitprofiles init`.
```
missing local key 'gitprofiles.profile'
```
That repository has not been assigned a profile marker yet. Run `gitprofiles set-profile <name>` in that repo.
```
profile '<name>' does not exist
```
The profile name is not in `~/.config/gitprofiles/profiles.toml`. Run `gitprofiles list` to see existing names, or create the profile first via `gitprofiles create`.
```
local user.name mismatch (expected '...', found '...')
```
The repository's local `user.name` drifted from the selected profile. Re-apply with `gitprofiles set-profile <name>`, then confirm with `gitprofiles check`.
```
local user.email mismatch (expected '...', found '...')
```
The repository's local `user.email` drifted from the selected profile. Re-apply with `gitprofiles set-profile <name>`, then confirm with `gitprofiles check`.
```
annotated tag 'refs/tags/<tag>' tagger mismatch
```
The tag object was created with a different `tagger` identity than the currently selected profile. Recreate the tag under the correct profile identity, then push again.
```
fatal: no email was given and auto-detection is disabled
```
Git is telling you there is still no valid local identity in that repository while `user.useConfigOnly=true` is active. Set the repo profile and retry.
During `init`, this program sets `user.useConfigOnly=true` globally so Git does not auto-guess identity. That means this error is expected in repositories where no local profile has been set yet.