@@ -104,14 +104,37 @@ Assisted-by: This is a very long value, with spaces and
- `-t` / `--type`: Commit type (required) - e.g., `feat`, `fix`, `refactor`
- `-m` / `--message`: Commit message (required) - the description after the colon
- `-s` / `--scope`: Commit scope (optional) - goes in parentheses after type
-- `-B` / `--breaking`: Boolean flag for breaking changes (adds `!` to subject)
+- `-B` / `--breaking`: String flag for breaking change description (optional) - adds `!` to subject AND creates `BREAKING CHANGE:` footer
- `-b` / `--body`: String flag for commit body text (can use heredoc for multiline)
- `-T` / `--trailer`: Repeatable flag accepting full trailer strings in `Key: value` format (not separate key/value args)
-- `-a` / `--add`: Boolean flag to stage all modified files before committing (optional)
-- `--amend`: Boolean flag to amend the previous commit instead of creating a new one (optional)
+- `-a` / `--amend`: Boolean flag to amend the previous commit instead of creating a new one
-Trailer format detail: Each `-T` flag takes a complete trailer string like `-T "Assisted-by: GLM 4.6 via Crush"`, NOT separate key and value arguments.
+Breaking change detail: The `-B` flag value becomes the description in a `BREAKING CHANGE:` footer (with space, per Conventional Commits spec). This footer is distinct from git trailers and allows spaces in the key. It's inserted between the body and trailers.
+
+Trailer format detail: Each `-T` flag takes a complete trailer string like `-T "Assisted-by: Claude Sonnet 4.5 via Crush"`, NOT separate key and value arguments.
### Final Output
-The formatted commit message must be piped to `git commit -F -` to read from stdin. When the `--amend` flag is used, it pipes to `git commit --amend -F -` instead.
+The formatted commit message structure:
+
+1. Subject: `type(scope)!: message` (or `type!: message` if no scope)
+2. Blank line
+3. Body (if `-b` provided)
+4. Blank line (if body or breaking change present)
+5. `BREAKING CHANGE: description` footer (if `-B` provided)
+6. Blank line (if trailers present)
+7. Trailers (if `-T` provided)
+
+Example with all components:
+```
+feat!: restructure config
+
+Improves readability and supports comments
+
+BREAKING CHANGE: Configuration format changed from JSON to TOML.
+Migrate by running: ./migrate-config.sh
+
+Assisted-by: Claude Sonnet 4.5 via Crush
+```
+
+The formatted commit message must be piped to `git commit -F -` to read from stdin. When the `-a`/`--amend` flag is used, it pipes to `git commit --amend -F -` instead.
@@ -37,15 +37,16 @@ body wrapping, and trailer formatting.`,
# With Assisted-by
formatted-commit -t feat -m "do a thing" -T "Assisted-by: GLM 4.6 via Crush"
-# Breaking change with longer body
-formatted-commit -t feat -m "do a thing that borks a thing" -B -b "$(cat <<'EOF'
-Multi-line
-- Body
-- Here
-
-This is what borked because of new shiny, this is how migrate
+# Breaking change with description
+formatted-commit -t feat -m "remove deprecated API" \
+ -B "The old /v1/users endpoint is removed. Use /v2/users instead."
+
+# Breaking change with multi-line description using heredoc
+formatted-commit -t feat -m "restructure config format" -B "$(cat <<'EOF'
+Configuration format changed from JSON to TOML.
+Migrate by running: ./migrate-config.sh
EOF
-)"
+)" -b "Improves readability and supports comments"
# Including scope for more precise changes
formatted-commit -t refactor -s "web/git-bug" -m "fancy shmancy" \
@@ -79,6 +80,16 @@ formatted-commit upgrade -a
commitMsg.WriteString(formattedBody)
}
+ if breakingChange != "" {
+ formattedBreaking, err := formatBody(breakingChange)
+ if err != nil {
+ return fmt.Errorf("failed to format breaking change: %w", err)
+ }
+ commitMsg.WriteString("\n\n")
+ commitMsg.WriteString("BREAKING CHANGE: ")
+ commitMsg.WriteString(formattedBreaking)
+ }
+
if len(trailers) > 0 {
trailersBlock, err := buildTrailersBlock(trailers)
if err != nil {
@@ -96,6 +107,7 @@ formatted-commit upgrade -a
gitArgs = append(gitArgs, "--amend")
}
gitArgs = append(gitArgs, "-F", "-")
+
gitCmd := exec.Command("git", gitArgs...)
gitCmd.Stdout = os.Stdout
gitCmd.Stderr = os.Stderr
@@ -143,7 +155,7 @@ func init() {
}
}
-func buildAndValidateSubject(commitType, scope, message string, breaking bool) (string, error) {
+func buildAndValidateSubject(commitType, scope, message string, breaking string) (string, error) {
var subject strings.Builder
subject.WriteString(commitType)
@@ -154,7 +166,7 @@ func buildAndValidateSubject(commitType, scope, message string, breaking bool) (
subject.WriteString(")")
}
- if breaking {
+ if breaking != "" {
subject.WriteString("!")
}