202 lines · 6.2 KB · Rust
RawDownload
use std::{    borrow::Cow,    fmt::{Display, Formatter},}; use git_object::bstr::BStr; #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]pub struct Outcome<'a> {    pub name: Cow<'a, BStr>,    pub id: git_hash::ObjectId,    pub depth: u32,} impl<'a> Outcome<'a> {    pub fn into_format(self, hex_len: usize) -> Format<'a> {        Format {            name: self.name,            id: self.id,            hex_len,            depth: self.depth,            long: false,            dirty_suffix: None,        }    }} #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]pub struct Format<'a> {    pub name: Cow<'a, BStr>,    pub id: git_hash::ObjectId,    pub hex_len: usize,    pub depth: u32,    pub long: bool,    pub dirty_suffix: Option<String>,} impl<'a> Format<'a> {    pub fn is_exact_match(&self) -> bool {        self.depth == 0    }    pub fn long(&mut self) -> &mut Self {        self.long = true;        self    }    pub fn short(&mut self) -> &mut Self {        self.long = false;        self    }} impl<'a> Display for Format<'a> {    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {        if !self.long && self.is_exact_match() {            self.name.fmt(f)?;        } else {            write!(                f,                "{}-{}-g{}",                self.name,                self.depth,                self.id.to_hex_with_len(self.hex_len)            )?;        }        if let Some(suffix) = &self.dirty_suffix {            write!(f, "-{}", suffix)?;        }        Ok(())    }} pub(crate) mod function {    use std::{        borrow::Cow,        collections::{hash_map, HashMap, VecDeque},        iter::FromIterator,    };     use git_hash::{oid, ObjectId};    use git_object::{bstr::BStr, CommitRefIter};     use super::Outcome;     #[allow(clippy::result_unit_err)]    pub fn describe<'a, Find, E>(        commit: &oid,        mut find: Find,        name_set: &HashMap<ObjectId, Cow<'a, BStr>>,    ) -> Result<Option<Outcome<'a>>, E>    where        Find: for<'b> FnMut(&oid, &'b mut Vec<u8>) -> Result<CommitRefIter<'b>, E>,        E: std::error::Error + Send + Sync + 'static,    {        if let Some(name) = name_set.get(commit) {            return Ok(Some(Outcome {                name: name.clone(),                id: commit.to_owned(),                depth: 0,            }));        }         let mut buf = Vec::new();        let mut parent_buf = Vec::new();        let mut parents = Vec::new();         let mut queue = VecDeque::from_iter(Some(commit.to_owned()));        let mut candidates = Vec::new();        let mut seen_commits = 0;        let mut _gave_up_on_commit = None;        let mut seen = hash_hasher::HashedMap::default();        seen.insert(commit.to_owned(), 0u32);         const MAX_CANDIDATES: usize = std::mem::size_of::<Flags>() * 8;        while let Some(commit) = queue.pop_front() {            seen_commits += 1;            if let Some(name) = name_set.get(&commit) {                if candidates.len() < MAX_CANDIDATES {                    let identity_bit = 1 << candidates.len();                    candidates.push(Candidate {                        name: name.clone(),                        commits_in_its_future: seen_commits - 1,                        identity_bit,                        order: candidates.len(),                    });                    *seen.get_mut(&commit).expect("inserted") |= identity_bit;                } else {                    _gave_up_on_commit = Some(commit);                    break;                }            }             let flags = seen[&commit];            for candidate in candidates                .iter_mut()                .filter(|c| (flags & c.identity_bit) != c.identity_bit)            {                candidate.commits_in_its_future += 1;            }             let commit_iter = find(&commit, &mut buf)?;            parents.clear();            for token in commit_iter {                match token {                    Ok(git_object::commit::ref_iter::Token::Tree { .. }) => continue,                    Ok(git_object::commit::ref_iter::Token::Parent { id: parent_id }) => {                        match seen.entry(parent_id) {                            hash_map::Entry::Vacant(entry) => {                                let parent = find(&parent_id, &mut parent_buf)?;                                // TODO: figure out if not having a date is a hard error.                                let parent_commit_date = parent                                    .committer()                                    .map(|committer| committer.time.seconds_since_unix_epoch)                                    .unwrap_or_default();                                 entry.insert(flags);                                parents.push((parent_id, parent_commit_date));                            }                            hash_map::Entry::Occupied(mut entry) => {                                *entry.get_mut() |= flags;                            }                        }                    }                    Ok(_unused_token) => break,                    Err(_err) => todo!("return a decode error"),                }            }             if !parents.is_empty() {                parents.sort_by(|a, b| a.1.cmp(&b.1).reverse());                seen.extend(parents.iter().map(|e| (e.0, flags)));                queue.extend(parents.iter().map(|e| e.0));            }        }         if candidates.is_empty() {            return Ok(None);        }         candidates.sort_by(|a, b| {            a.commits_in_its_future                .cmp(&b.commits_in_its_future)                .then_with(|| a.order.cmp(&b.order))        });        Ok(candidates.into_iter().next().map(|c| Outcome {            name: c.name,            id: commit.to_owned(),            depth: c.commits_in_its_future,        }))    }     type Flags = u32;     #[derive(Debug)]    struct Candidate<'a> {        name: Cow<'a, BStr>,        commits_in_its_future: Flags,        /// A single bit identifying this candidate uniquely in a bitset        identity_bit: Flags,        /// The order at which we found the candidate, first one has order = 0        order: usize,    }}