fix(changelog): add changelog_subject_only to skip body parsing#1974
fix(changelog): add changelog_subject_only to skip body parsing#1974bearomorphism wants to merge 3 commits intocommitizen-tools:masterfrom
Conversation
Closes commitizen-tools#1267. `generate_tree_from_commits()` historically parses the commit subject and each `\n\n`-separated body block against `commit_parser`, so a commit whose subject is `feat: ...` and whose body contains another `refactor: ...` line produces two changelog entries instead of one. Maintainer ack on commitizen-tools#1267 confirms this is undesirable, but changing the default is a behavioural break. This change introduces `changelog_subject_only` (default `false`) on `Settings`. When set to `true`, the body iteration in `generate_tree_from_commits()` is skipped, leaving only the subject line to be matched. The setting is plumbed through `commands/changelog.py` so both `cz changelog` and `cz bump --changelog` honour it. A regression test exercises both modes against a commit whose body contains a parser-matching block. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1974 +/- ##
=======================================
Coverage 98.23% 98.23%
=======================================
Files 61 61
Lines 2779 2782 +3
=======================================
+ Hits 2730 2733 +3
Misses 49 49 ☔ View full report in Codecov by Sentry. |
Followup to fix(changelog): the new default key broke `tests/test_conf.py::TestReadCfg` because the expected `Settings` literals pinned the full dict and didn''t know about the new field. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a new configuration setting to control whether changelog generation should parse commit body blocks as additional changelog entries, addressing issue #1267 while preserving historical behavior by default.
Changes:
- Introduces
changelog_subject_only(defaultfalse) to skip parsing\n\n-separated commit body blocks when generating changelogs. - Plumbs the setting through the
Changelogcommand socz changelog(andcz bump --changelogvia the same command) can honor it. - Adds a regression test for
generate_tree_from_commits(..., subject_only=True)and documents the new setting.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
commitizen/changelog.py |
Adds subject_only parameter to skip body-block parsing when building the changelog tree. |
commitizen/commands/changelog.py |
Passes changelog_subject_only from config into changelog tree generation. |
commitizen/defaults.py |
Adds changelog_subject_only to typed settings and default settings. |
tests/test_changelog.py |
Adds regression coverage for subject-only parsing behavior. |
tests/test_conf.py |
Updates expected/default settings fixtures to include changelog_subject_only. |
docs/commands/changelog.md |
Documents the new changelog_subject_only setting and its effect. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| tree = changelog.generate_tree_from_commits( | ||
| commits, | ||
| tags, | ||
| commit_parser, | ||
| changelog_pattern, | ||
| self.unreleased_version, | ||
| change_type_map=self.change_type_map, | ||
| changelog_message_builder_hook=self.cz.changelog_message_builder_hook, | ||
| changelog_release_hook=self.cz.changelog_release_hook, | ||
| rules=self.tag_rules, | ||
| during_version_bump=self.during_version_bump, | ||
| subject_only=self.config.settings["changelog_subject_only"], | ||
| ) |
Add an end-to-end test that sets `changelog_subject_only = true` in the project configuration, creates a commit with a parser-matching block in its body, and asserts that `cz changelog --dry-run` only emits the subject entry. Catches typos in the setting key at `commands/changelog.py:279` (which would otherwise silently fall back to `False`); manually verified by injecting a typo, observing the test fail with KeyError, then restoring. Addresses the only review finding from PR commitizen-tools#1974. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Closing this PR per maintainer-triage policy: feature-request issues should sit with the maintainers for design / scope review before any implementation lands. The issue's The implementation itself is preserved on the branch ( This PR is being closed so that #1267 reverts to "awaiting maintainer triage / decision" rather than "PR pending review", which is the correct state for a feature request. Closed via the round-2 triage cleanup in #1965. |
Description
Closes #1267.
Why
commitizen.changelog.generate_tree_from_commits()parses both the commit subject and each\n\n-separated block ofcommit.bodyagainstcommit_parser. So a commit such asproduces two changelog entries:
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
commitizen/defaults.pychangelog_subject_only: boolandDEFAULT_SETTINGS["changelog_subject_only"] = False.commitizen/changelog.pygenerate_tree_from_commits()gains asubject_only: bool = Falsekwarg. WhenTrue, the body iteration is replaced with an empty tuple, so only the subject is matched.commitizen/commands/changelog.pyChangelog.__call__forwardsself.config.settings["changelog_subject_only"]togenerate_tree_from_commits(). This single call site is shared bycz changelogandcz bump --changelog.tests/test_changelog.pytest_generate_tree_from_commits_subject_only_skips_body_blocksexercises both modes against a commit whose body contains a parser-matching block.tests/commands/test_changelog_command.pytest_changelog_subject_only_setting_skips_body_parsingis an end-to-end test (real config file, realcz changeloginvocation) that catches typos in the config-key wiring.tests/test_conf.pySettingsliterals updated to include the new key (otherwise the strict-equality assertions inTestReadCfg::test_load_conffail).docs/commands/changelog.md### changelog_subject_onlysection next to the existing--merge-prereleasedocumentation.How it works
changelog_subject_only = falseis the default in both theSettingsTypedDict andDEFAULT_SETTINGS, soBaseConfig.__init__always seeds the dict-key withFalseand the dict-index access inChangelog.__call__cannotKeyError.chain([subject_match], (body_map_pat.match(block) for block in commit.body.split("\n\n")))ingenerate_tree_from_commits(). Replacing the second argument ofchainwith()whensubject_only=Trueis the entire behavioural change.from __future__ import annotationsmatters. The newbody_matches: Iterable[re.Match[str] | None]annotation only resolves inTYPE_CHECKINGblocks at runtime; withoutfrom __future__ import annotationsthe import would have to leaveTYPE_CHECKING. The file already had it.self.config.settings["changelog_subject_only"]lookup atcommands/changelog.py. The fixup commit292feb4fadds an integration test that constructs a real config and runscz changelog --dry-run, then verified by hand that the test fails (withKeyError) when the config key is mistyped.Backward compatibility
falsepreserves the historical body-parsing behaviour for every user who hasn't opted in.tests/test_changelog.pyandtests/commands/test_changelog_command.pytests pass unchanged.subject_onlykwarg ongenerate_tree_from_commits()defaults toFalse, so any external caller that imports the function continues to work.Checklist
Was generative AI tooling used to co-author this PR?
Generated-by: Claude following the guidelines
Code Changes
uv run poe alllocally to ensure this change passes linter check and tests (poe lintclean; 159 changelog tests pass)Documentation Changes
### changelog_subject_onlysection todocs/commands/changelog.mduv run poe doclocally to ensure the documentation pages render correctlyExpected Behavior
feat: X+ bodyrefactor: Ychangelog_subject_onlyunset (defaultfalse)feat, onerefactor(legacy)changelog_subject_only = truefeat: XApplies to both
cz changelogandcz bump --changelog.Steps to Test This Pull Request
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
292feb4fafter both review passes (sonnet-4.6 and Copilot) flagged the wiring as the only path not exercised by tests.