1 use std
::collections
::HashSet
;
3 use gix_hash
::ObjectId
;
4 use gix_revision
::spec
::parse
::{
6 delegate
::{PeelTo, Traversal}
,
8 use gix_traverse
::commit
::Sorting
;
11 bstr
::{BStr, ByteSlice}
,
14 revision
::spec
::parse
::{
15 delegate
::{handle_errors_and_replacements, peel, Replacements}
,
21 impl<'repo
> delegate
::Navigate
for Delegate
<'repo
> {
22 fn traverse(&mut self, kind
: Traversal
) -> Option
<()> {
23 self.unset_disambiguate_call();
24 self.follow_refs_to_objects_if_needed()?
;
26 let mut replacements
= Replacements
::default();
27 let mut errors
= Vec
::new();
28 let objs
= self.objs
[self.idx
].as_mut()?
;
31 for obj
in objs
.iter() {
33 Traversal
::NthParent(num
) => {
34 match self.repo
.find_object(*obj
).map_err(Error
::from
).and_then(|obj
| {
35 obj
.try_into_commit().map_err(|err
| {
36 let object
::try_into
::Error { actual, expected, id }
= err
;
38 oid
: id
.attach(repo
).shorten_or_id(),
44 Ok(commit
) => match commit
.parent_ids().nth(num
.saturating_sub(1)) {
45 Some(id
) => replacements
.push((commit
.id
, id
.detach())),
48 Error
::ParentOutOfRange
{
49 oid
: commit
.id().shorten_or_id(),
51 available
: commit
.parent_ids().count(),
55 Err(err
) => errors
.push((*obj
, err
)),
58 Traversal
::NthAncestor(num
) => {
59 let id
= obj
.attach(repo
);
64 .expect("cannot fail without sorting")
68 Some(commit
) => replacements
.push((*obj
, commit
.id
)),
71 Error
::AncestorOutOfRange
{
72 oid
: id
.shorten_or_id(),
78 .expect("cannot fail without sorting")
88 handle_errors_and_replacements(&mut self.err
, objs
, errors
, &mut replacements
)
91 fn peel_until(&mut self, kind
: PeelTo
<'_
>) -> Option
<()> {
92 self.unset_disambiguate_call();
93 self.follow_refs_to_objects_if_needed()?
;
95 let mut replacements
= Replacements
::default();
96 let mut errors
= Vec
::new();
97 let objs
= self.objs
[self.idx
].as_mut()?
;
101 PeelTo
::ValidObject
=> {
102 for obj
in objs
.iter() {
103 match repo
.find_object(*obj
) {
106 errors
.push((*obj
, err
.into()));
111 PeelTo
::ObjectKind(kind
) => {
112 let peel
= |obj
| peel(repo
, obj
, kind
);
113 for obj
in objs
.iter() {
115 Ok(replace
) => replacements
.push((*obj
, replace
)),
116 Err(err
) => errors
.push((*obj
, err
)),
120 PeelTo
::Path(path
) => {
121 let lookup_path
= |obj
: &ObjectId
| {
122 let tree_id
= peel(repo
, obj
, gix_object
::Kind
::Tree
)?
;
124 return Ok((tree_id
, gix_object
::tree
::EntryKind
::Tree
.into()));
126 let mut tree
= repo
.find_object(tree_id
)?
.into_tree();
128 tree
.peel_to_entry_by_path(gix_path
::from_bstr(path
))?
129 .ok_or_else(|| Error
::PathNotFound
{
131 object
: obj
.attach(repo
).shorten_or_id(),
132 tree
: tree_id
.attach(repo
).shorten_or_id(),
134 Ok((entry
.object_id(), entry
.mode()))
136 for obj
in objs
.iter() {
137 match lookup_path(obj
) {
138 Ok((replace
, mode
)) => {
139 if !path
.is_empty() {
140 // Technically this is letting the last one win, but so be it.
141 self.paths
[self.idx
] = Some((path
.to_owned(), mode
));
143 replacements
.push((*obj
, replace
))
145 Err(err
) => errors
.push((*obj
, err
)),
149 PeelTo
::RecursiveTagObject
=> {
150 for oid
in objs
.iter() {
151 match oid
.attach(repo
).object().and_then(Object
::peel_tags_to_end
) {
152 Ok(obj
) => replacements
.push((*oid
, obj
.id
)),
153 Err(err
) => errors
.push((*oid
, err
.into())),
159 handle_errors_and_replacements(&mut self.err
, objs
, errors
, &mut replacements
)
162 fn find(&mut self, regex
: &BStr
, negated
: bool
) -> Option
<()> {
163 self.unset_disambiguate_call();
164 self.follow_refs_to_objects_if_needed()?
;
166 #[cfg(not(feature = "revparse-regex"))]
167 let matches
= |message
: &BStr
| -> bool { message.contains_str(regex) ^ negated }
;
168 #[cfg(feature = "revparse-regex")]
169 let matches
= match regex
::bytes
::Regex
::new(regex
.to_str_lossy().as_ref()) {
171 let needs_regex
= regex
::escape(compiled
.as_str()) != regex
;
172 move |message
: &BStr
| -> bool
{
174 compiled
.is_match(message
) ^ negated
176 message
.contains_str(regex
) ^ negated
181 self.err
.push(err
.into());
186 match self.objs
[self.idx
].as_mut() {
188 let repo
= self.repo
;
189 let mut errors
= Vec
::new();
190 let mut replacements
= Replacements
::default();
191 for oid
in objs
.iter() {
195 .sorting(Sorting
::ByCommitTimeNewestFirst
)
199 let mut matched
= false;
201 let commits
= iter
.map(|res
| {
202 res
.map_err(Error
::from
).and_then(|commit
| {
203 commit
.id().object().map_err(Error
::from
).map(Object
::into_commit
)
206 for commit
in commits
{
210 if matches(commit
.message_raw_sloppy()) {
211 replacements
.push((*oid
, commit
.id
));
216 Err(err
) => errors
.push((*oid
, err
)),
222 Error
::NoRegexMatch
{
224 commits_searched
: count
,
225 oid
: oid
.attach(repo
).shorten_or_id(),
230 Err(err
) => errors
.push((*oid
, err
.into())),
233 handle_errors_and_replacements(&mut self.err
, objs
, errors
, &mut replacements
)
235 None
=> match self.repo
.references() {
236 Ok(references
) => match references
.all() {
243 .filter_map(Result
::ok
)
248 .map_or(false, |obj
| obj
.kind
== gix_object
::Kind
::Commit
)
250 .filter_map(|r
| r
.detach().peeled
),
252 .sorting(Sorting
::ByCommitTimeNewestFirst
)
256 let mut matched
= false;
258 let commits
= iter
.map(|res
| {
259 res
.map_err(Error
::from
).and_then(|commit
| {
260 commit
.id().object().map_err(Error
::from
).map(Object
::into_commit
)
263 for commit
in commits
{
267 if matches(commit
.message_raw_sloppy()) {
269 .get_or_insert_with(HashSet
::default)
275 Err(err
) => self.err
.push(err
),
281 self.err
.push(Error
::NoRegexMatchAllRefs
{
283 commits_searched
: count
,
289 self.err
.push(err
.into());
295 self.err
.push(err
.into());
300 self.err
.push(err
.into());
307 fn index_lookup(&mut self, path
: &BStr
, stage
: u8) -> Option
<()> {
308 self.unset_disambiguate_call();
309 match self.repo
.index() {
310 Ok(index
) => match index
.entry_by_path_and_stage(path
, stage
.into()) {
313 .get_or_insert_with(HashSet
::default)
316 self.paths
[self.idx
] = Some((
320 .to_tree_entry_mode()
321 .unwrap_or(gix_object
::tree
::EntryKind
::Blob
.into()),
326 let stage_hint
= [0, 1, 2]
328 .filter(|our_stage
| **our_stage
!= stage
)
331 .entry_index_by_path_and_stage(path
, (*stage
).into())
332 .map(|_
| (*stage
).into())
337 .map_or(false, |root
| root
.join(gix_path
::from_bstr(path
)).exists());
338 self.err
.push(Error
::IndexLookup
{
339 desired_path
: path
.into(),
340 desired_stage
: stage
.into(),
348 self.err
.push(err
.into());