Show More
@@ -11,18 +11,21 b'' | |||||
11 |
|
11 | |||
12 | use crate::{ |
|
12 | use crate::{ | |
13 | dirstate::SIZE_FROM_OTHER_PARENT, |
|
13 | dirstate::SIZE_FROM_OTHER_PARENT, | |
14 | matchers::Matcher, |
|
14 | matchers::{Matcher, VisitChildrenSet}, | |
15 | utils::{ |
|
15 | utils::{ | |
16 | files::HgMetadata, |
|
16 | files::HgMetadata, | |
17 | hg_path::{ |
|
17 | hg_path::{ | |
18 | hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf, |
|
18 | hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf, | |
19 | }, |
|
19 | }, | |
20 | }, |
|
20 | }, | |
21 | CopyMap, DirstateEntry, DirstateMap, EntryState, |
|
21 | CopyMap, DirstateEntry, DirstateMap, EntryState, FastHashMap, | |
22 | }; |
|
22 | }; | |
23 | use rayon::prelude::*; |
|
23 | use rayon::prelude::*; | |
24 | use std::collections::HashSet; |
|
24 | use std::borrow::Cow; | |
|
25 | use std::collections::{HashSet, VecDeque}; | |||
25 | use std::fs::{read_dir, DirEntry}; |
|
26 | use std::fs::{read_dir, DirEntry}; | |
|
27 | use std::io::ErrorKind; | |||
|
28 | use std::ops::Deref; | |||
26 | use std::path::Path; |
|
29 | use std::path::Path; | |
27 |
|
30 | |||
28 | /// Wrong type of file from a `BadMatch` |
|
31 | /// Wrong type of file from a `BadMatch` | |
@@ -238,6 +241,178 b' pub struct StatusOptions {' | |||||
238 | /// Whether we are on a filesystem with UNIX-like exec flags |
|
241 | /// Whether we are on a filesystem with UNIX-like exec flags | |
239 | pub check_exec: bool, |
|
242 | pub check_exec: bool, | |
240 | pub list_clean: bool, |
|
243 | pub list_clean: bool, | |
|
244 | pub list_unknown: bool, | |||
|
245 | pub list_ignored: bool, | |||
|
246 | } | |||
|
247 | ||||
|
248 | /// Dispatch a single file found during `traverse`. | |||
|
249 | /// If `file` is a folder that needs to be traversed, it will be pushed into | |||
|
250 | /// `work`. | |||
|
251 | fn traverse_worker<'a>( | |||
|
252 | work: &mut VecDeque<HgPathBuf>, | |||
|
253 | matcher: &impl Matcher, | |||
|
254 | dmap: &DirstateMap, | |||
|
255 | filename: impl AsRef<HgPath>, | |||
|
256 | dir_entry: &DirEntry, | |||
|
257 | ignore_fn: &impl for<'r> Fn(&'r HgPath) -> bool, | |||
|
258 | dir_ignore_fn: &impl for<'r> Fn(&'r HgPath) -> bool, | |||
|
259 | options: StatusOptions, | |||
|
260 | ) -> Option<IoResult<(Cow<'a, HgPath>, Dispatch)>> { | |||
|
261 | let file_type = match dir_entry.file_type() { | |||
|
262 | Ok(x) => x, | |||
|
263 | Err(e) => return Some(Err(e.into())), | |||
|
264 | }; | |||
|
265 | let filename = filename.as_ref(); | |||
|
266 | let entry_option = dmap.get(filename); | |||
|
267 | ||||
|
268 | if file_type.is_dir() { | |||
|
269 | // Do we need to traverse it? | |||
|
270 | if !ignore_fn(&filename) || options.list_ignored { | |||
|
271 | work.push_front(filename.to_owned()); | |||
|
272 | } | |||
|
273 | // Nested `if` until `rust-lang/rust#53668` is stable | |||
|
274 | if let Some(entry) = entry_option { | |||
|
275 | // Used to be a file, is now a folder | |||
|
276 | if matcher.matches_everything() || matcher.matches(&filename) { | |||
|
277 | return Some(Ok(( | |||
|
278 | Cow::Owned(filename.to_owned()), | |||
|
279 | dispatch_missing(entry.state), | |||
|
280 | ))); | |||
|
281 | } | |||
|
282 | } | |||
|
283 | } else if file_type.is_file() || file_type.is_symlink() { | |||
|
284 | if let Some(entry) = entry_option { | |||
|
285 | if matcher.matches_everything() || matcher.matches(&filename) { | |||
|
286 | let metadata = match dir_entry.metadata() { | |||
|
287 | Ok(x) => x, | |||
|
288 | Err(e) => return Some(Err(e.into())), | |||
|
289 | }; | |||
|
290 | return Some(Ok(( | |||
|
291 | Cow::Owned(filename.to_owned()), | |||
|
292 | dispatch_found( | |||
|
293 | &filename, | |||
|
294 | *entry, | |||
|
295 | HgMetadata::from_metadata(metadata), | |||
|
296 | &dmap.copy_map, | |||
|
297 | options, | |||
|
298 | ), | |||
|
299 | ))); | |||
|
300 | } | |||
|
301 | } else if (matcher.matches_everything() || matcher.matches(&filename)) | |||
|
302 | && !ignore_fn(&filename) | |||
|
303 | { | |||
|
304 | if (options.list_ignored || matcher.exact_match(&filename)) | |||
|
305 | && dir_ignore_fn(&filename) | |||
|
306 | { | |||
|
307 | if options.list_ignored { | |||
|
308 | return Some(Ok(( | |||
|
309 | Cow::Owned(filename.to_owned()), | |||
|
310 | Dispatch::Ignored, | |||
|
311 | ))); | |||
|
312 | } | |||
|
313 | } else { | |||
|
314 | return Some(Ok(( | |||
|
315 | Cow::Owned(filename.to_owned()), | |||
|
316 | Dispatch::Unknown, | |||
|
317 | ))); | |||
|
318 | } | |||
|
319 | } | |||
|
320 | } else if let Some(entry) = entry_option { | |||
|
321 | // Used to be a file or a folder, now something else. | |||
|
322 | if matcher.matches_everything() || matcher.matches(&filename) { | |||
|
323 | return Some(Ok(( | |||
|
324 | Cow::Owned(filename.to_owned()), | |||
|
325 | dispatch_missing(entry.state), | |||
|
326 | ))); | |||
|
327 | } | |||
|
328 | } | |||
|
329 | None | |||
|
330 | } | |||
|
331 | ||||
|
332 | /// Walk the working directory recursively to look for changes compared to the | |||
|
333 | /// current `DirstateMap`. | |||
|
334 | fn traverse<'a>( | |||
|
335 | matcher: &(impl Matcher + Sync), | |||
|
336 | root_dir: impl AsRef<Path>, | |||
|
337 | dmap: &DirstateMap, | |||
|
338 | path: impl AsRef<HgPath>, | |||
|
339 | old_results: FastHashMap<Cow<'a, HgPath>, Dispatch>, | |||
|
340 | ignore_fn: &(impl for<'r> Fn(&'r HgPath) -> bool + Sync), | |||
|
341 | dir_ignore_fn: &(impl for<'r> Fn(&'r HgPath) -> bool + Sync), | |||
|
342 | options: StatusOptions, | |||
|
343 | ) -> IoResult<FastHashMap<Cow<'a, HgPath>, Dispatch>> { | |||
|
344 | let root_dir = root_dir.as_ref(); | |||
|
345 | let mut new_results = FastHashMap::default(); | |||
|
346 | ||||
|
347 | let mut work = VecDeque::new(); | |||
|
348 | work.push_front(path.as_ref().to_owned()); | |||
|
349 | ||||
|
350 | while let Some(ref directory) = work.pop_front() { | |||
|
351 | if directory.as_bytes() == b".hg" { | |||
|
352 | continue; | |||
|
353 | } | |||
|
354 | let visit_entries = match matcher.visit_children_set(directory) { | |||
|
355 | VisitChildrenSet::Empty => continue, | |||
|
356 | VisitChildrenSet::This | VisitChildrenSet::Recursive => None, | |||
|
357 | VisitChildrenSet::Set(set) => Some(set), | |||
|
358 | }; | |||
|
359 | let buf = hg_path_to_path_buf(directory)?; | |||
|
360 | let dir_path = root_dir.join(buf); | |||
|
361 | ||||
|
362 | let skip_dot_hg = !directory.as_bytes().is_empty(); | |||
|
363 | let entries = match list_directory(dir_path, skip_dot_hg) { | |||
|
364 | Err(e) => match e.kind() { | |||
|
365 | ErrorKind::NotFound | ErrorKind::PermissionDenied => { | |||
|
366 | new_results.insert( | |||
|
367 | Cow::Owned(directory.to_owned()), | |||
|
368 | Dispatch::Bad(BadMatch::OsError( | |||
|
369 | // Unwrapping here is OK because the error always | |||
|
370 | // is a real os error | |||
|
371 | e.raw_os_error().unwrap(), | |||
|
372 | )), | |||
|
373 | ); | |||
|
374 | continue; | |||
|
375 | } | |||
|
376 | _ => return Err(e), | |||
|
377 | }, | |||
|
378 | Ok(entries) => entries, | |||
|
379 | }; | |||
|
380 | ||||
|
381 | for (filename, dir_entry) in entries { | |||
|
382 | if let Some(ref set) = visit_entries { | |||
|
383 | if !set.contains(filename.deref()) { | |||
|
384 | continue; | |||
|
385 | } | |||
|
386 | } | |||
|
387 | // TODO normalize | |||
|
388 | let filename = if directory.is_empty() { | |||
|
389 | filename.to_owned() | |||
|
390 | } else { | |||
|
391 | directory.join(&filename) | |||
|
392 | }; | |||
|
393 | ||||
|
394 | if !old_results.contains_key(filename.deref()) { | |||
|
395 | if let Some((res, dispatch)) = traverse_worker( | |||
|
396 | &mut work, | |||
|
397 | matcher, | |||
|
398 | &dmap, | |||
|
399 | &filename, | |||
|
400 | &dir_entry, | |||
|
401 | &ignore_fn, | |||
|
402 | &dir_ignore_fn, | |||
|
403 | options, | |||
|
404 | ) | |||
|
405 | .transpose()? | |||
|
406 | { | |||
|
407 | new_results.insert(res, dispatch); | |||
|
408 | } | |||
|
409 | } | |||
|
410 | } | |||
|
411 | } | |||
|
412 | ||||
|
413 | new_results.extend(old_results.into_iter()); | |||
|
414 | ||||
|
415 | Ok(new_results) | |||
241 | } |
|
416 | } | |
242 |
|
417 | |||
243 | /// Stat all entries in the `DirstateMap` and mark them for dispatch into |
|
418 | /// Stat all entries in the `DirstateMap` and mark them for dispatch into |
General Comments 0
You need to be logged in to leave comments.
Login now