diff options
Diffstat (limited to 'vendor/gix/src/revision/spec/parse/delegate/navigate.rs')
-rw-r--r-- | vendor/gix/src/revision/spec/parse/delegate/navigate.rs | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/vendor/gix/src/revision/spec/parse/delegate/navigate.rs b/vendor/gix/src/revision/spec/parse/delegate/navigate.rs new file mode 100644 index 000000000..882c2835c --- /dev/null +++ b/vendor/gix/src/revision/spec/parse/delegate/navigate.rs @@ -0,0 +1,340 @@ +use std::collections::HashSet; + +use gix_hash::ObjectId; +use gix_revision::spec::parse::{ + delegate, + delegate::{PeelTo, Traversal}, +}; +use gix_traverse::commit::Sorting; + +use crate::{ + bstr::{BStr, ByteSlice}, + ext::ObjectIdExt, + object, + revision::spec::parse::{ + delegate::{handle_errors_and_replacements, peel, Replacements}, + Delegate, Error, + }, +}; + +impl<'repo> delegate::Navigate for Delegate<'repo> { + fn traverse(&mut self, kind: Traversal) -> Option<()> { + self.unset_disambiguate_call(); + self.follow_refs_to_objects_if_needed()?; + + let mut replacements = Replacements::default(); + let mut errors = Vec::new(); + let objs = self.objs[self.idx].as_mut()?; + let repo = self.repo; + + for obj in objs.iter() { + match kind { + Traversal::NthParent(num) => { + match self.repo.find_object(*obj).map_err(Error::from).and_then(|obj| { + obj.try_into_commit().map_err(|err| { + let object::try_into::Error { actual, expected, id } = err; + Error::ObjectKind { + oid: id.attach(repo).shorten_or_id(), + actual, + expected, + } + }) + }) { + Ok(commit) => match commit.parent_ids().nth(num.saturating_sub(1)) { + Some(id) => replacements.push((commit.id, id.detach())), + None => errors.push(( + commit.id, + Error::ParentOutOfRange { + oid: commit.id().shorten_or_id(), + desired: num, + available: commit.parent_ids().count(), + }, + )), + }, + Err(err) => errors.push((*obj, err)), + } + } + Traversal::NthAncestor(num) => { + let id = obj.attach(repo); + match id + .ancestors() + .first_parent_only() + .all() + .expect("cannot fail without sorting") + .skip(num) + .filter_map(Result::ok) + .next() + { + Some(id) => replacements.push((*obj, id.detach())), + None => errors.push(( + *obj, + Error::AncestorOutOfRange { + oid: id.shorten_or_id(), + desired: num, + available: id + .ancestors() + .first_parent_only() + .all() + .expect("cannot fail without sorting") + .skip(1) + .count(), + }, + )), + } + } + } + } + + handle_errors_and_replacements(&mut self.err, objs, errors, &mut replacements) + } + + fn peel_until(&mut self, kind: PeelTo<'_>) -> Option<()> { + self.unset_disambiguate_call(); + self.follow_refs_to_objects_if_needed()?; + + let mut replacements = Replacements::default(); + let mut errors = Vec::new(); + let objs = self.objs[self.idx].as_mut()?; + let repo = self.repo; + + match kind { + PeelTo::ValidObject => { + for obj in objs.iter() { + match repo.find_object(*obj) { + Ok(_) => {} + Err(err) => { + errors.push((*obj, err.into())); + } + }; + } + } + PeelTo::ObjectKind(kind) => { + let peel = |obj| peel(repo, obj, kind); + for obj in objs.iter() { + match peel(obj) { + Ok(replace) => replacements.push((*obj, replace)), + Err(err) => errors.push((*obj, err)), + } + } + } + PeelTo::Path(path) => { + let lookup_path = |obj: &ObjectId| { + let tree_id = peel(repo, obj, gix_object::Kind::Tree)?; + if path.is_empty() { + return Ok(tree_id); + } + let tree = repo.find_object(tree_id)?.into_tree(); + let entry = + tree.lookup_entry_by_path(gix_path::from_bstr(path))? + .ok_or_else(|| Error::PathNotFound { + path: path.into(), + object: obj.attach(repo).shorten_or_id(), + tree: tree_id.attach(repo).shorten_or_id(), + })?; + Ok(entry.object_id()) + }; + for obj in objs.iter() { + match lookup_path(obj) { + Ok(replace) => replacements.push((*obj, replace)), + Err(err) => errors.push((*obj, err)), + } + } + } + PeelTo::RecursiveTagObject => { + for oid in objs.iter() { + match oid.attach(repo).object().and_then(|obj| obj.peel_tags_to_end()) { + Ok(obj) => replacements.push((*oid, obj.id)), + Err(err) => errors.push((*oid, err.into())), + } + } + } + } + + handle_errors_and_replacements(&mut self.err, objs, errors, &mut replacements) + } + + fn find(&mut self, regex: &BStr, negated: bool) -> Option<()> { + self.unset_disambiguate_call(); + self.follow_refs_to_objects_if_needed()?; + + #[cfg(not(feature = "regex"))] + let matches = |message: &BStr| -> bool { message.contains_str(regex) ^ negated }; + #[cfg(feature = "regex")] + let matches = match regex::bytes::Regex::new(regex.to_str_lossy().as_ref()) { + Ok(compiled) => { + let needs_regex = regex::escape(compiled.as_str()) != regex; + move |message: &BStr| -> bool { + if needs_regex { + compiled.is_match(message) ^ negated + } else { + message.contains_str(regex) ^ negated + } + } + } + Err(err) => { + self.err.push(err.into()); + return None; + } + }; + + match self.objs[self.idx].as_mut() { + Some(objs) => { + let repo = self.repo; + let mut errors = Vec::new(); + let mut replacements = Replacements::default(); + for oid in objs.iter() { + match oid + .attach(repo) + .ancestors() + .sorting(Sorting::ByCommitTimeNewestFirst) + .all() + { + Ok(iter) => { + let mut matched = false; + let mut count = 0; + let commits = iter.map(|res| { + res.map_err(Error::from).and_then(|commit_id| { + commit_id.object().map_err(Error::from).map(|obj| obj.into_commit()) + }) + }); + for commit in commits { + count += 1; + match commit { + Ok(commit) => { + if matches(commit.message_raw_sloppy()) { + replacements.push((*oid, commit.id)); + matched = true; + break; + } + } + Err(err) => errors.push((*oid, err)), + } + } + if !matched { + errors.push(( + *oid, + Error::NoRegexMatch { + regex: regex.into(), + commits_searched: count, + oid: oid.attach(repo).shorten_or_id(), + }, + )) + } + } + Err(err) => errors.push((*oid, err.into())), + } + } + handle_errors_and_replacements(&mut self.err, objs, errors, &mut replacements) + } + None => match self.repo.references() { + Ok(references) => match references.all() { + Ok(references) => { + match self + .repo + .rev_walk( + references + .peeled() + .filter_map(Result::ok) + .filter(|r| { + r.id() + .object() + .ok() + .map(|obj| obj.kind == gix_object::Kind::Commit) + .unwrap_or(false) + }) + .filter_map(|r| r.detach().peeled), + ) + .sorting(Sorting::ByCommitTimeNewestFirst) + .all() + { + Ok(iter) => { + let mut matched = false; + let mut count = 0; + let commits = iter.map(|res| { + res.map_err(Error::from).and_then(|commit_id| { + commit_id.object().map_err(Error::from).map(|obj| obj.into_commit()) + }) + }); + for commit in commits { + count += 1; + match commit { + Ok(commit) => { + if matches(commit.message_raw_sloppy()) { + self.objs[self.idx] + .get_or_insert_with(HashSet::default) + .insert(commit.id); + matched = true; + break; + } + } + Err(err) => self.err.push(err), + } + } + if matched { + Some(()) + } else { + self.err.push(Error::NoRegexMatchAllRefs { + regex: regex.into(), + commits_searched: count, + }); + None + } + } + Err(err) => { + self.err.push(err.into()); + None + } + } + } + Err(err) => { + self.err.push(err.into()); + None + } + }, + Err(err) => { + self.err.push(err.into()); + None + } + }, + } + } + + fn index_lookup(&mut self, path: &BStr, stage: u8) -> Option<()> { + self.unset_disambiguate_call(); + match self.repo.index() { + Ok(index) => match index.entry_by_path_and_stage(path, stage.into()) { + Some(entry) => { + self.objs[self.idx] + .get_or_insert_with(HashSet::default) + .insert(entry.id); + Some(()) + } + None => { + let stage_hint = [0, 1, 2] + .iter() + .filter(|our_stage| **our_stage != stage) + .find_map(|stage| { + index + .entry_index_by_path_and_stage(path, (*stage).into()) + .map(|_| (*stage).into()) + }); + let exists = self + .repo + .work_dir() + .map_or(false, |root| root.join(gix_path::from_bstr(path)).exists()); + self.err.push(Error::IndexLookup { + desired_path: path.into(), + desired_stage: stage.into(), + exists, + stage_hint, + }); + None + } + }, + Err(err) => { + self.err.push(err.into()); + None + } + } + } +} |