1
1
mirror of https://github.com/orhun/git-cliff.git synced 2024-08-16 18:30:30 +03:00

Compare commits

...

3 Commits

Author SHA1 Message Date
dependabot[bot]
27e1129dea
Merge 3d78b729a4 into 3eb828e69a 2024-06-23 22:17:14 +03:00
Meitar Reihan
3eb828e69a
feat(changelog): add tag message to release context (#713)
* feat: add tag message to release context

- add to the release context the tag's message if it exists
- add message to the default config

* feat(args): add `--with-tag-message` argument

to allow setting the message of the last release in the context

* docs: update args

* docs: add adding-tag-messages

* refactor: polish implementation

* tests(test-tag-message): fix the template

---------

Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
2024-06-22 13:29:49 +03:00
dependabot[bot]
3d78b729a4
chore(deps): bump git2 from 0.18.3 to 0.19.0
Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.18.3 to 0.19.0.
- [Changelog](https://github.com/rust-lang/git2-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/git2-rs/compare/git2-0.18.3...git2-0.19.0)

---
updated-dependencies:
- dependency-name: git2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-19 08:54:41 +00:00
22 changed files with 442 additions and 90 deletions

View File

@ -0,0 +1,37 @@
[changelog]
# template for the changelog footer
header = """
# Changelog\n
All notable changes to this project will be documented in this file.\n
"""
# template for the changelog body
# https://keats.github.io/tera/docs/#introduction
body = """
{% if version %}\
## [{{ version | trim_start_matches(pat="v") }}]
{% if message %}
{{ message }}
{% endif %}\
{% else %}\
## [unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | upper_first }}
{% for commit in commits %}
- {{ commit.message | upper_first }}\
{% endfor %}
{% endfor %}\n
"""
# template for the changelog footer
footer = """
<!-- generated by git-cliff -->
"""
# remove the leading and trailing whitespace from the templates
trim = true
[git]
# regex for parsing and grouping commits
commit_parsers = [
{ message = "^feat", group = "Features", default_scope = "app" },
{ message = "^fix", group = "Bug Fixes", scope = "cli" },
]

View File

@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e
GIT_COMMITTER_DATE="2022-04-06 01:25:08" git commit --allow-empty -m "Initial commit"
GIT_COMMITTER_DATE="2022-04-06 01:25:09" git commit --allow-empty -m "feat: add feature 1"
GIT_COMMITTER_DATE="2022-04-06 01:25:10" git commit --allow-empty -m "fix: fix feature 1"
git tag v0.1.0
GIT_COMMITTER_DATE="2022-04-06 01:25:11" git commit --allow-empty -m "feat(gui): add feature 2"
GIT_COMMITTER_DATE="2022-04-06 01:25:12" git commit --allow-empty -m "fix(gui): fix feature 2"
git tag v0.2.0
GIT_COMMITTER_DATE="2022-04-06 01:25:13" git commit --allow-empty -m "test: add tests"

View File

@ -0,0 +1,13 @@
# Changelog
All notable changes to this project will be documented in this file.
## [0.2.1]
Some text
### Test
- Add tests
<!-- generated by git-cliff -->

View File

@ -0,0 +1,37 @@
[changelog]
# template for the changelog footer
header = """
# Changelog\n
All notable changes to this project will be documented in this file.\n
"""
# template for the changelog body
# https://keats.github.io/tera/docs/#introduction
body = """
{% if version %}\
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% if message %}
{{ message }}
{% endif %}\
{% else %}\
## [unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | upper_first }}
{% for commit in commits %}
- {{ commit.message | upper_first }}\
{% endfor %}
{% endfor %}\n
"""
# template for the changelog footer
footer = """
<!-- generated by git-cliff -->
"""
# remove the leading and trailing whitespace from the templates
trim = true
[git]
# regex for parsing and grouping commits
commit_parsers = [
{ message = "^feat", group = "Features", default_scope = "app" },
{ message = "^fix", group = "Bug Fixes", scope = "cli" },
]

11
.github/fixtures/test-tag-message/commit.sh vendored Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e
GIT_COMMITTER_DATE="2022-04-06 01:25:08" git commit --allow-empty -m "Initial commit"
GIT_COMMITTER_DATE="2022-04-06 01:25:09" git commit --allow-empty -m "feat: add feature 1"
GIT_COMMITTER_DATE="2022-04-06 01:25:10" git commit --allow-empty -m "fix: fix feature 1"
git tag v0.1.0 -m "Some text"
GIT_COMMITTER_DATE="2022-04-06 01:25:11" git commit --allow-empty -m "feat(gui): add feature 2"
GIT_COMMITTER_DATE="2022-04-06 01:25:12" git commit --allow-empty -m "fix(gui): fix feature 2"
git tag v0.2.0
GIT_COMMITTER_DATE="2022-04-06 01:25:13" git commit --allow-empty -m "test: add tests"

View File

@ -0,0 +1,33 @@
# Changelog
All notable changes to this project will be documented in this file.
## [unreleased]
### Test
- Add tests
## [0.2.0] - 2022-04-06
### Bug Fixes
- Fix feature 2
### Features
- Add feature 2
## [0.1.0] - 2022-04-06
Some text
### Bug Fixes
- Fix feature 1
### Features
- Add feature 1
<!-- generated by git-cliff -->

View File

@ -81,6 +81,9 @@ jobs:
command: --bump --tag=2.1.1
- fixtures-name: test-cli-arg-ignore-tags
command: --ignore-tags ".*beta"
- fixtures-name: test-tag-message
- fixtures-name: test-bump-unreleased-with-tag-message-arg
command: --bump --unreleased --with-tag-message "Some text"
steps:
- name: Checkout

8
Cargo.lock generated
View File

@ -828,9 +828,9 @@ dependencies = [
[[package]]
name = "git2"
version = "0.18.3"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "232e6a7bfe35766bf715e55a88b39a700596c0ccfd88cd3680b4cdb40d66ef70"
checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724"
dependencies = [
"bitflags 2.5.0",
"libc",
@ -1364,9 +1364,9 @@ dependencies = [
[[package]]
name = "libgit2-sys"
version = "0.16.2+1.7.2"
version = "0.17.0+1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee4126d8b4ee5c9d9ea891dd875cfdc1e9d0950437179104b183d7d8a74d24e8"
checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224"
dependencies = [
"cc",
"libc",

View File

@ -76,7 +76,7 @@ url = "2.5.2"
dyn-clone = "1.0.17"
[dependencies.git2]
version = "0.18.3"
version = "0.19.0"
default-features = false
optional = true

View File

@ -815,6 +815,7 @@ mod test {
};
let test_release = Release {
version: Some(String::from("v1.0.0")),
message: None,
commits: vec![
Commit::new(
String::from("coffee"),
@ -909,6 +910,7 @@ mod test {
},
Release {
version: None,
message: None,
commits: vec![
Commit::new(
String::from("abc123"),

View File

@ -33,6 +33,8 @@ pub mod remote;
/// Git repository.
#[cfg(feature = "repo")]
pub mod repo;
/// Git tag.
pub mod tag;
/// Template engine.
pub mod template;

View File

@ -21,6 +21,8 @@ use serde::{
pub struct Release<'a> {
/// Release version, git tag.
pub version: Option<String>,
/// git tag's message.
pub message: Option<String>,
/// Commits made for the release.
pub commits: Vec<Commit<'a>>,
/// Commit ID of the tag.
@ -174,6 +176,7 @@ mod test {
fn build_release<'a>(version: &str, commits: &'a [&str]) -> Release<'a> {
Release {
version: None,
message: None,
commits: commits
.iter()
.map(|v| Commit::from(v.to_string()))
@ -363,6 +366,7 @@ mod test {
let mut release = Release {
version: None,
message: None,
commits: vec![
Commit::from(String::from(
"1d244937ee6ceb8e0314a4a201ba93a7a61f2071 add github \
@ -648,6 +652,7 @@ mod test {
let mut release = Release {
version: None,
message: None,
commits: vec![
Commit::from(String::from(
"1d244937ee6ceb8e0314a4a201ba93a7a61f2071 add github \
@ -991,6 +996,7 @@ mod test {
let mut release = Release {
version: None,
message: None,
commits: vec![
Commit::from(String::from(
"1d244937ee6ceb8e0314a4a201ba93a7a61f2071 add github \

View File

@ -3,6 +3,7 @@ use crate::error::{
Error,
Result,
};
use crate::tag::Tag;
use git2::{
BranchType,
Commit,
@ -13,11 +14,20 @@ use git2::{
};
use glob::Pattern;
use indexmap::IndexMap;
use regex::Regex;
use lazy_regex::{
lazy_regex,
Lazy,
Regex,
};
use std::io;
use std::path::PathBuf;
use url::Url;
/// Regex for replacing the signature part of a tag message.
static TAG_SIGNATURE_REGEX: Lazy<Regex> = lazy_regex!(
r"(?s)-----BEGIN PGP SIGNATURE-----(.*?)-----END PGP SIGNATURE-----"
);
/// Wrapper for [`Repository`] type from git2.
///
/// [`Repository`]: GitRepository
@ -95,11 +105,38 @@ impl Repository {
/// Returns the current tag.
///
/// It is the same as running `git describe --tags`
pub fn current_tag(&self) -> Option<String> {
pub fn current_tag(&self) -> Option<Tag> {
self.inner
.describe(DescribeOptions::new().describe_tags())
.ok()
.and_then(|describe| describe.format(None).ok())
.and_then(|describe| {
describe
.format(None)
.ok()
.map(|name| self.resolve_tag(&name))
})
}
/// Returns the tag object of the given name.
///
/// If given name doesn't exist, it still returns `Tag` with the given name.
pub fn resolve_tag(&self, name: &str) -> Tag {
match self
.inner
.resolve_reference_from_short_name(name)
.and_then(|r| r.peel_to_tag())
{
Ok(tag) => Tag {
name: tag.name().unwrap_or_default().to_owned(),
message: tag.message().map(|msg| {
TAG_SIGNATURE_REGEX.replace(msg, "").trim().to_owned()
}),
},
_ => Tag {
name: name.to_owned(),
message: None,
},
}
}
/// Returns the commit object of the given ID.
@ -119,8 +156,8 @@ impl Repository {
&self,
pattern: &Option<Regex>,
topo_order: bool,
) -> Result<IndexMap<String, String>> {
let mut tags: Vec<(Commit, String)> = Vec::new();
) -> Result<IndexMap<String, Tag>> {
let mut tags: Vec<(Commit, Tag)> = Vec::new();
let tag_names = self.inner.tag_names(None)?;
for name in tag_names
.iter()
@ -132,14 +169,22 @@ impl Repository {
{
let obj = self.inner.revparse_single(&name)?;
if let Ok(commit) = obj.clone().into_commit() {
tags.push((commit, name));
tags.push((commit, Tag {
name,
message: None,
}));
} else if let Some(tag) = obj.as_tag() {
if let Some(commit) = tag
.target()
.ok()
.and_then(|target| target.into_commit().ok())
{
tags.push((commit, name));
tags.push((commit, Tag {
name: tag.name().map(String::from).unwrap_or(name),
message: tag.message().map(|msg| {
TAG_SIGNATURE_REGEX.replace(msg, "").trim().to_owned()
}),
}));
}
}
}
@ -261,7 +306,7 @@ mod test {
fn get_latest_tag() -> Result<()> {
let repository = get_repository()?;
let tags = repository.tags(&None, false)?;
assert_eq!(&get_last_tag()?, tags.last().expect("no tags found").1);
assert_eq!(get_last_tag()?, tags.last().expect("no tags found").1.name);
Ok(())
}
@ -270,16 +315,20 @@ mod test {
let repository = get_repository()?;
let tags = repository.tags(&None, true)?;
assert_eq!(
tags.get("2b8b4d3535f29231e05c3572e919634b9af907b6").expect(
"the commit hash does not exist in the repository (tag v0.1.0)"
),
tags.get("2b8b4d3535f29231e05c3572e919634b9af907b6")
.expect(
"the commit hash does not exist in the repository (tag v0.1.0)"
)
.name,
"v0.1.0"
);
assert_eq!(
tags.get("4ddef08debfff48117586296e49d5caa0800d1b5").expect(
"the commit hash does not exist in the repository (tag \
v0.1.0-beta.4)"
),
tags.get("4ddef08debfff48117586296e49d5caa0800d1b5")
.expect(
"the commit hash does not exist in the repository (tag \
v0.1.0-beta.4)"
)
.name,
"v0.1.0-beta.4"
);
let tags = repository.tags(
@ -290,9 +339,11 @@ mod test {
true,
)?;
assert_eq!(
tags.get("2b8b4d3535f29231e05c3572e919634b9af907b6").expect(
"the commit hash does not exist in the repository (tag v0.1.0)"
),
tags.get("2b8b4d3535f29231e05c3572e919634b9af907b6")
.expect(
"the commit hash does not exist in the repository (tag v0.1.0)"
)
.name,
"v0.1.0"
);
assert!(!tags.contains_key("4ddef08debfff48117586296e49d5caa0800d1b5"));
@ -313,4 +364,30 @@ mod test {
);
Ok(())
}
#[test]
fn resolves_existing_tag_with_name_and_message() -> Result<()> {
let repository = get_repository()?;
let tag = repository.resolve_tag("v0.2.3");
assert_eq!(tag.name, "v0.2.3");
assert_eq!(
tag.message,
Some(
"Release v0.2.3\n\nBug Fixes\n- Fetch the dependencies before \
copying the file to embed (9e29c95)"
.to_string()
)
);
Ok(())
}
#[test]
fn resolves_tag_when_no_tags_exist() -> Result<()> {
let repository = get_repository()?;
let tag = repository.resolve_tag("nonexistent-tag");
assert_eq!(tag.name, "nonexistent-tag");
assert_eq!(tag.message, None);
Ok(())
}
}

47
git-cliff-core/src/tag.rs Normal file
View File

@ -0,0 +1,47 @@
/// Common tag object that is parsed from a repository.
///
/// Lightweight tags will have `None` as message.
#[derive(Debug)]
pub struct Tag {
/// The name of the tag
pub name: String,
/// The message of the tag (only if it was annotated).
pub message: Option<String>,
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn create_tag_with_name_and_message() {
let tag = Tag {
name: String::from("v1.0"),
message: Some(String::from("Initial release")),
};
assert_eq!(tag.name, "v1.0");
assert_eq!(tag.message, Some(String::from("Initial release")));
}
#[test]
fn create_tag_with_name_and_no_message() {
let tag = Tag {
name: String::from("v1.0"),
message: None,
};
assert_eq!(tag.name, "v1.0");
assert_eq!(tag.message, None);
}
#[test]
fn debug_print_tag_with_message() {
let tag = Tag {
name: String::from("v1.0"),
message: Some(String::from("Initial release")),
};
assert_eq!(
format!("{:?}", tag),
"Tag { name: \"v1.0\", message: Some(\"Initial release\") }"
);
}
}

View File

@ -189,6 +189,7 @@ mod test {
fn get_fake_release_data() -> Release<'static> {
Release {
version: Some(String::from("1.0")),
message: None,
commits: vec![
Commit::new(
String::from("123123"),

View File

@ -149,6 +149,7 @@ fn generate_changelog() -> Result<()> {
let releases = vec![
Release {
version: Some(String::from("v2.0.0")),
message: None,
commits: vec![
Commit::new(
@ -212,6 +213,7 @@ fn generate_changelog() -> Result<()> {
},
Release {
version: Some(String::from("v1.0.0")),
message: None,
commits: vec![
Commit::new(
String::from("0bc123"),

View File

@ -64,7 +64,7 @@ pub struct Opt {
help = "Prints help information",
help_heading = "FLAGS"
)]
pub help: Option<bool>,
pub help: Option<bool>,
#[arg(
short = 'V',
long,
@ -73,10 +73,10 @@ pub struct Opt {
help = "Prints version information",
help_heading = "FLAGS"
)]
pub version: Option<bool>,
pub version: Option<bool>,
/// Increases the logging verbosity.
#[arg(short, long, action = ArgAction::Count, alias = "debug", help_heading = Some("FLAGS"))]
pub verbose: u8,
pub verbose: u8,
/// Writes the default configuration file to cliff.toml
#[arg(
short,
@ -85,7 +85,7 @@ pub struct Opt {
num_args = 0..=1,
required = false
)]
pub init: Option<Option<String>>,
pub init: Option<Option<String>>,
/// Sets the configuration file.
#[arg(
short,
@ -95,7 +95,7 @@ pub struct Opt {
default_value = DEFAULT_CONFIG,
value_parser = Opt::parse_dir
)]
pub config: PathBuf,
pub config: PathBuf,
/// Sets the working directory.
#[arg(
short,
@ -104,7 +104,7 @@ pub struct Opt {
value_name = "PATH",
value_parser = Opt::parse_dir
)]
pub workdir: Option<PathBuf>,
pub workdir: Option<PathBuf>,
/// Sets the git repository.
#[arg(
short,
@ -114,7 +114,7 @@ pub struct Opt {
num_args(1..),
value_parser = Opt::parse_dir
)]
pub repository: Option<Vec<PathBuf>>,
pub repository: Option<Vec<PathBuf>>,
/// Sets the path to include related commits.
#[arg(
long,
@ -122,7 +122,7 @@ pub struct Opt {
value_name = "PATTERN",
num_args(1..)
)]
pub include_path: Option<Vec<Pattern>>,
pub include_path: Option<Vec<Pattern>>,
/// Sets the path to exclude related commits.
#[arg(
long,
@ -130,10 +130,10 @@ pub struct Opt {
value_name = "PATTERN",
num_args(1..)
)]
pub exclude_path: Option<Vec<Pattern>>,
pub exclude_path: Option<Vec<Pattern>>,
/// Sets the regex for matching git tags.
#[arg(long, env = "GIT_CLIFF_TAG_PATTERN", value_name = "PATTERN")]
pub tag_pattern: Option<Regex>,
pub tag_pattern: Option<Regex>,
/// Sets custom commit messages to include in the changelog.
#[arg(
long,
@ -141,10 +141,18 @@ pub struct Opt {
value_name = "MSG",
num_args(1..)
)]
pub with_commit: Option<Vec<String>>,
pub with_commit: Option<Vec<String>>,
/// Sets custom message for the latest release.
#[arg(
long,
env = "GIT_CLIFF_WITH_TAG_MESSAGE",
value_name = "MSG",
num_args = 0..=1,
)]
pub with_tag_message: Option<String>,
/// Sets the tags to ignore in the changelog.
#[arg(long, env = "GIT_CLIFF_IGNORE_TAGS", value_name = "PATTERN")]
pub ignore_tags: Option<Regex>,
pub ignore_tags: Option<Regex>,
/// Sets commits that will be skipped in the changelog.
#[arg(
long,
@ -152,7 +160,7 @@ pub struct Opt {
value_name = "SHA1",
num_args(1..)
)]
pub skip_commit: Option<Vec<String>>,
pub skip_commit: Option<Vec<String>>,
/// Prepends entries to the given changelog file.
#[arg(
short,
@ -161,7 +169,7 @@ pub struct Opt {
value_name = "PATH",
value_parser = Opt::parse_dir
)]
pub prepend: Option<PathBuf>,
pub prepend: Option<PathBuf>,
/// Writes output to the given file.
#[arg(
short,
@ -172,7 +180,7 @@ pub struct Opt {
num_args = 0..=1,
default_missing_value = DEFAULT_OUTPUT
)]
pub output: Option<PathBuf>,
pub output: Option<PathBuf>,
/// Sets the tag for the latest version.
#[arg(
short,
@ -181,13 +189,13 @@ pub struct Opt {
value_name = "TAG",
allow_hyphen_values = true
)]
pub tag: Option<String>,
pub tag: Option<String>,
/// Bumps the version for unreleased changes.
#[arg(long, help_heading = Some("FLAGS"))]
pub bump: bool,
pub bump: bool,
/// Prints bumped version for unreleased changes.
#[arg(long, help_heading = Some("FLAGS"))]
pub bumped_version: bool,
pub bumped_version: bool,
/// Sets the template for the changelog body.
#[arg(
short,
@ -196,38 +204,38 @@ pub struct Opt {
value_name = "TEMPLATE",
allow_hyphen_values = true
)]
pub body: Option<String>,
pub body: Option<String>,
/// Processes the commits starting from the latest tag.
#[arg(short, long, help_heading = Some("FLAGS"))]
pub latest: bool,
pub latest: bool,
/// Processes the commits that belong to the current tag.
#[arg(long, help_heading = Some("FLAGS"))]
pub current: bool,
pub current: bool,
/// Processes the commits that do not belong to a tag.
#[arg(short, long, help_heading = Some("FLAGS"))]
pub unreleased: bool,
pub unreleased: bool,
/// Sorts the tags topologically.
#[arg(long, help_heading = Some("FLAGS"))]
pub topo_order: bool,
pub topo_order: bool,
/// Disables the external command execution.
#[arg(long, help_heading = Some("FLAGS"))]
pub no_exec: bool,
pub no_exec: bool,
/// Prints changelog context as JSON.
#[arg(short = 'x', long, help_heading = Some("FLAGS"))]
pub context: bool,
pub context: bool,
/// Strips the given parts from the changelog.
#[arg(short, long, value_name = "PART", value_enum)]
pub strip: Option<Strip>,
pub strip: Option<Strip>,
/// Sets sorting of the commits inside sections.
#[arg(
long,
value_enum,
default_value_t = Sort::Oldest
)]
pub sort: Sort,
pub sort: Sort,
/// Sets the commit range to process.
#[arg(value_name = "RANGE", help_heading = Some("ARGS"))]
pub range: Option<String>,
pub range: Option<String>,
/// Sets the GitHub API token.
#[arg(
long,
@ -236,7 +244,7 @@ pub struct Opt {
hide_env_values = true,
hide = !cfg!(feature = "github"),
)]
pub github_token: Option<SecretString>,
pub github_token: Option<SecretString>,
/// Sets the GitHub repository.
#[arg(
long,
@ -245,7 +253,7 @@ pub struct Opt {
value_name = "OWNER/REPO",
hide = !cfg!(feature = "github"),
)]
pub github_repo: Option<RemoteValue>,
pub github_repo: Option<RemoteValue>,
/// Sets the GitLab API token.
#[arg(
long,
@ -254,7 +262,7 @@ pub struct Opt {
hide_env_values = true,
hide = !cfg!(feature = "gitlab"),
)]
pub gitlab_token: Option<SecretString>,
pub gitlab_token: Option<SecretString>,
/// Sets the GitLab repository.
#[arg(
long,
@ -263,7 +271,7 @@ pub struct Opt {
value_name = "OWNER/REPO",
hide = !cfg!(feature = "gitlab"),
)]
pub gitlab_repo: Option<RemoteValue>,
pub gitlab_repo: Option<RemoteValue>,
/// Sets the Gitea API token.
#[arg(
long,
@ -272,7 +280,7 @@ pub struct Opt {
hide_env_values = true,
hide = !cfg!(feature = "gitea"),
)]
pub gitea_token: Option<SecretString>,
pub gitea_token: Option<SecretString>,
/// Sets the GitLab repository.
#[arg(
long,
@ -281,7 +289,7 @@ pub struct Opt {
value_name = "OWNER/REPO",
hide = !cfg!(feature = "gitea"),
)]
pub gitea_repo: Option<RemoteValue>,
pub gitea_repo: Option<RemoteValue>,
/// Sets the Bitbucket API token.
#[arg(
long,
@ -290,7 +298,7 @@ pub struct Opt {
hide_env_values = true,
hide = !cfg!(feature = "bitbucket"),
)]
pub bitbucket_token: Option<SecretString>,
pub bitbucket_token: Option<SecretString>,
/// Sets the Bitbucket repository.
#[arg(
long,
@ -299,7 +307,7 @@ pub struct Opt {
value_name = "OWNER/REPO",
hide = !cfg!(feature = "bitbucket"),
)]
pub bitbucket_repo: Option<RemoteValue>,
pub bitbucket_repo: Option<RemoteValue>,
}
/// Custom type for the remote value.

View File

@ -90,7 +90,9 @@ fn process_repository<'a>(
let ignore_regex = config.git.ignore_tags.as_ref();
tags = tags
.into_iter()
.filter(|(_, name)| {
.filter(|(_, tag)| {
let name = &tag.name;
// Keep skip tags to drop commits in the later stage.
let skip = skip_regex.map(|r| r.is_match(name)).unwrap_or_default();
@ -184,7 +186,7 @@ fn process_repository<'a>(
repository.current_tag().as_ref().and_then(|tag| {
tags.iter()
.enumerate()
.find(|(_, (_, v))| v == &tag)
.find(|(_, (_, v))| v.name == tag.name)
.map(|(i, _)| i)
}) {
match current_tag_index.checked_sub(1) {
@ -226,10 +228,10 @@ fn process_repository<'a>(
if let Some(commit_id) = commits.first().map(|c| c.id().to_string()) {
match tags.get(&commit_id) {
Some(tag) => {
warn!("There is already a tag ({}) for {}", tag, commit_id)
warn!("There is already a tag ({:?}) for {}", tag, commit_id)
}
None => {
tags.insert(commit_id, tag.to_string());
tags.insert(commit_id, repository.resolve_tag(tag));
}
}
}
@ -249,9 +251,13 @@ fn process_repository<'a>(
releases[release_index].commits.push(commit);
}
if let Some(tag) = tags.get(&commit_id) {
releases[release_index].version = Some(tag.to_string());
let tag_name = &tag.name;
releases[release_index].version = Some(tag_name.clone());
releases[release_index].message = tag.message.clone();
releases[release_index].commit_id = Some(commit_id);
releases[release_index].timestamp = if args.tag.as_deref() == Some(tag) {
releases[release_index].timestamp = if args.tag == Some(tag_name.clone())
{
SystemTime::now()
.duration_since(UNIX_EPOCH)?
.as_secs()
@ -286,6 +292,13 @@ fn process_repository<'a>(
}
}
// Set custom message for the latest release.
if let Some(message) = &args.with_tag_message {
if let Some(latest_release) = releases.iter_mut().last() {
latest_release.message = Some(message.to_owned());
}
}
// Set the previous release if the first release does not have one set.
if !releases.is_empty() &&
releases
@ -299,7 +312,7 @@ fn process_repository<'a>(
.map(|tag| {
tags.iter()
.enumerate()
.find(|(_, (_, v))| v == &tag)
.find(|(_, (_, v))| v.name == tag.name)
.and_then(|(i, _)| i.checked_sub(1))
.and_then(|i| tags.get_index(i))
})
@ -307,10 +320,10 @@ fn process_repository<'a>(
.flatten();
// Set the previous release if the first tag is found.
if let Some((commit_id, version)) = first_tag {
if let Some((commit_id, tag)) = first_tag {
let previous_release = Release {
commit_id: Some(commit_id.to_string()),
version: Some(version.to_string()),
version: Some(tag.name.clone()),
timestamp: repository
.find_commit(commit_id.to_string())
.map(|v| v.time().seconds())

View File

@ -25,6 +25,7 @@ following context is generated to use for templating:
```json
{
"version": "v0.1.0-rc.21",
"message": "The annotated tag message for the release"
"commits": [
{
"id": "e795460c9bb7275294d1fa53a9d73258fb51eb10",
@ -129,6 +130,7 @@ If [`conventional_commits`](/docs/configuration/git#conventional_commits) is set
```json
{
"version": "v0.1.0-rc.21",
"message": "The annotated tag message for the release"
"commits": [
{
"id": "e795460c9bb7275294d1fa53a9d73258fb51eb10",

View File

@ -0,0 +1,42 @@
---
sidebar_position: 9
---
# Adding version (tag) message
Sometimes, you might want to include a special message or note related to a version of your project.
This can be used to highlight significant milestones, provide additional context, or share information not captured by individual commit messages.
There are currently 2 ways of doing this, in both ways, the message is available in the context of the template under the name `message`:
```
{% if message %}
{{ message }}
{% endif %}\
```
## Using annotated tags
The recommended way of adding a version message is to add the message to the tag:
```bash
git tag v1.0.0 -m "first release, yay!"
```
So in the release's context, `message` will be "first release, yay!" (even if it is signed).
## Using `--with-tag-message`
If for some reason you don't want to have the message in the tag (or don't have a tag yet) but want to include it in the generated changelog, you can use the `--with-tag-message` flag:
```bash
git cliff --bump --unreleased --with-tag-message "some text"
```
In this case, you can only add a message to the latest release.
:::note
Please note that if you use `--with-tag-message` on a version it will ignore the original tag's message and use the one from the argument.
:::

View File

@ -20,33 +20,38 @@ git-cliff [FLAGS] [OPTIONS] [--] [RANGE]
--current Processes the commits that belong to the current tag
-u, --unreleased Processes the commits that do not belong to a tag
--topo-order Sorts the tags topologically
-x, --context Prints changelog context as JSON
--no-exec Disables the external command execution
-x, --context Prints changelog context as JSON
```
## Options
```
-i, --init [<CONFIG>] Writes the default configuration file to cliff.toml
-c, --config <PATH> Sets the configuration file [env: GIT_CLIFF_CONFIG=] [default: cliff.toml]
-w, --workdir <PATH> Sets the working directory [env: GIT_CLIFF_WORKDIR=]
-r, --repository <PATH>... Sets the git repository [env: GIT_CLIFF_REPOSITORY=]
--include-path <PATTERN>... Sets the path to include related commits [env: GIT_CLIFF_INCLUDE_PATH=]
--exclude-path <PATTERN>... Sets the path to exclude related commits [env: GIT_CLIFF_EXCLUDE_PATH=]
--tag-pattern <PATTERN> Sets the regex for matching git tags [env: GIT_CLIFF_TAG_PATTERN=]
--with-commit <MSG>... Sets custom commit messages to include in the changelog [env: GIT_CLIFF_WITH_COMMIT=]
--ignore-tags <PATTERN> Sets the tags to ignore in the changelog [env: GIT_CLIFF_IGNORE_TAGS=]
--skip-commit <SHA1>... Sets commits that will be skipped in the changelog [env: GIT_CLIFF_SKIP_COMMIT=]
-p, --prepend <PATH> Prepends entries to the given changelog file [env: GIT_CLIFF_PREPEND=]
-o, --output [<PATH>] Writes output to the given file [env: GIT_CLIFF_OUTPUT=]
-t, --tag <TAG> Sets the tag for the latest version [env: GIT_CLIFF_TAG=]
-b, --body <TEMPLATE> Sets the template for the changelog body [env: GIT_CLIFF_TEMPLATE=]
-s, --strip <PART> Strips the given parts from the changelog [possible values: header, footer, all]
--sort <SORT> Sets sorting of the commits inside sections [default: oldest] [possible values: oldest, newest]
--github-token <TOKEN> Sets the GitHub API token [env: GITHUB_TOKEN]
--github-repo <OWNER/REPO> Sets the GitHub repository [env: GITHUB_REPO=]
--gitlab-token <TOKEN> Sets the GitLab API token [env: GITLAB_TOKEN]
--gitlab-repo <OWNER/REPO> Sets the GitLab repository [env: GITLAB_REPO=]
-i, --init [<CONFIG>] Writes the default configuration file to cliff.toml
-c, --config <PATH> Sets the configuration file [env: GIT_CLIFF_CONFIG=] [default: cliff.toml]
-w, --workdir <PATH> Sets the working directory [env: GIT_CLIFF_WORKDIR=]
-r, --repository <PATH>... Sets the git repository [env: GIT_CLIFF_REPOSITORY=]
--include-path <PATTERN>... Sets the path to include related commits [env: GIT_CLIFF_INCLUDE_PATH=]
--exclude-path <PATTERN>... Sets the path to exclude related commits [env: GIT_CLIFF_EXCLUDE_PATH=]
--tag-pattern <PATTERN> Sets the regex for matching git tags [env: GIT_CLIFF_TAG_PATTERN=]
--with-commit <MSG>... Sets custom commit messages to include in the changelog [env: GIT_CLIFF_WITH_COMMIT=]
--with-tag-message [<MSG>] Sets custom message for the latest release [env: GIT_CLIFF_WITH_TAG_MESSAGE=]
--ignore-tags <PATTERN> Sets the tags to ignore in the changelog [env: GIT_CLIFF_IGNORE_TAGS=]
--skip-commit <SHA1>... Sets commits that will be skipped in the changelog [env: GIT_CLIFF_SKIP_COMMIT=]
-p, --prepend <PATH> Prepends entries to the given changelog file [env: GIT_CLIFF_PREPEND=]
-o, --output [<PATH>] Writes output to the given file [env: GIT_CLIFF_OUTPUT=]
-t, --tag <TAG> Sets the tag for the latest version [env: GIT_CLIFF_TAG=]
-b, --body <TEMPLATE> Sets the template for the changelog body [env: GIT_CLIFF_TEMPLATE=]
-s, --strip <PART> Strips the given parts from the changelog [possible values: header, footer, all]
--sort <SORT> Sets sorting of the commits inside sections [default: oldest] [possible values: oldest, newest]
--github-token <TOKEN> Sets the GitHub API token [env: GITHUB_TOKEN]
--github-repo <OWNER/REPO> Sets the GitHub repository [env: GITHUB_REPO=]
--gitlab-token <TOKEN> Sets the GitLab API token [env: GITLAB_TOKEN]
--gitlab-repo <OWNER/REPO> Sets the GitLab repository [env: GITLAB_REPO=]
--gitea-token <TOKEN> Sets the Gitea API token [env: GITEA_TOKEN]
--gitea-repo <OWNER/REPO> Sets the GitLab repository [env: GITEA_REPO=]
--bitbucket-token <TOKEN> Sets the Bitbucket API token [env: BITBUCKET_TOKEN]
--bitbucket-repo <OWNER/REPO> Sets the Bitbucket repository [env: BITBUCKET_REPO=]
```
## Args

View File

@ -1,5 +1,5 @@
---
sidebar_position: 9
sidebar_position: 10
---
# Print context