summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/revision/spec/parse/delegate/navigate.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix/src/revision/spec/parse/delegate/navigate.rs')
-rw-r--r--vendor/gix/src/revision/spec/parse/delegate/navigate.rs340
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
+ }
+ }
+ }
+}