diff --git a/Cargo.toml b/Cargo.toml index aaaad90..4e72259 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ fancy-regex = { version = "0.13.0" } chrono = { version = "0.4.38" } lazy_static = { version = "1.4.0" } Inflector = { version = "0.11.4" } +indexmap = { version = "2.2.6" } openssl = { version = "0.10.64", features = ["vendored"] } libz-sys = { version = "1.1.16" } \ No newline at end of file diff --git a/src/commands/changelog.rs b/src/commands/changelog.rs index 942fa3a..4fcc94f 100644 --- a/src/commands/changelog.rs +++ b/src/commands/changelog.rs @@ -1,18 +1,17 @@ extern crate inflector; use inflector::Inflector; -use crate::conventional::create_conventional_commit; -use crate::git::{retrieve_branch_commits, retrieve_tags}; +use crate::conventional::{create_conventional_commit, scope_filter_check}; +use crate::git::{retrieve_branch_commits, retrieve_commit_tag_map}; use crate::semantic::{add_commit_to_version, Version}; // Todo: find a nice template engine for Rust to create the changelog document: // Todo: use map for tag associated with commits on current branch: pub fn run(path: Option, _commit_git_hook: Option, scope_filter: Option) { let commits = retrieve_branch_commits(path.clone()); - let tags = retrieve_tags(path.clone()); + let optional_oid_commit_tag_map = retrieve_commit_tag_map(path); let mut version = Version::new(0, 0, 0); - let mut past_tag_version = Version::new(0, 0, 0); let mut simple_changelog = "".to_owned(); let mut unreleased_count: usize = 0; @@ -26,35 +25,38 @@ pub fn run(path: Option, _commit_git_hook: Option, scope_filter: match create_conventional_commit(message) { Some(conventional_commit) => { - simple_changelog.insert_str(0, " \n"); - simple_changelog.insert_str(0, &format!(" {}\n", commit.committer.to_changelog_string())); - simple_changelog.insert_str(0, &format!(" {}\n", commit.author.to_changelog_string())); - simple_changelog.insert_str(0, &format!(" {}\n", if conventional_commit.is_deprecrated { 'X' } else { ' ' })); - simple_changelog.insert_str(0, &format!(" {}\n", if conventional_commit.is_breaking { 'X' } else { ' ' })); - simple_changelog.insert_str(0, &format!(" {}.\n", conventional_commit.commit_description.to_sentence_case())); - simple_changelog.insert_str(0, &format!(" {}\n", conventional_commit.commit_type)); - simple_changelog.insert_str(0, &format!(" {}\n", version.format())); - simple_changelog.insert_str(0, " \n"); - }, - None => (), - } - - let found_version_as_tag = tags.clone() - .into_iter() - .filter(|tag| tag.contains(version.format().as_str())) - .count() > 0; + if scope_filter_check(scope_filter.clone(), conventional_commit.scope) { + // Add commit to log: + simple_changelog.insert_str(0, " \n"); + simple_changelog.insert_str(0, &format!(" {}\n", commit.committer.to_changelog_string())); + simple_changelog.insert_str(0, &format!(" {}\n", commit.author.to_changelog_string())); + simple_changelog.insert_str(0, &format!(" {}\n", if conventional_commit.is_deprecrated { 'X' } else { ' ' })); + simple_changelog.insert_str(0, &format!(" {}\n", if conventional_commit.is_breaking { 'X' } else { ' ' })); + simple_changelog.insert_str(0, &format!(" {}.\n", conventional_commit.commit_description.to_sentence_case())); + simple_changelog.insert_str(0, &format!(" {}\n", conventional_commit.commit_type)); + simple_changelog.insert_str(0, &format!(" {}\n", version.format())); + simple_changelog.insert_str(0, " \n"); - let version_updated_since_previous_releate = !past_tag_version.equal(&version); - - if found_version_as_tag && version_updated_since_previous_releate && unreleased_count != 0 { - simple_changelog.insert_str(0, " \n"); - simple_changelog.insert_str(0, &format!(" Release: {}\n", version.format())); - simple_changelog.insert_str(0, " \n"); - - past_tag_version = version.clone(); - unreleased_count = 0; - } else { - unreleased_count = unreleased_count + 1; + match optional_oid_commit_tag_map { + Some(ref map) => { + // Add release entry to log: + match map.get(&commit.oid) { + Some(found_tag) => { + simple_changelog.insert_str(0, " \n"); + simple_changelog.insert_str(0, &format!(" Release: {}\n", found_tag)); + simple_changelog.insert_str(0, " \n"); + unreleased_count = 0; + }, + // No release associated with commit, add to counter to determine no release header: + None => unreleased_count = unreleased_count + 1, + } + }, + // No release associated with commit, add to counter to determine no release header: + None => unreleased_count = unreleased_count + 1, + }; + } + }, + None => {}, } }, None => {}, diff --git a/src/git.rs b/src/git.rs index 5c09e6f..9016429 100644 --- a/src/git.rs +++ b/src/git.rs @@ -1,5 +1,6 @@ use std::env; use git2::Repository; +use indexmap::IndexMap; pub struct Author { @@ -56,6 +57,7 @@ pub struct Commit { pub message: Option, pub author: Author, pub committer: Committer, + pub oid: String } @@ -85,22 +87,54 @@ fn determine_path(path: Option) -> String { } } -pub fn retrieve_tags(path: Option) -> Vec { +pub fn retrieve_commit_tag_map(path: Option) -> Option> { + // Determine repository path: let found_path = determine_path(path); + + // Open repository: let repo = retrieve_git_repository(found_path); - match repo.tag_names(None) { - Ok(found_tags) => { - found_tags - .into_iter() - .filter(|value| value.is_some()) - .map(|value| value.unwrap().to_owned()) - .collect() - }, - Err(_) => Vec::new(), + // Vector for keeping tags and commit associated: + let mut tags: Vec<(git2::Commit, String)> = Vec::new(); + + // Retrieve tags from repository: + let tag_names = match repo.tag_names(None) { + Ok(tags) => tags, + Err(_) => panic!("Could not retrieve tag names"), + }; + + if tag_names.len() == 0 { + return None; + } + + // For each tag find commit: + for name in tag_names.iter().flatten().map(String::from) { + // Retrieve git object found for tag: + let git_object = match repo.revparse_single(&name) { + Ok(object) => object, + Err(_) => panic!("Could not retrieve git object for processing"), + }; + // See if git object is commit: + if let Ok(commit) = git_object.clone().into_commit() { + tags.push((commit, name)) + + // If it is not commit, it might be a tag, ignore git tree and blobs: + } else if let Some(tag) = git_object.as_tag() { + + // Find commit for tag object: + if let Some(commit) = tag.target().ok().and_then(|target| target.into_commit().ok()) { + tags.push((commit, name)); + } + } } + + return Some(tags + .into_iter() + .map(|(commit, tag)| (commit.id().to_string(), tag)) + .collect()); } + pub fn retrieve_branch_commits(path: Option) -> Vec { let found_path = determine_path(path); let repo = retrieve_git_repository(found_path); @@ -147,7 +181,7 @@ pub fn retrieve_branch_commits(path: Option) -> Vec { }, }; - Commit { message, author, committer } + Commit { message, author, committer, oid: oid.to_string() } }, Err(_) => panic!("Could not retrieve oid of commit"), } diff --git a/src/semantic.rs b/src/semantic.rs index 89506f2..68cafaa 100644 --- a/src/semantic.rs +++ b/src/semantic.rs @@ -21,13 +21,6 @@ impl Version { format!("{}.{}.{}", self.major, self.minor, self.patch) } - pub fn equal(&self, comparator: &Version) -> bool { - self.major == comparator.major && self.minor == comparator.minor && self.patch == comparator.patch - } - - pub fn clone(&self) -> Version { - Version { major: self.major, minor: self.minor, patch: self.patch } - } } pub enum Impact {