59 /// How many (non-inclusive) descendants of this node are tracked files |
59 /// How many (non-inclusive) descendants of this node are tracked files |
60 tracked_descendants_count: usize, |
60 tracked_descendants_count: usize, |
61 } |
61 } |
62 |
62 |
63 impl Node { |
63 impl Node { |
64 /// Whether this node has a `DirstateEntry` with `.state.is_tracked()` |
|
65 fn is_tracked_file(&self) -> bool { |
|
66 if let Some(entry) = &self.entry { |
|
67 entry.state.is_tracked() |
|
68 } else { |
|
69 false |
|
70 } |
|
71 } |
|
72 |
|
73 pub(super) fn state(&self) -> Option<EntryState> { |
64 pub(super) fn state(&self) -> Option<EntryState> { |
74 self.entry.as_ref().map(|entry| entry.state) |
65 self.entry.as_ref().map(|entry| entry.state) |
75 } |
66 } |
76 } |
67 } |
77 |
68 |
115 /// other fields while the returned borrow is still valid |
106 /// other fields while the returned borrow is still valid |
116 fn get_node_mut<'tree>( |
107 fn get_node_mut<'tree>( |
117 root: &'tree mut ChildNodes, |
108 root: &'tree mut ChildNodes, |
118 path: &HgPath, |
109 path: &HgPath, |
119 ) -> Option<&'tree mut Node> { |
110 ) -> Option<&'tree mut Node> { |
120 Self::each_and_get(root, path, |_| {}) |
111 Self::get_node_mut_tracing_ancestors(root, path, |_| {}) |
121 } |
112 } |
122 |
113 |
123 /// Call `each` for each ancestor node of the one at `path` (not including |
114 /// Same as `get_node_mut`, and calls `each_ancestor` for each ancestor of |
124 /// that node itself), starting from nearest the root. |
115 /// the node. |
125 /// |
116 /// |
126 /// Panics (possibly after some calls to `each`) if there is no node at |
117 /// Note that `each_ancestor` may be called (with what would be ancestors) |
127 /// `path`. |
118 /// even if it turns out there is no node at `path`. |
128 fn for_each_ancestor_node<'tree>( |
119 fn get_node_mut_tracing_ancestors<'tree>( |
129 &mut self, |
|
130 path: &HgPath, |
|
131 each: impl FnMut(&mut Node), |
|
132 ) { |
|
133 let parent = path.parent(); |
|
134 if !parent.is_empty() { |
|
135 Self::each_and_get(&mut self.root, parent, each) |
|
136 .expect("missing dirstate node"); |
|
137 } |
|
138 } |
|
139 |
|
140 /// Common implementation detail of `get_node_mut` and |
|
141 /// `for_each_ancestor_node` |
|
142 fn each_and_get<'tree>( |
|
143 root: &'tree mut ChildNodes, |
120 root: &'tree mut ChildNodes, |
144 path: &HgPath, |
121 path: &HgPath, |
145 mut each: impl FnMut(&mut Node), |
122 mut each_ancestor: impl FnMut(&mut Node), |
146 ) -> Option<&'tree mut Node> { |
123 ) -> Option<&'tree mut Node> { |
147 let mut children = root; |
124 let mut children = root; |
148 let mut components = path.components(); |
125 let mut components = path.components(); |
149 let mut component = |
126 let mut component = |
150 components.next().expect("expected at least one components"); |
127 components.next().expect("expected at least one components"); |
151 loop { |
128 loop { |
152 let child = children.get_mut(component)?; |
129 let child = children.get_mut(component)?; |
153 each(child); |
|
154 if let Some(next_component) = components.next() { |
130 if let Some(next_component) = components.next() { |
|
131 each_ancestor(child); |
155 component = next_component; |
132 component = next_component; |
156 children = &mut child.children; |
133 children = &mut child.children; |
157 } else { |
134 } else { |
158 return Some(child); |
135 return Some(child); |
159 } |
136 } |
161 } |
138 } |
162 |
139 |
163 fn get_or_insert_node<'tree>( |
140 fn get_or_insert_node<'tree>( |
164 root: &'tree mut ChildNodes, |
141 root: &'tree mut ChildNodes, |
165 path: &HgPath, |
142 path: &HgPath, |
|
143 ) -> &'tree mut Node { |
|
144 Self::get_or_insert_node_tracing_ancestors(root, path, |_| {}) |
|
145 } |
|
146 |
|
147 fn get_or_insert_node_tracing_ancestors<'tree>( |
|
148 root: &'tree mut ChildNodes, |
|
149 path: &HgPath, |
|
150 mut each_ancestor: impl FnMut(&mut Node), |
166 ) -> &'tree mut Node { |
151 ) -> &'tree mut Node { |
167 let mut child_nodes = root; |
152 let mut child_nodes = root; |
168 let mut inclusive_ancestor_paths = |
153 let mut inclusive_ancestor_paths = |
169 WithBasename::inclusive_ancestors_of(path); |
154 WithBasename::inclusive_ancestors_of(path); |
170 let mut ancestor_path = inclusive_ancestor_paths |
155 let mut ancestor_path = inclusive_ancestor_paths |
175 // map already contains that key, without introducing double |
160 // map already contains that key, without introducing double |
176 // lookup? |
161 // lookup? |
177 let child_node = |
162 let child_node = |
178 child_nodes.entry(ancestor_path.to_owned()).or_default(); |
163 child_nodes.entry(ancestor_path.to_owned()).or_default(); |
179 if let Some(next) = inclusive_ancestor_paths.next() { |
164 if let Some(next) = inclusive_ancestor_paths.next() { |
|
165 each_ancestor(child_node); |
180 ancestor_path = next; |
166 ancestor_path = next; |
181 child_nodes = &mut child_node.children; |
167 child_nodes = &mut child_node.children; |
182 } else { |
168 } else { |
183 return child_node; |
169 return child_node; |
184 } |
170 } |
185 } |
171 } |
186 } |
172 } |
187 |
173 |
188 /// The meaning of `new_copy_source` is: |
174 fn add_or_remove_file( |
189 /// |
|
190 /// * `Some(Some(x))`: set `Node::copy_source` to `Some(x)` |
|
191 /// * `Some(None)`: set `Node::copy_source` to `None` |
|
192 /// * `None`: leave `Node::copy_source` unchanged |
|
193 fn add_file_node( |
|
194 &mut self, |
175 &mut self, |
195 path: &HgPath, |
176 path: &HgPath, |
|
177 old_state: EntryState, |
196 new_entry: DirstateEntry, |
178 new_entry: DirstateEntry, |
197 new_copy_source: Option<Option<HgPathBuf>>, |
|
198 ) { |
179 ) { |
199 let node = Self::get_or_insert_node(&mut self.root, path); |
|
200 if node.entry.is_none() { |
|
201 self.nodes_with_entry_count += 1 |
|
202 } |
|
203 if let Some(source) = &new_copy_source { |
|
204 if node.copy_source.is_none() && source.is_some() { |
|
205 self.nodes_with_copy_source_count += 1 |
|
206 } |
|
207 if node.copy_source.is_some() && source.is_none() { |
|
208 self.nodes_with_copy_source_count -= 1 |
|
209 } |
|
210 } |
|
211 let tracked_count_increment = |
180 let tracked_count_increment = |
212 match (node.is_tracked_file(), new_entry.state.is_tracked()) { |
181 match (old_state.is_tracked(), new_entry.state.is_tracked()) { |
213 (false, true) => 1, |
182 (false, true) => 1, |
214 (true, false) => -1, |
183 (true, false) => -1, |
215 _ => 0, |
184 _ => 0, |
216 }; |
185 }; |
217 |
186 |
218 node.entry = Some(new_entry); |
187 let node = Self::get_or_insert_node_tracing_ancestors( |
219 if let Some(source) = new_copy_source { |
188 &mut self.root, |
220 node.copy_source = source |
189 path, |
221 } |
190 |ancestor| { |
222 // Borrow of `self.root` through `node` ends here |
191 // We can’t use `+= increment` because the counter is unsigned, |
223 |
192 // and we want debug builds to detect accidental underflow |
224 match tracked_count_increment { |
193 // through zero |
225 1 => self.for_each_ancestor_node(path, |node| { |
194 match tracked_count_increment { |
226 node.tracked_descendants_count += 1 |
195 1 => ancestor.tracked_descendants_count += 1, |
227 }), |
196 -1 => ancestor.tracked_descendants_count -= 1, |
228 // We can’t use `+= -1` because the counter is unsigned |
197 _ => {} |
229 -1 => self.for_each_ancestor_node(path, |node| { |
198 } |
230 node.tracked_descendants_count -= 1 |
199 }, |
231 }), |
200 ); |
232 _ => {} |
201 if node.entry.is_none() { |
233 } |
202 self.nodes_with_entry_count += 1 |
|
203 } |
|
204 node.entry = Some(new_entry) |
234 } |
205 } |
235 |
206 |
236 fn iter_nodes<'a>( |
207 fn iter_nodes<'a>( |
237 &'a self, |
208 &'a self, |
238 ) -> impl Iterator<Item = (&'a WithBasename<HgPathBuf>, &'a Node)> + 'a |
209 ) -> impl Iterator<Item = (&'a WithBasename<HgPathBuf>, &'a Node)> + 'a |
327 } |
298 } |
328 |
299 |
329 fn add_file( |
300 fn add_file( |
330 &mut self, |
301 &mut self, |
331 filename: &HgPath, |
302 filename: &HgPath, |
332 _old_state: EntryState, |
303 old_state: EntryState, |
333 entry: DirstateEntry, |
304 entry: DirstateEntry, |
334 ) -> Result<(), DirstateMapError> { |
305 ) -> Result<(), DirstateMapError> { |
335 self.add_file_node(filename, entry, None); |
306 self.add_or_remove_file(filename, old_state, entry); |
336 Ok(()) |
307 Ok(()) |
337 } |
308 } |
338 |
309 |
339 fn remove_file( |
310 fn remove_file( |
340 &mut self, |
311 &mut self, |
341 filename: &HgPath, |
312 filename: &HgPath, |
342 _old_state: EntryState, |
313 old_state: EntryState, |
343 size: i32, |
314 size: i32, |
344 ) -> Result<(), DirstateMapError> { |
315 ) -> Result<(), DirstateMapError> { |
345 let entry = DirstateEntry { |
316 let entry = DirstateEntry { |
346 state: EntryState::Removed, |
317 state: EntryState::Removed, |
347 mode: 0, |
318 mode: 0, |
348 size, |
319 size, |
349 mtime: 0, |
320 mtime: 0, |
350 }; |
321 }; |
351 self.add_file_node(filename, entry, None); |
322 self.add_or_remove_file(filename, old_state, entry); |
352 Ok(()) |
323 Ok(()) |
353 } |
324 } |
354 |
325 |
355 fn drop_file( |
326 fn drop_file( |
356 &mut self, |
327 &mut self, |
357 filename: &HgPath, |
328 filename: &HgPath, |
358 _old_state: EntryState, |
329 old_state: EntryState, |
359 ) -> Result<bool, DirstateMapError> { |
330 ) -> Result<bool, DirstateMapError> { |
360 if let Some(node) = Self::get_node_mut(&mut self.root, filename) { |
331 let was_tracked = old_state.is_tracked(); |
361 let was_tracked = node.is_tracked_file(); |
332 if let Some(node) = Self::get_node_mut_tracing_ancestors( |
|
333 &mut self.root, |
|
334 filename, |
|
335 |ancestor| { |
|
336 if was_tracked { |
|
337 ancestor.tracked_descendants_count -= 1 |
|
338 } |
|
339 }, |
|
340 ) { |
362 let had_entry = node.entry.is_some(); |
341 let had_entry = node.entry.is_some(); |
363 let had_copy_source = node.copy_source.is_some(); |
342 let had_copy_source = node.copy_source.is_some(); |
364 |
343 |
365 // TODO: this leaves in the tree a "non-file" node. Should we |
344 // TODO: this leaves in the tree a "non-file" node. Should we |
366 // remove the node instead, together with ancestor nodes for |
345 // remove the node instead, together with ancestor nodes for |
511 } |
486 } |
512 |
487 |
513 let parents = parse_dirstate_entries( |
488 let parents = parse_dirstate_entries( |
514 file_contents, |
489 file_contents, |
515 |path, entry, copy_source| { |
490 |path, entry, copy_source| { |
516 self.add_file_node( |
491 let tracked = entry.state.is_tracked(); |
|
492 let node = Self::get_or_insert_node_tracing_ancestors( |
|
493 &mut self.root, |
517 path, |
494 path, |
518 *entry, |
495 |ancestor| { |
519 Some(copy_source.map(HgPath::to_owned)), |
496 if tracked { |
520 ) |
497 ancestor.tracked_descendants_count += 1 |
|
498 } |
|
499 }, |
|
500 ); |
|
501 assert!( |
|
502 node.entry.is_none(), |
|
503 "duplicate dirstate entry in read" |
|
504 ); |
|
505 assert!( |
|
506 node.copy_source.is_none(), |
|
507 "duplicate dirstate entry in read" |
|
508 ); |
|
509 node.entry = Some(*entry); |
|
510 node.copy_source = copy_source.map(HgPath::to_owned); |
|
511 self.nodes_with_entry_count += 1; |
|
512 if copy_source.is_some() { |
|
513 self.nodes_with_copy_source_count += 1 |
|
514 } |
521 }, |
515 }, |
522 )?; |
516 )?; |
523 |
517 |
524 if !self.dirty_parents { |
518 if !self.dirty_parents { |
525 self.set_parents(parents); |
519 self.set_parents(parents); |