Title: fix(changelog): add changelog_subject_only to skip body parsing by bearomorphism · Pull Request #1974 · commitizen-tools/commitizen · GitHub
Open Graph Title: fix(changelog): add changelog_subject_only to skip body parsing by bearomorphism · Pull Request #1974 · commitizen-tools/commitizen
X Title: fix(changelog): add changelog_subject_only to skip body parsing by bearomorphism · Pull Request #1974 · commitizen-tools/commitizen
Description: Description Closes #1267. Why commitizen.changelog.generate_tree_from_commits() parses both the commit subject and each \n\n-separated block of commit.body against commit_parser. So a commit such as feat: new feature refactor: incidental cleanup produces two changelog entries: ### Feat - new feature ### Refactor - incidental cleanup This is rarely what the author intended — they wrote one feat, the body was just a note. The maintainer ack on #1267 confirms the body-parsing is undesirable, but flipping the default would be a behaviour break for existing users (some intentionally rely on it for multi-message commits). What changed File Change commitizen/defaults.py New TypedDict field changelog_subject_only: bool and DEFAULT_SETTINGS["changelog_subject_only"] = False. commitizen/changelog.py generate_tree_from_commits() gains a subject_only: bool = False kwarg. When True, the body iteration is replaced with an empty tuple, so only the subject is matched. commitizen/commands/changelog.py Changelog.__call__ forwards self.config.settings["changelog_subject_only"] to generate_tree_from_commits(). This single call site is shared by cz changelog and cz bump --changelog. tests/test_changelog.py New test_generate_tree_from_commits_subject_only_skips_body_blocks exercises both modes against a commit whose body contains a parser-matching block. tests/commands/test_changelog_command.py New test_changelog_subject_only_setting_skips_body_parsing is an end-to-end test (real config file, real cz changelog invocation) that catches typos in the config-key wiring. tests/test_conf.py Two expected-Settings literals updated to include the new key (otherwise the strict-equality assertions in TestReadCfg::test_load_conf fail). docs/commands/changelog.md New ### changelog_subject_only section next to the existing --merge-prerelease documentation. How it works Default-off semantics. changelog_subject_only = false is the default in both the Settings TypedDict and DEFAULT_SETTINGS, so BaseConfig.__init__ always seeds the dict-key with False and the dict-index access in Changelog.__call__ cannot KeyError. Single chokepoint. Body iteration happens at one place — the chain([subject_match], (body_map_pat.match(block) for block in commit.body.split("\n\n"))) in generate_tree_from_commits(). Replacing the second argument of chain with () when subject_only=True is the entire behavioural change. Why from __future__ import annotations matters. The new body_matches: Iterable[re.Match[str] | None] annotation only resolves in TYPE_CHECKING blocks at runtime; without from __future__ import annotations the import would have to leave TYPE_CHECKING. The file already had it. The end-to-end test was specifically requested. Both reviewers (sonnet-4.6 and GitHub Copilot) flagged that the lib-level test alone wouldn't catch a typo in the self.config.settings["changelog_subject_only"] lookup at commands/changelog.py. The fixup commit 292feb4f adds an integration test that constructs a real config and runs cz changelog --dry-run, then verified by hand that the test fails (with KeyError) when the config key is mistyped. Backward compatibility The default false preserves the historical body-parsing behaviour for every user who hasn't opted in. All 158 pre-existing tests/test_changelog.py and tests/commands/test_changelog_command.py tests pass unchanged. The new subject_only kwarg on generate_tree_from_commits() defaults to False, so any external caller that imports the function continues to work. Checklist I have read the contributing guidelines Was generative AI tooling used to co-author this PR? Yes (please specify the tool below) Generated-by: Claude following the guidelines Code Changes Add test cases to all the changes you introduce (1 unit test + 1 integration test) Run uv run poe all locally to ensure this change passes linter check and tests (poe lint clean; 159 changelog tests pass) Manually test the changes (see "Steps to Test" below) Update the documentation for the changes Documentation Changes Added a ### changelog_subject_only section to docs/commands/changelog.md Run uv run poe doc locally to ensure the documentation pages render correctly Check and fix any broken links (internal or external) Expected Behavior Setting Subject feat: X + body refactor: Y Result changelog_subject_only unset (default false) Both subject and body block parsed Two entries: one feat, one refactor (legacy) changelog_subject_only = true Only subject parsed One entry: feat: X Applies to both cz changelog and cz bump --changelog. Steps to Test This Pull Request git fetch fork fix/1267-changelog-subject-only git checkout fork/fix/1267-changelog-subject-only # 1. Lib-level regression test (forces the body-skip behaviour at the call site). uv run pytest tests/test_changelog.py -k subject_only -v # 2. End-to-end test (real config file, real cz changelog invocation). uv run pytest tests/commands/test_changelog_command.py::test_changelog_subject_only_setting_skips_body_parsing -v # 3. Verify the e2e test would catch a typo in the config-key wiring. sed -i.bak 's/changelog_subject_only/changelog_subject_only_TYPO/' commitizen/commands/changelog.py uv run pytest tests/commands/test_changelog_command.py::test_changelog_subject_only_setting_skips_body_parsing -v # expected: KeyError: 'changelog_subject_only_TYPO' mv commitizen/commands/changelog.py.bak commitizen/commands/changelog.py uv run pytest tests/commands/test_changelog_command.py::test_changelog_subject_only_setting_skips_body_parsing -v # expected: 1 passed # 4. Full changelog suite (no regressions). uv run pytest tests/test_changelog.py tests/commands/test_changelog_command.py -q # expected: 159 passed Additional Context Surfaced while triaging open issues in #1965. Maintainer ack on #1267 confirmed body-parsing is at-best opt-in. The end-to-end integration test was added in fixup commit 292feb4f after both review passes (sonnet-4.6 and Copilot) flagged the wiring as the only path not exercised by tests.
Open Graph Description: Description Closes #1267. Why commitizen.changelog.generate_tree_from_commits() parses both the commit subject and each \n\n-separated block of commit.body against commit_parser. So a commit such a...
X Description: Description Closes #1267. Why commitizen.changelog.generate_tree_from_commits() parses both the commit subject and each \n\n-separated block of commit.body against commit_parser. So a commit such a...
Opengraph URL: https://github.com/commitizen-tools/commitizen/pull/1974
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:a046a1fc-18eb-6ec2-4cb2-f10839738594 |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | C268:39522F:1342398:1A933F1:6A4DE967 |
| html-safe-nonce | fcf047a2b6bcffbed63da91d1c2c09367553282670b1dc05f74580aad6916961 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDMjY4OjM5NTIyRjoxMzQyMzk4OjFBOTMzRjE6NkE0REU5NjciLCJ2aXNpdG9yX2lkIjoiMTA4ODMzNTg0MDA1OTAwNjQ3IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | aaa6a7c99a88deb1351df0e6b2924f5f4a9d9c6bd45c2cb96857a4b4852a1771 |
| hovercard-subject-tag | pull_request:3654741141 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/commitizen-tools/commitizen/pull/1974/files |
| twitter:image | https://avatars.githubusercontent.com/u/26526132?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/26526132?s=400&v=4 |
| og:image:alt | Description Closes #1267. Why commitizen.changelog.generate_tree_from_commits() parses both the commit subject and each \n\n-separated block of commit.body against commit_parser. So a commit such a... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | 06b8a6144231bf3a234f1c2e9993861e07ce98a905912b114aa386c2d7e84b33 |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/commitizen-tools/commitizen git https://github.com/commitizen-tools/commitizen.git |
| octolytics-dimension-user_id | 62252524 |
| octolytics-dimension-user_login | commitizen-tools |
| octolytics-dimension-repository_id | 106127589 |
| octolytics-dimension-repository_nwo | commitizen-tools/commitizen |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 106127589 |
| octolytics-dimension-repository_network_root_nwo | commitizen-tools/commitizen |
| turbo-body-classes | logged-out env-production page-responsive full-width |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 1d344bdb7547fe6bca17a59bb2b8aac3dc9532a0 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width