Files
small-scripts/specs/git-identity.spec.md
Paul O'Reilly f21506af19 Add git-identity: switch git author identity from a saved menu
Identities live in ~/.git-identities (alias|Name|email). Interactive
menu to select, add, or remove identities; direct mode via alias
argument; --global/-g scope flag (defaults to local inside a repo,
global fallback outside); --list/--current helpers. Full --dryrun
support and a 27-assertion test suite driven through dryrun.
GIT_IDENTITIES_FILE env override for testability.
2026-08-18 20:20:45 +12:00

103 lines
4.1 KiB
Markdown

# git-identity
## Purpose
Switch the git author identity (user.name + user.email) for the current repo or globally, from a menu of identities saved in `~/.git-identities`.
## Usage
```
git-identity [OPTIONS] [ALIAS]
```
### Arguments
| Argument | Default | Description |
|----------|---------|-------------|
| `ALIAS` | — | Apply this saved identity directly (non-interactive). Omit for the interactive menu. |
### Flags
| Flag | Description |
|------|-------------|
| `--help`, `-h` | Show usage information |
| `--dryrun`, `-n` | Preview all actions without changing config or the identities file |
| `--global`, `-g` | Apply to global git config instead of the current repo |
| `--list`, `-l` | List saved identities and exit |
| `--current`, `-c` | Show the identity in effect for the current directory and exit |
### Environment
| Variable | Description |
|----------|-------------|
| `GIT_IDENTITIES_FILE` | Override the identities file path (default `~/.git-identities`). Used by tests. |
## Identities file format
One identity per line: `alias|Name|email`. Blank lines and lines starting with `#` are ignored.
```
# alias|Name|email
personal|Paul Example|paul@example.net
work|Paul Example|paul.example@corp.com
```
## Behaviour
1. Determine scope: `--global` → global config; otherwise local config if inside a git repository, else fall back to global (with a note).
2. **Direct mode** (`ALIAS` given): look up the alias in the identities file; apply `git config user.name` + `user.email` in the chosen scope; print confirmation showing scope, name, and email.
3. **Interactive mode** (no alias): print the identity currently in effect, then a numbered menu of saved identities plus a final option `s) Something else...`.
- Selecting a number applies that identity (as in direct mode).
- `s` opens a submenu: `a) Add identity`, `r) Remove identity`, `q) Cancel`.
- **Add**: prompts for alias, name, email; validates alias is unique and contains no `|`; validates email contains `@`; appends to the file; then offers to apply it now.
- **Remove**: numbered menu of identities; selected line is deleted from the file. Never touches git config.
- `q` or empty input cancels with exit 0.
4. Applying an identity never modifies the identities file; add/remove never modify git config (except the post-add "apply now" offer).
## Dryrun Behaviour
Every mutating action prints a `[dryrun]` line instead of acting:
- Apply: `[dryrun] Would set user.name 'NAME' and user.email 'EMAIL' (--local in /path/to/repo)` (or `(--global)`)
- Add: `[dryrun] Would append 'alias|NAME|EMAIL' to FILE`
- Remove: `[dryrun] Would remove identity 'alias' from FILE`
Menus and prompts still function in dryrun so a full flow can be rehearsed.
## Edge Cases
| Case | Handling |
|------|----------|
| Identities file missing (interactive) | Offer to create it seeded with the current global identity, then continue to menu |
| Identities file missing (direct/`--list`) | Error: "No identities file at FILE (run git-identity to create one)", exit 1 |
| Unknown alias in direct mode | Error: "No identity 'ALIAS' in FILE", exit 1; list available aliases |
| Empty identities file | Menu shows only "Something else..." |
| Duplicate alias on add | Error: "Alias 'X' already exists", re-prompt |
| Alias containing `\|` | Error: "Alias must not contain '\|'", re-prompt |
| Not in a git repo, no `--global` | Note "not inside a git repository — applying globally", apply to global |
| Malformed line in file | Skipped silently (comment/blank handling covers this) |
## Examples
```bash
# Interactive menu
$ git-identity
Current identity (local): Paul Example <paul@example.net>
1) personal Paul Example <paul@example.net>
2) work Paul Example <paul.example@corp.com>
s) Something else...
Select [1-2/s/q]: 2
Set user.name 'Paul Example' and user.email 'paul.example@corp.com' (--local in /home/paul/dev/myrepo)
# Direct, dryrun
$ git-identity -n work
[dryrun] Would set user.name 'Paul Example' and user.email 'paul.example@corp.com' (--local in /home/paul/dev/myrepo)
# List
$ git-identity --list
personal Paul Example <paul@example.net>
work Paul Example <paul.example@corp.com>
```