From d98195bc4d3e18da05d1fb29c56528f6180a9b2f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 15 Jun 2024 22:27:21 +0200 Subject: [PATCH 01/13] refactor: clean up some code --- git-cliff-core/src/changelog.rs | 11 +-- git-cliff-core/src/repo.rs | 58 ++++++------ git-cliff/src/lib.rs | 160 ++++++++++++++------------------ 3 files changed, 106 insertions(+), 123 deletions(-) diff --git a/git-cliff-core/src/changelog.rs b/git-cliff-core/src/changelog.rs index 7707f37596..712d4efd95 100644 --- a/git-cliff-core/src/changelog.rs +++ b/git-cliff-core/src/changelog.rs @@ -494,7 +494,7 @@ impl<'a> Changelog<'a> { } /// Generates the changelog and writes it to the given output. - pub fn generate(&self, out: &mut W) -> Result<()> { + pub fn generate(&self, out: &mut W) -> Result<()> { debug!("Generating changelog..."); let postprocessors = self .config @@ -510,8 +510,7 @@ impl<'a> Changelog<'a> { } } } - let mut releases = self.releases.clone(); - for release in releases.iter_mut() { + for release in &self.releases { let write_result = write!( out, "{}", @@ -533,7 +532,7 @@ impl<'a> Changelog<'a> { "{}", footer_template.render( &Releases { - releases: &releases, + releases: &self.releases, }, Some(&self.additional_context), &postprocessors, @@ -549,7 +548,7 @@ impl<'a> Changelog<'a> { } /// Generates a changelog and prepends it to the given changelog. - pub fn prepend( + pub fn prepend( &self, mut changelog: String, out: &mut W, @@ -564,7 +563,7 @@ impl<'a> Changelog<'a> { } /// Prints the changelog context to the given output. - pub fn write_context(&self, out: &mut W) -> Result<()> { + pub fn write_context(&self, out: &mut W) -> Result<()> { let output = Releases { releases: &self.releases, } diff --git a/git-cliff-core/src/repo.rs b/git-cliff-core/src/repo.rs index 8723a640e7..771c5c45c7 100644 --- a/git-cliff-core/src/repo.rs +++ b/git-cliff-core/src/repo.rs @@ -45,14 +45,14 @@ impl Repository { /// Sorts the commits by their time. pub fn commits( &self, - range: Option, - include_path: Option>, - exclude_path: Option>, + range: Option<&str>, + include_path: Option<&[Pattern]>, + exclude_path: Option<&[Pattern]>, ) -> Result> { let mut revwalk = self.inner.revwalk()?; revwalk.set_sorting(Sort::TOPOLOGICAL)?; if let Some(range) = range { - revwalk.push_range(&range)?; + revwalk.push_range(range)?; } else { revwalk.push_head()?; } @@ -62,31 +62,31 @@ impl Repository { .collect(); if include_path.is_some() || exclude_path.is_some() { commits.retain(|commit| { - if let Ok(prev_commit) = commit.parent(0) { - if let Ok(diff) = self.inner.diff_tree_to_tree( - commit.tree().ok().as_ref(), - prev_commit.tree().ok().as_ref(), - None, - ) { - return diff - .deltas() - .filter_map(|delta| delta.new_file().path()) - .any(|new_file_path| { - if let Some(include_path) = &include_path { - include_path - .iter() - .any(|glob| glob.matches_path(new_file_path)) - } else if let Some(exclude_path) = &exclude_path { - !exclude_path - .iter() - .any(|glob| glob.matches_path(new_file_path)) - } else { - false - } - }); - } - } - false + let Ok(prev_commit) = commit.parent(0) else { + return false; + }; + let Ok(diff) = self.inner.diff_tree_to_tree( + commit.tree().ok().as_ref(), + prev_commit.tree().ok().as_ref(), + None, + ) else { + return false; + }; + diff.deltas() + .filter_map(|delta| delta.new_file().path()) + .any(|new_file_path| { + if let Some(include_path) = include_path { + return include_path + .iter() + .any(|glob| glob.matches_path(new_file_path)); + } + if let Some(exclude_path) = exclude_path { + return !exclude_path + .iter() + .any(|glob| glob.matches_path(new_file_path)); + } + unreachable!() + }) }); } Ok(commits) diff --git a/git-cliff/src/lib.rs b/git-cliff/src/lib.rs index 61a835f6a5..a042174a7b 100644 --- a/git-cliff/src/lib.rs +++ b/git-cliff/src/lib.rs @@ -44,11 +44,8 @@ use std::fs::{ self, File, }; -use std::io::{ - self, - Write, -}; -use std::path::PathBuf; +use std::io; +use std::path::Path; use std::time::{ SystemTime, UNIX_EPOCH, @@ -88,29 +85,26 @@ fn process_repository<'a>( let mut tags = repository.tags(&config.git.tag_pattern, args.topo_order)?; let skip_regex = config.git.skip_tags.as_ref(); let ignore_regex = config.git.ignore_tags.as_ref(); - tags = tags - .into_iter() - .filter(|(_, name)| { - // Keep skip tags to drop commits in the later stage. - let skip = skip_regex.map(|r| r.is_match(name)).unwrap_or_default(); - - let ignore = ignore_regex - .map(|r| { - if r.as_str().trim().is_empty() { - return false; - } + tags.retain(|_, name| { + // Keep skip tags to drop commits in the later stage. + let skip = skip_regex.is_some_and(|r| r.is_match(name)); + if skip { + return true; + } - let ignore_tag = r.is_match(name); - if ignore_tag { - trace!("Ignoring release: {}", name) - } - ignore_tag - }) - .unwrap_or_default(); + let ignore = ignore_regex.is_some_and(|r| { + if r.as_str().trim().is_empty() { + return false; + } - skip || !ignore - }) - .collect(); + let ignore_tag = r.is_match(name); + if ignore_tag { + trace!("Ignoring release: {}", name) + } + ignore_tag + }); + !ignore + }); if !config.remote.github.is_set() { match repository.upstream_remote() { @@ -211,14 +205,12 @@ fn process_repository<'a>( } } let mut commits = repository.commits( - commit_range, - args.include_path.clone(), - args.exclude_path.clone(), + commit_range.as_deref(), + args.include_path.as_deref(), + args.exclude_path.as_deref(), )?; if let Some(commit_limit_value) = config.git.limit_commits { - commits = commits - .drain(..commits.len().min(commit_limit_value)) - .collect(); + commits.truncate(commit_limit_value); } // Update tags. @@ -237,21 +229,17 @@ fn process_repository<'a>( // Process releases. let mut releases = vec![Release::default()]; - let mut release_index = 0; let mut previous_release = Release::default(); let mut first_processed_tag = None; for git_commit in commits.iter().rev() { + let release = releases.last_mut().unwrap(); let commit = Commit::from(git_commit); let commit_id = commit.id.to_string(); - if args.sort == Sort::Newest { - releases[release_index].commits.insert(0, commit); - } else { - releases[release_index].commits.push(commit); - } + release.commits.push(commit); if let Some(tag) = tags.get(&commit_id) { - releases[release_index].version = Some(tag.to_string()); - releases[release_index].commit_id = Some(commit_id); - releases[release_index].timestamp = if args.tag.as_deref() == Some(tag) { + release.version = Some(tag.to_string()); + release.commit_id = Some(commit_id); + release.timestamp = if args.tag.as_deref() == Some(tag) { SystemTime::now() .duration_since(UNIX_EPOCH)? .as_secs() @@ -263,36 +251,40 @@ fn process_repository<'a>( first_processed_tag = Some(tag); } previous_release.previous = None; - releases[release_index].previous = Some(Box::new(previous_release)); - previous_release = releases[release_index].clone(); + release.previous = Some(Box::new(previous_release)); + previous_release = release.clone(); releases.push(Release::default()); - release_index += 1; } } - if release_index > 0 { + debug_assert!(!releases.is_empty()); + + if releases.len() > 1 { previous_release.previous = None; - releases[release_index].previous = Some(Box::new(previous_release)); + releases.last_mut().unwrap().previous = Some(Box::new(previous_release)); + } + + if args.sort == Sort::Newest { + for release in &mut releases { + release.commits.reverse(); + } } // Add custom commit messages to the latest release. if let Some(custom_commits) = &args.with_commit { - if let Some(latest_release) = releases.iter_mut().last() { - custom_commits.iter().for_each(|message| { - latest_release - .commits - .push(Commit::from(message.to_string())) - }); - } + releases + .last_mut() + .unwrap() + .commits + .extend(custom_commits.iter().cloned().map(Commit::from)); } // Set the previous release if the first release does not have one set. - if !releases.is_empty() && - releases - .first() - .and_then(|r| r.previous.as_ref()) - .and_then(|p| p.version.as_ref()) - .is_none() + if releases[0] + .previous + .as_ref() + .and_then(|p| p.version.as_ref()) + .is_none() { // Get the previous tag of the first processed tag in the release loop. let first_tag = first_processed_tag @@ -532,6 +524,16 @@ pub fn run(mut args: Opt) -> Result<()> { let mut changelog = Changelog::new(releases, &config)?; // Print the result. + let mut out: Box = if let Some(path) = &args.output { + if path == Path::new("-") { + Box::new(io::stdout()) + } else { + Box::new(io::BufWriter::new(File::create(path)?)) + } + } else { + Box::new(io::stdout()) + }; + let out = &mut *out; if args.bump || args.bumped_version { let next_version = if let Some(next_version) = changelog.bump_version()? { next_version @@ -544,40 +546,22 @@ pub fn run(mut args: Opt) -> Result<()> { return Ok(()); }; if args.bumped_version { - if let Some(path) = args.output { - let mut output = File::create(path)?; - output.write_all(next_version.as_bytes())?; - } else { - println!("{next_version}"); - } + writeln!(out, "{next_version}")?; return Ok(()); } } + if args.context { - return if let Some(path) = args.output { - let mut output = File::create(path)?; - changelog.write_context(&mut output) - } else { - changelog.write_context(&mut io::stdout()) - }; + changelog.write_context(out)?; + return Ok(()); } - if let Some(ref path) = args.prepend { - changelog.prepend(fs::read_to_string(path)?, &mut File::create(path)?)?; + + if let Some(path) = &args.prepend { + changelog.prepend(fs::read_to_string(path)?, out)?; } - if let Some(path) = args.output { - let mut output: Box = if path == PathBuf::from("-") { - Box::new(io::stdout()) - } else { - Box::new(File::create(path)?) - }; - if args.context { - changelog.write_context(&mut output) - } else { - changelog.generate(&mut output) - } - } else if args.prepend.is_none() { - changelog.generate(&mut io::stdout()) - } else { - Ok(()) + if args.output.is_some() || args.prepend.is_none() { + changelog.generate(out)?; } + + Ok(()) } From db4807db37606030583d98bc87fcbc35412f6930 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sun, 16 Jun 2024 00:56:34 +0200 Subject: [PATCH 02/13] fix: assign commits to closest tag --- git-cliff-core/src/repo.rs | 134 ++++++++++++++++++++++++++++++++----- git-cliff/src/lib.rs | 130 +++++++++++++++++++++-------------- 2 files changed, 194 insertions(+), 70 deletions(-) diff --git a/git-cliff-core/src/repo.rs b/git-cliff-core/src/repo.rs index 771c5c45c7..69ca0cb13d 100644 --- a/git-cliff-core/src/repo.rs +++ b/git-cliff-core/src/repo.rs @@ -14,6 +14,7 @@ use git2::{ use glob::Pattern; use indexmap::IndexMap; use regex::Regex; +use std::cmp::Reverse; use std::io; use std::path::PathBuf; use url::Url; @@ -62,12 +63,13 @@ impl Repository { .collect(); if include_path.is_some() || exclude_path.is_some() { commits.retain(|commit| { - let Ok(prev_commit) = commit.parent(0) else { - return false; - }; + let prev_tree = commit + .parent(0) + .and_then(|prev_commit| prev_commit.tree()) + .ok(); let Ok(diff) = self.inner.diff_tree_to_tree( commit.tree().ok().as_ref(), - prev_commit.tree().ok().as_ref(), + prev_tree.as_ref(), None, ) else { return false; @@ -117,16 +119,17 @@ impl Repository { /// It collects lightweight and annotated tags. pub fn tags( &self, - pattern: &Option, + pattern: Option<&Regex>, topo_order: bool, - ) -> Result> { + ) -> Result> { let mut tags: Vec<(Commit, String)> = Vec::new(); let tag_names = self.inner.tag_names(None)?; for name in tag_names .iter() .flatten() .filter(|tag_name| { - pattern.as_ref().map_or(true, |pat| pat.is_match(tag_name)) + pattern.is_none() || + pattern.is_some_and(|pat| pat.is_match(tag_name)) }) .map(String::from) { @@ -144,12 +147,9 @@ impl Repository { } } if !topo_order { - tags.sort_by(|a, b| a.0.time().seconds().cmp(&b.0.time().seconds())); + tags.sort_by_key(|(commit, _)| commit.time().seconds()); } - Ok(tags - .into_iter() - .map(|(a, b)| (a.id().to_string(), b)) - .collect()) + TaggedCommits::new(self, &tags) } /// Returns the remote of the upstream repository. @@ -206,6 +206,104 @@ impl Repository { } } +/// Stores which commits are tagged with which tags. +pub struct TaggedCommits<'a> { + /// All the commits in the repository. + pub commits: IndexMap>, + /// List of tags with their commit index. + /// + /// Sorted in reverse order, meaning the first element is the latest tag. + pub tags: Vec<(usize, String)>, +} + +impl<'a> TaggedCommits<'a> { + fn new(repository: &'a Repository, tags: &[(Commit, String)]) -> Result { + let commits = repository.commits(None, None, None)?; + let commits: IndexMap<_, _> = commits + .into_iter() + .map(|c| (c.id().to_string(), c)) + .collect(); + let mut tags: Vec<_> = tags + .iter() + .map(|(commit, tag)| { + let id = commit.id().to_string(); + let idx = commits.get_index_of(&id).expect("commit exists"); + (idx, tag.to_string()) + }) + .collect(); + tags.sort_by_key(|(idx, _)| Reverse(*idx)); + Ok(Self { commits, tags }) + } + + /// Returns the number of tags. + pub fn len(&self) -> usize { + self.tags.len() + } + + /// Returns `true` if there are no tags. + pub fn is_empty(&self) -> bool { + self.tags.is_empty() + } + + /// Returns an iterator over all the tags. + pub fn tags(&self) -> impl Iterator { + self.tags.iter().map(|(_, tag)| tag.as_str()) + } + + /// Returns the tag of the given commit. + /// + /// Note that this only searches for an exact match. + /// For a more general search, use [`get_closest`](Self::get_closest) + /// instead. + pub fn get(&self, commit: &str) -> Option<&str> { + let index = self.commits.get_index_of(commit)?; + let idx = self.binary_search(index).ok()?; + Some(&self.tags[idx].1) + } + + /// Returns the tag at the given index. + /// + /// The index can be calculated with `tags().position()`. + pub fn get_index(&self, idx: usize) -> Option<&str> { + self.tags.get(idx).map(|(_, tag)| tag.as_str()) + } + + /// Returns the tag closest to the given commit. + pub fn get_closest(&self, commit: &str) -> Option<&str> { + let index = self.commits.get_index_of(commit)?; + let (_, tag) = self.tags.iter().find(|(tag_idx, _)| index >= *tag_idx)?; + Some(tag) + } + + /// Returns the commit of the given tag. + pub fn get_commit(&self, tag: &str) -> Option<&Commit> { + let (tag_idx, _) = self.tags.iter().find(|(_, t)| t == tag)?; + let (_, commit) = self.commits.get_index(*tag_idx)?; + Some(commit) + } + + /// Returns `true` if the given tag exists. + pub fn contains(&self, tag: &str) -> bool { + self.tags.iter().any(|(_, t)| t == tag) + } + + /// Inserts a new tagged commit. + /// + /// Returns `None` if the commit does not exist or if is already tagged. + #[must_use] + pub fn insert(&mut self, commit: &str, tag: String) -> Option<()> { + let index = self.commits.get_index_of(commit)?; + let idx = self.binary_search(index).err()?; + self.tags.insert(idx, (index, tag)); + Some(()) + } + + fn binary_search(&self, index: usize) -> std::result::Result { + self.tags + .binary_search_by_key(&Reverse(index), |(tag_idx, _)| Reverse(*tag_idx)) + } +} + #[cfg(test)] mod test { use super::*; @@ -260,15 +358,15 @@ mod test { #[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); + let tags = repository.tags(None, false)?; + assert_eq!(&get_last_tag()?, tags.tags().last().expect("no tags found")); Ok(()) } #[test] fn git_tags() -> Result<()> { let repository = get_repository()?; - let tags = repository.tags(&None, true)?; + 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)" @@ -283,8 +381,8 @@ mod test { "v0.1.0-beta.4" ); let tags = repository.tags( - &Some( - Regex::new("^v[0-9]+\\.[0-9]+\\.[0-9]$") + Some( + &Regex::new("^v[0-9]+\\.[0-9]+\\.[0-9]$") .expect("the regex is not valid"), ), true, @@ -295,7 +393,7 @@ mod test { ), "v0.1.0" ); - assert!(!tags.contains_key("4ddef08debfff48117586296e49d5caa0800d1b5")); + assert!(!tags.contains("4ddef08debfff48117586296e49d5caa0800d1b5")); Ok(()) } diff --git a/git-cliff/src/lib.rs b/git-cliff/src/lib.rs index a042174a7b..b1d44690f9 100644 --- a/git-cliff/src/lib.rs +++ b/git-cliff/src/lib.rs @@ -34,7 +34,10 @@ use git_cliff_core::error::{ Result, }; use git_cliff_core::release::Release; -use git_cliff_core::repo::Repository; +use git_cliff_core::repo::{ + Repository, + TaggedCommits, +}; use git_cliff_core::{ DEFAULT_CONFIG, IGNORE_FILE, @@ -82,10 +85,11 @@ fn process_repository<'a>( config: &mut Config, args: &Opt, ) -> Result>> { - let mut tags = repository.tags(&config.git.tag_pattern, args.topo_order)?; + let mut tags = + repository.tags(config.git.tag_pattern.as_ref(), args.topo_order)?; let skip_regex = config.git.skip_tags.as_ref(); let ignore_regex = config.git.ignore_tags.as_ref(); - tags.retain(|_, name| { + tags.tags.retain(|(_, name)| { // Keep skip tags to drop commits in the later stage. let skip = skip_regex.is_some_and(|r| r.is_match(name)); if skip { @@ -159,16 +163,14 @@ fn process_repository<'a>( // Parse commits. let mut commit_range = args.range.clone(); if args.unreleased { - if let Some(last_tag) = tags.last().map(|(k, _)| k) { + if let Some((_, last_tag)) = tags.tags.last() { commit_range = Some(format!("{last_tag}..HEAD")); } } else if args.latest || args.current { if tags.len() < 2 { - let commits = repository.commits(None, None, None)?; - if let (Some(tag1), Some(tag2)) = ( - commits.last().map(|c| c.id().to_string()), - tags.get_index(0).map(|(k, _)| k), - ) { + if let (Some((tag1, _)), Some(tag2)) = + (tags.commits.last(), tags.get_index(0)) + { commit_range = Some(format!("{tag1}..{tag2}")); } } else { @@ -176,10 +178,7 @@ fn process_repository<'a>( if args.current { if let Some(current_tag_index) = repository.current_tag().as_ref().and_then(|tag| { - tags.iter() - .enumerate() - .find(|(_, (_, v))| v == &tag) - .map(|(i, _)| i) + tags.tags().enumerate().position(|(_, v)| v == tag) }) { match current_tag_index.checked_sub(1) { Some(i) => tag_index = i, @@ -196,10 +195,9 @@ fn process_repository<'a>( ))); } } - if let (Some(tag1), Some(tag2)) = ( - tags.get_index(tag_index).map(|(k, _)| k), - tags.get_index(tag_index + 1).map(|(k, _)| k), - ) { + if let (Some(tag1), Some(tag2)) = + (tags.get_index(tag_index), tags.get_index(tag_index + 1)) + { commit_range = Some(format!("{tag1}..{tag2}")); } } @@ -221,48 +219,40 @@ fn process_repository<'a>( warn!("There is already a tag ({}) for {}", tag, commit_id) } None => { - tags.insert(commit_id, tag.to_string()); + tags.insert(&commit_id, tag.to_string()).unwrap(); } } } } // Process releases. - let mut releases = vec![Release::default()]; - let mut previous_release = Release::default(); + let mut releases = Vec::::new(); + let mut release = Release::default(); + let mut current_tag = commits + .last() + .and_then(|root| tags.get_closest(&root.id().to_string())); let mut first_processed_tag = None; for git_commit in commits.iter().rev() { - let release = releases.last_mut().unwrap(); let commit = Commit::from(git_commit); - let commit_id = commit.id.to_string(); - release.commits.push(commit); - if let Some(tag) = tags.get(&commit_id) { - release.version = Some(tag.to_string()); - release.commit_id = Some(commit_id); - release.timestamp = if args.tag.as_deref() == Some(tag) { - SystemTime::now() - .duration_since(UNIX_EPOCH)? - .as_secs() - .try_into()? - } else { - git_commit.time().seconds() - }; - if first_processed_tag.is_none() { - first_processed_tag = Some(tag); - } - previous_release.previous = None; - release.previous = Some(Box::new(previous_release)); - previous_release = release.clone(); - releases.push(Release::default()); - } - } - debug_assert!(!releases.is_empty()); + let new_tag = tags.get_closest(&commit.id); + if first_processed_tag.is_none() { + first_processed_tag = new_tag; + } + if new_tag == current_tag { + release.commits.push(commit); + continue; + } - if releases.len() > 1 { - previous_release.previous = None; - releases.last_mut().unwrap().previous = Some(Box::new(previous_release)); + // Found a new release, finalize the current one and append the commit to the + // new release. + fill_release(&mut release, args, current_tag, &tags); + current_tag = new_tag; + append_release(&mut releases, &mut release); + release.commits.push(commit); } + fill_release(&mut release, args, current_tag, &tags); + append_release(&mut releases, &mut release); if args.sort == Sort::Newest { for release in &mut releases { @@ -289,20 +279,22 @@ fn process_repository<'a>( // Get the previous tag of the first processed tag in the release loop. let first_tag = first_processed_tag .map(|tag| { - tags.iter() + tags.tags() .enumerate() - .find(|(_, (_, v))| v == &tag) + .find(|(_, v)| *v == tag) .and_then(|(i, _)| i.checked_sub(1)) .and_then(|i| tags.get_index(i)) }) - .or_else(|| Some(tags.last())) + .or_else(|| Some(tags.tags().last())) .flatten(); // Set the previous release if the first tag is found. - if let Some((commit_id, version)) = first_tag { + if let Some(tag) = first_tag { + let commit = tags.get_commit(tag).unwrap(); + let commit_id = commit.id(); let previous_release = Release { commit_id: Some(commit_id.to_string()), - version: Some(version.to_string()), + version: Some(tag.to_string()), timestamp: repository .find_commit(commit_id.to_string()) .map(|v| v.time().seconds()) @@ -316,6 +308,40 @@ fn process_repository<'a>( Ok(releases) } +fn fill_release( + release: &mut Release<'_>, + args: &Opt, + tag: Option<&str>, + tags: &TaggedCommits<'_>, +) { + let Some(tag) = tag else { return }; + let Some(release_commit) = tags.get_commit(tag) else { + return; + }; + release.version = Some(tag.to_string()); + release.commit_id = Some(release_commit.id().to_string()); + release.timestamp = if args.tag.as_deref() == Some(tag) { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system time is before unix epoch") + .as_secs() + .try_into() + .expect("unix epoch seconds overflows i64") + } else { + release_commit.time().seconds() + }; +} + +/// Appends `release` to `releases`, also setting the `previous` field and +/// resetting `release`. +fn append_release<'a>(releases: &mut Vec>, release: &mut Release<'a>) { + if let Some(mut previous) = releases.last().cloned() { + previous.previous = None; + release.previous = Some(Box::new(previous)); + } + releases.push(std::mem::take(release)); +} + /// Runs `git-cliff`. pub fn run(mut args: Opt) -> Result<()> { // Check if there is a new version available. From 91c1348bece940af879e657b21bb6b677247b855 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sun, 16 Jun 2024 12:21:55 +0200 Subject: [PATCH 03/13] fix: filter out tags that are not in HEAD --- git-cliff-core/src/repo.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/git-cliff-core/src/repo.rs b/git-cliff-core/src/repo.rs index 69ca0cb13d..96756288ef 100644 --- a/git-cliff-core/src/repo.rs +++ b/git-cliff-core/src/repo.rs @@ -207,6 +207,7 @@ impl Repository { } /// Stores which commits are tagged with which tags. +#[derive(Debug)] pub struct TaggedCommits<'a> { /// All the commits in the repository. pub commits: IndexMap>, @@ -225,10 +226,10 @@ impl<'a> TaggedCommits<'a> { .collect(); let mut tags: Vec<_> = tags .iter() - .map(|(commit, tag)| { + .filter_map(|(commit, tag)| { let id = commit.id().to_string(); - let idx = commits.get_index_of(&id).expect("commit exists"); - (idx, tag.to_string()) + let idx = commits.get_index_of(&id)?; + Some((idx, tag.to_string())) }) .collect(); tags.sort_by_key(|(idx, _)| Reverse(*idx)); @@ -309,6 +310,7 @@ mod test { use super::*; use crate::commit::Commit as AppCommit; use std::env; + use std::path::Path; use std::process::Command; use std::str; @@ -338,7 +340,7 @@ mod test { fn get_repository() -> Result { Repository::init( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) + Path::new(env!("CARGO_MANIFEST_DIR")) .parent() .expect("parent directory not found") .to_path_buf(), From 362b123707185bc850909bdd08edfaa4f1eec1f3 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sun, 16 Jun 2024 13:51:15 +0200 Subject: [PATCH 04/13] fix: bring back commit to tag map --- git-cliff-core/src/repo.rs | 83 +++++++++++++++++++++++++------------- git-cliff/src/lib.rs | 41 ++++++++++--------- 2 files changed, 78 insertions(+), 46 deletions(-) diff --git a/git-cliff-core/src/repo.rs b/git-cliff-core/src/repo.rs index 96756288ef..92bc4aa4ea 100644 --- a/git-cliff-core/src/repo.rs +++ b/git-cliff-core/src/repo.rs @@ -149,7 +149,7 @@ impl Repository { if !topo_order { tags.sort_by_key(|(commit, _)| commit.time().seconds()); } - TaggedCommits::new(self, &tags) + TaggedCommits::new(self, tags) } /// Returns the remote of the upstream repository. @@ -211,20 +211,27 @@ impl Repository { pub struct TaggedCommits<'a> { /// All the commits in the repository. pub commits: IndexMap>, + /// Commit ID to tag map. + tags: IndexMap, /// List of tags with their commit index. /// /// Sorted in reverse order, meaning the first element is the latest tag. - pub tags: Vec<(usize, String)>, + /// + /// Used for lookups. + tag_ids: Vec<(usize, String)>, } impl<'a> TaggedCommits<'a> { - fn new(repository: &'a Repository, tags: &[(Commit, String)]) -> Result { + fn new( + repository: &'a Repository, + tags: Vec<(Commit<'a>, String)>, + ) -> Result { let commits = repository.commits(None, None, None)?; let commits: IndexMap<_, _> = commits .into_iter() .map(|c| (c.id().to_string(), c)) .collect(); - let mut tags: Vec<_> = tags + let mut tag_ids: Vec<_> = tags .iter() .filter_map(|(commit, tag)| { let id = commit.id().to_string(); @@ -232,8 +239,16 @@ impl<'a> TaggedCommits<'a> { Some((idx, tag.to_string())) }) .collect(); - tags.sort_by_key(|(idx, _)| Reverse(*idx)); - Ok(Self { commits, tags }) + tag_ids.sort_by_key(|(idx, _)| Reverse(*idx)); + let tags = tags + .into_iter() + .map(|(commit, tag)| (commit.id().to_string(), tag)) + .collect(); + Ok(Self { + commits, + tag_ids, + tags, + }) } /// Returns the number of tags. @@ -248,7 +263,12 @@ impl<'a> TaggedCommits<'a> { /// Returns an iterator over all the tags. pub fn tags(&self) -> impl Iterator { - self.tags.iter().map(|(_, tag)| tag.as_str()) + self.tags.iter().map(|(_, v)| v.as_str()) + } + + /// Returns the last tag. + pub fn last(&self) -> Option<&str> { + self.tags().last() } /// Returns the tag of the given commit. @@ -257,50 +277,57 @@ impl<'a> TaggedCommits<'a> { /// For a more general search, use [`get_closest`](Self::get_closest) /// instead. pub fn get(&self, commit: &str) -> Option<&str> { - let index = self.commits.get_index_of(commit)?; - let idx = self.binary_search(index).ok()?; - Some(&self.tags[idx].1) + self.tags + .iter() + .find(|(c, _)| *c == commit) + .map(|(_, v)| v.as_str()) } /// Returns the tag at the given index. /// /// The index can be calculated with `tags().position()`. pub fn get_index(&self, idx: usize) -> Option<&str> { - self.tags.get(idx).map(|(_, tag)| tag.as_str()) + self.tags.get_index(idx).map(|(_, v)| v.as_str()) } /// Returns the tag closest to the given commit. pub fn get_closest(&self, commit: &str) -> Option<&str> { let index = self.commits.get_index_of(commit)?; - let (_, tag) = self.tags.iter().find(|(tag_idx, _)| index >= *tag_idx)?; + let (_, tag) = self.tag_ids.iter().find(|(tag_idx, _)| index >= *tag_idx)?; Some(tag) } /// Returns the commit of the given tag. - pub fn get_commit(&self, tag: &str) -> Option<&Commit> { - let (tag_idx, _) = self.tags.iter().find(|(_, t)| t == tag)?; - let (_, commit) = self.commits.get_index(*tag_idx)?; - Some(commit) + pub fn get_commit(&self, tag: &str) -> Option<&str> { + self.tags + .iter() + .find(|(_, t)| *t == tag) + .map(|(commit, _)| commit.as_str()) } /// Returns `true` if the given tag exists. - pub fn contains(&self, tag: &str) -> bool { - self.tags.iter().any(|(_, t)| t == tag) + pub fn contains_commit(&self, commit: &str) -> bool { + self.tags.contains_key(commit) } /// Inserts a new tagged commit. - /// - /// Returns `None` if the commit does not exist or if is already tagged. - #[must_use] - pub fn insert(&mut self, commit: &str, tag: String) -> Option<()> { - let index = self.commits.get_index_of(commit)?; - let idx = self.binary_search(index).err()?; - self.tags.insert(idx, (index, tag)); - Some(()) + pub fn insert(&mut self, commit: String, tag: String) { + if let Some(index) = self.commits.get_index_of(&commit) { + if let Err(idx) = self.binary_search(index) { + self.tag_ids.insert(idx, (index, tag.clone())); + } + } + self.tags.insert(commit, tag); + } + + /// Retains only the tags specified by the predicate. + pub fn retain(&mut self, mut f: impl FnMut(&str) -> bool) { + self.tags.retain(|_, tag| f(tag)); + self.tag_ids.retain(|(_, tag)| f(tag)); } fn binary_search(&self, index: usize) -> std::result::Result { - self.tags + self.tag_ids .binary_search_by_key(&Reverse(index), |(tag_idx, _)| Reverse(*tag_idx)) } } @@ -395,7 +422,7 @@ mod test { ), "v0.1.0" ); - assert!(!tags.contains("4ddef08debfff48117586296e49d5caa0800d1b5")); + assert!(!tags.contains_commit("4ddef08debfff48117586296e49d5caa0800d1b5")); Ok(()) } diff --git a/git-cliff/src/lib.rs b/git-cliff/src/lib.rs index b1d44690f9..5ba55b6871 100644 --- a/git-cliff/src/lib.rs +++ b/git-cliff/src/lib.rs @@ -89,7 +89,7 @@ fn process_repository<'a>( repository.tags(config.git.tag_pattern.as_ref(), args.topo_order)?; let skip_regex = config.git.skip_tags.as_ref(); let ignore_regex = config.git.ignore_tags.as_ref(); - tags.tags.retain(|(_, name)| { + tags.retain(|name| { // Keep skip tags to drop commits in the later stage. let skip = skip_regex.is_some_and(|r| r.is_match(name)); if skip { @@ -157,13 +157,13 @@ fn process_repository<'a>( } // Print debug information about configuration and arguments. - log::trace!("Arguments: {:#?}", args); - log::trace!("Config: {:#?}", config); + // log::trace!("Arguments: {:#?}", args); + // log::trace!("Config: {:#?}", config); // Parse commits. let mut commit_range = args.range.clone(); if args.unreleased { - if let Some((_, last_tag)) = tags.tags.last() { + if let Some(last_tag) = tags.last() { commit_range = Some(format!("{last_tag}..HEAD")); } } else if args.latest || args.current { @@ -195,6 +195,8 @@ fn process_repository<'a>( ))); } } + dbg!(tag_index); + dbg!(&tags); if let (Some(tag1), Some(tag2)) = (tags.get_index(tag_index), tags.get_index(tag_index + 1)) { @@ -202,6 +204,7 @@ fn process_repository<'a>( } } } + dbg!(&commit_range); let mut commits = repository.commits( commit_range.as_deref(), args.include_path.as_deref(), @@ -219,7 +222,7 @@ fn process_repository<'a>( warn!("There is already a tag ({}) for {}", tag, commit_id) } None => { - tags.insert(&commit_id, tag.to_string()).unwrap(); + tags.insert(commit_id, tag.to_string()); } } } @@ -246,12 +249,12 @@ fn process_repository<'a>( // Found a new release, finalize the current one and append the commit to the // new release. - fill_release(&mut release, args, current_tag, &tags); + fill_release(repository, &mut release, args, current_tag, &tags)?; current_tag = new_tag; append_release(&mut releases, &mut release); release.commits.push(commit); } - fill_release(&mut release, args, current_tag, &tags); + fill_release(repository, &mut release, args, current_tag, &tags)?; append_release(&mut releases, &mut release); if args.sort == Sort::Newest { @@ -290,8 +293,7 @@ fn process_repository<'a>( // Set the previous release if the first tag is found. if let Some(tag) = first_tag { - let commit = tags.get_commit(tag).unwrap(); - let commit_id = commit.id(); + let commit_id = tags.get_commit(tag).unwrap(); let previous_release = Release { commit_id: Some(commit_id.to_string()), version: Some(tag.to_string()), @@ -309,27 +311,30 @@ fn process_repository<'a>( } fn fill_release( + repository: &Repository, release: &mut Release<'_>, args: &Opt, tag: Option<&str>, tags: &TaggedCommits<'_>, -) { - let Some(tag) = tag else { return }; +) -> Result<()> { + let Some(tag) = tag else { return Ok(()) }; let Some(release_commit) = tags.get_commit(tag) else { - return; + return Ok(()); }; release.version = Some(tag.to_string()); - release.commit_id = Some(release_commit.id().to_string()); + release.commit_id = Some(release_commit.to_string()); release.timestamp = if args.tag.as_deref() == Some(tag) { SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("system time is before unix epoch") + .duration_since(UNIX_EPOCH)? .as_secs() - .try_into() - .expect("unix epoch seconds overflows i64") + .try_into()? } else { - release_commit.time().seconds() + repository + .find_commit(release_commit.to_string()) + .map(|c| c.time().seconds()) + .unwrap_or_default() }; + Ok(()) } /// Appends `release` to `releases`, also setting the `previous` field and From c472d04482bffe42fbb328e154b0136eb649fdff Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sun, 16 Jun 2024 14:10:37 +0200 Subject: [PATCH 05/13] fix: always set the previous release --- git-cliff/src/lib.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/git-cliff/src/lib.rs b/git-cliff/src/lib.rs index 5ba55b6871..af4c7c3ffb 100644 --- a/git-cliff/src/lib.rs +++ b/git-cliff/src/lib.rs @@ -195,8 +195,6 @@ fn process_repository<'a>( ))); } } - dbg!(tag_index); - dbg!(&tags); if let (Some(tag1), Some(tag2)) = (tags.get_index(tag_index), tags.get_index(tag_index + 1)) { @@ -204,7 +202,6 @@ fn process_repository<'a>( } } } - dbg!(&commit_range); let mut commits = repository.commits( commit_range.as_deref(), args.include_path.as_deref(), @@ -283,12 +280,11 @@ fn process_repository<'a>( let first_tag = first_processed_tag .map(|tag| { tags.tags() - .enumerate() - .find(|(_, v)| *v == tag) - .and_then(|(i, _)| i.checked_sub(1)) + .position(|v| v == tag) + .and_then(|i| i.checked_sub(1)) .and_then(|i| tags.get_index(i)) }) - .or_else(|| Some(tags.tags().last())) + .or_else(|| Some(tags.last())) .flatten(); // Set the previous release if the first tag is found. @@ -340,10 +336,9 @@ fn fill_release( /// Appends `release` to `releases`, also setting the `previous` field and /// resetting `release`. fn append_release<'a>(releases: &mut Vec>, release: &mut Release<'a>) { - if let Some(mut previous) = releases.last().cloned() { - previous.previous = None; - release.previous = Some(Box::new(previous)); - } + let mut previous = releases.last().cloned().unwrap_or_default(); + previous.previous = None; + release.previous = Some(Box::new(previous)); releases.push(std::mem::take(release)); } From 302f28aee47048e3f3799c684cffdf71f5d5a173 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sun, 16 Jun 2024 14:35:07 +0200 Subject: [PATCH 06/13] fix: use get --- git-cliff-core/src/repo.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/git-cliff-core/src/repo.rs b/git-cliff-core/src/repo.rs index 92bc4aa4ea..9e0dde802f 100644 --- a/git-cliff-core/src/repo.rs +++ b/git-cliff-core/src/repo.rs @@ -277,10 +277,7 @@ impl<'a> TaggedCommits<'a> { /// For a more general search, use [`get_closest`](Self::get_closest) /// instead. pub fn get(&self, commit: &str) -> Option<&str> { - self.tags - .iter() - .find(|(c, _)| *c == commit) - .map(|(_, v)| v.as_str()) + self.tags.get(commit).map(String::as_str) } /// Returns the tag at the given index. @@ -292,6 +289,10 @@ impl<'a> TaggedCommits<'a> { /// Returns the tag closest to the given commit. pub fn get_closest(&self, commit: &str) -> Option<&str> { + if let Some(tagged) = self.get(commit) { + return Some(tagged); + } + let index = self.commits.get_index_of(commit)?; let (_, tag) = self.tag_ids.iter().find(|(tag_idx, _)| index >= *tag_idx)?; Some(tag) From edfc0da5721cc4efad41f73cb9302da913201f4e Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 25 Sep 2024 15:11:48 +0200 Subject: [PATCH 07/13] fix --- git-cliff/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-cliff/src/lib.rs b/git-cliff/src/lib.rs index 89c20eb4bd..dbcedc0891 100644 --- a/git-cliff/src/lib.rs +++ b/git-cliff/src/lib.rs @@ -87,7 +87,7 @@ fn process_repository<'a>( args: &Opt, ) -> Result>> { let mut tags = repository.tags( - &config.git.tag_pattern, + config.git.tag_pattern.as_ref(), args.topo_order, args.use_branch_tags, )?; @@ -364,7 +364,7 @@ fn fill_release( .try_into()? } else { repository - .find_commit(release_commit.to_string()) + .find_commit(release_commit) .map(|c| c.time().seconds()) .unwrap_or_default() }; From e45c7fd38d20cca1a91bf59cdf2a59bb2abb5e8b Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 25 Sep 2024 15:43:59 +0200 Subject: [PATCH 08/13] reconcile implementation --- git-cliff/src/lib.rs | 118 +++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 66 deletions(-) diff --git a/git-cliff/src/lib.rs b/git-cliff/src/lib.rs index dbcedc0891..3c0a07c07f 100644 --- a/git-cliff/src/lib.rs +++ b/git-cliff/src/lib.rs @@ -35,10 +35,8 @@ use git_cliff_core::error::{ Result, }; use git_cliff_core::release::Release; -use git_cliff_core::repo::{ - Repository, - TaggedCommits, -}; +use git_cliff_core::repo::Repository; +use git_cliff_core::tag::Tag; use git_cliff_core::{ DEFAULT_CONFIG, IGNORE_FILE, @@ -158,8 +156,8 @@ fn process_repository<'a>( } // Print debug information about configuration and arguments. - // log::trace!("Arguments: {:#?}", args); - // log::trace!("Config: {:#?}", config); + log::trace!("Arguments: {:#?}", args); + log::trace!("Config: {:#?}", config); // Parse commits. let mut commit_range = args.range.clone(); @@ -241,45 +239,60 @@ fn process_repository<'a>( } // Process releases. - let mut previous_release = Release::default(); + let mut releases = Vec::::new(); + let mut release = Release::default(); + let mut current_tag = commits + .last() + .and_then(|root| tags.get_closest(&root.id().to_string())); let mut first_processed_tag = None; - for git_commit in commits.iter().rev() { - let release = releases.last_mut().unwrap(); - let commit = Commit::from(git_commit); - let commit_id = commit.id.to_string(); - release.commits.push(commit); + + let fill_release = |release: &mut Release, tag: Option<&Tag>| -> Result<()> { release.repository = Some(repository.path.to_string_lossy().into_owned()); - if let Some(tag) = tags.get(&commit_id) { - release.version = Some(tag.name.to_string()); - release.message.clone_from(&tag.message); - release.commit_id = Some(commit_id); - release.timestamp = if args.tag.as_deref() == Some(tag.name.as_str()) { - match tag_timestamp { - Some(timestamp) => timestamp, - None => SystemTime::now() - .duration_since(UNIX_EPOCH)? - .as_secs() - .try_into()?, - } - } else { - git_commit.time().seconds() - }; - if first_processed_tag.is_none() { - first_processed_tag = Some(tag); + let Some(tag) = tag else { return Ok(()) }; + let Some(release_commit) = tags.get_commit(&tag.name) else { + return Ok(()); + }; + release.version = Some(tag.name.to_string()); + release.message.clone_from(&tag.message); + release.commit_id = Some(release_commit.to_string()); + release.timestamp = if args.tag.as_deref() == Some(&tag.name) { + match tag_timestamp { + Some(timestamp) => timestamp, + None => SystemTime::now() + .duration_since(UNIX_EPOCH)? + .as_secs() + .try_into()?, } - previous_release.previous = None; - release.previous = Some(Box::new(previous_release)); - previous_release = release.clone(); - releases.push(Release::default()); - } - } + } else { + repository + .find_commit(release_commit) + .map(|c| c.time().seconds()) + .unwrap_or_default() + }; + Ok(()) + }; - debug_assert!(!releases.is_empty()); + for git_commit in commits.iter().rev() { + let commit = Commit::from(git_commit); - if releases.len() > 1 { - previous_release.previous = None; - releases.last_mut().unwrap().previous = Some(Box::new(previous_release)); + let new_tag = tags.get_closest(&commit.id); + if first_processed_tag.is_none() { + first_processed_tag = new_tag; + } + if new_tag == current_tag { + release.commits.push(commit); + continue; + } + + // Found a new release, finalize the current one and append the commit to the + // new release. + fill_release(&mut release, current_tag)?; + current_tag = new_tag; + append_release(&mut releases, &mut release); + release.commits.push(commit); } + fill_release(&mut release, current_tag)?; + append_release(&mut releases, &mut release); if args.sort == Sort::Newest { for release in &mut releases { @@ -344,33 +357,6 @@ fn process_repository<'a>( Ok(releases) } -fn fill_release( - repository: &Repository, - release: &mut Release<'_>, - args: &Opt, - tag: Option<&str>, - tags: &TaggedCommits<'_>, -) -> Result<()> { - let Some(tag) = tag else { return Ok(()) }; - let Some(release_commit) = tags.get_commit(tag) else { - return Ok(()); - }; - release.version = Some(tag.to_string()); - release.commit_id = Some(release_commit.to_string()); - release.timestamp = if args.tag.as_deref() == Some(tag) { - SystemTime::now() - .duration_since(UNIX_EPOCH)? - .as_secs() - .try_into()? - } else { - repository - .find_commit(release_commit) - .map(|c| c.time().seconds()) - .unwrap_or_default() - }; - Ok(()) -} - /// Appends `release` to `releases`, also setting the `previous` field and /// resetting `release`. fn append_release<'a>(releases: &mut Vec>, release: &mut Release<'a>) { From d811d05ebf6716dca6797f9591eb46c9599bd816 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 25 Sep 2024 15:58:22 +0200 Subject: [PATCH 09/13] fix: tag_ids points into commits, not tags --- git-cliff-core/src/repo.rs | 33 +++++++++++++++++++++++---------- git-cliff/src/lib.rs | 1 + 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/git-cliff-core/src/repo.rs b/git-cliff-core/src/repo.rs index b55820688d..ac1b4ae7c5 100644 --- a/git-cliff-core/src/repo.rs +++ b/git-cliff-core/src/repo.rs @@ -449,12 +449,12 @@ pub struct TaggedCommits<'a> { pub commits: IndexMap>, /// Commit ID to tag map. tags: IndexMap, - /// List of tags' commit indexes. + /// List of tags' commit indexes. Points into `commits`. /// /// Sorted in reverse order, meaning the first element is the latest tag. /// /// Used for lookups. - tag_ids: Vec, + tag_indexes: Vec, } impl<'a> TaggedCommits<'a> { @@ -481,7 +481,7 @@ impl<'a> TaggedCommits<'a> { .collect(); Ok(Self { commits, - tag_ids, + tag_indexes: tag_ids, tags, }) } @@ -524,13 +524,20 @@ impl<'a> TaggedCommits<'a> { /// Returns the tag closest to the given commit. pub fn get_closest(&self, commit: &str) -> Option<&Tag> { + // Try exact match first. if let Some(tagged) = self.get(commit) { return Some(tagged); } let index = self.commits.get_index_of(commit)?; - let tag_index = self.tag_ids.iter().find(|tag_idx| index >= **tag_idx)?; - Some(&self.tags[*tag_index]) + let tag_index = + *self.tag_indexes.iter().find(|tag_idx| index >= **tag_idx)?; + self.get_tag_by_id(tag_index) + } + + fn get_tag_by_id(&self, id: usize) -> Option<&Tag> { + let (commit_of_tag, _) = self.commits.get_index(id)?; + self.tags.get(commit_of_tag) } /// Returns the commit of the given tag. @@ -550,7 +557,7 @@ impl<'a> TaggedCommits<'a> { pub fn insert(&mut self, commit: String, tag: Tag) { if let Some(index) = self.commits.get_index_of(&commit) { if let Err(idx) = self.binary_search(index) { - self.tag_ids.insert(idx, index); + self.tag_indexes.insert(idx, index); } } self.tags.insert(commit, tag); @@ -558,13 +565,19 @@ impl<'a> TaggedCommits<'a> { /// Retains only the tags specified by the predicate. pub fn retain(&mut self, mut f: impl FnMut(&Tag) -> bool) { - self.tag_ids - .retain(|idx| f(self.tags.get_index(*idx).unwrap().1)); - self.tags.retain(|_, tag| f(tag)); + self.tag_indexes.retain(|&idx| { + let (commit_of_tag, _) = self.commits.get_index(idx).unwrap(); + let tag = self.tags.get(commit_of_tag).unwrap(); + let retain = f(tag); + if !retain { + self.tags.shift_remove(commit_of_tag); + } + retain + }); } fn binary_search(&self, index: usize) -> std::result::Result { - self.tag_ids + self.tag_indexes .binary_search_by_key(&Reverse(index), |tag_idx| Reverse(*tag_idx)) } } diff --git a/git-cliff/src/lib.rs b/git-cliff/src/lib.rs index 3c0a07c07f..7783d9f55f 100644 --- a/git-cliff/src/lib.rs +++ b/git-cliff/src/lib.rs @@ -92,6 +92,7 @@ fn process_repository<'a>( 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(); + eprintln!("{tags:#?}"); tags.retain(|tag| { let name = &tag.name; From 793c8911b1220ed8eccd2d180cf5cefd34d87e0d Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 25 Sep 2024 15:59:32 +0200 Subject: [PATCH 10/13] fmt --- git-cliff-core/src/changelog.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/git-cliff-core/src/changelog.rs b/git-cliff-core/src/changelog.rs index 362333b70a..0c644c794c 100644 --- a/git-cliff-core/src/changelog.rs +++ b/git-cliff-core/src/changelog.rs @@ -444,14 +444,16 @@ impl<'a> Changelog<'a> { self.add_remote_context()?; #[cfg(feature = "github")] - let (github_commits, github_pull_requests) = if self.config.remote.github.is_set() { + let (github_commits, github_pull_requests) = if self.config.remote.github.is_set() + { self.get_github_metadata() .expect("Could not get github metadata") } else { (vec![], vec![]) }; #[cfg(feature = "gitlab")] - let (gitlab_commits, gitlab_merge_request) = if self.config.remote.gitlab.is_set() { + let (gitlab_commits, gitlab_merge_request) = if self.config.remote.gitlab.is_set() + { self.get_gitlab_metadata() .expect("Could not get gitlab metadata") } else { From 585271b63022d3b8513a6530369a844a6ba00f28 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:06:12 +0200 Subject: [PATCH 11/13] chore: clippy --- git-cliff-core/src/repo.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/git-cliff-core/src/repo.rs b/git-cliff-core/src/repo.rs index ac1b4ae7c5..876b17d317 100644 --- a/git-cliff-core/src/repo.rs +++ b/git-cliff-core/src/repo.rs @@ -566,8 +566,9 @@ impl<'a> TaggedCommits<'a> { /// Retains only the tags specified by the predicate. pub fn retain(&mut self, mut f: impl FnMut(&Tag) -> bool) { self.tag_indexes.retain(|&idx| { - let (commit_of_tag, _) = self.commits.get_index(idx).unwrap(); - let tag = self.tags.get(commit_of_tag).unwrap(); + let panic_msg = "invalid TaggedCommits state"; + let (commit_of_tag, _) = self.commits.get_index(idx).expect(panic_msg); + let tag = self.tags.get(commit_of_tag).expect(panic_msg); let retain = f(tag); if !retain { self.tags.shift_remove(commit_of_tag); From bfd9c0cc3a8f67ecdc7e66d3ae058f970616cc43 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 25 Sep 2024 17:28:32 +0200 Subject: [PATCH 12/13] chore: remove println --- git-cliff/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/git-cliff/src/lib.rs b/git-cliff/src/lib.rs index 7783d9f55f..3c0a07c07f 100644 --- a/git-cliff/src/lib.rs +++ b/git-cliff/src/lib.rs @@ -92,7 +92,6 @@ fn process_repository<'a>( 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(); - eprintln!("{tags:#?}"); tags.retain(|tag| { let name = &tag.name; From 740ad6eca48c0f82b0db909aeaf4a0acbbf1bea2 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 8 Nov 2024 05:51:08 +0100 Subject: [PATCH 13/13] fix --- git-cliff/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/git-cliff/src/lib.rs b/git-cliff/src/lib.rs index e2aca2b04f..8d2469a355 100644 --- a/git-cliff/src/lib.rs +++ b/git-cliff/src/lib.rs @@ -174,6 +174,7 @@ fn process_repository<'a>( tags.get_index(0).map(|tag| &tag.name), ) { if tags.len() == 1 { + let tag2 = tags.get_commit(tag2).unwrap(); commit_range = Some(tag2.to_owned()); } else { commit_range = Some(format!("{tag1}..{tag2}"));