Skip to content

Commit

Permalink
feat(tag): add configurable tag prefix as described in #122
Browse files Browse the repository at this point in the history
  • Loading branch information
oknozor committed Nov 30, 2021
1 parent 72b2722 commit 38f9eab
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 91 deletions.
1 change: 1 addition & 0 deletions src/git/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod hook;
pub mod repository;
pub mod status;
pub mod tag;
62 changes: 37 additions & 25 deletions src/git/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use std::fmt::{Debug, Formatter};
use std::path::Path;

use crate::error::CocogittoError::{self, Git};
use crate::git::status::Statuses;
use crate::OidOf;
use crate::git::{status::Statuses, tag::Tag};
use crate::{OidOf, SETTINGS};

use anyhow::{anyhow, ensure, Result};
use colored::Colorize;
Expand All @@ -12,7 +12,8 @@ use git2::{
Repository as Git2Repository, StatusOptions,
};
use itertools::Itertools;
use semver::Version;

use git2::string_array::StringArray;

pub(crate) struct Repository(pub(crate) Git2Repository);

Expand Down Expand Up @@ -119,41 +120,43 @@ impl Repository {
}
}

pub(crate) fn resolve_lightweight_tag(&self, tag: &str) -> Result<Oid> {
pub(crate) fn resolve_lightweight_tag(&self, tag: &str) -> Result<Tag> {
self.0
.resolve_reference_from_short_name(tag)
.map(|reference| reference.target().unwrap())
.map_err(|err| anyhow!("Cannot resolve tag {}:{}", tag, err.message()))
.map(|oid| Tag::new(tag, oid))?
}

pub(crate) fn get_latest_tag(&self) -> Result<String> {
let tag_names = self.0.tag_names(None)?;
let latest_tag = tag_names.iter().flatten().flat_map(Version::parse).max();
pub(crate) fn get_latest_tag(&self) -> Result<Tag> {
let latest_tag: Option<Tag> = self
.tags()?
.iter()
.flatten()
.map(|tag| self.resolve_lightweight_tag(tag))
.filter_map(Result::ok)
.max();

match latest_tag {
Some(tag) => Ok(tag.to_string()),
Some(tag) => Ok(tag),
None => Err(anyhow!("Unable to get any tag")),
}
}

pub(crate) fn get_tag_commits(&self, target_tag: &str) -> Result<(OidOf, OidOf)> {
let tag_names = self.0.tag_names(None)?;
let oid_of_target_tag = OidOf::Tag(
target_tag.to_string(),
self.resolve_lightweight_tag(target_tag)?,
);
let oid_of_target_tag = OidOf::Tag(self.resolve_lightweight_tag(target_tag)?);

// Starting point is set to first commit
let oid_of_first_commit = OidOf::Other(self.get_first_commit()?);

let oid_of_previous_tag = tag_names
let oid_of_previous_tag = self
.tags()?
.iter()
.flatten()
.sorted()
.tuple_windows()
.find(|&(_, cur)| cur == target_tag)
.map(|(prev, _)| {
OidOf::Tag(
prev.to_string(),
self.resolve_lightweight_tag(prev)
.expect("Unexpected tag parsing error"),
)
Expand All @@ -165,16 +168,13 @@ impl Repository {

pub(crate) fn get_latest_tag_oid(&self) -> Result<Oid> {
self.get_latest_tag()
.and_then(|tag| self.resolve_lightweight_tag(&tag))
.map(|tag| tag.oid().to_owned())
.map_err(|err| anyhow!("Could not resolve latest tag:{}", err))
}

pub(crate) fn get_latest_tag_oidof(&self) -> Result<OidOf> {
self.get_latest_tag()
.and_then(|tag| {
self.resolve_lightweight_tag(&tag)
.map(|oid| OidOf::Tag(tag, oid))
})
.map(OidOf::Tag)
.map_err(|err| anyhow!("Could not resolve latest tag:{}", err))
}

Expand Down Expand Up @@ -274,6 +274,17 @@ impl Repository {
let tree = obj.peel(ObjectType::Tree)?;
Ok(Some(tree))
}

fn tags(&self) -> Result<StringArray> {
let pattern = SETTINGS
.tag_prefix
.as_ref()
.map(|prefix| format!("{}*", prefix));

self.0
.tag_names(pattern.as_deref())
.map_err(|err| anyhow!("{}", err))
}
}

impl Debug for Repository {
Expand Down Expand Up @@ -561,9 +572,9 @@ mod test {
repo.commit("second commit")?;
repo.create_tag("0.2.0")?;

let tag = repo.get_latest_tag();
let tag = repo.get_latest_tag()?;

assert_that!(tag).is_ok().is_equal_to("0.2.0".to_string());
assert_that!(tag.to_string_with_prefix()).is_equal_to("0.2.0".to_string());
Ok(())
}

Expand Down Expand Up @@ -669,8 +680,9 @@ mod test {

let commit_range = repo.get_tag_commits("1.0.0")?;

assert_that!(commit_range)
.is_equal_to((OidOf::Other(start), OidOf::Tag("1.0.0".to_string(), end)));
assert_that!(commit_range.0).is_equal_to(OidOf::Other(start));
assert_that!(commit_range.1.get_oid()).is_equal_to(end);
assert_that!(commit_range.1.to_string()).is_equal_to("1.0.0".to_string());
Ok(())
}

Expand Down
61 changes: 61 additions & 0 deletions src/git/tag.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use crate::SETTINGS;
use anyhow::anyhow;
use anyhow::Result;
use git2::Oid;
use semver::Version;
use std::cmp::Ordering;
use std::fmt;
use std::fmt::Formatter;

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct Tag {
tag: String,
oid: Oid,
}

impl Tag {
pub(crate) fn oid(&self) -> &Oid {
&self.oid
}

pub(crate) fn new(name: &str, oid: Oid) -> Result<Tag> {
let tag = match SETTINGS.tag_prefix.as_ref() {
None => Ok(name),
Some(prefix) => name
.strip_prefix(prefix)
.ok_or_else(|| anyhow!("Expected a tag with prefix {}, got {}", prefix, name)),
}?
.to_string();

Ok(Tag { tag, oid })
}

pub(crate) fn to_version(&self) -> Result<Version> {
Version::parse(&self.tag).map_err(|err| anyhow!("{}", err))
}

pub(crate) fn to_string_with_prefix(&self) -> String {
match SETTINGS.tag_prefix.as_ref() {
None => self.tag.to_string(),
Some(prefix) => format!("{}{}", prefix, self.tag),
}
}
}

impl fmt::Display for Tag {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_string_with_prefix())
}
}

impl Ord for Tag {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}

impl PartialOrd<Tag> for Tag {
fn partial_cmp(&self, other: &Tag) -> Option<Ordering> {
Some(self.to_version().ok().cmp(&other.to_version().ok()))
}
}
Loading

0 comments on commit 38f9eab

Please sign in to comment.