3131from collections import OrderedDict , defaultdict
3232from dataclasses import dataclass
3333from datetime import date
34+ from enum import Enum
3435from itertools import chain
3536from typing import TYPE_CHECKING , Any
3637
@@ -85,6 +86,13 @@ class IncrementalMergeInfo:
8586 index : int | None = None
8687
8788
89+ class AuthorSource (str , Enum ):
90+ """Source of the author shown in each changelog entry (`--add-author`)"""
91+
92+ AUTHOR = "author"
93+ SIGNED_OFF_BY = "signed-off-by"
94+
95+
8896def get_commit_tag (commit : GitCommit , tags : list [GitTag ]) -> GitTag | None :
8997 return next ((tag for tag in tags if tag .rev == commit .rev ), None )
9098
@@ -100,6 +108,7 @@ def generate_tree_from_commits(
100108 changelog_release_hook : ChangelogReleaseHook | None = None ,
101109 rules : TagRules | None = None ,
102110 during_version_bump : bool = False ,
111+ author_source : AuthorSource | None = None ,
103112) -> Generator [dict [str , Any ], None , None ]:
104113 pat = re .compile (changelog_pattern )
105114 map_pat = re .compile (commit_parser , re .MULTILINE )
@@ -159,6 +168,7 @@ def generate_tree_from_commits(
159168 commit ,
160169 changes ,
161170 change_type_map ,
171+ author_source ,
162172 )
163173
164174 release = {
@@ -171,12 +181,33 @@ def generate_tree_from_commits(
171181 yield release
172182
173183
184+ def resolve_changelog_author (
185+ commit : GitCommit , author_source : AuthorSource
186+ ) -> tuple [str , str ]:
187+ """Get the author name and email to show in a changelog entry.
188+
189+ Args:
190+ commit: The commit of the changelog entry.
191+ author_source: Where to read the author from.
192+
193+ Returns:
194+ A `(name, email)` tuple. For `SIGNED_OFF_BY`, it is the first
195+ `Signed-off-by` trailer, or the commit author when there is no trailer.
196+ """
197+ if author_source is AuthorSource .SIGNED_OFF_BY and (
198+ signed_off_by := commit .signed_off_by
199+ ):
200+ return signed_off_by
201+ return commit .author , commit .author_email
202+
203+
174204def process_commit_message (
175205 hook : MessageBuilderHook | None ,
176206 parsed : re .Match [str ],
177207 commit : GitCommit ,
178208 ref_changes : MutableMapping [str | None , list ],
179209 change_type_map : Mapping [str , str ] | None = None ,
210+ author_source : AuthorSource | None = None ,
180211) -> None :
181212 message : dict [str , Any ] = {
182213 "sha1" : commit .rev ,
@@ -185,6 +216,10 @@ def process_commit_message(
185216 "author_email" : commit .author_email ,
186217 ** parsed .groupdict (),
187218 }
219+ if author_source is not None :
220+ name , email = resolve_changelog_author (commit , author_source )
221+ message ["changelog_author" ] = name
222+ message ["changelog_author_email" ] = email
188223
189224 processed_msg = hook (message , commit ) if hook else message
190225 if not processed_msg :
0 commit comments