Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "gitkit"
version = "0.5.0"
version = "0.6.0"
edition = "2021"
description = "Standalone CLI for configuring git repos — hooks, .gitignore, and .gitattributes"
license = "MIT"
Expand All @@ -21,6 +21,7 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1"
tar = "0.4"
toml = "0.8"
regex = "1"
ureq = "2"

[dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Set up a git repo the way you actually work — one guided flow for hooks, `.git

### Demo

![Demo](assets/demo.gif)
![Demo](demo/dist/demo.gif)

---

Expand Down
Binary file removed assets/demo.gif
Binary file not shown.
14 changes: 14 additions & 0 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ Running `gitkit` with no command starts the interactive wizard.
`git commit --no-verify` and `git push --no-verify` bypass the lock — see [Lock](lock.md) for why
that is accepted rather than defended against.

## Uninstall

| Command | Description |
|---|---|
| `gitkit uninstall` | Remove gitkit hooks from every repository it has touched |
| `gitkit uninstall --data` | Also remove local state under `~/.gitkit` (builds, registry) |
| `gitkit uninstall --yes` | Skip the confirmation prompt |
| `gitkit uninstall --dry-run` | Print what would be done without changing anything |

By default, `gitkit uninstall` lists every repository in the registry, shows what hooks are
installed, and asks for confirmation before removing anything. It restores any hand-written hook
that gitkit had absorbed when it first installed its dispatcher. The gitkit binary itself is never
removed — see [Installation](installation.md#uninstall) for how to remove it.

## Ignore

| Command | Description |
Expand Down
95 changes: 94 additions & 1 deletion docs/hooks.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Hooks
description: Built-in hooks (conventional commits, no-body messages, AI trailer rejection, secret detection, branch naming, invisible Unicode detection) and custom shell commands.
description: Built-in hooks (conventional commits, no-body messages, AI trailer rejection, secret detection, branch naming, invisible Unicode detection, user-defined message rules) and custom shell commands.
order: 4
---

Expand All @@ -15,6 +15,7 @@ Built-ins are embedded in the binary — no network required.
| `conventional-commits` | `commit-msg` | Validates Conventional Commits format |
| `no-body` | `commit-msg` | Rejects a commit message that has a body |
| `no-trailers` | `commit-msg` | Rejects commit messages carrying AI attribution trailers |
| `message-rules` | `commit-msg` | Validates commit messages against user-defined regex rules in `.gitmessage-rules.json` |
| `no-secrets` | `pre-commit` | Detects common secret patterns in staged changes |
| `branch-naming` | `pre-commit` | Validates branch name matches convention |
| `no-invisibles` | `pre-commit` | Rejects added lines carrying invisible Unicode characters |
Expand Down Expand Up @@ -65,6 +66,98 @@ with" line. Genuine human `Co-Authored-By:` trailers are left untouched — a
rule in a prompt is advisory, this hook is not. The commit is refused with
the offending line and its line number; it never rewrites your message.

### `message-rules`

Validates the commit message against **rules you define** in a committed
file at `.gitmessage-rules.json` in the repository root. Each rule is
a regex pattern with a direction (`must_match` or `must_not_match`), a
scope (`subject` or `whole_message`), and a message shown when the rule
fires. Because the rules file is tracked by git, it travels with the
repository and applies to everyone who clones it.

#### Configuring rules

Create `.gitmessage-rules.json` in the repository root with a JSON
array of rules:

```json
[
{
"name": "jira-prefix",
"pattern": "^[A-Z]+-\\d+",
"direction": "must_match",
"scope": "subject",
"message": "Subject must start with a JIRA ticket prefix (e.g. PROJ-123)"
}
]
```

A negative rule — forbidding something — uses `must_not_match`:

```json
[
{
"name": "no-trailer-in-subject",
"pattern": "Co-Authored-By:",
"direction": "must_not_match",
"scope": "subject",
"message": "Subject must not contain trailer lines; move Co-Authored-By to the body"
}
]
```

Commit this file to the repository so every contributor shares the same
rules.

#### Directions

- **`must_match`** — the pattern must match the scoped text. A JIRA prefix
rule is a positive match: the subject must contain `^[A-Z]+-\d+`.
- **`must_not_match`** — the pattern must not match. A forbidden-trailer
rule is a negative match: the subject must not contain `Co-Authored-By:`.
This catches what `no-trailers` misses — a trailer smuggled into the
subject line after a semicolon.

#### Scopes

- **`subject`** — only the first line of the commit message is checked.
- **`whole_message`** — the entire commit message (subject, body, trailers)
is checked.

#### Installing

```bash
gitkit hooks add message-rules
```

If no rules are configured, the command refuses with a clear message rather
than installing a hook that always passes. Patterns are validated at install
time — a regex that does not compile is rejected immediately, naming the
rule and the compile error, not deferred to commit time.

#### Regex flavour

Patterns use **Rust's `regex` crate** syntax, which is ERE-like: character
classes, alternation, grouping, anchors, and quantifiers all work. **No
lookahead, lookbehind, or backreferences** — if a pattern uses these PCRE
features, it will be rejected at configuration time with a compile error.
When in doubt, test the pattern with `rg '<pattern>'` (ripgrep uses the
same engine).

The installed hook delegates to `gitkit` itself for regex evaluation, so
the same engine that validated the pattern at install time evaluates it at
commit time — no lossy conversion to POSIX ERE.

#### Multiple rules

All rules run on every commit. The hook reports **every** failing rule, not
just the first, then exits non-zero. Rules compose — a JIRA prefix rule and
a subject-length rule are separate rules that fire independently.

Revert (`Revert "..."`), merge (`Merge branch '...'`), `fixup!` and
`squash!` commit messages are auto-generated and are always accepted
regardless of rules.

### `no-invisibles`

Rejects a commit that **adds** a line containing an invisible Unicode
Expand Down
20 changes: 19 additions & 1 deletion docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,33 @@ the update to maintain consistency.

## Uninstall

First, remove gitkit's hooks from every repository it has touched:

```bash
gitkit uninstall
```

This lists every repository in the registry, shows what hooks are installed, and asks for
confirmation before removing anything. It restores any hand-written hook that gitkit had absorbed
when it first installed its dispatcher. Add `--data` to also remove local state under `~/.gitkit`
(builds, registry).

Then remove the binary itself:

**Linux / macOS:**

```bash
rm -f ~/.local/bin/gitkit
rm -rf ~/.gitkit/ # saved builds (optional)
```

**Windows (PowerShell):**

```powershell
Remove-Item "$env:LOCALAPPDATA\gitkit\gitkit.exe" -Force
```

If gitkit was installed via `cargo install gitkit`, remove it with:

```bash
cargo uninstall gitkit
```
Loading
Loading