feat: added support for parsing headers in body, e.g. to support squashed commits

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
This commit is contained in:
Frederic BIDON 2019-06-04 19:08:47 +02:00 committed by Vincent Guilbert
parent d93ef22384
commit d6459d4601
6 changed files with 145 additions and 81 deletions

186
README.md
View file

@ -116,15 +116,19 @@ go install github.com/git-chglog/git-chglog/cmd/git-chglog@latest
```
### [Docker](https://www.docker.com/)
The compiled docker images are maintained on [quay.io](https://quay.io/repository/git-chglog/git-chglog).
The compiled docker images are maintained on [quay.io](https://quay.io/repository/git-chglog/git-chglog).
We maintain the following tags:
- `edge`: Image that is build from the current `HEAD` of the main line branch.
- `latest`: Image that is built from the [latest released version](https://github.com/git-chglog/git-chglog/releases)
- `x.y.y` (versions): Images that are build from the tagged versions within Github.
```bash
docker pull quay.io/git-chglog/git-chglog:latest
docker run -v "$PWD":/workdir quay.io/git-chglog/git-chglog --version
```
---
If you are using another platform, you can download a binary from the [releases page]
@ -253,7 +257,7 @@ You can specify which commits to include in the generation of CHANGELOG using `<
The table below shows Query patterns and summaries, and Query examples.
| Query | Description | Example |
|:---------------|:-----------------------------------------------|:----------------------------|
| :------------- | :--------------------------------------------- | :-------------------------- |
| `<old>..<new>` | Commit contained in `<new>` tags from `<old>`. | `$ git-chglog 1.0.0..2.0.0` |
| `<name>..` | Commit from the `<name>` to the latest tag. | `$ git-chglog 1.0.0..` |
| `..<name>` | Commit from the oldest tag to `<name>`. | `$ git-chglog ..2.0.0` |
@ -275,7 +279,7 @@ info:
repository_url: https://github.com/git-chglog/git-chglog
options:
tag_filter_pattern: '^v'
tag_filter_pattern: "^v"
sort: "date"
commits:
@ -283,6 +287,7 @@ options:
Type:
- feat
sort_by: Scope
multiline_commits: false # when true, allows for parsing body and retrieve squashed commits independently
commit_groups:
group_by: Type
@ -299,7 +304,7 @@ options:
issues:
prefix:
- #
- #
refs:
actions:
@ -326,7 +331,7 @@ options:
Git execution command.
| Required | Type | Default | Description |
|:---------|:-------|:--------|:------------|
| :------- | :----- | :------ | :---------- |
| N | String | `"git"` | - |
### `style`
@ -335,7 +340,7 @@ CHANGELOG style. Automatic linking of issues and notices, initial value setting
such as merges etc. are done automatically.
| Required | Type | Default | Description |
|:---------|:-------|:---------|:-------------------------------------------------------|
| :------- | :----- | :------- | :----------------------------------------------------- |
| N | String | `"none"` | Should be `"github"` `"gitlab"` `"bitbucket"` `"none"` |
### `template`
@ -344,7 +349,7 @@ Path for the template file. It is specified by a relative path from the setting
file. Absolute paths are also ok.
| Required | Type | Default | Description |
|:---------|:-------|:---------------------|:------------|
| :------- | :----- | :------------------- | :---------- |
| N | String | `"CHANGELOG.tpl.md"` | - |
### `info`
@ -353,7 +358,7 @@ Metadata for CHANGELOG. Depending on Style, it is sometimes used in processing,
so it is recommended to specify it.
| Key | Required | Type | Default | Description |
|:-----------------|:---------|:-------|:--------------|:-----------------------|
| :--------------- | :------- | :----- | :------------ | :--------------------- |
| `title` | N | String | `"CHANGELOG"` | Title of CHANGELOG. |
| `repository_url` | N | String | none | URL of git repository. |
@ -365,25 +370,26 @@ Options used to process commits.
Options concerning the acquisition and sort of commits.
| Required | Type | Default | Description |
|:---------|:------------|:----------|:--------------------------------------------------------------------------------------------------------------------|
| N | String | `"date"` | Defines how tags are sorted in the generated change log. Values: "date", "semver". |
| Required | Type | Default | Description |
| :------- | :----- | :------- | :--------------------------------------------------------------------------------- |
| N | String | `"date"` | Defines how tags are sorted in the generated change log. Values: "date", "semver". |
#### `options.commits`
Options concerning the acquisition and sort of commits.
| Key | Required | Type | Default | Description |
|:----------|:---------|:------------|:----------|:----------------------------------------------------------------------------------------------------|
| `filters` | N | Map in List | none | Filter by using `Commit` properties and values. Filtering is not done by specifying an empty value. |
| `sort_by` | N | String | `"Scope"` | Property name to use for sorting `Commit`. See [Commit]. |
| Key | Required | Type | Default | Description |
| :------------------- | :------- | :---------- | :-------- | :-------------------------------------------------------------------------------------------------- |
| `filters` | N | Map in List | none | Filter by using `Commit` properties and values. Filtering is not done by specifying an empty value. |
| `sort_by` | N | String | `"Scope"` | Property name to use for sorting `Commit`. See [Commit]. |
| `multiline_commits:` | N | Bool | `false` | Allows for commit body parsing to find change log entries. |
#### `options.commit_groups`
Options for groups of commits.
| Key | Required | Type | Default | Description |
|:--------------|:---------|:------------|:----------|:-------------------------------------------------------------------------------------------|
| :------------ | :------- | :---------- | :-------- | :----------------------------------------------------------------------------------------- |
| `group_by` | N | String | `"Type"` | Property name of `Commit` to be grouped into `CommitGroup`. See [CommitGroup][doc-commit]. |
| `sort_by` | N | String | `"Title"` | Property name to use for sorting `CommitGroup`. See [CommitGroup][doc-commit-group]. |
| `title_order` | N | List | none | Predefined order of titles to use for sorting `CommitGroup`. Only if `sort_by` is `Custom` |
@ -394,7 +400,7 @@ Options for groups of commits.
This option is used for parsing the commit header.
| Key | Required | Type | Default | Description |
|:---------------|:---------|:-------|:--------|:--------------------------------------------------------------------------------------------------------|
| :------------- | :------- | :----- | :------ | :------------------------------------------------------------------------------------------------------ |
| `pattern` | Y | String | none | A regular expression to use for parsing the commit header. |
| `pattern_maps` | Y | List | none | A rule for mapping the result of `HeaderPattern` to the property of `Commit`. See [Commit][doc-commit]. |
@ -403,7 +409,7 @@ This option is used for parsing the commit header.
This option is used to detect issues.
| Key | Required | Type | Default | Description |
|:---------|:---------|:-----|:--------|:-------------------------------------------|
| :------- | :------- | :--- | :------ | :----------------------------------------- |
| `prefix` | N | List | none | Prefix used for issues. (e.g. `#`, `#gh-`) |
#### `options.refs`
@ -411,7 +417,7 @@ This option is used to detect issues.
This option is for parsing references.
| Key | Required | Type | Default | Description |
|:----------|:---------|:-----|:--------|:-----------------------------------------------|
| :-------- | :------- | :--- | :------ | :--------------------------------------------- |
| `actions` | N | List | none | Word list of `Ref.Action`. See [Ref][doc-ref]. |
#### `options.merges`
@ -419,7 +425,7 @@ This option is for parsing references.
Options to detect and parse merge commits.
| Key | Required | Type | Default | Description |
|:---------------|:---------|:-------|:--------|:------------------------------------------|
| :------------- | :------- | :----- | :------ | :---------------------------------------- |
| `pattern` | N | String | none | Similar to `options.header.pattern`. |
| `pattern_maps` | N | List | none | Similar to `options.header.pattern_maps`. |
@ -428,7 +434,7 @@ Options to detect and parse merge commits.
Options to detect and parse revert commits.
| Key | Required | Type | Default | Description |
|:---------------|:---------|:-------|:--------|:------------------------------------------|
| :------------- | :------- | :----- | :------ | :---------------------------------------- |
| `pattern` | N | String | none | Similar to `options.header.pattern`. |
| `pattern_maps` | N | List | none | Similar to `options.header.pattern_maps`. |
@ -437,7 +443,7 @@ Options to detect and parse revert commits.
Options to detect notes contained in commit bodies.
| Key | Required | Type | Default | Description |
|:-----------|:---------|:-----|:--------|:-----------------------------------------------------------------------------------------------------|
| :--------- | :------- | :--- | :------ | :--------------------------------------------------------------------------------------------------- |
| `keywords` | N | List | none | Keyword list to find `Note`. A semicolon is a separator, like `<keyword>:` (e.g. `BREAKING CHANGE`). |
## Templates
@ -470,45 +476,62 @@ The basic templates are as follows.
```markdown
{{ if .Versions -}}
<a name="unreleased"></a>
## [Unreleased]
{{ if .Unreleased.CommitGroups -}}
{{ range .Unreleased.CommitGroups -}}
### {{ .Title }}
{{ range .Commits -}}
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}
{{ range .Versions }}
<a name="{{ .Tag.Name }}"></a>
## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }}
{{ range .CommitGroups -}}
### {{ .Title }}
{{ range .Commits -}}
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
{{ end }}
{{ end -}}
{{ end }}
{{ end -}}
{{- if .RevertCommits -}}
### Reverts
{{ range .RevertCommits -}}
- {{ .Revert.Header }}
{{ end }}
{{ end -}}
{{ end }}
{{ end -}}
{{- if .MergeCommits -}}
### Pull Requests
{{ range .MergeCommits -}}
- {{ .Header }}
{{ end }}
{{ end -}}
{{ end }}
{{ end -}}
{{- if .NoteGroups -}}
{{ range .NoteGroups -}}
### {{ .Title }}
{{ range .Notes }}
{{ .Body }}
{{ end }}
@ -531,13 +554,13 @@ See the godoc [RenderData][doc-render-data] documentation for available variable
## Supported Styles
| Name | Status | Features |
|:-------------------------------------------|:-------------------|:-------------------------------------------------------|
| :----------------------------------------- | :----------------- | :----------------------------------------------------- |
| [GitHub](https://github.com/) | :white_check_mark: | Mentions automatic link. Automatic link to references. |
| [GitLab](https://about.gitlab.com/) | :white_check_mark: | Mentions automatic link. Automatic link to references. |
| [Bitbucket](https://bitbucket.org/product) | :white_check_mark: | Mentions automatic link. Automatic link to references. |
> :memo: Even with styles that are not yet supported, it is possible to make
ordinary CHANGELOG.
> ordinary CHANGELOG.
## Jira Integration
@ -551,18 +574,18 @@ Take the following steps to add Jira integration:
### 1. Change the header parse pattern to recognize Jira issue id in the configure file
__Where Jira issue is identical Jira story.__
**Where Jira issue is identical Jira story.**
The following is a sample pattern:
```yaml
header:
pattern: "^(?:(\\w*)|(?:\\[(.*)\\])?)\\:\\s(.*)$"
pattern_maps:
- Type
- JiraIssueID
- Subject
```
```yaml
header:
pattern: "^(?:(\\w*)|(?:\\[(.*)\\])?)\\:\\s(.*)$"
pattern_maps:
- Type
- JiraIssueID
- Subject
```
This sample pattern can match both forms of commit headers:
@ -573,18 +596,18 @@ This sample pattern can match both forms of commit headers:
The following is a sample:
```yaml
jira:
info:
username: u
token: p
url: https://jira.com
issue:
type_maps:
Task: fix
Story: feat
description_pattern: "<changelog>(.*)</changelog>"
```
```yaml
jira:
info:
username: u
token: p
url: https://jira.com
issue:
type_maps:
Task: fix
Story: feat
description_pattern: "<changelog>(.*)</changelog>"
```
Here you need to define Jira URL, access username and token (password). If you
don't want to write your Jira access credential in configure file, you may define
@ -605,13 +628,16 @@ data. For example:
```markdown
{{ range .CommitGroups -}}
### {{ .Title }}
{{ range .Commits -}}
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
{{ if .JiraIssue }} {{ .JiraIssue.Description }}
{{ end }}
{{ end }}
{{ end -}}
{{ if .JiraIssue }} {{ .JiraIssue.Description }}
{{ end }}
{{ end }}
{{ end -}}
```
Within a `Commit`, the following Jira data can be used in template:
@ -628,44 +654,46 @@ Within a `Commit`, the following Jira data can be used in template:
This is not for the purpose of completely automating the generation of CHANGELOG
files, it is only for assisting generation.
It is ideal to describe everything included in CHANGELOG in your commits. But
actually it is very difficult to do it perfectly.
It is ideal to describe everything included in CHANGELOG in your commits. But
actually it is very difficult to do it perfectly.
There are times when you need to edit the generated output to write a great CHANGELOG.
There are times when you need to edit the generated output to write a great CHANGELOG.
By displaying it on the standard output, it makes it easy to change the contents.
By displaying it on the standard output, it makes it easy to change the contents.
</details>
<details>
<summary>Can I commit CHANGELOG changes before creating tags?</summary>
Yes, it can be solved by using the `--next-tag` flag.
Yes, it can be solved by using the `--next-tag` flag.
For example, let's say you want to upgrade your project to `2.0.0`.
You can create CHANGELOG containing `2.0.0` as follows.
For example, let's say you want to upgrade your project to `2.0.0`.
You can create CHANGELOG containing `2.0.0` as follows.
```bash
git-chglog --next-tag 2.0.0 -o CHANGELOG.md
git commit -am "release 2.0.0"
git tag 2.0.0
```
```bash
git-chglog --next-tag 2.0.0 -o CHANGELOG.md
git commit -am "release 2.0.0"
git tag 2.0.0
```
The point to notice is that before actually creating a tag with `git`, it is
conveying the next version with `--next-tag` :+1:
The point to notice is that before actually creating a tag with `git`, it is
conveying the next version with `--next-tag` :+1:
This is a step that is necessary for project operation in many cases.
This is a step that is necessary for project operation in many cases.
</details>
<details>
<summary>Can I generate a CHANGELOG based on certain tags?</summary>
Yes, it can be solved by use the `--tag-filter-pattern` flag.
Yes, it can be solved by use the `--tag-filter-pattern` flag.
For example, the following command will only include tags starting with "v":
For example, the following command will only include tags starting with "v":
```bash
git-chglog --tag-filter-pattern '^v'
```
```bash
git-chglog --tag-filter-pattern '^v'
```
</details>

View file

@ -38,6 +38,7 @@ type Options struct {
RevertPattern string // A regular expression to use for parsing the revert commit
RevertPatternMaps []string // Similar to `HeaderPatternMaps`
NoteKeywords []string // Keyword list to find `Note`. A semicolon is a separator, like `<keyword>:` (e.g. `BREAKING CHANGE`)
MultilineCommit bool // Attempt to match header several times in commit body. Useful to parse squashed commits.
JiraUsername string
JiraToken string
JiraURL string

View file

@ -17,8 +17,9 @@ type Info struct {
// CommitOptions ...
type CommitOptions struct {
Filters map[string][]string `yaml:"filters"`
SortBy string `yaml:"sort_by"`
Filters map[string][]string `yaml:"filters"`
SortBy string `yaml:"sort_by"`
MultilineCommit bool `yaml:"multiline_commit"`
}
// CommitGroupOptions ...
@ -326,6 +327,7 @@ func (config *Config) Convert(ctx *CLIContext) *chglog.Config {
RevertPattern: opts.Reverts.Pattern,
RevertPatternMaps: opts.Reverts.PatternMaps,
NoteKeywords: opts.Notes.Keywords,
MultilineCommit: opts.Commits.MultilineCommit,
JiraUsername: orValue(ctx.JiraUsername, opts.Jira.ClintInfo.Username),
JiraToken: orValue(ctx.JiraToken, opts.Jira.ClintInfo.Token),
JiraURL: orValue(ctx.JiraURL, opts.Jira.ClintInfo.URL),

View file

@ -10,8 +10,17 @@ func commitFilter(commits []*Commit, filters map[string][]string, noCaseSensitiv
// be reasonably moved into an injected dependency.
res := []*Commit{}
expandedCommits := []*Commit{}
for _, commit := range commits {
// expand squashed entries
expandedCommits = append(expandedCommits, commit)
if len(commit.AllHeaders) > 0 {
expandedCommits = append(expandedCommits, commit.AllHeaders...)
}
}
for _, commit := range expandedCommits {
include := false
if len(filters) == 0 {

View file

@ -268,6 +268,29 @@ func (p *commitParser) processBody(commit *Commit, input string) {
// body
commit.Body = input
opts := p.config.Options
if opts.MultilineCommit {
// additional headers in body
body := input
body = p.reNotes.ReplaceAllString(body, "") // strip notes from mody
res := p.reHeader.FindAllStringSubmatch(body, -1)
if len(res) > 0 {
assignDynamicValues(commit, opts.HeaderPatternMaps, res[0][1:])
if len(res) > 1 {
commit.AllHeaders = make([]*Commit, 0, len(res)-1)
for _, matchGroups := range res[1:] {
// parses all matches
h := *commit
h.Header = matchGroups[0]
h.AllHeaders = nil
h.Body = ""
assignDynamicValues(&h, opts.HeaderPatternMaps, matchGroups[1:])
commit.AllHeaders = append(commit.AllHeaders, &h)
}
}
}
}
// notes & refs & mentions
commit.Notes = []*Note{}
inNote := false

View file

@ -86,6 +86,7 @@ type Commit struct {
JiraIssueID string // (e.g. `RNWY-310`)
Body string
TrimmedBody string // Body without any Notes/Refs/Mentions/CoAuthors/Signers
AllHeaders []*Commit // when multiple headers are matched in a single commit
}
// CommitGroup is a collection of commits grouped according to the `CommitGroupBy` option