]>
Commit | Line | Data |
---|---|---|
0a29b90c FG |
1 | use std::collections::HashSet; |
2 | ||
3 | use gix_hash::ObjectId; | |
4 | use gix_revision::spec::parse::{ | |
5 | delegate, | |
6 | delegate::{PeelTo, Traversal}, | |
7 | }; | |
8 | use gix_traverse::commit::Sorting; | |
9 | ||
10 | use crate::{ | |
11 | bstr::{BStr, ByteSlice}, | |
12 | ext::ObjectIdExt, | |
13 | object, | |
14 | revision::spec::parse::{ | |
15 | delegate::{handle_errors_and_replacements, peel, Replacements}, | |
16 | Delegate, Error, | |
17 | }, | |
781aab86 | 18 | Object, |
0a29b90c FG |
19 | }; |
20 | ||
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()?; | |
25 | ||
26 | let mut replacements = Replacements::default(); | |
27 | let mut errors = Vec::new(); | |
28 | let objs = self.objs[self.idx].as_mut()?; | |
29 | let repo = self.repo; | |
30 | ||
31 | for obj in objs.iter() { | |
32 | match kind { | |
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; | |
37 | Error::ObjectKind { | |
38 | oid: id.attach(repo).shorten_or_id(), | |
39 | actual, | |
40 | expected, | |
41 | } | |
42 | }) | |
43 | }) { | |
44 | Ok(commit) => match commit.parent_ids().nth(num.saturating_sub(1)) { | |
45 | Some(id) => replacements.push((commit.id, id.detach())), | |
46 | None => errors.push(( | |
47 | commit.id, | |
48 | Error::ParentOutOfRange { | |
49 | oid: commit.id().shorten_or_id(), | |
50 | desired: num, | |
51 | available: commit.parent_ids().count(), | |
52 | }, | |
53 | )), | |
54 | }, | |
55 | Err(err) => errors.push((*obj, err)), | |
56 | } | |
57 | } | |
58 | Traversal::NthAncestor(num) => { | |
59 | let id = obj.attach(repo); | |
60 | match id | |
61 | .ancestors() | |
62 | .first_parent_only() | |
63 | .all() | |
64 | .expect("cannot fail without sorting") | |
65 | .skip(num) | |
781aab86 | 66 | .find_map(Result::ok) |
0a29b90c | 67 | { |
781aab86 | 68 | Some(commit) => replacements.push((*obj, commit.id)), |
0a29b90c FG |
69 | None => errors.push(( |
70 | *obj, | |
71 | Error::AncestorOutOfRange { | |
72 | oid: id.shorten_or_id(), | |
73 | desired: num, | |
74 | available: id | |
75 | .ancestors() | |
76 | .first_parent_only() | |
77 | .all() | |
78 | .expect("cannot fail without sorting") | |
79 | .skip(1) | |
80 | .count(), | |
81 | }, | |
82 | )), | |
83 | } | |
84 | } | |
85 | } | |
86 | } | |
87 | ||
88 | handle_errors_and_replacements(&mut self.err, objs, errors, &mut replacements) | |
89 | } | |
90 | ||
91 | fn peel_until(&mut self, kind: PeelTo<'_>) -> Option<()> { | |
92 | self.unset_disambiguate_call(); | |
93 | self.follow_refs_to_objects_if_needed()?; | |
94 | ||
95 | let mut replacements = Replacements::default(); | |
96 | let mut errors = Vec::new(); | |
97 | let objs = self.objs[self.idx].as_mut()?; | |
98 | let repo = self.repo; | |
99 | ||
100 | match kind { | |
101 | PeelTo::ValidObject => { | |
102 | for obj in objs.iter() { | |
103 | match repo.find_object(*obj) { | |
104 | Ok(_) => {} | |
105 | Err(err) => { | |
106 | errors.push((*obj, err.into())); | |
107 | } | |
108 | }; | |
109 | } | |
110 | } | |
111 | PeelTo::ObjectKind(kind) => { | |
112 | let peel = |obj| peel(repo, obj, kind); | |
113 | for obj in objs.iter() { | |
114 | match peel(obj) { | |
115 | Ok(replace) => replacements.push((*obj, replace)), | |
116 | Err(err) => errors.push((*obj, err)), | |
117 | } | |
118 | } | |
119 | } | |
120 | PeelTo::Path(path) => { | |
121 | let lookup_path = |obj: &ObjectId| { | |
122 | let tree_id = peel(repo, obj, gix_object::Kind::Tree)?; | |
123 | if path.is_empty() { | |
124 | return Ok(tree_id); | |
125 | } | |
781aab86 | 126 | let mut tree = repo.find_object(tree_id)?.into_tree(); |
0a29b90c | 127 | let entry = |
781aab86 | 128 | tree.peel_to_entry_by_path(gix_path::from_bstr(path))? |
0a29b90c FG |
129 | .ok_or_else(|| Error::PathNotFound { |
130 | path: path.into(), | |
131 | object: obj.attach(repo).shorten_or_id(), | |
132 | tree: tree_id.attach(repo).shorten_or_id(), | |
133 | })?; | |
134 | Ok(entry.object_id()) | |
135 | }; | |
136 | for obj in objs.iter() { | |
137 | match lookup_path(obj) { | |
138 | Ok(replace) => replacements.push((*obj, replace)), | |
139 | Err(err) => errors.push((*obj, err)), | |
140 | } | |
141 | } | |
142 | } | |
143 | PeelTo::RecursiveTagObject => { | |
144 | for oid in objs.iter() { | |
781aab86 | 145 | match oid.attach(repo).object().and_then(Object::peel_tags_to_end) { |
0a29b90c FG |
146 | Ok(obj) => replacements.push((*oid, obj.id)), |
147 | Err(err) => errors.push((*oid, err.into())), | |
148 | } | |
149 | } | |
150 | } | |
151 | } | |
152 | ||
153 | handle_errors_and_replacements(&mut self.err, objs, errors, &mut replacements) | |
154 | } | |
155 | ||
156 | fn find(&mut self, regex: &BStr, negated: bool) -> Option<()> { | |
157 | self.unset_disambiguate_call(); | |
158 | self.follow_refs_to_objects_if_needed()?; | |
159 | ||
781aab86 | 160 | #[cfg(not(feature = "revparse-regex"))] |
0a29b90c | 161 | let matches = |message: &BStr| -> bool { message.contains_str(regex) ^ negated }; |
781aab86 | 162 | #[cfg(feature = "revparse-regex")] |
0a29b90c FG |
163 | let matches = match regex::bytes::Regex::new(regex.to_str_lossy().as_ref()) { |
164 | Ok(compiled) => { | |
165 | let needs_regex = regex::escape(compiled.as_str()) != regex; | |
166 | move |message: &BStr| -> bool { | |
167 | if needs_regex { | |
168 | compiled.is_match(message) ^ negated | |
169 | } else { | |
170 | message.contains_str(regex) ^ negated | |
171 | } | |
172 | } | |
173 | } | |
174 | Err(err) => { | |
175 | self.err.push(err.into()); | |
176 | return None; | |
177 | } | |
178 | }; | |
179 | ||
180 | match self.objs[self.idx].as_mut() { | |
181 | Some(objs) => { | |
182 | let repo = self.repo; | |
183 | let mut errors = Vec::new(); | |
184 | let mut replacements = Replacements::default(); | |
185 | for oid in objs.iter() { | |
186 | match oid | |
187 | .attach(repo) | |
188 | .ancestors() | |
189 | .sorting(Sorting::ByCommitTimeNewestFirst) | |
190 | .all() | |
191 | { | |
192 | Ok(iter) => { | |
193 | let mut matched = false; | |
194 | let mut count = 0; | |
195 | let commits = iter.map(|res| { | |
781aab86 FG |
196 | res.map_err(Error::from).and_then(|commit| { |
197 | commit.id().object().map_err(Error::from).map(Object::into_commit) | |
0a29b90c FG |
198 | }) |
199 | }); | |
200 | for commit in commits { | |
201 | count += 1; | |
202 | match commit { | |
203 | Ok(commit) => { | |
204 | if matches(commit.message_raw_sloppy()) { | |
205 | replacements.push((*oid, commit.id)); | |
206 | matched = true; | |
207 | break; | |
208 | } | |
209 | } | |
210 | Err(err) => errors.push((*oid, err)), | |
211 | } | |
212 | } | |
213 | if !matched { | |
214 | errors.push(( | |
215 | *oid, | |
216 | Error::NoRegexMatch { | |
217 | regex: regex.into(), | |
218 | commits_searched: count, | |
219 | oid: oid.attach(repo).shorten_or_id(), | |
220 | }, | |
221 | )) | |
222 | } | |
223 | } | |
224 | Err(err) => errors.push((*oid, err.into())), | |
225 | } | |
226 | } | |
227 | handle_errors_and_replacements(&mut self.err, objs, errors, &mut replacements) | |
228 | } | |
229 | None => match self.repo.references() { | |
230 | Ok(references) => match references.all() { | |
231 | Ok(references) => { | |
232 | match self | |
233 | .repo | |
234 | .rev_walk( | |
235 | references | |
236 | .peeled() | |
237 | .filter_map(Result::ok) | |
238 | .filter(|r| { | |
239 | r.id() | |
240 | .object() | |
241 | .ok() | |
fe692bf9 | 242 | .map_or(false, |obj| obj.kind == gix_object::Kind::Commit) |
0a29b90c FG |
243 | }) |
244 | .filter_map(|r| r.detach().peeled), | |
245 | ) | |
246 | .sorting(Sorting::ByCommitTimeNewestFirst) | |
247 | .all() | |
248 | { | |
249 | Ok(iter) => { | |
250 | let mut matched = false; | |
251 | let mut count = 0; | |
252 | let commits = iter.map(|res| { | |
781aab86 FG |
253 | res.map_err(Error::from).and_then(|commit| { |
254 | commit.id().object().map_err(Error::from).map(Object::into_commit) | |
0a29b90c FG |
255 | }) |
256 | }); | |
257 | for commit in commits { | |
258 | count += 1; | |
259 | match commit { | |
260 | Ok(commit) => { | |
261 | if matches(commit.message_raw_sloppy()) { | |
262 | self.objs[self.idx] | |
263 | .get_or_insert_with(HashSet::default) | |
264 | .insert(commit.id); | |
265 | matched = true; | |
266 | break; | |
267 | } | |
268 | } | |
269 | Err(err) => self.err.push(err), | |
270 | } | |
271 | } | |
272 | if matched { | |
273 | Some(()) | |
274 | } else { | |
275 | self.err.push(Error::NoRegexMatchAllRefs { | |
276 | regex: regex.into(), | |
277 | commits_searched: count, | |
278 | }); | |
279 | None | |
280 | } | |
281 | } | |
282 | Err(err) => { | |
283 | self.err.push(err.into()); | |
284 | None | |
285 | } | |
286 | } | |
287 | } | |
288 | Err(err) => { | |
289 | self.err.push(err.into()); | |
290 | None | |
291 | } | |
292 | }, | |
293 | Err(err) => { | |
294 | self.err.push(err.into()); | |
295 | None | |
296 | } | |
297 | }, | |
298 | } | |
299 | } | |
300 | ||
301 | fn index_lookup(&mut self, path: &BStr, stage: u8) -> Option<()> { | |
302 | self.unset_disambiguate_call(); | |
303 | match self.repo.index() { | |
304 | Ok(index) => match index.entry_by_path_and_stage(path, stage.into()) { | |
305 | Some(entry) => { | |
306 | self.objs[self.idx] | |
307 | .get_or_insert_with(HashSet::default) | |
308 | .insert(entry.id); | |
309 | Some(()) | |
310 | } | |
311 | None => { | |
312 | let stage_hint = [0, 1, 2] | |
313 | .iter() | |
314 | .filter(|our_stage| **our_stage != stage) | |
315 | .find_map(|stage| { | |
316 | index | |
317 | .entry_index_by_path_and_stage(path, (*stage).into()) | |
318 | .map(|_| (*stage).into()) | |
319 | }); | |
320 | let exists = self | |
321 | .repo | |
322 | .work_dir() | |
323 | .map_or(false, |root| root.join(gix_path::from_bstr(path)).exists()); | |
324 | self.err.push(Error::IndexLookup { | |
325 | desired_path: path.into(), | |
326 | desired_stage: stage.into(), | |
327 | exists, | |
328 | stage_hint, | |
329 | }); | |
330 | None | |
331 | } | |
332 | }, | |
333 | Err(err) => { | |
334 | self.err.push(err.into()); | |
335 | None | |
336 | } | |
337 | } | |
338 | } | |
339 | } |