Mercurial > public > mercurial-scm > hg
comparison rust/hg-core/src/dirstate_tree/status.rs @ 47333:69530e5d4fe5
dirstate-tree: Add `NodeRef` and `ChildNodesRef` enums
They are used instead of `&Node` and `&ChildNodes` respectively.
The `ChildNodes` type alias also becomes a similar enum.
For now they only have one variant each, to be extended later.
Adding enums now forces various use sites go through new methods
instead of manipulating the underlying data structure directly.
Differential Revision: https://phab.mercurial-scm.org/D10747
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Wed, 19 May 2021 13:15:00 +0200 |
parents | 0252600fd1cf |
children | ed1583a845d2 |
comparison
equal
deleted
inserted
replaced
47332:4ee9f419c52e | 47333:69530e5d4fe5 |
---|---|
1 use crate::dirstate::status::IgnoreFnType; | 1 use crate::dirstate::status::IgnoreFnType; |
2 use crate::dirstate_tree::dirstate_map::ChildNodes; | 2 use crate::dirstate_tree::dirstate_map::ChildNodesRef; |
3 use crate::dirstate_tree::dirstate_map::DirstateMap; | 3 use crate::dirstate_tree::dirstate_map::DirstateMap; |
4 use crate::dirstate_tree::dirstate_map::Node; | 4 use crate::dirstate_tree::dirstate_map::NodeRef; |
5 use crate::matchers::get_ignore_function; | 5 use crate::matchers::get_ignore_function; |
6 use crate::matchers::Matcher; | 6 use crate::matchers::Matcher; |
7 use crate::utils::files::get_bytes_from_os_string; | 7 use crate::utils::files::get_bytes_from_os_string; |
8 use crate::utils::hg_path::HgPath; | 8 use crate::utils::hg_path::HgPath; |
9 use crate::BadMatch; | 9 use crate::BadMatch; |
54 let is_at_repo_root = true; | 54 let is_at_repo_root = true; |
55 let hg_path = HgPath::new(""); | 55 let hg_path = HgPath::new(""); |
56 let has_ignored_ancestor = false; | 56 let has_ignored_ancestor = false; |
57 common.traverse_fs_directory_and_dirstate( | 57 common.traverse_fs_directory_and_dirstate( |
58 has_ignored_ancestor, | 58 has_ignored_ancestor, |
59 &dmap.root, | 59 dmap.root.as_ref(), |
60 hg_path, | 60 hg_path, |
61 &root_dir, | 61 &root_dir, |
62 is_at_repo_root, | 62 is_at_repo_root, |
63 ); | 63 ); |
64 Ok((common.outcome.into_inner().unwrap(), warnings)) | 64 Ok((common.outcome.into_inner().unwrap(), warnings)) |
91 } | 91 } |
92 | 92 |
93 fn traverse_fs_directory_and_dirstate( | 93 fn traverse_fs_directory_and_dirstate( |
94 &self, | 94 &self, |
95 has_ignored_ancestor: bool, | 95 has_ignored_ancestor: bool, |
96 dirstate_nodes: &'tree ChildNodes, | 96 dirstate_nodes: ChildNodesRef<'tree, '_>, |
97 directory_hg_path: &'tree HgPath, | 97 directory_hg_path: &'tree HgPath, |
98 directory_fs_path: &Path, | 98 directory_fs_path: &Path, |
99 is_at_repo_root: bool, | 99 is_at_repo_root: bool, |
100 ) { | 100 ) { |
101 let mut fs_entries = if let Ok(entries) = self.read_dir( | 101 let mut fs_entries = if let Ok(entries) = self.read_dir( |
108 return; | 108 return; |
109 }; | 109 }; |
110 | 110 |
111 // `merge_join_by` requires both its input iterators to be sorted: | 111 // `merge_join_by` requires both its input iterators to be sorted: |
112 | 112 |
113 let dirstate_nodes = Node::sorted(dirstate_nodes); | 113 let dirstate_nodes = dirstate_nodes.sorted(); |
114 // `sort_unstable_by_key` doesn’t allow keys borrowing from the value: | 114 // `sort_unstable_by_key` doesn’t allow keys borrowing from the value: |
115 // https://github.com/rust-lang/rust/issues/34162 | 115 // https://github.com/rust-lang/rust/issues/34162 |
116 fs_entries.sort_unstable_by(|e1, e2| e1.base_name.cmp(&e2.base_name)); | 116 fs_entries.sort_unstable_by(|e1, e2| e1.base_name.cmp(&e2.base_name)); |
117 | 117 |
118 itertools::merge_join_by( | 118 itertools::merge_join_by( |
119 dirstate_nodes, | 119 dirstate_nodes, |
120 &fs_entries, | 120 &fs_entries, |
121 |(full_path, _node), fs_entry| { | 121 |dirstate_node, fs_entry| { |
122 full_path.base_name().cmp(&fs_entry.base_name) | 122 dirstate_node.base_name().cmp(&fs_entry.base_name) |
123 }, | 123 }, |
124 ) | 124 ) |
125 .par_bridge() | 125 .par_bridge() |
126 .for_each(|pair| { | 126 .for_each(|pair| { |
127 use itertools::EitherOrBoth::*; | 127 use itertools::EitherOrBoth::*; |
128 match pair { | 128 match pair { |
129 Both((hg_path, dirstate_node), fs_entry) => { | 129 Both(dirstate_node, fs_entry) => { |
130 self.traverse_fs_and_dirstate( | 130 self.traverse_fs_and_dirstate( |
131 fs_entry, | 131 fs_entry, |
132 hg_path.full_path(), | |
133 dirstate_node, | 132 dirstate_node, |
134 has_ignored_ancestor, | 133 has_ignored_ancestor, |
135 ); | 134 ); |
136 } | 135 } |
137 Left((hg_path, dirstate_node)) => self.traverse_dirstate_only( | 136 Left(dirstate_node) => { |
138 hg_path.full_path(), | 137 self.traverse_dirstate_only(dirstate_node) |
139 dirstate_node, | 138 } |
140 ), | |
141 Right(fs_entry) => self.traverse_fs_only( | 139 Right(fs_entry) => self.traverse_fs_only( |
142 has_ignored_ancestor, | 140 has_ignored_ancestor, |
143 directory_hg_path, | 141 directory_hg_path, |
144 fs_entry, | 142 fs_entry, |
145 ), | 143 ), |
148 } | 146 } |
149 | 147 |
150 fn traverse_fs_and_dirstate( | 148 fn traverse_fs_and_dirstate( |
151 &self, | 149 &self, |
152 fs_entry: &DirEntry, | 150 fs_entry: &DirEntry, |
153 hg_path: &'tree HgPath, | 151 dirstate_node: NodeRef<'tree, '_>, |
154 dirstate_node: &'tree Node, | |
155 has_ignored_ancestor: bool, | 152 has_ignored_ancestor: bool, |
156 ) { | 153 ) { |
154 let hg_path = dirstate_node.full_path(); | |
157 let file_type = fs_entry.metadata.file_type(); | 155 let file_type = fs_entry.metadata.file_type(); |
158 let file_or_symlink = file_type.is_file() || file_type.is_symlink(); | 156 let file_or_symlink = file_type.is_file() || file_type.is_symlink(); |
159 if !file_or_symlink { | 157 if !file_or_symlink { |
160 // If we previously had a file here, it was removed (with | 158 // If we previously had a file here, it was removed (with |
161 // `hg rm` or similar) or deleted before it could be | 159 // `hg rm` or similar) or deleted before it could be |
162 // replaced by a directory or something else. | 160 // replaced by a directory or something else. |
163 self.mark_removed_or_deleted_if_file( | 161 self.mark_removed_or_deleted_if_file( |
164 hg_path, | 162 dirstate_node.full_path(), |
165 dirstate_node.state(), | 163 dirstate_node.state(), |
166 ); | 164 ); |
167 } | 165 } |
168 if file_type.is_dir() { | 166 if file_type.is_dir() { |
169 if self.options.collect_traversed_dirs { | 167 if self.options.collect_traversed_dirs { |
171 } | 169 } |
172 let is_ignored = has_ignored_ancestor || (self.ignore_fn)(hg_path); | 170 let is_ignored = has_ignored_ancestor || (self.ignore_fn)(hg_path); |
173 let is_at_repo_root = false; | 171 let is_at_repo_root = false; |
174 self.traverse_fs_directory_and_dirstate( | 172 self.traverse_fs_directory_and_dirstate( |
175 is_ignored, | 173 is_ignored, |
176 &dirstate_node.children, | 174 dirstate_node.children(), |
177 hg_path, | 175 hg_path, |
178 &fs_entry.full_path, | 176 &fs_entry.full_path, |
179 is_at_repo_root, | 177 is_at_repo_root, |
180 ); | 178 ); |
181 } else { | 179 } else { |
182 if file_or_symlink && self.matcher.matches(hg_path) { | 180 if file_or_symlink && self.matcher.matches(hg_path) { |
183 let full_path = Cow::from(hg_path); | 181 let full_path = Cow::from(hg_path); |
184 if let Some(entry) = &dirstate_node.entry { | 182 if let Some(state) = dirstate_node.state() { |
185 match entry.state { | 183 match state { |
186 EntryState::Added => { | 184 EntryState::Added => { |
187 self.outcome.lock().unwrap().added.push(full_path) | 185 self.outcome.lock().unwrap().added.push(full_path) |
188 } | 186 } |
189 EntryState::Removed => self | 187 EntryState::Removed => self |
190 .outcome | 188 .outcome |
197 .lock() | 195 .lock() |
198 .unwrap() | 196 .unwrap() |
199 .modified | 197 .modified |
200 .push(full_path), | 198 .push(full_path), |
201 EntryState::Normal => { | 199 EntryState::Normal => { |
202 self.handle_normal_file( | 200 self.handle_normal_file(&dirstate_node, fs_entry); |
203 full_path, | |
204 dirstate_node, | |
205 entry, | |
206 fs_entry, | |
207 ); | |
208 } | 201 } |
209 // This variant is not used in DirstateMap | 202 // This variant is not used in DirstateMap |
210 // nodes | 203 // nodes |
211 EntryState::Unknown => unreachable!(), | 204 EntryState::Unknown => unreachable!(), |
212 } | 205 } |
218 full_path, | 211 full_path, |
219 ) | 212 ) |
220 } | 213 } |
221 } | 214 } |
222 | 215 |
223 for (child_hg_path, child_node) in &dirstate_node.children { | 216 for child_node in dirstate_node.children().iter() { |
224 self.traverse_dirstate_only( | 217 self.traverse_dirstate_only(child_node) |
225 child_hg_path.full_path(), | |
226 child_node, | |
227 ) | |
228 } | 218 } |
229 } | 219 } |
230 } | 220 } |
231 | 221 |
232 /// A file with `EntryState::Normal` in the dirstate was found in the | 222 /// A file with `EntryState::Normal` in the dirstate was found in the |
233 /// filesystem | 223 /// filesystem |
234 fn handle_normal_file( | 224 fn handle_normal_file( |
235 &self, | 225 &self, |
236 full_path: Cow<'tree, HgPath>, | 226 dirstate_node: &NodeRef<'tree, '_>, |
237 dirstate_node: &Node, | |
238 entry: &crate::DirstateEntry, | |
239 fs_entry: &DirEntry, | 227 fs_entry: &DirEntry, |
240 ) { | 228 ) { |
241 // Keep the low 31 bits | 229 // Keep the low 31 bits |
242 fn truncate_u64(value: u64) -> i32 { | 230 fn truncate_u64(value: u64) -> i32 { |
243 (value & 0x7FFF_FFFF) as i32 | 231 (value & 0x7FFF_FFFF) as i32 |
244 } | 232 } |
245 fn truncate_i64(value: i64) -> i32 { | 233 fn truncate_i64(value: i64) -> i32 { |
246 (value & 0x7FFF_FFFF) as i32 | 234 (value & 0x7FFF_FFFF) as i32 |
247 } | 235 } |
248 | 236 |
237 let entry = dirstate_node | |
238 .entry() | |
239 .expect("handle_normal_file called with entry-less node"); | |
240 let full_path = Cow::from(dirstate_node.full_path()); | |
249 let mode_changed = || { | 241 let mode_changed = || { |
250 self.options.check_exec && entry.mode_changed(&fs_entry.metadata) | 242 self.options.check_exec && entry.mode_changed(&fs_entry.metadata) |
251 }; | 243 }; |
252 let size_changed = entry.size != truncate_u64(fs_entry.metadata.len()); | 244 let size_changed = entry.size != truncate_u64(fs_entry.metadata.len()); |
253 if entry.size >= 0 | 245 if entry.size >= 0 |
255 && fs_entry.metadata.file_type().is_symlink() | 247 && fs_entry.metadata.file_type().is_symlink() |
256 { | 248 { |
257 // issue6456: Size returned may be longer due to encryption | 249 // issue6456: Size returned may be longer due to encryption |
258 // on EXT-4 fscrypt. TODO maybe only do it on EXT4? | 250 // on EXT-4 fscrypt. TODO maybe only do it on EXT4? |
259 self.outcome.lock().unwrap().unsure.push(full_path) | 251 self.outcome.lock().unwrap().unsure.push(full_path) |
260 } else if dirstate_node.copy_source.is_some() | 252 } else if dirstate_node.copy_source().is_some() |
261 || entry.is_from_other_parent() | 253 || entry.is_from_other_parent() |
262 || (entry.size >= 0 && (size_changed || mode_changed())) | 254 || (entry.size >= 0 && (size_changed || mode_changed())) |
263 { | 255 { |
264 self.outcome.lock().unwrap().modified.push(full_path) | 256 self.outcome.lock().unwrap().modified.push(full_path) |
265 } else { | 257 } else { |
273 } | 265 } |
274 } | 266 } |
275 } | 267 } |
276 | 268 |
277 /// A node in the dirstate tree has no corresponding filesystem entry | 269 /// A node in the dirstate tree has no corresponding filesystem entry |
278 fn traverse_dirstate_only( | 270 fn traverse_dirstate_only(&self, dirstate_node: NodeRef<'tree, '_>) { |
279 &self, | 271 self.mark_removed_or_deleted_if_file( |
280 hg_path: &'tree HgPath, | 272 dirstate_node.full_path(), |
281 dirstate_node: &'tree Node, | 273 dirstate_node.state(), |
282 ) { | 274 ); |
283 self.mark_removed_or_deleted_if_file(hg_path, dirstate_node.state()); | 275 dirstate_node |
284 dirstate_node.children.par_iter().for_each( | 276 .children() |
285 |(child_hg_path, child_node)| { | 277 .par_iter() |
286 self.traverse_dirstate_only( | 278 .for_each(|child_node| self.traverse_dirstate_only(child_node)) |
287 child_hg_path.full_path(), | |
288 child_node, | |
289 ) | |
290 }, | |
291 ) | |
292 } | 279 } |
293 | 280 |
294 /// A node in the dirstate tree has no corresponding *file* on the | 281 /// A node in the dirstate tree has no corresponding *file* on the |
295 /// filesystem | 282 /// filesystem |
296 /// | 283 /// |