Skip to content

Commit

Permalink
feat(changelog): support generating changelog for different branches (#…
Browse files Browse the repository at this point in the history
…808)

* feat: support generating change log for different branches

* check if the head commit is also a tag commit

* simplify implementation without requiring a branch

* remove unused import

* update test-topo-order fixture

* add comment for should_include_tag

* fix typo

* rename variable

* generate fixture considering timezone difference

* make --use-branch-tags optional for backward compatibility

* add missing field

* refactor: polish implementation

---------

Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
  • Loading branch information
braineo and orhun authored Sep 9, 2024
1 parent 08e761c commit 2a581a8
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 13 deletions.
1 change: 1 addition & 0 deletions git-cliff-core/src/changelog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,7 @@ mod test {
skip_tags: Regex::new("v3.*").ok(),
ignore_tags: None,
count_tags: None,
use_branch_tags: Some(false),
topo_order: Some(false),
sort_commits: Some(String::from("oldest")),
link_parsers: None,
Expand Down
2 changes: 2 additions & 0 deletions git-cliff-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ pub struct GitConfig {
/// Regex to count matched tags.
#[serde(with = "serde_regex", default)]
pub count_tags: Option<Regex>,
/// Include only the tags that belong to the current branch.
pub use_branch_tags: Option<bool>,
/// Whether to sort tags topologically.
pub topo_order: Option<bool>,
/// Sorting of the commits inside sections.
Expand Down
35 changes: 33 additions & 2 deletions git-cliff-core/src/repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,16 +310,35 @@ impl Repository {
None
}

/// Decide whether to include tag
///
/// `head_commit` is the `latest` commit to generate changelog. It can be a
/// branch head or a detached head. `tag_commit` is a tagged commit. If the
/// commit is in the descendant graph of the head_commit or is the
/// head_commit itself, Changelog should include the tag.
fn should_include_tag(
&self,
head_commit: &Commit,
tag_commit: &Commit,
) -> Result<bool> {
Ok(self
.inner
.graph_descendant_of(head_commit.id(), tag_commit.id())? ||
head_commit.id() == tag_commit.id())
}

/// Parses and returns a commit-tag map.
///
/// It collects lightweight and annotated tags.
pub fn tags(
&self,
pattern: &Option<Regex>,
topo_order: bool,
use_branch_tags: bool,
) -> Result<IndexMap<String, Tag>> {
let mut tags: Vec<(Commit, Tag)> = Vec::new();
let tag_names = self.inner.tag_names(None)?;
let head_commit = self.inner.head()?.peel_to_commit()?;
for name in tag_names
.iter()
.flatten()
Expand All @@ -330,6 +349,12 @@ impl Repository {
{
let obj = self.inner.revparse_single(&name)?;
if let Ok(commit) = obj.clone().into_commit() {
if use_branch_tags &&
!self.should_include_tag(&head_commit, &commit)?
{
continue;
}

tags.push((commit, Tag {
name,
message: None,
Expand All @@ -340,6 +365,11 @@ impl Repository {
.ok()
.and_then(|target| target.into_commit().ok())
{
if use_branch_tags &&
!self.should_include_tag(&head_commit, &commit)?
{
continue;
}
tags.push((commit, Tag {
name: tag.name().map(String::from).unwrap_or(name),
message: tag.message().map(|msg| {
Expand Down Expand Up @@ -468,15 +498,15 @@ mod test {
#[test]
fn get_latest_tag() -> Result<()> {
let repository = get_repository()?;
let tags = repository.tags(&None, false)?;
let tags = repository.tags(&None, false, false)?;
assert_eq!(get_last_tag()?, tags.last().expect("no tags found").1.name);
Ok(())
}

#[test]
fn git_tags() -> Result<()> {
let repository = get_repository()?;
let tags = repository.tags(&None, true)?;
let tags = repository.tags(&None, true, false)?;
assert_eq!(
tags.get("2b8b4d3535f29231e05c3572e919634b9af907b6")
.expect(
Expand All @@ -500,6 +530,7 @@ mod test {
.expect("the regex is not valid"),
),
true,
false,
)?;
assert_eq!(
tags.get("2b8b4d3535f29231e05c3572e919634b9af907b6")
Expand Down
1 change: 1 addition & 0 deletions git-cliff-core/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ fn generate_changelog() -> Result<()> {
skip_tags: None,
ignore_tags: None,
count_tags: None,
use_branch_tags: None,
topo_order: None,
sort_commits: None,
link_parsers: Some(vec![
Expand Down
3 changes: 3 additions & 0 deletions git-cliff/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@ pub struct Opt {
/// Sorts the tags topologically.
#[arg(long, help_heading = Some("FLAGS"))]
pub topo_order: bool,
/// Include only the tags that belong to the current branch.
#[arg(long, help_heading = Some("FLAGS"))]
pub use_branch_tags: bool,
/// Disables the external command execution.
#[arg(long, help_heading = Some("FLAGS"))]
pub no_exec: bool,
Expand Down
6 changes: 5 additions & 1 deletion git-cliff/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ fn process_repository<'a>(
config: &mut Config,
args: &Opt,
) -> Result<Vec<Release<'a>>> {
let mut tags = repository.tags(&config.git.tag_pattern, args.topo_order)?;
let mut tags = repository.tags(
&config.git.tag_pattern,
args.topo_order,
args.use_branch_tags,
)?;
let skip_regex = config.git.skip_tags.as_ref();
let ignore_regex = config.git.ignore_tags.as_ref();
let count_tags = config.git.count_tags.as_ref();
Expand Down
21 changes: 11 additions & 10 deletions website/docs/usage/args.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@ git-cliff [FLAGS] [OPTIONS] [--] [RANGE]
## Flags

```
-h, --help Prints help information
-V, --version Prints version information
-v, --verbose... Increases the logging verbosity
--bumped-version Prints bumped version for unreleased changes
-l, --latest Processes the commits starting from the latest tag
--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
--no-exec Disables the external command execution
-x, --context Prints changelog context as JSON
-h, --help Prints help information
-V, --version Prints version information
-v, --verbose... Increases the logging verbosity
--bumped-version Prints bumped version for unreleased changes
-l, --latest Processes the commits starting from the latest tag
--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
--use-branch-tags Include only the tags that belong to the current branch
--no-exec Disables the external command execution
-x, --context Prints changelog context as JSON
```

## Options
Expand Down
6 changes: 6 additions & 0 deletions website/docs/usage/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ git cliff v2.2.1..
git cliff v0.1.0..HEAD
```

Only include the tags from the current branch:

```bash
git cliff --use-branch-tags
```

Sort the commits inside sections:

```bash
Expand Down

0 comments on commit 2a581a8

Please sign in to comment.