##// END OF EJS Templates
rust-status: improve status performance...
Raphaël Gomès -
r44000:889ac87e default
parent child Browse files
Show More
@@ -10,22 +10,120 b''
10 //! and will only be triggered in narrow cases.
10 //! and will only be triggered in narrow cases.
11
11
12 use crate::utils::files::HgMetadata;
12 use crate::utils::files::HgMetadata;
13 use crate::utils::hg_path::{hg_path_to_path_buf, HgPathBuf};
13 use crate::utils::hg_path::{hg_path_to_path_buf, HgPath};
14 use crate::{DirstateEntry, DirstateMap, EntryState};
14 use crate::{CopyMap, DirstateEntry, DirstateMap, EntryState};
15 use rayon::prelude::*;
15 use rayon::prelude::*;
16 use std::path::Path;
16 use std::path::Path;
17
17
18 // Stat all entries in the `DirstateMap` and return their new metadata.
18 /// Marker enum used to dispatch new status entries into the right collections.
19 pub fn stat_dmap_entries(
19 /// Is similar to `crate::EntryState`, but represents the transient state of
20 /// entries during the lifetime of a command.
21 enum Dispatch {
22 Unsure,
23 Modified,
24 Added,
25 Removed,
26 Deleted,
27 Clean,
28 Unknown,
29 }
30
31 /// The file corresponding to the dirstate entry was found on the filesystem.
32 fn dispatch_found(
33 filename: impl AsRef<HgPath>,
34 entry: DirstateEntry,
35 metadata: HgMetadata,
36 copy_map: &CopyMap,
37 check_exec: bool,
38 list_clean: bool,
39 last_normal_time: i64,
40 ) -> Dispatch {
41 let DirstateEntry {
42 state,
43 mode,
44 mtime,
45 size,
46 } = entry;
47
48 let HgMetadata {
49 st_mode,
50 st_size,
51 st_mtime,
52 ..
53 } = metadata;
54
55 match state {
56 EntryState::Normal => {
57 // Dates and times that are outside the 31-bit signed
58 // range are compared modulo 2^31. This should prevent
59 // it from behaving badly with very large files or
60 // corrupt dates while still having a high probability
61 // of detecting changes. (issue2608)
62 let range_mask = 0x7fffffff;
63
64 let size_changed = (size != st_size as i32)
65 && size != (st_size as i32 & range_mask);
66 let mode_changed =
67 (mode ^ st_mode as i32) & 0o100 != 0o000 && check_exec;
68 if size >= 0
69 && (size_changed || mode_changed)
70 || size == -2 // other parent
71 || copy_map.contains_key(filename.as_ref())
72 {
73 Dispatch::Modified
74 } else if mtime != st_mtime as i32
75 && mtime != (st_mtime as i32 & range_mask)
76 {
77 Dispatch::Unsure
78 } else if st_mtime == last_normal_time {
79 // the file may have just been marked as normal and
80 // it may have changed in the same second without
81 // changing its size. This can happen if we quickly
82 // do multiple commits. Force lookup, so we don't
83 // miss such a racy file change.
84 Dispatch::Unsure
85 } else if list_clean {
86 Dispatch::Clean
87 } else {
88 Dispatch::Unknown
89 }
90 }
91 EntryState::Merged => Dispatch::Modified,
92 EntryState::Added => Dispatch::Added,
93 EntryState::Removed => Dispatch::Removed,
94 EntryState::Unknown => Dispatch::Unknown,
95 }
96 }
97
98 /// The file corresponding to this Dirstate entry is missing.
99 fn dispatch_missing(state: EntryState) -> Dispatch {
100 match state {
101 // File was removed from the filesystem during commands
102 EntryState::Normal | EntryState::Merged | EntryState::Added => {
103 Dispatch::Deleted
104 }
105 // File was removed, everything is normal
106 EntryState::Removed => Dispatch::Removed,
107 // File is unknown to Mercurial, everything is normal
108 EntryState::Unknown => Dispatch::Unknown,
109 }
110 }
111
112 /// Stat all entries in the `DirstateMap` and mark them for dispatch into
113 /// the relevant collections.
114 fn stat_dmap_entries(
20 dmap: &DirstateMap,
115 dmap: &DirstateMap,
21 root_dir: impl AsRef<Path> + Sync,
116 root_dir: impl AsRef<Path> + Sync,
22 ) -> std::io::Result<Vec<(HgPathBuf, Option<HgMetadata>)>> {
117 check_exec: bool,
118 list_clean: bool,
119 last_normal_time: i64,
120 ) -> std::io::Result<Vec<(&HgPath, Dispatch)>> {
23 dmap.par_iter()
121 dmap.par_iter()
24 .filter_map(
122 .filter_map(
25 // Getting file metadata is costly, so we don't do it if the
123 // Getting file metadata is costly, so we don't do it if the
26 // file is already present in the results, hence `filter_map`
124 // file is already present in the results, hence `filter_map`
27 |(filename, _)| -> Option<
125 |(filename, entry)| -> Option<
28 std::io::Result<(HgPathBuf, Option<HgMetadata>)>
126 std::io::Result<(&HgPath, Dispatch)>
29 > {
127 > {
30 let meta = match hg_path_to_path_buf(filename) {
128 let meta = match hg_path_to_path_buf(filename) {
31 Ok(p) => root_dir.as_ref().join(p).symlink_metadata(),
129 Ok(p) => root_dir.as_ref().join(p).symlink_metadata(),
@@ -37,12 +135,16 b' pub fn stat_dmap_entries('
37 if !(m.file_type().is_file()
135 if !(m.file_type().is_file()
38 || m.file_type().is_symlink()) =>
136 || m.file_type().is_symlink()) =>
39 {
137 {
40 Ok((filename.to_owned(), None))
138 Ok((filename, dispatch_missing(entry.state)))
41 }
139 }
42 Ok(m) => Ok((
140 Ok(m) => Ok((filename, dispatch_found(
43 filename.to_owned(),
141 filename,
44 Some(HgMetadata::from_metadata(m)),
142 *entry,
45 )),
143 HgMetadata::from_metadata(m),
144 &dmap.copy_map,
145 check_exec,
146 list_clean,
147 last_normal_time))),
46 Err(ref e)
148 Err(ref e)
47 if e.kind() == std::io::ErrorKind::NotFound
149 if e.kind() == std::io::ErrorKind::NotFound
48 || e.raw_os_error() == Some(20) =>
150 || e.raw_os_error() == Some(20) =>
@@ -51,7 +153,7 b' pub fn stat_dmap_entries('
51 // `NotADirectory` (errno 20)
153 // `NotADirectory` (errno 20)
52 // It happens if the dirstate contains `foo/bar` and
154 // It happens if the dirstate contains `foo/bar` and
53 // foo is not a directory
155 // foo is not a directory
54 Ok((filename.to_owned(), None))
156 Ok((filename, dispatch_missing(entry.state)))
55 }
157 }
56 Err(e) => Err(e),
158 Err(e) => Err(e),
57 })
159 })
@@ -60,23 +162,19 b' pub fn stat_dmap_entries('
60 .collect()
162 .collect()
61 }
163 }
62
164
63 pub struct StatusResult {
165 pub struct StatusResult<'a> {
64 pub modified: Vec<HgPathBuf>,
166 pub modified: Vec<&'a HgPath>,
65 pub added: Vec<HgPathBuf>,
167 pub added: Vec<&'a HgPath>,
66 pub removed: Vec<HgPathBuf>,
168 pub removed: Vec<&'a HgPath>,
67 pub deleted: Vec<HgPathBuf>,
169 pub deleted: Vec<&'a HgPath>,
68 pub clean: Vec<HgPathBuf>,
170 pub clean: Vec<&'a HgPath>,
69 // TODO ignored
171 // TODO ignored
70 // TODO unknown
172 // TODO unknown
71 }
173 }
72
174
73 fn build_response(
175 fn build_response(
74 dmap: &DirstateMap,
176 results: Vec<(&HgPath, Dispatch)>,
75 list_clean: bool,
177 ) -> (Vec<&HgPath>, StatusResult) {
76 last_normal_time: i64,
77 check_exec: bool,
78 results: Vec<(HgPathBuf, Option<HgMetadata>)>,
79 ) -> (Vec<HgPathBuf>, StatusResult) {
80 let mut lookup = vec![];
178 let mut lookup = vec![];
81 let mut modified = vec![];
179 let mut modified = vec![];
82 let mut added = vec![];
180 let mut added = vec![];
@@ -84,76 +182,15 b' fn build_response('
84 let mut deleted = vec![];
182 let mut deleted = vec![];
85 let mut clean = vec![];
183 let mut clean = vec![];
86
184
87 for (filename, metadata_option) in results.into_iter() {
185 for (filename, dispatch) in results.into_iter() {
88 let DirstateEntry {
186 match dispatch {
89 state,
187 Dispatch::Unknown => {}
90 mode,
188 Dispatch::Unsure => lookup.push(filename),
91 mtime,
189 Dispatch::Modified => modified.push(filename),
92 size,
190 Dispatch::Added => added.push(filename),
93 } = match dmap.get(&filename) {
191 Dispatch::Removed => removed.push(filename),
94 None => {
192 Dispatch::Deleted => deleted.push(filename),
95 continue;
193 Dispatch::Clean => clean.push(filename),
96 }
97 Some(e) => *e,
98 };
99
100 match metadata_option {
101 None => {
102 match state {
103 EntryState::Normal
104 | EntryState::Merged
105 | EntryState::Added => deleted.push(filename),
106 EntryState::Removed => removed.push(filename),
107 _ => {}
108 };
109 }
110 Some(HgMetadata {
111 st_mode,
112 st_size,
113 st_mtime,
114 ..
115 }) => {
116 match state {
117 EntryState::Normal => {
118 // Dates and times that are outside the 31-bit signed
119 // range are compared modulo 2^31. This should prevent
120 // it from behaving badly with very large files or
121 // corrupt dates while still having a high probability
122 // of detecting changes. (issue2608)
123 let range_mask = 0x7fffffff;
124
125 let size_changed = (size != st_size as i32)
126 && size != (st_size as i32 & range_mask);
127 let mode_changed = (mode ^ st_mode as i32) & 0o100
128 != 0o000
129 && check_exec;
130 if size >= 0
131 && (size_changed || mode_changed)
132 || size == -2 // other parent
133 || dmap.copy_map.contains_key(&filename)
134 {
135 modified.push(filename);
136 } else if mtime != st_mtime as i32
137 && mtime != (st_mtime as i32 & range_mask)
138 {
139 lookup.push(filename);
140 } else if st_mtime == last_normal_time {
141 // the file may have just been marked as normal and
142 // it may have changed in the same second without
143 // changing its size. This can happen if we quickly
144 // do multiple commits. Force lookup, so we don't
145 // miss such a racy file change.
146 lookup.push(filename);
147 } else if list_clean {
148 clean.push(filename);
149 }
150 }
151 EntryState::Merged => modified.push(filename),
152 EntryState::Added => added.push(filename),
153 EntryState::Removed => removed.push(filename),
154 EntryState::Unknown => {}
155 }
156 }
157 }
194 }
158 }
195 }
159
196
@@ -175,14 +212,14 b' pub fn status('
175 list_clean: bool,
212 list_clean: bool,
176 last_normal_time: i64,
213 last_normal_time: i64,
177 check_exec: bool,
214 check_exec: bool,
178 ) -> std::io::Result<(Vec<HgPathBuf>, StatusResult)> {
215 ) -> std::io::Result<(Vec<&HgPath>, StatusResult)> {
179 let results = stat_dmap_entries(&dmap, root_dir)?;
216 let results = stat_dmap_entries(
180
181 Ok(build_response(
182 &dmap,
217 &dmap,
218 root_dir,
219 check_exec,
183 list_clean,
220 list_clean,
184 last_normal_time,
221 last_normal_time,
185 check_exec,
222 )?;
186 results,
223
187 ))
224 Ok(build_response(results))
188 }
225 }
General Comments 0
You need to be logged in to leave comments. Login now