Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi line event split #30

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
198 changes: 198 additions & 0 deletions .gitchangelog.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
##
## Format
##
## ACTION: [AUDIENCE:] COMMIT_MSG [!TAG ...]
##
## Description
##
## ACTION is one of 'chg', 'fix', 'new'
##
## Is WHAT the change is about.
##
## 'chg' is for refactor, small improvement, cosmetic changes...
## 'fix' is for bug fixes
## 'new' is for new features, big improvement
##
## AUDIENCE is optional and one of 'dev', 'usr', 'pkg', 'test', 'doc'
##
## Is WHO is concerned by the change.
##
## 'dev' is for developpers (API changes, refactors...)
## 'usr' is for final users (UI changes)
## 'pkg' is for packagers (packaging changes)
## 'test' is for testers (test only related changes)
## 'doc' is for doc guys (doc only changes)
##
## COMMIT_MSG is ... well ... the commit message itself.
##
## TAGs are additionnal adjective as 'refactor' 'minor' 'cosmetic'
##
## They are preceded with a '!' or a '@' (prefer the former, as the
## latter is wrongly interpreted in github.) Commonly used tags are:
##
## 'refactor' is obviously for refactoring code only
## 'minor' is for a very meaningless change (a typo, adding a comment)
## 'cosmetic' is for cosmetic driven change (re-indentation, 80-col...)
## 'wip' is for partial functionality but complete subfunctionality.
##
## Example:
##
## new: usr: support of bazaar implemented
## chg: re-indentend some lines !cosmetic
## new: dev: updated code to be compatible with last version of killer lib.
## fix: pkg: updated year of licence coverage.
## new: test: added a bunch of test around user usability of feature X.
## fix: typo in spelling my name in comment. !minor
##
## Please note that multi-line commit message are supported, and only the
## first line will be considered as the "summary" of the commit message. So
## tags, and other rules only applies to the summary. The body of the commit
## message will be displayed in the changelog without reformatting.

##
## ``split_regex`` is a regex
##
## It is used to split a commit log entry into events before processing each event.
##
split_regex = r'(?s)(.+?)(?=[Cc]hg|[Ff]ix|[Nn]ew|$)'


##
## ``ignore_regexps`` is a line of regexps
##
## Any commit having its full commit message matching any regexp listed here
## will be ignored and won't be reported in the changelog.
##
ignore_regexps = [
r'@minor', r'!minor',
r'@cosmetic', r'!cosmetic',
r'@refactor', r'!refactor',
r'@wip', r'!wip',
r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[p|P]kg:',
r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[d|D]ev:',
r'^(.{3,3}\s*:)?\s*[fF]irst commit.?\s*$',
]


## ``section_regexps`` is a list of 2-tuples associating a string label and a
## list of regexp
##
## Commit messages will be classified in sections thanks to this. Section
## titles are the label, and a commit is classified under this section if any
## of the regexps associated is matching.
##
section_regexps = [
('New', [
r'^[nN]ew\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
]),
('Changes', [
r'^[cC]hg\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
]),
('Fix', [
r'^[fF]ix\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
]),

('Other', None ## Match all lines
),

]


## ``body_process`` is a callable
##
## This callable will be given the original body and result will
## be used in the changelog.
##
## Available constructs are:
##
## - any python callable that take one txt argument and return txt argument.
##
## - ReSub(pattern, replacement): will apply regexp substitution.
##
## - Indent(chars=" "): will indent the text with the prefix
## Please remember that template engines gets also to modify the text and
## will usually indent themselves the text if needed.
##
## - Wrap(regexp=r"\n\n"): re-wrap text in separate paragraph to fill 80-Columns
##
## - noop: do nothing
##
## - ucfirst: ensure the first letter is uppercase.
## (usually used in the ``subject_process`` pipeline)
##
## - final_dot: ensure text finishes with a dot
## (usually used in the ``subject_process`` pipeline)
##
## - strip: remove any spaces before or after the content of the string
##
## Additionally, you can `pipe` the provided filters, for instance:
#body_process = Wrap(regexp=r'\n(?=\w+\s*:)') | Indent(chars=" ")
#body_process = Wrap(regexp=r'\n(?=\w+\s*:)')
#body_process = noop
body_process = ReSub(r'((^|\n)[A-Z]\w+(-\w+)*: .*(\n\s+.*)*)+$', r'') | strip


## ``subject_process`` is a callable
##
## This callable will be given the original subject and result will
## be used in the changelog.
##
## Available constructs are those listed in ``body_process`` doc.
subject_process = (strip |
ReSub(r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n@]*)(@[a-z]+\s+)*$', r'\4') |
ucfirst | final_dot)


## ``tag_filter_regexp`` is a regexp
##
## Tags that will be used for the changelog must match this regexp.
##
tag_filter_regexp = r'^[0-9]+\.[0-9]+(\.[0-9]+)?$'


## ``unreleased_version_label`` is a string
##
## This label will be used as the changelog Title of the last set of changes
## between last valid tag and HEAD if any.
unreleased_version_label = "%%version%% (unreleased)"


## ``output_engine`` is a callable
##
## This will change the output format of the generated changelog file
##
## Available choices are:
##
## - rest_py
##
## Legacy pure python engine, outputs ReSTructured text.
## This is the default.
##
## - mustache(<template_name>)
##
## Template name could be any of the available templates in
## ``templates/mustache/*.tpl``.
## Requires python package ``pystache``.
## Examples:
## - mustache("markdown")
## - mustache("restructuredtext")
##
## - makotemplate(<template_name>)
##
## Template name could be any of the available templates in
## ``templates/mako/*.tpl``.
## Requires python package ``mako``.
## Examples:
## - makotemplate("restructuredtext")
##
output_engine = rest_py
#output_engine = mustache("restructuredtext")
#output_engine = mustache("markdown")
#output_engine = makotemplate("restructuredtext")


## ``include_merges`` is a boolean
##
## This option tells git-log whether to include merge commits in the log.
## The default is to include them.
include_merges = True
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.settings
.project
.pydevproject
46 changes: 34 additions & 12 deletions gitchangelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,7 @@ def makotemplate(template_name):
##

def changelog(repository,
split_event_regex=None,
ignore_regexps=[],
section_regexps=[(None,'')],
unreleased_version_label="unreleased",
Expand Down Expand Up @@ -879,20 +880,40 @@ def changelog(repository,
excludes=tags[idx + 1:],
include_merge=include_merge)

## Loop through all the commits
for commit in commits:
if any(re.search(pattern, commit.subject) is not None
for pattern in ignore_regexps):
continue

matched_section = first_matching(section_regexps, commit.subject)

## Finally storing the commit in the matching section

sections[matched_section].append({
"author": commit.author_name,
"subject": subject_process(commit.subject),
"body": body_process(commit.body),
})
## Split the subject if using a split_event_regex otherwise just
## use the subject
if split_event_regex:
events = re.split(split_event_regex, commit.subject)
else:
events = [commit.subject]

## Loop through the resulting event list
for event in events:

## Check that the event is not just an empty string (presumably the split
## regex is not perfect)
event = strip(event)
if not event:
continue

## If this event matches an ignore regex then skip it
if any(re.search(pattern, event) is not None
for pattern in ignore_regexps):
continue

## Find the first matching section (not sure why this is certain to work
## or what happens if it fails)
matched_section = first_matching(section_regexps, event)

## Finally store the commit in the matching section
sections[matched_section].append({
"author": commit.author_name,
"subject": subject_process(event),
"body": '' if split_event_regex else body_process(commit.body),
})

## Flush current version
current_version["sections"] = [{"label": k, "commits": sections[k]}
Expand Down Expand Up @@ -1024,6 +1045,7 @@ def main():

content = changelog(
repository,
split_event_regex=config['split_event_regex'],
ignore_regexps=config['ignore_regexps'],
section_regexps=config['section_regexps'],
unreleased_version_label=config['unreleased_version_label'],
Expand Down
8 changes: 8 additions & 0 deletions gitchangelog.rc.reference
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@
## tags, and other rules only applies to the summary. The body of the commit
## message will be displayed in the changelog without reformatting.

##
## ``split_event_regex`` is a regex
##
## It is used to split a commit log entry into events before processing each event.
##
#split_event_regex = r'(?s)(.+?)(?=(?:[Cc]hg|[Ff]ix|[Nn]ew)\s*:|\s*$)'
split_event_regex = None


##
## ``ignore_regexps`` is a line of regexps
Expand Down