gitprofiles 0.2.0

A CLI for managing multiple Git identities with local enforcement
Documentation
# 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.