From 350d9a4e0d22be1b9f3f5a54a3b044350f803483 Mon Sep 17 00:00:00 2001 From: Justin Rubek <25621857+justinrubek@users.noreply.github.com> Date: Thu, 16 May 2024 17:13:56 -0500 Subject: [PATCH] feat: support repos with no existing tags Repos with no existing tags will instead use the root commit as the start, and behave as if it was tagged with `0.0.0`. This allows for a changelog to be generated from everything but the first commit. For my projects, I do an empty commit as the first one so this is perfect. --- src/app.rs | 30 +++++++++++++++++++++++++----- src/versioning.rs | 34 ++++++++++++++++++++++++++++------ 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/app.rs b/src/app.rs index 925a817..e569ce2 100644 --- a/src/app.rs +++ b/src/app.rs @@ -7,7 +7,7 @@ use bomper::{ cargo::CargoReplacer, file::FileReplacer, search::SearchReplacer, simple::SimpleReplacer, Replacer, VersionReplacement, }, - versioning::{get_commits_between_tags, get_commits_since_tag, get_latest_tag, Tag}, + versioning::{get_commits_between_tags, get_commits_since_tag, get_latest_tag, Commit, Tag}, }; use console::{style, Style}; use gix::refs::transaction::PreviousValue; @@ -29,8 +29,7 @@ impl App { pub fn bump(&self, opts: &Bump) -> Result<()> { let repo = gix::discover(".")?; - let tag = get_latest_tag(&repo)?; - let commits = get_commits_since_tag(&repo, &tag)?; + let (tag, commits) = changelog_commits(&repo)?; let increment = opts.options.determine_increment(&commits, &tag.version)?; let new_tag = tag.increment_version(increment); @@ -101,8 +100,7 @@ impl App { println!("{}", changelog_entry); } None => { - let tag = get_latest_tag(&repo)?; - let commits = get_commits_since_tag(&repo, &tag)?; + let (_, commits) = changelog_commits(&repo)?; let changelog_entry = generate_changelog_entry( &repo, &commits, @@ -388,3 +386,25 @@ fn prompt_for_description() -> Result> { } } } + +/// Retrieve all the commits that should be included in a new changelog entry. +/// This will start at the current head commit and walk back to the latest tag. +/// The latest tag is determined by the highest semver tag in the repository. +/// If no tags are found, the root commit will be used as the starting point and a version of `0.0.0` will be used. +fn changelog_commits(repo: &gix::Repository) -> Result<(Tag, Vec)> { + let tag = match get_latest_tag(repo)? { + Some(tag) => tag, + None => { + let head = repo.head_commit()?; + let ancestors = head.ancestors(); + let root_commit = ancestors.all()?.last(); + Tag { + version: semver::Version::new(0, 0, 0), + commit_id: root_commit.unwrap().unwrap().id().into(), + prefix_v: false, + } + } + }; + let commits = get_commits_since_tag(repo, &tag)?; + Ok((tag, commits)) +} diff --git a/src/versioning.rs b/src/versioning.rs index c78b972..a92d5c8 100644 --- a/src/versioning.rs +++ b/src/versioning.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::error::{Error, Result}; +use crate::error::Result; use conventional_commit_parser::commit::{CommitType, ConventionalCommit}; #[derive(Clone, Debug, Eq)] @@ -95,11 +95,8 @@ pub enum VersionIncrement { Patch, } -pub fn get_latest_tag(repo: &gix::Repository) -> Result { - let tag = Tag::get_version_tags(repo)? - .into_iter() - .max() - .ok_or_else(|| Error::TagError)?; +pub fn get_latest_tag(repo: &gix::Repository) -> Result> { + let tag = Tag::get_version_tags(repo)?.into_iter().max(); Ok(tag) } @@ -144,6 +141,31 @@ pub fn get_commits_since_tag(repo: &gix::Repository, tag: &Tag) -> Result Result> { + let head = repo.head_commit()?; + let ancestors = head.ancestors(); + let mut parsed_commits = Vec::new(); + for commit in ancestors.all()? { + let commit = commit.unwrap(); + let object = commit.object().unwrap(); + let message = object.message().unwrap(); + let mut full_message = String::new(); + full_message.push_str(message.title.to_string().trim()); + if let Some(body) = message.body { + full_message.push_str("\n\n"); + full_message.push_str(&body.to_string()); + } + let parsed = conventional_commit_parser::parse(&full_message)?; + parsed_commits.push(Commit { + commit_id: commit.id().into(), + conventional_commit: parsed, + signature: object.author().to_owned()?.into(), + }); + } + + Ok(parsed_commits) +} + pub fn get_commits_between_tags( repo: &gix::Repository, from: &Tag,