Skip to content

Commit 642f2c8

Browse files
authored
Merge pull request #677 from sacha-c/fix/changelog-commit-order
fix(commands/changelog): use topological order for commit ordering
2 parents 9b5f311 + b5125cb commit 642f2c8

File tree

3 files changed

+103
-4
lines changed

3 files changed

+103
-4
lines changed

commitizen/commands/changelog.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,7 @@ def __call__(self):
144144
tag_format=self.tag_format,
145145
)
146146

147-
commits = git.get_commits(
148-
start=start_rev, end=end_rev, args="--author-date-order"
149-
)
147+
commits = git.get_commits(start=start_rev, end=end_rev, args="--topo-order")
150148
if not commits:
151149
raise NoCommitsFoundError("No commits found")
152150

tests/commands/test_changelog_command.py

+77-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@
1313
NotAGitProjectError,
1414
NotAllowed,
1515
)
16-
from tests.utils import create_file_and_commit, wait_for_tag
16+
from tests.utils import (
17+
create_branch,
18+
create_file_and_commit,
19+
get_current_branch,
20+
merge_branch,
21+
switch_branch,
22+
wait_for_tag,
23+
)
1724

1825

1926
@pytest.mark.usefixtures("tmp_commitizen_project")
@@ -268,6 +275,75 @@ def test_changelog_hook_customize(mocker: MockFixture, config_customize):
268275
changelog_hook_mock.assert_called_with(full_changelog, full_changelog)
269276

270277

278+
@pytest.mark.usefixtures("tmp_commitizen_project")
279+
def test_changelog_with_non_linear_merges_commit_order(
280+
mocker: MockFixture, config_customize
281+
):
282+
"""Test that commits merged non-linearly are correctly ordered in the changelog
283+
284+
A typical scenario is having two branches from main like so:
285+
* feat: I will be merged first - (2023-03-01 11:35:51 +0100) | (branchB)
286+
| * feat: I will be merged second - (2023-03-01 11:35:22 +0100) | (branchA)
287+
|/
288+
* feat: initial commit - (2023-03-01 11:34:54 +0100) | (HEAD -> main)
289+
290+
And merging them, for example in the reverse order they were created on would give the following:
291+
* Merge branch 'branchA' - (2023-03-01 11:42:59 +0100) | (HEAD -> main)
292+
|\
293+
| * feat: I will be merged second - (2023-03-01 11:35:22 +0100) | (branchA)
294+
* | feat: I will be merged first - (2023-03-01 11:35:51 +0100) | (branchB)
295+
|/
296+
* feat: initial commit - (2023-03-01 11:34:54 +0100) |
297+
298+
In this case we want the changelog to reflect the topological order of commits,
299+
i.e. the order in which they were merged into the main branch
300+
301+
So the above example should result in the following:
302+
## Unreleased
303+
304+
### Feat
305+
- I will be merged second
306+
- I will be merged first
307+
- initial commit
308+
"""
309+
changelog_hook_mock = mocker.Mock()
310+
changelog_hook_mock.return_value = "cool changelog hook"
311+
312+
create_file_and_commit("feat: initial commit")
313+
314+
main_branch = get_current_branch()
315+
316+
create_branch("branchA")
317+
create_branch("branchB")
318+
319+
switch_branch("branchA")
320+
create_file_and_commit("feat: I will be merged second")
321+
322+
switch_branch("branchB")
323+
create_file_and_commit("feat: I will be merged first")
324+
325+
# Note we merge branches opposite order than author_date
326+
switch_branch(main_branch)
327+
merge_branch("branchB")
328+
merge_branch("branchA")
329+
330+
changelog = Changelog(
331+
config_customize,
332+
{"unreleased_version": None, "incremental": True, "dry_run": False},
333+
)
334+
mocker.patch.object(changelog.cz, "changelog_hook", changelog_hook_mock)
335+
changelog()
336+
full_changelog = "\
337+
## Unreleased\n\n\
338+
\
339+
### Feat\n\n\
340+
- I will be merged second\n\
341+
- I will be merged first\n\
342+
- initial commit\n"
343+
344+
changelog_hook_mock.assert_called_with(full_changelog, full_changelog)
345+
346+
271347
@pytest.mark.usefixtures("tmp_commitizen_project")
272348
def test_changelog_multiple_incremental_do_not_add_new_lines(
273349
mocker: MockFixture, capsys, changelog_path, file_regression

tests/utils.py

+25
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,31 @@ def create_file_and_commit(message: str, filename: Optional[str] = None):
2626
raise exceptions.CommitError(c.err)
2727

2828

29+
def create_branch(name: str):
30+
c = cmd.run(f"git branch {name}")
31+
if c.return_code != 0:
32+
raise exceptions.GitCommandError(c.err)
33+
34+
35+
def switch_branch(branch: str):
36+
c = cmd.run(f"git switch {branch}")
37+
if c.return_code != 0:
38+
raise exceptions.GitCommandError(c.err)
39+
40+
41+
def merge_branch(branch: str):
42+
c = cmd.run(f"git merge {branch}")
43+
if c.return_code != 0:
44+
raise exceptions.GitCommandError(c.err)
45+
46+
47+
def get_current_branch() -> str:
48+
c = cmd.run("git rev-parse --abbrev-ref HEAD")
49+
if c.return_code != 0:
50+
raise exceptions.GitCommandError(c.err)
51+
return c.out
52+
53+
2954
def create_tag(tag: str):
3055
c = git.tag(tag)
3156
if c.return_code != 0:

0 commit comments

Comments
 (0)