Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions commitizen/changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ def generate_tree_from_commits(
changelog_release_hook: ChangelogReleaseHook | None = None,
rules: TagRules | None = None,
during_version_bump: bool = False,
subject_only: bool = False,
) -> Generator[dict[str, Any], None, None]:
pat = re.compile(changelog_pattern)
map_pat = re.compile(commit_parser, re.MULTILINE)
Expand Down Expand Up @@ -147,11 +148,15 @@ def generate_tree_from_commits(
if not pat.match(commit.message):
continue

# Process subject and body from commit message
for message in chain(
[map_pat.match(commit.message)],
(body_map_pat.match(block) for block in commit.body.split("\n\n")),
):
# Process subject; optionally also parse body blocks for nested entries
# (legacy default behaviour, controlled by `changelog_subject_only`).
subject_match = map_pat.match(commit.message)
body_matches: Iterable[re.Match[str] | None] = (
()
if subject_only
else (body_map_pat.match(block) for block in commit.body.split("\n\n"))
)
for message in chain([subject_match], body_matches):
if message:
process_commit_message(
changelog_message_builder_hook,
Expand Down
1 change: 1 addition & 0 deletions commitizen/commands/changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ def __call__(self) -> None:
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"],
)
if self.change_type_order:
tree = changelog.generate_ordered_changelog_tree(
Expand Down
2 changes: 2 additions & 0 deletions commitizen/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class Settings(TypedDict, total=False):
changelog_incremental: bool
changelog_merge_prerelease: bool
changelog_start_rev: str | None
changelog_subject_only: bool
customize: CzSettings
encoding: str
extras: dict[str, Any]
Expand Down Expand Up @@ -103,6 +104,7 @@ class Settings(TypedDict, total=False):
"changelog_incremental": False,
"changelog_start_rev": None,
"changelog_merge_prerelease": False,
"changelog_subject_only": False,
"update_changelog_on_bump": False,
"use_shortcuts": False,
"major_version_zero": False,
Expand Down
20 changes: 20 additions & 0 deletions docs/commands/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,26 @@ This flag can be set in the configuration file with the key `changelog_merge_pre
changelog_merge_prerelease = true
```

### `changelog_subject_only`

By default, Commitizen parses both the subject line and any `\n\n`-separated body blocks of each commit against `commit_parser`, so a commit such as

```
feat: new feature

refactor: incidental cleanup
```

produces *two* changelog entries (one under `feat`, one under `refactor`). Set this configuration to `true` to limit changelog parsing to the subject line only.

```toml
[tool.commitizen]
# ...
changelog_subject_only = true
```

The default (`false`) preserves the historical behaviour.

### `--template`

Provide your own changelog Jinja template by using the `template` settings or the `--template` parameter.
Expand Down
26 changes: 26 additions & 0 deletions tests/commands/test_changelog_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,32 @@ def test_changelog_with_different_cz(
file_regression.check(out, extension=".md")


@pytest.mark.usefixtures("tmp_commitizen_project")
def test_changelog_subject_only_setting_skips_body_parsing(
util: UtilFixture,
config_path: Path,
capsys: pytest.CaptureFixture,
):
"""End-to-end coverage for `changelog_subject_only` (#1267 / #1974).

Verifies the config-key wiring in `commands/changelog.py`: when the
setting is `true`, parser-matching blocks in commit bodies are
ignored. Catches regressions where the setting key is mistyped or
not propagated to `generate_tree_from_commits`.
"""
with config_path.open("a") as f:
f.write("changelog_subject_only = true\n")

util.create_file_and_commit("feat: add new output\n\nrefactor: incidental cleanup")

with pytest.raises(DryRunExit):
util.run_cli("changelog", "--dry-run")
out, _ = capsys.readouterr()

assert "add new output" in out
assert "incidental cleanup" not in out


@pytest.mark.usefixtures("tmp_commitizen_project")
def test_changelog_from_start(
changelog_format: ChangelogFormat,
Expand Down
35 changes: 35 additions & 0 deletions tests/test_changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -1191,6 +1191,41 @@ def test_generate_tree_from_commits_with_no_commits(tags):
assert tuple(tree) == ({"changes": {}, "date": "", "version": "Unreleased"},)


def test_generate_tree_from_commits_subject_only_skips_body_blocks(tags):
"""`subject_only=True` ignores parser-matching blocks inside `commit.body`.

Regression for #1267 where, e.g., a commit subject `feat: new feature`
with a body containing `refactor: cleanup` produced two changelog
entries instead of one.
"""
parser = ConventionalCommitsCz.commit_parser
changelog_pattern = ConventionalCommitsCz.bump_pattern

commit = git.GitCommit(
rev="abc123",
title="feat: new feature",
body="some prose\n\nrefactor: incidental cleanup",
author="Commitizen",
author_email="author@cz.dev",
)

default_tree = list(
changelog.generate_tree_from_commits([commit], tags, parser, changelog_pattern)
)
assert default_tree[0]["changes"].keys() == {"feat", "refactor"}

subject_only_tree = list(
changelog.generate_tree_from_commits(
[commit],
tags,
parser,
changelog_pattern,
subject_only=True,
)
)
assert subject_only_tree[0]["changes"].keys() == {"feat"}


@pytest.mark.parametrize(
("change_type_order", "expected_reordering"),
[
Expand Down
2 changes: 2 additions & 0 deletions tests/test_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
"changelog_incremental": False,
"changelog_start_rev": None,
"changelog_merge_prerelease": False,
"changelog_subject_only": False,
"update_changelog_on_bump": False,
"use_shortcuts": False,
"major_version_zero": False,
Expand Down Expand Up @@ -140,6 +141,7 @@
"changelog_incremental": False,
"changelog_start_rev": None,
"changelog_merge_prerelease": False,
"changelog_subject_only": False,
"update_changelog_on_bump": False,
"use_shortcuts": False,
"major_version_zero": False,
Expand Down
Loading