commitfmt-windows-arm64 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,426 @@
1
+ Metadata-Version: 2.4
2
+ Name: commitfmt-windows-arm64
3
+ Version: 1.2.0
4
+ Summary: Utility for formatting and verifying the commit message.
5
+ Author-email: Mikhael Khrustik <misha@myrt.co>
6
+ License: MIT
7
+ Keywords: git,hook
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Operating System :: Microsoft :: Windows
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+
21
+ <p align="center">
22
+ <img width="350" src="./docs/assets/logo.svg" alt="commitfmt logo" />
23
+ <br />
24
+ <br />
25
+ Utility for formatting and verifying commit messages.
26
+ </p>
27
+
28
+ ---
29
+
30
+ <p align="center">
31
+ <a href="https://github.com/mishamyrt/commitfmt/actions/workflows/qa.yaml">
32
+ <img src="https://github.com/mishamyrt/commitfmt/actions/workflows/qa.yaml/badge.svg" alt="Quality Assurance" />
33
+ </a>
34
+ <a href="https://npmjs.com/package/commitfmt">
35
+ <img src="https://img.shields.io/npm/v/commitfmt.svg?color=red" alt="NPM Version" />
36
+ </a>
37
+ <a href="https://pypi.org/project/commitfmt/">
38
+ <img src="https://img.shields.io/pypi/v/commitfmt.svg?color=blue" alt="PyPI Version" />
39
+ </a>
40
+ </p>
41
+
42
+ commitfmt is an opinionated formatter and configurable linter for Git commit messages. Designed primarily for use in a Git `prepare-commit-msg` hook, it helps keep commit history clean and readable.
43
+
44
+ ## Quick start
45
+
46
+ The example below installs commitfmt as a local npm development dependency. See [Installation](#installation) for standalone and Python alternatives.
47
+
48
+ ```bash
49
+ npm install --save-dev commitfmt
50
+ ```
51
+
52
+ Try the default formatting mode:
53
+
54
+ ```bash
55
+ printf '%s\n' "feat ( parser ) : add new option." | npm exec -- commitfmt
56
+ # feat(parser): add new option
57
+ ```
58
+
59
+ Then add `npm exec -- commitfmt` to a Git `prepare-commit-msg` hook using one of the [hook examples](#hook). No configuration is required for formatting. To enforce project-specific rules, add a [configuration file](#configuration) to the repository root.
60
+
61
+ See [Modes](#modes) for the difference between formatting a message, linting a message, and checking commit history.
62
+
63
+ ## Features
64
+
65
+ ### Formatting
66
+
67
+ By default, commitfmt transforms a message like this:
68
+
69
+ ```
70
+ feat ( scope , scope ) : add new feature.
71
+ body description
72
+ ```
73
+
74
+ into a well-formatted message:
75
+
76
+ ```
77
+ feat(scope, scope): add new feature
78
+
79
+ body description
80
+ ```
81
+
82
+ ### Linting
83
+
84
+ commitfmt can enforce project-specific commit rules.
85
+
86
+ For example, to restrict commit types and scopes, add the following to the [configuration file](#configuration):
87
+
88
+ ```toml
89
+ [lint.header]
90
+ # Check allowed commit type
91
+ type-enum = ["chore", "ci", "feat", "fix", "refactor", "style", "test"]
92
+ # Check allowed commit scopes
93
+ scope-enum = ["cc", "config", "git", "linter"]
94
+
95
+ [lint.footer]
96
+ # Check required footers
97
+ exists = ["Issue-ID", "Authored-By"]
98
+ ```
99
+
100
+ ### Performance
101
+
102
+ commitfmt is designed for low-overhead local hooks. The [comparison benchmark](docs/benchmark.md) records mean latency and throughput for commitfmt and commitlint when checking the current commit and a 10-commit history range.
103
+
104
+ ## Installation
105
+
106
+ Prebuilt binaries are available for the following platforms and installation methods:
107
+
108
+ | OS | Installation script | npm or pip |
109
+ | --- | --- | --- |
110
+ | macOS | x64, arm64 | x64, arm64 |
111
+ | Windows | x64, x86, arm64 | x64, arm64 |
112
+ | Linux | x64, x86, arm64 | x64, arm64 |
113
+
114
+ ### Script
115
+
116
+ You can use a simple [script](https://github.com/mishamyrt/commitfmt/blob/refs/heads/main/scripts/install.sh) to install commitfmt.
117
+ It downloads and installs the latest prebuilt binary.
118
+
119
+ The installer requires Bash, `curl` or `wget`, and the appropriate archive tools: `tar` on macOS and Linux, with `xz` or `unxz` as a fallback, or `unzip` on Windows. On Windows, run it from Git Bash, MSYS2, or Cygwin. The binary is installed in `/usr/local/bin` when that directory is writable; otherwise, it is installed in `~/.local/bin`.
120
+
121
+ ```bash
122
+ # Install latest version
123
+ curl -sSfL https://raw.githubusercontent.com/mishamyrt/commitfmt/refs/heads/main/scripts/install.sh | bash
124
+ ```
125
+
126
+ ### pnpm
127
+
128
+ ```bash
129
+ pnpm add --save-dev commitfmt
130
+ ```
131
+
132
+ ### npm
133
+
134
+ ```bash
135
+ npm install --save-dev commitfmt
136
+ ```
137
+
138
+ ### yarn
139
+
140
+ ```bash
141
+ yarn add --dev commitfmt
142
+ ```
143
+
144
+ ### pip
145
+
146
+ ```bash
147
+ pip install commitfmt
148
+ ```
149
+
150
+ ## Hook
151
+
152
+ After installing commitfmt, add a Git `prepare-commit-msg` hook. You can configure it manually or use any hook manager.
153
+
154
+ > **Command used in hooks:** the examples below assume an installation made with the script or pip and use the direct `commitfmt` command. For a local Node.js development dependency, replace it with `pnpm exec commitfmt`, `npm exec -- commitfmt`, or `yarn exec commitfmt`.
155
+
156
+ ### Script
157
+
158
+ You can use a simple script to add a hook.
159
+
160
+ ```bash
161
+ echo "#!/bin/sh" > .git/hooks/prepare-commit-msg
162
+ echo "commitfmt" >> .git/hooks/prepare-commit-msg
163
+ chmod +x .git/hooks/prepare-commit-msg
164
+ ```
165
+
166
+ ### [Lefthook](https://github.com/evilmartians/lefthook)
167
+
168
+ Add to your `lefthook.yml` file:
169
+
170
+ ```yaml
171
+ prepare-commit-msg:
172
+ jobs:
173
+ - name: format commit message
174
+ run: commitfmt
175
+ args: ""
176
+ ```
177
+
178
+ `args` is intentionally empty: Lefthook otherwise forwards Git hook arguments, while commitfmt locates Git's `COMMIT_EDITMSG` itself.
179
+
180
+ ### [Husky](https://github.com/typicode/husky)
181
+
182
+ Create `.husky/prepare-commit-msg` with the following content:
183
+
184
+ ```bash
185
+ #!/bin/sh
186
+ commitfmt
187
+ ```
188
+
189
+ ## Configuration
190
+
191
+ Core formatting behavior, such as removing extra whitespace, is intentionally not configurable. This keeps formatted commit messages consistent and predictable across a project. Project-specific constraints can be configured using the lint rules below.
192
+
193
+ ### Linting
194
+
195
+ Most linting rules are disabled by default. Two rules are enabled because the formatter can fix them safely:
196
+
197
+ ```toml
198
+ [lint.header]
199
+ description-full-stop = true # Remove trailing periods from header descriptions
200
+
201
+ [lint.footer]
202
+ breaking-exclamation = true # Add ! for BREAKING CHANGE or BREAKING-CHANGE
203
+ ```
204
+
205
+ Breaking-change footer keys are case-sensitive. commitfmt recognizes `BREAKING CHANGE` and `BREAKING-CHANGE`; `BREAKING CHANGES` is not supported.
206
+
207
+ To enable more rules, create a `.commitfmt.toml` or `commitfmt.toml` file in the root of your Git repository. commitfmt loads the configuration from the repository root even when it is run from a subdirectory. If both files exist, `.commitfmt.toml` takes precedence. Available lint rules can be found in the [rules.md](https://github.com/mishamyrt/commitfmt/blob/main/crates/commitfmt-linter/docs/rules.md) file.
208
+
209
+ In the default formatting mode, safe fixes are applied automatically. The command fails on violations that cannot be fixed; when used as a hook, this aborts the commit. In `--lint` mode, the message is never modified and any violation causes a non-zero exit code.
210
+
211
+ #### Unsafe fixes
212
+
213
+ Some rules have fixes that may be undesirable in certain contexts. For example, adding a period to the end of a body may distort an embedded log. Rules with unsafe fixes are marked in the same `rules.md` file.
214
+
215
+ To enable unsafe fixes, add the following to your config file:
216
+
217
+ ```toml
218
+ [lint]
219
+ unsafe-fixes = true
220
+ ```
221
+
222
+ ### Extending
223
+
224
+ To reuse another configuration, add `extends` with a path relative to the current configuration file:
225
+
226
+ ```toml
227
+ extends = "node_modules/commitfmt-config-standard/commitfmt.toml"
228
+ ```
229
+
230
+ Only one inheritance level is supported. If the referenced configuration also contains `extends`, commitfmt returns an error.
231
+
232
+ Configuration inheritance uses a shallow merge:
233
+
234
+ - top-level parser options from the current file override inherited values
235
+ - redefining `[lint.header]`, `[lint.body]`, or `[lint.footer]` replaces the entire inherited group; groups omitted from the current file remain inherited
236
+ - `[lint]` options from the current file replace inherited `[lint]` options, so repeat `unsafe-fixes = true` when the current file defines lint settings and still needs unsafe fixes
237
+ - additional footers from the current file are appended after inherited footers
238
+
239
+ ### Parser Configuration
240
+
241
+ commitfmt can be configured to use custom footer separators and comment symbols for parsing commit messages.
242
+
243
+ #### Footer separators
244
+
245
+ By default, commitfmt uses git's `trailer.separators` configuration to determine which characters separate footer keys from values. You can override this in your config file:
246
+
247
+ ```toml
248
+ footer-separators = ":#"
249
+ ```
250
+
251
+ This allows footers like `Issue-ID: 123` or `Issue-ID #123` to be recognized.
252
+
253
+ #### Comment symbol
254
+
255
+ By default, commitfmt uses git's `core.commentChar` or `core.commentString` configuration to identify comment lines in commit messages. You can override this:
256
+
257
+ ```toml
258
+ comment-symbol = "//"
259
+ ```
260
+
261
+ Comment lines immediately after the header and at the end of the commit message are ignored during parsing. Comment lines elsewhere are preserved as message content, and the header is always parsed as content.
262
+
263
+ ### Additional footers
264
+
265
+ commitfmt can add additional footers to the commit message.
266
+
267
+ #### Static value
268
+
269
+ You can add a footer with a static value:
270
+
271
+ ```toml
272
+ [[additional-footers]]
273
+ key = "Authored-By"
274
+ value = "John Doe"
275
+ ```
276
+
277
+ #### Shell commands
278
+
279
+ You can use shell commands to dynamically generate footer values:
280
+
281
+ ```toml
282
+ [[additional-footers]]
283
+ key = "Authored-By"
284
+ value = "{{ printf %s \"$USER\" }}"
285
+ ```
286
+
287
+ Commands are executed with `sh -c`. The `sh` executable and any external commands must be available through `PATH`; shell built-ins such as `printf` require no separate executable. A non-zero exit code aborts formatting, and trailing line endings are removed from standard output before it is used as the footer value.
288
+
289
+ > **Security:** shell templates execute arbitrary commands. Use configuration files only from trusted sources.
290
+
291
+ #### Branch value pattern
292
+
293
+ You can also add the ticket number from the task tracker to the footer if it is in the branch name:
294
+
295
+ ```toml
296
+ [[additional-footers]]
297
+ key = "Ticket-ID"
298
+ branch-pattern = "^(?:[^/]+/)*(?<TICKET_ID>[A-Z][A-Z0-9]*-[0-9]+)(?:/.*)?$"
299
+ value = "${{ TICKET_ID }}"
300
+ ```
301
+
302
+ For example, if your branch name is `feature/CC-123/add-new-feature` or `feature/CC-123`, the `Ticket-ID` footer will be added to the commit message with the value `CC-123`.
303
+
304
+ The named capture is available to the value template as `TICKET_ID`. If the current branch is unavailable or does not match the pattern, the footer is skipped.
305
+
306
+ Branch patterns use the [regex-lite syntax](https://docs.rs/regex-lite/latest/regex_lite/#syntax).
307
+ Unicode character classes and Unicode-aware case folding are not supported.
308
+
309
+ ##### Patterns
310
+
311
+ Examples of patterns for branch names in git flow format:
312
+
313
+ - Jira/YouTrack: `^(?:[^/]+/)*(?<TICKET_ID>[A-Z][A-Z0-9]*-[0-9]+)(?:/.*)?$`
314
+ - `feature/CFMT-123`
315
+ - `feature/CFMT-123/add-new-feature`
316
+ - GitHub: `^(?:[^/]+/)*(?<ISSUE_ID>[0-9]+)(?:/.*)?$`
317
+ - `feature/123`
318
+ - `feature/123/add-new-feature`
319
+
320
+ #### On conflict
321
+
322
+ If the message already contains a footer with the same key, `on-conflict` determines whether commitfmt adds the configured footer. The existing footer is never removed.
323
+
324
+ ```toml
325
+ [[additional-footers]]
326
+ key = "Ticket-ID"
327
+ branch-pattern = "^(?:[^/]+/)*(?<TICKET_ID>[A-Z][A-Z0-9]*-[0-9]+)(?:/.*)?$"
328
+ value = "${{ TICKET_ID }}"
329
+ on-conflict = "error" # optional; default: skip
330
+ ```
331
+
332
+ Available options:
333
+
334
+ - `skip` — keep the existing footer and do not add the configured one (default)
335
+ - `append` — keep the existing footer and add the configured one at the end
336
+ - `error` — return an error without writing the formatted message; when running as a hook, this aborts the commit
337
+
338
+ #### Footer formatting
339
+
340
+ You can customize how footers are formatted using `separator` and `alignment`. The separator must be a single character.
341
+
342
+ ```toml
343
+ [[additional-footers]]
344
+ key = "Ticket-ID"
345
+ value = "CFMT-123"
346
+ separator = "#"
347
+ alignment = "right"
348
+ ```
349
+
350
+ Available alignment options:
351
+
352
+ - `left` — no space before the separator and one after it (default): `Ticket-ID# CFMT-123`
353
+ - `right` — one space before the separator and none after it: `Ticket-ID #CFMT-123`
354
+
355
+ ### Recipe
356
+
357
+ To enforce conventional commits, you can use the following configuration:
358
+
359
+ ```toml
360
+ [lint.header]
361
+ type-enum = ["chore", "ci", "feat", "fix", "refactor", "style", "test", "docs", "revert"]
362
+ description-case = "lower-first"
363
+ description-max-length = 72
364
+ description-full-stop = true
365
+ type-required = true
366
+ # scope-enum = ["cc", "config", "git", "linter"] # optional
367
+
368
+ [lint.body]
369
+ max-line-length = 72
370
+ case = "upper-first"
371
+
372
+ [lint.footer]
373
+ breaking-exclamation = true
374
+ ```
375
+
376
+ ## Modes
377
+
378
+ ### Formatting mode
379
+
380
+ Formatting is the default mode. commitfmt normalizes the message structure and applies fixes from enabled rules. Safe fixes are always applied; unsafe fixes are applied only when [`unsafe-fixes`](#unsafe-fixes) is enabled. An unfixable violation returns a non-zero exit status and prevents the formatted message from being written.
381
+
382
+ When a message is supplied through standard input, `commitfmt` formats it and writes the result to standard output:
383
+
384
+ ```bash
385
+ printf '%s\n' "chore ( test ) : test commit." | commitfmt
386
+ # chore(test): test commit
387
+
388
+ # or
389
+ commitfmt < commit_text.txt
390
+ ```
391
+
392
+ The command exits with a non-zero status if configuration loading, parsing, or an unfixable rule fails.
393
+
394
+ When run without redirected standard input during an active Git commit, as in a `prepare-commit-msg` hook, commitfmt reads and updates Git's `COMMIT_EDITMSG` file instead of writing the formatted message to standard output.
395
+
396
+ ### Lint mode
397
+
398
+ Use `--lint` to check a message without modifying it or applying available fixes:
399
+
400
+ ```bash
401
+ printf '%s\n' "chore(test): test commit" | commitfmt --lint
402
+ ```
403
+
404
+ A valid message produces no output and exits with status 0. Any violation produces a report and a non-zero exit status. When used in a hook, lint mode checks `COMMIT_EDITMSG` but never rewrites it.
405
+
406
+ ### History mode
407
+
408
+ Use `--from` to lint a Git commit range:
409
+
410
+ ```bash
411
+ commitfmt --from HEAD~20
412
+ # or
413
+ commitfmt --from v1.1.0 --to HEAD
414
+ ```
415
+
416
+ The lower boundary is excluded. `--to` defaults to `HEAD` and can only be used together with `--from`. History mode never modifies existing commits and always lints messages, so `--lint` is unnecessary and ignored.
417
+
418
+ ## Ignoring commits
419
+
420
+ commitfmt skips messages whose text starts with the exact, case-sensitive prefix `Merge` or `Revert`. This avoids rewriting Git-generated merge and revert messages.
421
+
422
+ The check is based only on the message text, not commit metadata: `merge`, `revert:`, and other differently cased prefixes are not skipped, while any custom message starting with `Merge` or `Revert` is skipped. This applies both when formatting a single message and when linting history.
423
+
424
+ ## License
425
+
426
+ [MIT](./LICENSE).
@@ -0,0 +1,406 @@
1
+ <p align="center">
2
+ <img width="350" src="./docs/assets/logo.svg" alt="commitfmt logo" />
3
+ <br />
4
+ <br />
5
+ Utility for formatting and verifying commit messages.
6
+ </p>
7
+
8
+ ---
9
+
10
+ <p align="center">
11
+ <a href="https://github.com/mishamyrt/commitfmt/actions/workflows/qa.yaml">
12
+ <img src="https://github.com/mishamyrt/commitfmt/actions/workflows/qa.yaml/badge.svg" alt="Quality Assurance" />
13
+ </a>
14
+ <a href="https://npmjs.com/package/commitfmt">
15
+ <img src="https://img.shields.io/npm/v/commitfmt.svg?color=red" alt="NPM Version" />
16
+ </a>
17
+ <a href="https://pypi.org/project/commitfmt/">
18
+ <img src="https://img.shields.io/pypi/v/commitfmt.svg?color=blue" alt="PyPI Version" />
19
+ </a>
20
+ </p>
21
+
22
+ commitfmt is an opinionated formatter and configurable linter for Git commit messages. Designed primarily for use in a Git `prepare-commit-msg` hook, it helps keep commit history clean and readable.
23
+
24
+ ## Quick start
25
+
26
+ The example below installs commitfmt as a local npm development dependency. See [Installation](#installation) for standalone and Python alternatives.
27
+
28
+ ```bash
29
+ npm install --save-dev commitfmt
30
+ ```
31
+
32
+ Try the default formatting mode:
33
+
34
+ ```bash
35
+ printf '%s\n' "feat ( parser ) : add new option." | npm exec -- commitfmt
36
+ # feat(parser): add new option
37
+ ```
38
+
39
+ Then add `npm exec -- commitfmt` to a Git `prepare-commit-msg` hook using one of the [hook examples](#hook). No configuration is required for formatting. To enforce project-specific rules, add a [configuration file](#configuration) to the repository root.
40
+
41
+ See [Modes](#modes) for the difference between formatting a message, linting a message, and checking commit history.
42
+
43
+ ## Features
44
+
45
+ ### Formatting
46
+
47
+ By default, commitfmt transforms a message like this:
48
+
49
+ ```
50
+ feat ( scope , scope ) : add new feature.
51
+ body description
52
+ ```
53
+
54
+ into a well-formatted message:
55
+
56
+ ```
57
+ feat(scope, scope): add new feature
58
+
59
+ body description
60
+ ```
61
+
62
+ ### Linting
63
+
64
+ commitfmt can enforce project-specific commit rules.
65
+
66
+ For example, to restrict commit types and scopes, add the following to the [configuration file](#configuration):
67
+
68
+ ```toml
69
+ [lint.header]
70
+ # Check allowed commit type
71
+ type-enum = ["chore", "ci", "feat", "fix", "refactor", "style", "test"]
72
+ # Check allowed commit scopes
73
+ scope-enum = ["cc", "config", "git", "linter"]
74
+
75
+ [lint.footer]
76
+ # Check required footers
77
+ exists = ["Issue-ID", "Authored-By"]
78
+ ```
79
+
80
+ ### Performance
81
+
82
+ commitfmt is designed for low-overhead local hooks. The [comparison benchmark](docs/benchmark.md) records mean latency and throughput for commitfmt and commitlint when checking the current commit and a 10-commit history range.
83
+
84
+ ## Installation
85
+
86
+ Prebuilt binaries are available for the following platforms and installation methods:
87
+
88
+ | OS | Installation script | npm or pip |
89
+ | --- | --- | --- |
90
+ | macOS | x64, arm64 | x64, arm64 |
91
+ | Windows | x64, x86, arm64 | x64, arm64 |
92
+ | Linux | x64, x86, arm64 | x64, arm64 |
93
+
94
+ ### Script
95
+
96
+ You can use a simple [script](https://github.com/mishamyrt/commitfmt/blob/refs/heads/main/scripts/install.sh) to install commitfmt.
97
+ It downloads and installs the latest prebuilt binary.
98
+
99
+ The installer requires Bash, `curl` or `wget`, and the appropriate archive tools: `tar` on macOS and Linux, with `xz` or `unxz` as a fallback, or `unzip` on Windows. On Windows, run it from Git Bash, MSYS2, or Cygwin. The binary is installed in `/usr/local/bin` when that directory is writable; otherwise, it is installed in `~/.local/bin`.
100
+
101
+ ```bash
102
+ # Install latest version
103
+ curl -sSfL https://raw.githubusercontent.com/mishamyrt/commitfmt/refs/heads/main/scripts/install.sh | bash
104
+ ```
105
+
106
+ ### pnpm
107
+
108
+ ```bash
109
+ pnpm add --save-dev commitfmt
110
+ ```
111
+
112
+ ### npm
113
+
114
+ ```bash
115
+ npm install --save-dev commitfmt
116
+ ```
117
+
118
+ ### yarn
119
+
120
+ ```bash
121
+ yarn add --dev commitfmt
122
+ ```
123
+
124
+ ### pip
125
+
126
+ ```bash
127
+ pip install commitfmt
128
+ ```
129
+
130
+ ## Hook
131
+
132
+ After installing commitfmt, add a Git `prepare-commit-msg` hook. You can configure it manually or use any hook manager.
133
+
134
+ > **Command used in hooks:** the examples below assume an installation made with the script or pip and use the direct `commitfmt` command. For a local Node.js development dependency, replace it with `pnpm exec commitfmt`, `npm exec -- commitfmt`, or `yarn exec commitfmt`.
135
+
136
+ ### Script
137
+
138
+ You can use a simple script to add a hook.
139
+
140
+ ```bash
141
+ echo "#!/bin/sh" > .git/hooks/prepare-commit-msg
142
+ echo "commitfmt" >> .git/hooks/prepare-commit-msg
143
+ chmod +x .git/hooks/prepare-commit-msg
144
+ ```
145
+
146
+ ### [Lefthook](https://github.com/evilmartians/lefthook)
147
+
148
+ Add to your `lefthook.yml` file:
149
+
150
+ ```yaml
151
+ prepare-commit-msg:
152
+ jobs:
153
+ - name: format commit message
154
+ run: commitfmt
155
+ args: ""
156
+ ```
157
+
158
+ `args` is intentionally empty: Lefthook otherwise forwards Git hook arguments, while commitfmt locates Git's `COMMIT_EDITMSG` itself.
159
+
160
+ ### [Husky](https://github.com/typicode/husky)
161
+
162
+ Create `.husky/prepare-commit-msg` with the following content:
163
+
164
+ ```bash
165
+ #!/bin/sh
166
+ commitfmt
167
+ ```
168
+
169
+ ## Configuration
170
+
171
+ Core formatting behavior, such as removing extra whitespace, is intentionally not configurable. This keeps formatted commit messages consistent and predictable across a project. Project-specific constraints can be configured using the lint rules below.
172
+
173
+ ### Linting
174
+
175
+ Most linting rules are disabled by default. Two rules are enabled because the formatter can fix them safely:
176
+
177
+ ```toml
178
+ [lint.header]
179
+ description-full-stop = true # Remove trailing periods from header descriptions
180
+
181
+ [lint.footer]
182
+ breaking-exclamation = true # Add ! for BREAKING CHANGE or BREAKING-CHANGE
183
+ ```
184
+
185
+ Breaking-change footer keys are case-sensitive. commitfmt recognizes `BREAKING CHANGE` and `BREAKING-CHANGE`; `BREAKING CHANGES` is not supported.
186
+
187
+ To enable more rules, create a `.commitfmt.toml` or `commitfmt.toml` file in the root of your Git repository. commitfmt loads the configuration from the repository root even when it is run from a subdirectory. If both files exist, `.commitfmt.toml` takes precedence. Available lint rules can be found in the [rules.md](https://github.com/mishamyrt/commitfmt/blob/main/crates/commitfmt-linter/docs/rules.md) file.
188
+
189
+ In the default formatting mode, safe fixes are applied automatically. The command fails on violations that cannot be fixed; when used as a hook, this aborts the commit. In `--lint` mode, the message is never modified and any violation causes a non-zero exit code.
190
+
191
+ #### Unsafe fixes
192
+
193
+ Some rules have fixes that may be undesirable in certain contexts. For example, adding a period to the end of a body may distort an embedded log. Rules with unsafe fixes are marked in the same `rules.md` file.
194
+
195
+ To enable unsafe fixes, add the following to your config file:
196
+
197
+ ```toml
198
+ [lint]
199
+ unsafe-fixes = true
200
+ ```
201
+
202
+ ### Extending
203
+
204
+ To reuse another configuration, add `extends` with a path relative to the current configuration file:
205
+
206
+ ```toml
207
+ extends = "node_modules/commitfmt-config-standard/commitfmt.toml"
208
+ ```
209
+
210
+ Only one inheritance level is supported. If the referenced configuration also contains `extends`, commitfmt returns an error.
211
+
212
+ Configuration inheritance uses a shallow merge:
213
+
214
+ - top-level parser options from the current file override inherited values
215
+ - redefining `[lint.header]`, `[lint.body]`, or `[lint.footer]` replaces the entire inherited group; groups omitted from the current file remain inherited
216
+ - `[lint]` options from the current file replace inherited `[lint]` options, so repeat `unsafe-fixes = true` when the current file defines lint settings and still needs unsafe fixes
217
+ - additional footers from the current file are appended after inherited footers
218
+
219
+ ### Parser Configuration
220
+
221
+ commitfmt can be configured to use custom footer separators and comment symbols for parsing commit messages.
222
+
223
+ #### Footer separators
224
+
225
+ By default, commitfmt uses git's `trailer.separators` configuration to determine which characters separate footer keys from values. You can override this in your config file:
226
+
227
+ ```toml
228
+ footer-separators = ":#"
229
+ ```
230
+
231
+ This allows footers like `Issue-ID: 123` or `Issue-ID #123` to be recognized.
232
+
233
+ #### Comment symbol
234
+
235
+ By default, commitfmt uses git's `core.commentChar` or `core.commentString` configuration to identify comment lines in commit messages. You can override this:
236
+
237
+ ```toml
238
+ comment-symbol = "//"
239
+ ```
240
+
241
+ Comment lines immediately after the header and at the end of the commit message are ignored during parsing. Comment lines elsewhere are preserved as message content, and the header is always parsed as content.
242
+
243
+ ### Additional footers
244
+
245
+ commitfmt can add additional footers to the commit message.
246
+
247
+ #### Static value
248
+
249
+ You can add a footer with a static value:
250
+
251
+ ```toml
252
+ [[additional-footers]]
253
+ key = "Authored-By"
254
+ value = "John Doe"
255
+ ```
256
+
257
+ #### Shell commands
258
+
259
+ You can use shell commands to dynamically generate footer values:
260
+
261
+ ```toml
262
+ [[additional-footers]]
263
+ key = "Authored-By"
264
+ value = "{{ printf %s \"$USER\" }}"
265
+ ```
266
+
267
+ Commands are executed with `sh -c`. The `sh` executable and any external commands must be available through `PATH`; shell built-ins such as `printf` require no separate executable. A non-zero exit code aborts formatting, and trailing line endings are removed from standard output before it is used as the footer value.
268
+
269
+ > **Security:** shell templates execute arbitrary commands. Use configuration files only from trusted sources.
270
+
271
+ #### Branch value pattern
272
+
273
+ You can also add the ticket number from the task tracker to the footer if it is in the branch name:
274
+
275
+ ```toml
276
+ [[additional-footers]]
277
+ key = "Ticket-ID"
278
+ branch-pattern = "^(?:[^/]+/)*(?<TICKET_ID>[A-Z][A-Z0-9]*-[0-9]+)(?:/.*)?$"
279
+ value = "${{ TICKET_ID }}"
280
+ ```
281
+
282
+ For example, if your branch name is `feature/CC-123/add-new-feature` or `feature/CC-123`, the `Ticket-ID` footer will be added to the commit message with the value `CC-123`.
283
+
284
+ The named capture is available to the value template as `TICKET_ID`. If the current branch is unavailable or does not match the pattern, the footer is skipped.
285
+
286
+ Branch patterns use the [regex-lite syntax](https://docs.rs/regex-lite/latest/regex_lite/#syntax).
287
+ Unicode character classes and Unicode-aware case folding are not supported.
288
+
289
+ ##### Patterns
290
+
291
+ Examples of patterns for branch names in git flow format:
292
+
293
+ - Jira/YouTrack: `^(?:[^/]+/)*(?<TICKET_ID>[A-Z][A-Z0-9]*-[0-9]+)(?:/.*)?$`
294
+ - `feature/CFMT-123`
295
+ - `feature/CFMT-123/add-new-feature`
296
+ - GitHub: `^(?:[^/]+/)*(?<ISSUE_ID>[0-9]+)(?:/.*)?$`
297
+ - `feature/123`
298
+ - `feature/123/add-new-feature`
299
+
300
+ #### On conflict
301
+
302
+ If the message already contains a footer with the same key, `on-conflict` determines whether commitfmt adds the configured footer. The existing footer is never removed.
303
+
304
+ ```toml
305
+ [[additional-footers]]
306
+ key = "Ticket-ID"
307
+ branch-pattern = "^(?:[^/]+/)*(?<TICKET_ID>[A-Z][A-Z0-9]*-[0-9]+)(?:/.*)?$"
308
+ value = "${{ TICKET_ID }}"
309
+ on-conflict = "error" # optional; default: skip
310
+ ```
311
+
312
+ Available options:
313
+
314
+ - `skip` — keep the existing footer and do not add the configured one (default)
315
+ - `append` — keep the existing footer and add the configured one at the end
316
+ - `error` — return an error without writing the formatted message; when running as a hook, this aborts the commit
317
+
318
+ #### Footer formatting
319
+
320
+ You can customize how footers are formatted using `separator` and `alignment`. The separator must be a single character.
321
+
322
+ ```toml
323
+ [[additional-footers]]
324
+ key = "Ticket-ID"
325
+ value = "CFMT-123"
326
+ separator = "#"
327
+ alignment = "right"
328
+ ```
329
+
330
+ Available alignment options:
331
+
332
+ - `left` — no space before the separator and one after it (default): `Ticket-ID# CFMT-123`
333
+ - `right` — one space before the separator and none after it: `Ticket-ID #CFMT-123`
334
+
335
+ ### Recipe
336
+
337
+ To enforce conventional commits, you can use the following configuration:
338
+
339
+ ```toml
340
+ [lint.header]
341
+ type-enum = ["chore", "ci", "feat", "fix", "refactor", "style", "test", "docs", "revert"]
342
+ description-case = "lower-first"
343
+ description-max-length = 72
344
+ description-full-stop = true
345
+ type-required = true
346
+ # scope-enum = ["cc", "config", "git", "linter"] # optional
347
+
348
+ [lint.body]
349
+ max-line-length = 72
350
+ case = "upper-first"
351
+
352
+ [lint.footer]
353
+ breaking-exclamation = true
354
+ ```
355
+
356
+ ## Modes
357
+
358
+ ### Formatting mode
359
+
360
+ Formatting is the default mode. commitfmt normalizes the message structure and applies fixes from enabled rules. Safe fixes are always applied; unsafe fixes are applied only when [`unsafe-fixes`](#unsafe-fixes) is enabled. An unfixable violation returns a non-zero exit status and prevents the formatted message from being written.
361
+
362
+ When a message is supplied through standard input, `commitfmt` formats it and writes the result to standard output:
363
+
364
+ ```bash
365
+ printf '%s\n' "chore ( test ) : test commit." | commitfmt
366
+ # chore(test): test commit
367
+
368
+ # or
369
+ commitfmt < commit_text.txt
370
+ ```
371
+
372
+ The command exits with a non-zero status if configuration loading, parsing, or an unfixable rule fails.
373
+
374
+ When run without redirected standard input during an active Git commit, as in a `prepare-commit-msg` hook, commitfmt reads and updates Git's `COMMIT_EDITMSG` file instead of writing the formatted message to standard output.
375
+
376
+ ### Lint mode
377
+
378
+ Use `--lint` to check a message without modifying it or applying available fixes:
379
+
380
+ ```bash
381
+ printf '%s\n' "chore(test): test commit" | commitfmt --lint
382
+ ```
383
+
384
+ A valid message produces no output and exits with status 0. Any violation produces a report and a non-zero exit status. When used in a hook, lint mode checks `COMMIT_EDITMSG` but never rewrites it.
385
+
386
+ ### History mode
387
+
388
+ Use `--from` to lint a Git commit range:
389
+
390
+ ```bash
391
+ commitfmt --from HEAD~20
392
+ # or
393
+ commitfmt --from v1.1.0 --to HEAD
394
+ ```
395
+
396
+ The lower boundary is excluded. `--to` defaults to `HEAD` and can only be used together with `--from`. History mode never modifies existing commits and always lints messages, so `--lint` is unnecessary and ignored.
397
+
398
+ ## Ignoring commits
399
+
400
+ commitfmt skips messages whose text starts with the exact, case-sensitive prefix `Merge` or `Revert`. This avoids rewriting Git-generated merge and revert messages.
401
+
402
+ The check is based only on the message text, not commit metadata: `merge`, `revert:`, and other differently cased prefixes are not skipped, while any custom message starting with `Merge` or `Revert` is skipped. This applies both when formatting a single message and when linting history.
403
+
404
+ ## License
405
+
406
+ [MIT](./LICENSE).
@@ -0,0 +1 @@
1
+ """commitfmt binary for Windows arm64."""
@@ -0,0 +1,426 @@
1
+ Metadata-Version: 2.4
2
+ Name: commitfmt-windows-arm64
3
+ Version: 1.2.0
4
+ Summary: Utility for formatting and verifying the commit message.
5
+ Author-email: Mikhael Khrustik <misha@myrt.co>
6
+ License: MIT
7
+ Keywords: git,hook
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Operating System :: Microsoft :: Windows
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+
21
+ <p align="center">
22
+ <img width="350" src="./docs/assets/logo.svg" alt="commitfmt logo" />
23
+ <br />
24
+ <br />
25
+ Utility for formatting and verifying commit messages.
26
+ </p>
27
+
28
+ ---
29
+
30
+ <p align="center">
31
+ <a href="https://github.com/mishamyrt/commitfmt/actions/workflows/qa.yaml">
32
+ <img src="https://github.com/mishamyrt/commitfmt/actions/workflows/qa.yaml/badge.svg" alt="Quality Assurance" />
33
+ </a>
34
+ <a href="https://npmjs.com/package/commitfmt">
35
+ <img src="https://img.shields.io/npm/v/commitfmt.svg?color=red" alt="NPM Version" />
36
+ </a>
37
+ <a href="https://pypi.org/project/commitfmt/">
38
+ <img src="https://img.shields.io/pypi/v/commitfmt.svg?color=blue" alt="PyPI Version" />
39
+ </a>
40
+ </p>
41
+
42
+ commitfmt is an opinionated formatter and configurable linter for Git commit messages. Designed primarily for use in a Git `prepare-commit-msg` hook, it helps keep commit history clean and readable.
43
+
44
+ ## Quick start
45
+
46
+ The example below installs commitfmt as a local npm development dependency. See [Installation](#installation) for standalone and Python alternatives.
47
+
48
+ ```bash
49
+ npm install --save-dev commitfmt
50
+ ```
51
+
52
+ Try the default formatting mode:
53
+
54
+ ```bash
55
+ printf '%s\n' "feat ( parser ) : add new option." | npm exec -- commitfmt
56
+ # feat(parser): add new option
57
+ ```
58
+
59
+ Then add `npm exec -- commitfmt` to a Git `prepare-commit-msg` hook using one of the [hook examples](#hook). No configuration is required for formatting. To enforce project-specific rules, add a [configuration file](#configuration) to the repository root.
60
+
61
+ See [Modes](#modes) for the difference between formatting a message, linting a message, and checking commit history.
62
+
63
+ ## Features
64
+
65
+ ### Formatting
66
+
67
+ By default, commitfmt transforms a message like this:
68
+
69
+ ```
70
+ feat ( scope , scope ) : add new feature.
71
+ body description
72
+ ```
73
+
74
+ into a well-formatted message:
75
+
76
+ ```
77
+ feat(scope, scope): add new feature
78
+
79
+ body description
80
+ ```
81
+
82
+ ### Linting
83
+
84
+ commitfmt can enforce project-specific commit rules.
85
+
86
+ For example, to restrict commit types and scopes, add the following to the [configuration file](#configuration):
87
+
88
+ ```toml
89
+ [lint.header]
90
+ # Check allowed commit type
91
+ type-enum = ["chore", "ci", "feat", "fix", "refactor", "style", "test"]
92
+ # Check allowed commit scopes
93
+ scope-enum = ["cc", "config", "git", "linter"]
94
+
95
+ [lint.footer]
96
+ # Check required footers
97
+ exists = ["Issue-ID", "Authored-By"]
98
+ ```
99
+
100
+ ### Performance
101
+
102
+ commitfmt is designed for low-overhead local hooks. The [comparison benchmark](docs/benchmark.md) records mean latency and throughput for commitfmt and commitlint when checking the current commit and a 10-commit history range.
103
+
104
+ ## Installation
105
+
106
+ Prebuilt binaries are available for the following platforms and installation methods:
107
+
108
+ | OS | Installation script | npm or pip |
109
+ | --- | --- | --- |
110
+ | macOS | x64, arm64 | x64, arm64 |
111
+ | Windows | x64, x86, arm64 | x64, arm64 |
112
+ | Linux | x64, x86, arm64 | x64, arm64 |
113
+
114
+ ### Script
115
+
116
+ You can use a simple [script](https://github.com/mishamyrt/commitfmt/blob/refs/heads/main/scripts/install.sh) to install commitfmt.
117
+ It downloads and installs the latest prebuilt binary.
118
+
119
+ The installer requires Bash, `curl` or `wget`, and the appropriate archive tools: `tar` on macOS and Linux, with `xz` or `unxz` as a fallback, or `unzip` on Windows. On Windows, run it from Git Bash, MSYS2, or Cygwin. The binary is installed in `/usr/local/bin` when that directory is writable; otherwise, it is installed in `~/.local/bin`.
120
+
121
+ ```bash
122
+ # Install latest version
123
+ curl -sSfL https://raw.githubusercontent.com/mishamyrt/commitfmt/refs/heads/main/scripts/install.sh | bash
124
+ ```
125
+
126
+ ### pnpm
127
+
128
+ ```bash
129
+ pnpm add --save-dev commitfmt
130
+ ```
131
+
132
+ ### npm
133
+
134
+ ```bash
135
+ npm install --save-dev commitfmt
136
+ ```
137
+
138
+ ### yarn
139
+
140
+ ```bash
141
+ yarn add --dev commitfmt
142
+ ```
143
+
144
+ ### pip
145
+
146
+ ```bash
147
+ pip install commitfmt
148
+ ```
149
+
150
+ ## Hook
151
+
152
+ After installing commitfmt, add a Git `prepare-commit-msg` hook. You can configure it manually or use any hook manager.
153
+
154
+ > **Command used in hooks:** the examples below assume an installation made with the script or pip and use the direct `commitfmt` command. For a local Node.js development dependency, replace it with `pnpm exec commitfmt`, `npm exec -- commitfmt`, or `yarn exec commitfmt`.
155
+
156
+ ### Script
157
+
158
+ You can use a simple script to add a hook.
159
+
160
+ ```bash
161
+ echo "#!/bin/sh" > .git/hooks/prepare-commit-msg
162
+ echo "commitfmt" >> .git/hooks/prepare-commit-msg
163
+ chmod +x .git/hooks/prepare-commit-msg
164
+ ```
165
+
166
+ ### [Lefthook](https://github.com/evilmartians/lefthook)
167
+
168
+ Add to your `lefthook.yml` file:
169
+
170
+ ```yaml
171
+ prepare-commit-msg:
172
+ jobs:
173
+ - name: format commit message
174
+ run: commitfmt
175
+ args: ""
176
+ ```
177
+
178
+ `args` is intentionally empty: Lefthook otherwise forwards Git hook arguments, while commitfmt locates Git's `COMMIT_EDITMSG` itself.
179
+
180
+ ### [Husky](https://github.com/typicode/husky)
181
+
182
+ Create `.husky/prepare-commit-msg` with the following content:
183
+
184
+ ```bash
185
+ #!/bin/sh
186
+ commitfmt
187
+ ```
188
+
189
+ ## Configuration
190
+
191
+ Core formatting behavior, such as removing extra whitespace, is intentionally not configurable. This keeps formatted commit messages consistent and predictable across a project. Project-specific constraints can be configured using the lint rules below.
192
+
193
+ ### Linting
194
+
195
+ Most linting rules are disabled by default. Two rules are enabled because the formatter can fix them safely:
196
+
197
+ ```toml
198
+ [lint.header]
199
+ description-full-stop = true # Remove trailing periods from header descriptions
200
+
201
+ [lint.footer]
202
+ breaking-exclamation = true # Add ! for BREAKING CHANGE or BREAKING-CHANGE
203
+ ```
204
+
205
+ Breaking-change footer keys are case-sensitive. commitfmt recognizes `BREAKING CHANGE` and `BREAKING-CHANGE`; `BREAKING CHANGES` is not supported.
206
+
207
+ To enable more rules, create a `.commitfmt.toml` or `commitfmt.toml` file in the root of your Git repository. commitfmt loads the configuration from the repository root even when it is run from a subdirectory. If both files exist, `.commitfmt.toml` takes precedence. Available lint rules can be found in the [rules.md](https://github.com/mishamyrt/commitfmt/blob/main/crates/commitfmt-linter/docs/rules.md) file.
208
+
209
+ In the default formatting mode, safe fixes are applied automatically. The command fails on violations that cannot be fixed; when used as a hook, this aborts the commit. In `--lint` mode, the message is never modified and any violation causes a non-zero exit code.
210
+
211
+ #### Unsafe fixes
212
+
213
+ Some rules have fixes that may be undesirable in certain contexts. For example, adding a period to the end of a body may distort an embedded log. Rules with unsafe fixes are marked in the same `rules.md` file.
214
+
215
+ To enable unsafe fixes, add the following to your config file:
216
+
217
+ ```toml
218
+ [lint]
219
+ unsafe-fixes = true
220
+ ```
221
+
222
+ ### Extending
223
+
224
+ To reuse another configuration, add `extends` with a path relative to the current configuration file:
225
+
226
+ ```toml
227
+ extends = "node_modules/commitfmt-config-standard/commitfmt.toml"
228
+ ```
229
+
230
+ Only one inheritance level is supported. If the referenced configuration also contains `extends`, commitfmt returns an error.
231
+
232
+ Configuration inheritance uses a shallow merge:
233
+
234
+ - top-level parser options from the current file override inherited values
235
+ - redefining `[lint.header]`, `[lint.body]`, or `[lint.footer]` replaces the entire inherited group; groups omitted from the current file remain inherited
236
+ - `[lint]` options from the current file replace inherited `[lint]` options, so repeat `unsafe-fixes = true` when the current file defines lint settings and still needs unsafe fixes
237
+ - additional footers from the current file are appended after inherited footers
238
+
239
+ ### Parser Configuration
240
+
241
+ commitfmt can be configured to use custom footer separators and comment symbols for parsing commit messages.
242
+
243
+ #### Footer separators
244
+
245
+ By default, commitfmt uses git's `trailer.separators` configuration to determine which characters separate footer keys from values. You can override this in your config file:
246
+
247
+ ```toml
248
+ footer-separators = ":#"
249
+ ```
250
+
251
+ This allows footers like `Issue-ID: 123` or `Issue-ID #123` to be recognized.
252
+
253
+ #### Comment symbol
254
+
255
+ By default, commitfmt uses git's `core.commentChar` or `core.commentString` configuration to identify comment lines in commit messages. You can override this:
256
+
257
+ ```toml
258
+ comment-symbol = "//"
259
+ ```
260
+
261
+ Comment lines immediately after the header and at the end of the commit message are ignored during parsing. Comment lines elsewhere are preserved as message content, and the header is always parsed as content.
262
+
263
+ ### Additional footers
264
+
265
+ commitfmt can add additional footers to the commit message.
266
+
267
+ #### Static value
268
+
269
+ You can add a footer with a static value:
270
+
271
+ ```toml
272
+ [[additional-footers]]
273
+ key = "Authored-By"
274
+ value = "John Doe"
275
+ ```
276
+
277
+ #### Shell commands
278
+
279
+ You can use shell commands to dynamically generate footer values:
280
+
281
+ ```toml
282
+ [[additional-footers]]
283
+ key = "Authored-By"
284
+ value = "{{ printf %s \"$USER\" }}"
285
+ ```
286
+
287
+ Commands are executed with `sh -c`. The `sh` executable and any external commands must be available through `PATH`; shell built-ins such as `printf` require no separate executable. A non-zero exit code aborts formatting, and trailing line endings are removed from standard output before it is used as the footer value.
288
+
289
+ > **Security:** shell templates execute arbitrary commands. Use configuration files only from trusted sources.
290
+
291
+ #### Branch value pattern
292
+
293
+ You can also add the ticket number from the task tracker to the footer if it is in the branch name:
294
+
295
+ ```toml
296
+ [[additional-footers]]
297
+ key = "Ticket-ID"
298
+ branch-pattern = "^(?:[^/]+/)*(?<TICKET_ID>[A-Z][A-Z0-9]*-[0-9]+)(?:/.*)?$"
299
+ value = "${{ TICKET_ID }}"
300
+ ```
301
+
302
+ For example, if your branch name is `feature/CC-123/add-new-feature` or `feature/CC-123`, the `Ticket-ID` footer will be added to the commit message with the value `CC-123`.
303
+
304
+ The named capture is available to the value template as `TICKET_ID`. If the current branch is unavailable or does not match the pattern, the footer is skipped.
305
+
306
+ Branch patterns use the [regex-lite syntax](https://docs.rs/regex-lite/latest/regex_lite/#syntax).
307
+ Unicode character classes and Unicode-aware case folding are not supported.
308
+
309
+ ##### Patterns
310
+
311
+ Examples of patterns for branch names in git flow format:
312
+
313
+ - Jira/YouTrack: `^(?:[^/]+/)*(?<TICKET_ID>[A-Z][A-Z0-9]*-[0-9]+)(?:/.*)?$`
314
+ - `feature/CFMT-123`
315
+ - `feature/CFMT-123/add-new-feature`
316
+ - GitHub: `^(?:[^/]+/)*(?<ISSUE_ID>[0-9]+)(?:/.*)?$`
317
+ - `feature/123`
318
+ - `feature/123/add-new-feature`
319
+
320
+ #### On conflict
321
+
322
+ If the message already contains a footer with the same key, `on-conflict` determines whether commitfmt adds the configured footer. The existing footer is never removed.
323
+
324
+ ```toml
325
+ [[additional-footers]]
326
+ key = "Ticket-ID"
327
+ branch-pattern = "^(?:[^/]+/)*(?<TICKET_ID>[A-Z][A-Z0-9]*-[0-9]+)(?:/.*)?$"
328
+ value = "${{ TICKET_ID }}"
329
+ on-conflict = "error" # optional; default: skip
330
+ ```
331
+
332
+ Available options:
333
+
334
+ - `skip` — keep the existing footer and do not add the configured one (default)
335
+ - `append` — keep the existing footer and add the configured one at the end
336
+ - `error` — return an error without writing the formatted message; when running as a hook, this aborts the commit
337
+
338
+ #### Footer formatting
339
+
340
+ You can customize how footers are formatted using `separator` and `alignment`. The separator must be a single character.
341
+
342
+ ```toml
343
+ [[additional-footers]]
344
+ key = "Ticket-ID"
345
+ value = "CFMT-123"
346
+ separator = "#"
347
+ alignment = "right"
348
+ ```
349
+
350
+ Available alignment options:
351
+
352
+ - `left` — no space before the separator and one after it (default): `Ticket-ID# CFMT-123`
353
+ - `right` — one space before the separator and none after it: `Ticket-ID #CFMT-123`
354
+
355
+ ### Recipe
356
+
357
+ To enforce conventional commits, you can use the following configuration:
358
+
359
+ ```toml
360
+ [lint.header]
361
+ type-enum = ["chore", "ci", "feat", "fix", "refactor", "style", "test", "docs", "revert"]
362
+ description-case = "lower-first"
363
+ description-max-length = 72
364
+ description-full-stop = true
365
+ type-required = true
366
+ # scope-enum = ["cc", "config", "git", "linter"] # optional
367
+
368
+ [lint.body]
369
+ max-line-length = 72
370
+ case = "upper-first"
371
+
372
+ [lint.footer]
373
+ breaking-exclamation = true
374
+ ```
375
+
376
+ ## Modes
377
+
378
+ ### Formatting mode
379
+
380
+ Formatting is the default mode. commitfmt normalizes the message structure and applies fixes from enabled rules. Safe fixes are always applied; unsafe fixes are applied only when [`unsafe-fixes`](#unsafe-fixes) is enabled. An unfixable violation returns a non-zero exit status and prevents the formatted message from being written.
381
+
382
+ When a message is supplied through standard input, `commitfmt` formats it and writes the result to standard output:
383
+
384
+ ```bash
385
+ printf '%s\n' "chore ( test ) : test commit." | commitfmt
386
+ # chore(test): test commit
387
+
388
+ # or
389
+ commitfmt < commit_text.txt
390
+ ```
391
+
392
+ The command exits with a non-zero status if configuration loading, parsing, or an unfixable rule fails.
393
+
394
+ When run without redirected standard input during an active Git commit, as in a `prepare-commit-msg` hook, commitfmt reads and updates Git's `COMMIT_EDITMSG` file instead of writing the formatted message to standard output.
395
+
396
+ ### Lint mode
397
+
398
+ Use `--lint` to check a message without modifying it or applying available fixes:
399
+
400
+ ```bash
401
+ printf '%s\n' "chore(test): test commit" | commitfmt --lint
402
+ ```
403
+
404
+ A valid message produces no output and exits with status 0. Any violation produces a report and a non-zero exit status. When used in a hook, lint mode checks `COMMIT_EDITMSG` but never rewrites it.
405
+
406
+ ### History mode
407
+
408
+ Use `--from` to lint a Git commit range:
409
+
410
+ ```bash
411
+ commitfmt --from HEAD~20
412
+ # or
413
+ commitfmt --from v1.1.0 --to HEAD
414
+ ```
415
+
416
+ The lower boundary is excluded. `--to` defaults to `HEAD` and can only be used together with `--from`. History mode never modifies existing commits and always lints messages, so `--lint` is unnecessary and ignored.
417
+
418
+ ## Ignoring commits
419
+
420
+ commitfmt skips messages whose text starts with the exact, case-sensitive prefix `Merge` or `Revert`. This avoids rewriting Git-generated merge and revert messages.
421
+
422
+ The check is based only on the message text, not commit metadata: `merge`, `revert:`, and other differently cased prefixes are not skipped, while any custom message starting with `Merge` or `Revert` is skipped. This applies both when formatting a single message and when linting history.
423
+
424
+ ## License
425
+
426
+ [MIT](./LICENSE).
@@ -0,0 +1,8 @@
1
+ README.md
2
+ pyproject.toml
3
+ commitfmt_windows_arm64/__init__.py
4
+ commitfmt_windows_arm64/commitfmt.exe
5
+ commitfmt_windows_arm64.egg-info/PKG-INFO
6
+ commitfmt_windows_arm64.egg-info/SOURCES.txt
7
+ commitfmt_windows_arm64.egg-info/dependency_links.txt
8
+ commitfmt_windows_arm64.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ commitfmt_windows_arm64
@@ -0,0 +1,26 @@
1
+ [project]
2
+ name = "commitfmt-windows-arm64"
3
+ authors = [
4
+ {name = "Mikhael Khrustik", email = "misha@myrt.co"},
5
+ ]
6
+ description = "Utility for formatting and verifying the commit message."
7
+ readme = "README.md"
8
+ requires-python = ">=3.9"
9
+ version = "1.2.0"
10
+ keywords = ["git", "hook"]
11
+ license = {text = "MIT"}
12
+ classifiers = [
13
+ "License :: OSI Approved :: MIT License",
14
+ "Development Status :: 5 - Production/Stable",
15
+ "Intended Audience :: Developers",
16
+ "Programming Language :: Python :: 3.9",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Programming Language :: Python :: 3.14",
22
+ "Operating System :: Microsoft :: Windows",
23
+ ]
24
+
25
+ [tool.setuptools.package-data]
26
+ commitfmt_windows_arm64 = ["commitfmt.exe"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+