]> git.proxmox.com Git - rustc.git/blob - vendor/gix/src/revision/spec/parse/delegate/navigate.rs
New upstream version 1.76.0+dfsg1
[rustc.git] / vendor / gix / src / revision / spec / parse / delegate / navigate.rs
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 },
18 Object,
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)
66 .find_map(Result::ok)
67 {
68 Some(commit) => replacements.push((*obj, commit.id)),
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, gix_object::tree::EntryKind::Tree.into()));
125 }
126 let mut tree = repo.find_object(tree_id)?.into_tree();
127 let entry =
128 tree.peel_to_entry_by_path(gix_path::from_bstr(path))?
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(), entry.mode()))
135 };
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));
142 }
143 replacements.push((*obj, replace))
144 }
145 Err(err) => errors.push((*obj, err)),
146 }
147 }
148 }
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())),
154 }
155 }
156 }
157 }
158
159 handle_errors_and_replacements(&mut self.err, objs, errors, &mut replacements)
160 }
161
162 fn find(&mut self, regex: &BStr, negated: bool) -> Option<()> {
163 self.unset_disambiguate_call();
164 self.follow_refs_to_objects_if_needed()?;
165
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()) {
170 Ok(compiled) => {
171 let needs_regex = regex::escape(compiled.as_str()) != regex;
172 move |message: &BStr| -> bool {
173 if needs_regex {
174 compiled.is_match(message) ^ negated
175 } else {
176 message.contains_str(regex) ^ negated
177 }
178 }
179 }
180 Err(err) => {
181 self.err.push(err.into());
182 return None;
183 }
184 };
185
186 match self.objs[self.idx].as_mut() {
187 Some(objs) => {
188 let repo = self.repo;
189 let mut errors = Vec::new();
190 let mut replacements = Replacements::default();
191 for oid in objs.iter() {
192 match oid
193 .attach(repo)
194 .ancestors()
195 .sorting(Sorting::ByCommitTimeNewestFirst)
196 .all()
197 {
198 Ok(iter) => {
199 let mut matched = false;
200 let mut count = 0;
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)
204 })
205 });
206 for commit in commits {
207 count += 1;
208 match commit {
209 Ok(commit) => {
210 if matches(commit.message_raw_sloppy()) {
211 replacements.push((*oid, commit.id));
212 matched = true;
213 break;
214 }
215 }
216 Err(err) => errors.push((*oid, err)),
217 }
218 }
219 if !matched {
220 errors.push((
221 *oid,
222 Error::NoRegexMatch {
223 regex: regex.into(),
224 commits_searched: count,
225 oid: oid.attach(repo).shorten_or_id(),
226 },
227 ))
228 }
229 }
230 Err(err) => errors.push((*oid, err.into())),
231 }
232 }
233 handle_errors_and_replacements(&mut self.err, objs, errors, &mut replacements)
234 }
235 None => match self.repo.references() {
236 Ok(references) => match references.all() {
237 Ok(references) => {
238 match self
239 .repo
240 .rev_walk(
241 references
242 .peeled()
243 .filter_map(Result::ok)
244 .filter(|r| {
245 r.id()
246 .object()
247 .ok()
248 .map_or(false, |obj| obj.kind == gix_object::Kind::Commit)
249 })
250 .filter_map(|r| r.detach().peeled),
251 )
252 .sorting(Sorting::ByCommitTimeNewestFirst)
253 .all()
254 {
255 Ok(iter) => {
256 let mut matched = false;
257 let mut count = 0;
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)
261 })
262 });
263 for commit in commits {
264 count += 1;
265 match commit {
266 Ok(commit) => {
267 if matches(commit.message_raw_sloppy()) {
268 self.objs[self.idx]
269 .get_or_insert_with(HashSet::default)
270 .insert(commit.id);
271 matched = true;
272 break;
273 }
274 }
275 Err(err) => self.err.push(err),
276 }
277 }
278 if matched {
279 Some(())
280 } else {
281 self.err.push(Error::NoRegexMatchAllRefs {
282 regex: regex.into(),
283 commits_searched: count,
284 });
285 None
286 }
287 }
288 Err(err) => {
289 self.err.push(err.into());
290 None
291 }
292 }
293 }
294 Err(err) => {
295 self.err.push(err.into());
296 None
297 }
298 },
299 Err(err) => {
300 self.err.push(err.into());
301 None
302 }
303 },
304 }
305 }
306
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()) {
311 Some(entry) => {
312 self.objs[self.idx]
313 .get_or_insert_with(HashSet::default)
314 .insert(entry.id);
315
316 self.paths[self.idx] = Some((
317 path.to_owned(),
318 entry
319 .mode
320 .to_tree_entry_mode()
321 .unwrap_or(gix_object::tree::EntryKind::Blob.into()),
322 ));
323 Some(())
324 }
325 None => {
326 let stage_hint = [0, 1, 2]
327 .iter()
328 .filter(|our_stage| **our_stage != stage)
329 .find_map(|stage| {
330 index
331 .entry_index_by_path_and_stage(path, (*stage).into())
332 .map(|_| (*stage).into())
333 });
334 let exists = self
335 .repo
336 .work_dir()
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(),
341 exists,
342 stage_hint,
343 });
344 None
345 }
346 },
347 Err(err) => {
348 self.err.push(err.into());
349 None
350 }
351 }
352 }
353 }