|
| 1 | +# Commit Message Guide (Concise) |
| 2 | + |
| 3 | +Conventional Commits + release-please. Multi-entry (multiple headers in one commit) is MANDATORY whenever a commit affects more than one substantive component (class, module, folder, feature). One header per component. |
| 4 | + |
| 5 | +## 1. Header |
| 6 | + |
| 7 | +Format: `type(scope): summary` |
| 8 | + |
| 9 | +Types: feat | fix | perf | refactor | docs | test | build | ci | chore | style | deps |
| 10 | + |
| 11 | +Scope: EXACT component identifier (file/class/module/folder keyword): e.g. `scriptProperties`, `scriptCompletion`, `languageFiles`, `extension`, `lua`, `build`. Do NOT use umbrella scopes (e.g. `scripts`) or comma scopes. If multiple components change, add additional headers (multi-entry) instead of broadening scope. |
| 12 | + |
| 13 | +Summary: imperative, <=72 chars, no period, describes intent + effect. |
| 14 | + |
| 15 | +## 2. Body |
| 16 | + |
| 17 | +Blank line after header. Optional. With two space indentation per line. One line per significant file or class: |
| 18 | + |
| 19 | +```text |
| 20 | + src/path/file.ts: what changed (why/impact) |
| 21 | + Class Name: what changed (why) |
| 22 | +``` |
| 23 | + |
| 24 | +User-facing first, internal later. Trivial style-only edits: separate `style:` commit or prefix line with `style:`. |
| 25 | + |
| 26 | +### 2.1 Granular Change Points (MANDATORY DETAIL LEVEL) |
| 27 | + |
| 28 | +Body lines MUST reference the most specific changed program element(s) when substantive (logic / API / behavior). Use one line per element category when clarity improves scanability. Prefer stable identifiers. |
| 29 | + |
| 30 | +Allowed granular targets (in order of preference): |
| 31 | + |
| 32 | +* File path (broad grouping) |
| 33 | +* Class \<Name\> |
| 34 | +* Class \<Name\>#methodName(params) |
| 35 | +* function functionName(params) |
| 36 | +* interface \<Name\> / type \<Name\> |
| 37 | +* enum \<Name\> / enum \<Name\>.\<Member\> |
| 38 | +* variable/const \<NAME\> (top-level exported) or prop \<Class\>.\<prop\> |
| 39 | + |
| 40 | +Recommended notation tokens (prefix at start of description segment, optional but encouraged): |
| 41 | + |
| 42 | +* `+` added |
| 43 | +* `-` removed |
| 44 | +* `~` modified (non-breaking) |
| 45 | +* `!` breaking change (also still add BREAKING CHANGE footer if external) |
| 46 | +* `≈` refactored equivalent behavior |
| 47 | +* `Δ` performance-impacting modification |
| 48 | + |
| 49 | +Format patterns: |
| 50 | + |
| 51 | +```text |
| 52 | +Class ScriptProperties#initialize(): +async init path (non-blocking startup) |
| 53 | +function parseLanguageFile(path): ~stream parsing (reduce memory) |
| 54 | +src/scripts/scriptCompletion.ts: Δ loop optimization (avoid quadratic filtering) |
| 55 | +Class CompletionProvider#provideCompletionItems(): ! signature drop 'token' param (internal) (see below) |
| 56 | +const MAX_KEYWORDS: -removed (unused) |
| 57 | +prop ScriptProperties.cache: +added (memoize keyword lookups) |
| 58 | +interface KeywordEntry: ~add optional 'category' |
| 59 | +``` |
| 60 | + |
| 61 | +If multiple tiny related changes occur inside one class, aggregate under the class line and enumerate methods inline separated by semicolons: |
| 62 | + |
| 63 | +```text |
| 64 | +Class LanguageFiles: ~load(); +indexLanguages(); ~parseFile(): streaming SAX |
| 65 | +``` |
| 66 | + |
| 67 | +Signature changes should specify old → new when not obvious: |
| 68 | + |
| 69 | +```text |
| 70 | +Class ScriptProperties#processKeywords(files[]): signature files[] -> sources[] (generalized input) |
| 71 | +``` |
| 72 | + |
| 73 | +Variable lines only for additions/removals/renames of exported or widely used constants / config toggles. Skip local implementation detail variables. |
| 74 | + |
| 75 | +Avoid vague descriptions; specify what changed AND the direct impact (latency, memory, correctness, API surface, safety, etc.). |
| 76 | + |
| 77 | +When a line uses `!` include a concise migration note either in parentheses or in the BREAKING footer. |
| 78 | + |
| 79 | +## 3. Footers |
| 80 | + |
| 81 | +Use only as needed (with the same indentation rules as for the bodies): |
| 82 | + |
| 83 | +* BREAKING CHANGE: impact + migration |
| 84 | +* Closes #123 / Refs #456 |
| 85 | +* deps: bump \<pkg\> from \<old\> to \<new\> |
| 86 | +* (Security) still use fix:; add CVE if any |
| 87 | + |
| 88 | +## 4. Breaking Changes |
| 89 | + |
| 90 | +Only for externally observable or API behavior changes. Each breaking entry (primary or secondary) needs its own `BREAKING CHANGE:` footer placed directly under that entry. |
| 91 | + |
| 92 | +## 5. Multi-Entry Commits (Mandatory for Multi-Component Changes) |
| 93 | + |
| 94 | +Required whenever the diff includes more than one substantive component. Each component gets its own header block. Prefer separate physical commits only if they can stand independently AND keep history bisectable; otherwise multi-entry. |
| 95 | + |
| 96 | +Structure (repeat header/body/footers per component): |
| 97 | + |
| 98 | +```text |
| 99 | +<primary header> |
| 100 | +
|
| 101 | + <primary body> |
| 102 | + <primary footers> |
| 103 | +
|
| 104 | +<secondary header> |
| 105 | + <secondary body (optional)> |
| 106 | + <secondary footers (optional)> |
| 107 | +
|
| 108 | +<tertiary header> ... etc |
| 109 | +``` |
| 110 | + |
| 111 | +Rules: |
| 112 | + |
| 113 | +1. Additional headers start at column 1 after a blank line. |
| 114 | +2. Exactly one component per header (no umbrella or multi-component scopes). |
| 115 | +3. Body lines under a header list ONLY that component's files/classes. |
| 116 | +4. Footers apply only to the immediately preceding header block. |
| 117 | +5. Breaking for multiple components? Duplicate appropriately tailored `BREAKING CHANGE:` footers under each affected header (avoid vague shared footer). |
| 118 | +6. Nothing after the final entry's footers. |
| 119 | +7. Do NOT fabricate extra headers for trivial style noise—exclude or separate into its own commit. |
| 120 | + |
| 121 | +Example (three components changed): |
| 122 | + |
| 123 | +```text |
| 124 | +feat: add v4 UUID support |
| 125 | +
|
| 126 | + Implements UUID v4 generation for scripts. |
| 127 | +
|
| 128 | +fix(utils): unicode encode no longer throws |
| 129 | + BREAKING CHANGE: encode() now returns fallback string instead of throwing. |
| 130 | +
|
| 131 | +feat(utils): add unicode-capable encode variant |
| 132 | +``` |
| 133 | + |
| 134 | +### Indentation Rules (Multi-Entry Metadata) |
| 135 | + |
| 136 | +When including metadata/footer lines (e.g. `PiperOrigin-RevId:`, `Source-Link:`, internal IDs) for secondary or tertiary headers you MAY indent them by two spaces for readability. Indentation is optional; consistency within a commit is preferred. The `BREAKING CHANGE:` footer line itself should NOT be indented (to ensure parsers pick it up cleanly). |
| 137 | + |
| 138 | +Indented example with metadata: |
| 139 | + |
| 140 | +```text |
| 141 | +feat: adds v4 UUID to crypto |
| 142 | +
|
| 143 | +This adds support for v4 UUIDs to the library. |
| 144 | +
|
| 145 | +fix(utils): unicode no longer throws exception |
| 146 | + PiperOrigin-RevId: 345559154 |
| 147 | + BREAKING CHANGE: encode method no longer throws. |
| 148 | + Source-Link: googleapis/googleapis@5e0dcb2 |
| 149 | +
|
| 150 | +feat(utils): update encode to support unicode |
| 151 | + PiperOrigin-RevId: 345559182 |
| 152 | + Source-Link: googleapis/googleapis@e5eef86 |
| 153 | +``` |
| 154 | + |
| 155 | +Parsing expectations: |
| 156 | + |
| 157 | +* Each unindented `type(scope?): summary` line starts a new entry. |
| 158 | +* Indented lines (2 leading spaces) immediately following a header or its body belong to that entry's body/footers. |
| 159 | +* A blank line separates entries. |
| 160 | +* `BREAKING CHANGE:` (indented or not) is associated with the closest preceding header without an intervening new header line. |
| 161 | + |
| 162 | +## 6. Examples |
| 163 | + |
| 164 | +Feature + perf: |
| 165 | + |
| 166 | +```text |
| 167 | +feat(languageFiles): async load with concurrency |
| 168 | +
|
| 169 | + Class LanguageFiles#loadAll(): ~async refactor (prevent extension host blocking) |
| 170 | + function parseLanguageFile(path): +concurrency limit (8 workers) |
| 171 | +
|
| 172 | +feat(scriptProperties): async initialization |
| 173 | +
|
| 174 | + Class ScriptProperties#initialize(): +async; -sync path (non-blocking startup) |
| 175 | + prop ScriptProperties.cache: +added memoization (reduces repeat parsing) |
| 176 | +
|
| 177 | +refactor(extension): await readiness of scriptProperties & languageFiles |
| 178 | +
|
| 179 | + src/extension.ts: ~activation flow (providers registered after data ready) |
| 180 | +``` |
| 181 | + |
| 182 | +Bug fix: |
| 183 | + |
| 184 | +```text |
| 185 | +fix(completion): suppress suggestions inside single-quoted attribute values |
| 186 | +
|
| 187 | + function isInsideSingleQuotedString(text,pos): +added (central quote detection) |
| 188 | + Class CompletionProvider#provideCompletionItems(): ~early return when inside single-quoted value (reduces noise) |
| 189 | +``` |
| 190 | + |
| 191 | +Breaking refactor: |
| 192 | + |
| 193 | +```text |
| 194 | +refactor(api): remove deprecated synchronous init path |
| 195 | + |
| 196 | + src/init/legacyInit.ts: -removed (obsolete) |
| 197 | + Class ExtensionActivator#activate(): ~drop legacyInit(); ~await ScriptProperties.initialize() |
| 198 | + BREAKING CHANGE: callers must await initialize() before provider use (sync path removed). |
| 199 | +``` |
| 200 | + |
| 201 | +## 7. Checklist (Pre-Commit) |
| 202 | + |
| 203 | +1. Every substantive component changed has its own header (no umbrella / multi scopes). |
| 204 | +2. Each header: valid type + precise component scope + imperative summary (<=72 chars, no period). |
| 205 | +3. Bodies: per-header; only files/classes of that component; concise what + why. |
| 206 | +4. Breaking changes duplicated under each affected component's header (if applicable). |
| 207 | +5. Footers (refs, deps) scoped to relevant header only. |
| 208 | +6. No vague verbs (avoid solitary "update", "improve"). |
| 209 | +7. No build artifacts or timestamps. |
| 210 | +8. Regex validation passes for every header. |
| 211 | +9. Only substantive components get headers (don't add headers for trivial whitespace). |
| 212 | + |
| 213 | +## 8. Validation Regex (Header) |
| 214 | + |
| 215 | +```regex |
| 216 | +^(feat|fix|perf|refactor|docs|test|build|ci|chore|style|deps)(\([a-z0-9_.,-]+\))?: [A-Z0-9].{0,71}$ |
| 217 | +``` |
| 218 | + |
| 219 | +Secondary headers in multi-entry commits must also match this pattern starting at column 1 after a blank line. |
| 220 | + |
| 221 | +## 9. Multi-Entry Detection Heuristic |
| 222 | + |
| 223 | +After the first block, treat any line matching the header regex (column 1, preceded by a blank line) as a new entry until EOF. |
| 224 | + |
| 225 | +Adhere to these concise rules for consistent automated releases, readable history, and accurate changelogs. |
0 commit comments