##// END OF EJS Templates
rust-dirstatemap: add `set_untracked` method...
Raphaël Gomès -
r49999:119c7e2b default
parent child Browse files
Show More
@@ -1,1435 +1,1494 b''
1 1 use bytes_cast::BytesCast;
2 2 use micro_timer::timed;
3 3 use std::borrow::Cow;
4 4 use std::path::PathBuf;
5 5
6 6 use super::on_disk;
7 7 use super::on_disk::DirstateV2ParseError;
8 8 use super::owning::OwningDirstateMap;
9 9 use super::path_with_basename::WithBasename;
10 10 use crate::dirstate::parsers::pack_entry;
11 11 use crate::dirstate::parsers::packed_entry_size;
12 12 use crate::dirstate::parsers::parse_dirstate_entries;
13 13 use crate::dirstate::CopyMapIter;
14 14 use crate::dirstate::DirstateV2Data;
15 15 use crate::dirstate::ParentFileData;
16 16 use crate::dirstate::StateMapIter;
17 17 use crate::dirstate::TruncatedTimestamp;
18 18 use crate::dirstate::SIZE_FROM_OTHER_PARENT;
19 19 use crate::dirstate::SIZE_NON_NORMAL;
20 20 use crate::matchers::Matcher;
21 21 use crate::utils::hg_path::{HgPath, HgPathBuf};
22 22 use crate::DirstateEntry;
23 23 use crate::DirstateError;
24 24 use crate::DirstateMapError;
25 25 use crate::DirstateParents;
26 26 use crate::DirstateStatus;
27 27 use crate::EntryState;
28 28 use crate::FastHashbrownMap as FastHashMap;
29 29 use crate::PatternFileWarning;
30 30 use crate::StatusError;
31 31 use crate::StatusOptions;
32 32
33 33 /// Append to an existing data file if the amount of unreachable data (not used
34 34 /// anymore) is less than this fraction of the total amount of existing data.
35 35 const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5;
36 36
37 37 pub struct DirstateMap<'on_disk> {
38 38 /// Contents of the `.hg/dirstate` file
39 39 pub(super) on_disk: &'on_disk [u8],
40 40
41 41 pub(super) root: ChildNodes<'on_disk>,
42 42
43 43 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
44 44 pub(super) nodes_with_entry_count: u32,
45 45
46 46 /// Number of nodes anywhere in the tree that have
47 47 /// `.copy_source.is_some()`.
48 48 pub(super) nodes_with_copy_source_count: u32,
49 49
50 50 /// See on_disk::Header
51 51 pub(super) ignore_patterns_hash: on_disk::IgnorePatternsHash,
52 52
53 53 /// How many bytes of `on_disk` are not used anymore
54 54 pub(super) unreachable_bytes: u32,
55 55 }
56 56
57 57 /// Using a plain `HgPathBuf` of the full path from the repository root as a
58 58 /// map key would also work: all paths in a given map have the same parent
59 59 /// path, so comparing full paths gives the same result as comparing base
60 60 /// names. However `HashMap` would waste time always re-hashing the same
61 61 /// string prefix.
62 62 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
63 63
64 64 /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned
65 65 /// for on-disk nodes that don’t actually have a `Cow` to borrow.
66 66 pub(super) enum BorrowedPath<'tree, 'on_disk> {
67 67 InMemory(&'tree HgPathBuf),
68 68 OnDisk(&'on_disk HgPath),
69 69 }
70 70
71 71 pub(super) enum ChildNodes<'on_disk> {
72 72 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
73 73 OnDisk(&'on_disk [on_disk::Node]),
74 74 }
75 75
76 76 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
77 77 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
78 78 OnDisk(&'on_disk [on_disk::Node]),
79 79 }
80 80
81 81 pub(super) enum NodeRef<'tree, 'on_disk> {
82 82 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
83 83 OnDisk(&'on_disk on_disk::Node),
84 84 }
85 85
86 86 impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> {
87 87 pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> {
88 88 match *self {
89 89 BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()),
90 90 BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk),
91 91 }
92 92 }
93 93 }
94 94
95 95 impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> {
96 96 type Target = HgPath;
97 97
98 98 fn deref(&self) -> &HgPath {
99 99 match *self {
100 100 BorrowedPath::InMemory(in_memory) => in_memory,
101 101 BorrowedPath::OnDisk(on_disk) => on_disk,
102 102 }
103 103 }
104 104 }
105 105
106 106 impl Default for ChildNodes<'_> {
107 107 fn default() -> Self {
108 108 ChildNodes::InMemory(Default::default())
109 109 }
110 110 }
111 111
112 112 impl<'on_disk> ChildNodes<'on_disk> {
113 113 pub(super) fn as_ref<'tree>(
114 114 &'tree self,
115 115 ) -> ChildNodesRef<'tree, 'on_disk> {
116 116 match self {
117 117 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
118 118 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
119 119 }
120 120 }
121 121
122 122 pub(super) fn is_empty(&self) -> bool {
123 123 match self {
124 124 ChildNodes::InMemory(nodes) => nodes.is_empty(),
125 125 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
126 126 }
127 127 }
128 128
129 129 fn make_mut(
130 130 &mut self,
131 131 on_disk: &'on_disk [u8],
132 132 unreachable_bytes: &mut u32,
133 133 ) -> Result<
134 134 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
135 135 DirstateV2ParseError,
136 136 > {
137 137 match self {
138 138 ChildNodes::InMemory(nodes) => Ok(nodes),
139 139 ChildNodes::OnDisk(nodes) => {
140 140 *unreachable_bytes +=
141 141 std::mem::size_of_val::<[on_disk::Node]>(nodes) as u32;
142 142 let nodes = nodes
143 143 .iter()
144 144 .map(|node| {
145 145 Ok((
146 146 node.path(on_disk)?,
147 147 node.to_in_memory_node(on_disk)?,
148 148 ))
149 149 })
150 150 .collect::<Result<_, _>>()?;
151 151 *self = ChildNodes::InMemory(nodes);
152 152 match self {
153 153 ChildNodes::InMemory(nodes) => Ok(nodes),
154 154 ChildNodes::OnDisk(_) => unreachable!(),
155 155 }
156 156 }
157 157 }
158 158 }
159 159 }
160 160
161 161 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
162 162 pub(super) fn get(
163 163 &self,
164 164 base_name: &HgPath,
165 165 on_disk: &'on_disk [u8],
166 166 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
167 167 match self {
168 168 ChildNodesRef::InMemory(nodes) => Ok(nodes
169 169 .get_key_value(base_name)
170 170 .map(|(k, v)| NodeRef::InMemory(k, v))),
171 171 ChildNodesRef::OnDisk(nodes) => {
172 172 let mut parse_result = Ok(());
173 173 let search_result = nodes.binary_search_by(|node| {
174 174 match node.base_name(on_disk) {
175 175 Ok(node_base_name) => node_base_name.cmp(base_name),
176 176 Err(e) => {
177 177 parse_result = Err(e);
178 178 // Dummy comparison result, `search_result` won’t
179 179 // be used since `parse_result` is an error
180 180 std::cmp::Ordering::Equal
181 181 }
182 182 }
183 183 });
184 184 parse_result.map(|()| {
185 185 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
186 186 })
187 187 }
188 188 }
189 189 }
190 190
191 191 /// Iterate in undefined order
192 192 pub(super) fn iter(
193 193 &self,
194 194 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
195 195 match self {
196 196 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
197 197 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
198 198 ),
199 199 ChildNodesRef::OnDisk(nodes) => {
200 200 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
201 201 }
202 202 }
203 203 }
204 204
205 205 /// Iterate in parallel in undefined order
206 206 pub(super) fn par_iter(
207 207 &self,
208 208 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
209 209 {
210 210 use rayon::prelude::*;
211 211 match self {
212 212 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
213 213 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
214 214 ),
215 215 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
216 216 nodes.par_iter().map(NodeRef::OnDisk),
217 217 ),
218 218 }
219 219 }
220 220
221 221 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
222 222 match self {
223 223 ChildNodesRef::InMemory(nodes) => {
224 224 let mut vec: Vec<_> = nodes
225 225 .iter()
226 226 .map(|(k, v)| NodeRef::InMemory(k, v))
227 227 .collect();
228 228 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
229 229 match node {
230 230 NodeRef::InMemory(path, _node) => path.base_name(),
231 231 NodeRef::OnDisk(_) => unreachable!(),
232 232 }
233 233 }
234 234 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
235 235 // value: https://github.com/rust-lang/rust/issues/34162
236 236 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
237 237 vec
238 238 }
239 239 ChildNodesRef::OnDisk(nodes) => {
240 240 // Nodes on disk are already sorted
241 241 nodes.iter().map(NodeRef::OnDisk).collect()
242 242 }
243 243 }
244 244 }
245 245 }
246 246
247 247 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
248 248 pub(super) fn full_path(
249 249 &self,
250 250 on_disk: &'on_disk [u8],
251 251 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
252 252 match self {
253 253 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
254 254 NodeRef::OnDisk(node) => node.full_path(on_disk),
255 255 }
256 256 }
257 257
258 258 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
259 259 /// HgPath>` detached from `'tree`
260 260 pub(super) fn full_path_borrowed(
261 261 &self,
262 262 on_disk: &'on_disk [u8],
263 263 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
264 264 match self {
265 265 NodeRef::InMemory(path, _node) => match path.full_path() {
266 266 Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)),
267 267 Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)),
268 268 },
269 269 NodeRef::OnDisk(node) => {
270 270 Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?))
271 271 }
272 272 }
273 273 }
274 274
275 275 pub(super) fn base_name(
276 276 &self,
277 277 on_disk: &'on_disk [u8],
278 278 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
279 279 match self {
280 280 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
281 281 NodeRef::OnDisk(node) => node.base_name(on_disk),
282 282 }
283 283 }
284 284
285 285 pub(super) fn children(
286 286 &self,
287 287 on_disk: &'on_disk [u8],
288 288 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
289 289 match self {
290 290 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
291 291 NodeRef::OnDisk(node) => {
292 292 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
293 293 }
294 294 }
295 295 }
296 296
297 297 pub(super) fn has_copy_source(&self) -> bool {
298 298 match self {
299 299 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
300 300 NodeRef::OnDisk(node) => node.has_copy_source(),
301 301 }
302 302 }
303 303
304 304 pub(super) fn copy_source(
305 305 &self,
306 306 on_disk: &'on_disk [u8],
307 307 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
308 308 match self {
309 309 NodeRef::InMemory(_path, node) => {
310 310 Ok(node.copy_source.as_ref().map(|s| &**s))
311 311 }
312 312 NodeRef::OnDisk(node) => node.copy_source(on_disk),
313 313 }
314 314 }
315 315 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
316 316 /// HgPath>` detached from `'tree`
317 317 pub(super) fn copy_source_borrowed(
318 318 &self,
319 319 on_disk: &'on_disk [u8],
320 320 ) -> Result<Option<BorrowedPath<'tree, 'on_disk>>, DirstateV2ParseError>
321 321 {
322 322 Ok(match self {
323 323 NodeRef::InMemory(_path, node) => {
324 324 node.copy_source.as_ref().map(|source| match source {
325 325 Cow::Borrowed(on_disk) => BorrowedPath::OnDisk(on_disk),
326 326 Cow::Owned(in_memory) => BorrowedPath::InMemory(in_memory),
327 327 })
328 328 }
329 329 NodeRef::OnDisk(node) => node
330 330 .copy_source(on_disk)?
331 331 .map(|source| BorrowedPath::OnDisk(source)),
332 332 })
333 333 }
334 334
335 335 pub(super) fn entry(
336 336 &self,
337 337 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
338 338 match self {
339 339 NodeRef::InMemory(_path, node) => {
340 340 Ok(node.data.as_entry().copied())
341 341 }
342 342 NodeRef::OnDisk(node) => node.entry(),
343 343 }
344 344 }
345 345
346 346 pub(super) fn state(
347 347 &self,
348 348 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
349 349 Ok(self.entry()?.and_then(|e| {
350 350 if e.any_tracked() {
351 351 Some(e.state())
352 352 } else {
353 353 None
354 354 }
355 355 }))
356 356 }
357 357
358 358 pub(super) fn cached_directory_mtime(
359 359 &self,
360 360 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
361 361 match self {
362 362 NodeRef::InMemory(_path, node) => Ok(match node.data {
363 363 NodeData::CachedDirectory { mtime } => Some(mtime),
364 364 _ => None,
365 365 }),
366 366 NodeRef::OnDisk(node) => node.cached_directory_mtime(),
367 367 }
368 368 }
369 369
370 370 pub(super) fn descendants_with_entry_count(&self) -> u32 {
371 371 match self {
372 372 NodeRef::InMemory(_path, node) => {
373 373 node.descendants_with_entry_count
374 374 }
375 375 NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
376 376 }
377 377 }
378 378
379 379 pub(super) fn tracked_descendants_count(&self) -> u32 {
380 380 match self {
381 381 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
382 382 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
383 383 }
384 384 }
385 385 }
386 386
387 387 /// Represents a file or a directory
388 388 #[derive(Default)]
389 389 pub(super) struct Node<'on_disk> {
390 390 pub(super) data: NodeData,
391 391
392 392 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
393 393
394 394 pub(super) children: ChildNodes<'on_disk>,
395 395
396 396 /// How many (non-inclusive) descendants of this node have an entry.
397 397 pub(super) descendants_with_entry_count: u32,
398 398
399 399 /// How many (non-inclusive) descendants of this node have an entry whose
400 400 /// state is "tracked".
401 401 pub(super) tracked_descendants_count: u32,
402 402 }
403 403
404 404 pub(super) enum NodeData {
405 405 Entry(DirstateEntry),
406 406 CachedDirectory { mtime: TruncatedTimestamp },
407 407 None,
408 408 }
409 409
410 410 impl Default for NodeData {
411 411 fn default() -> Self {
412 412 NodeData::None
413 413 }
414 414 }
415 415
416 416 impl NodeData {
417 417 fn has_entry(&self) -> bool {
418 418 match self {
419 419 NodeData::Entry(_) => true,
420 420 _ => false,
421 421 }
422 422 }
423 423
424 424 fn as_entry(&self) -> Option<&DirstateEntry> {
425 425 match self {
426 426 NodeData::Entry(entry) => Some(entry),
427 427 _ => None,
428 428 }
429 429 }
430 430
431 431 fn as_entry_mut(&mut self) -> Option<&mut DirstateEntry> {
432 432 match self {
433 433 NodeData::Entry(entry) => Some(entry),
434 434 _ => None,
435 435 }
436 436 }
437 437 }
438 438
439 439 impl<'on_disk> DirstateMap<'on_disk> {
440 440 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
441 441 Self {
442 442 on_disk,
443 443 root: ChildNodes::default(),
444 444 nodes_with_entry_count: 0,
445 445 nodes_with_copy_source_count: 0,
446 446 ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
447 447 unreachable_bytes: 0,
448 448 }
449 449 }
450 450
451 451 #[timed]
452 452 pub fn new_v2(
453 453 on_disk: &'on_disk [u8],
454 454 data_size: usize,
455 455 metadata: &[u8],
456 456 ) -> Result<Self, DirstateError> {
457 457 if let Some(data) = on_disk.get(..data_size) {
458 458 Ok(on_disk::read(data, metadata)?)
459 459 } else {
460 460 Err(DirstateV2ParseError.into())
461 461 }
462 462 }
463 463
464 464 #[timed]
465 465 pub fn new_v1(
466 466 on_disk: &'on_disk [u8],
467 467 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
468 468 let mut map = Self::empty(on_disk);
469 469 if map.on_disk.is_empty() {
470 470 return Ok((map, None));
471 471 }
472 472
473 473 let parents = parse_dirstate_entries(
474 474 map.on_disk,
475 475 |path, entry, copy_source| {
476 476 let tracked = entry.state().is_tracked();
477 477 let node = Self::get_or_insert_node(
478 478 map.on_disk,
479 479 &mut map.unreachable_bytes,
480 480 &mut map.root,
481 481 path,
482 482 WithBasename::to_cow_borrowed,
483 483 |ancestor| {
484 484 if tracked {
485 485 ancestor.tracked_descendants_count += 1
486 486 }
487 487 ancestor.descendants_with_entry_count += 1
488 488 },
489 489 )?;
490 490 assert!(
491 491 !node.data.has_entry(),
492 492 "duplicate dirstate entry in read"
493 493 );
494 494 assert!(
495 495 node.copy_source.is_none(),
496 496 "duplicate dirstate entry in read"
497 497 );
498 498 node.data = NodeData::Entry(*entry);
499 499 node.copy_source = copy_source.map(Cow::Borrowed);
500 500 map.nodes_with_entry_count += 1;
501 501 if copy_source.is_some() {
502 502 map.nodes_with_copy_source_count += 1
503 503 }
504 504 Ok(())
505 505 },
506 506 )?;
507 507 let parents = Some(parents.clone());
508 508
509 509 Ok((map, parents))
510 510 }
511 511
512 512 /// Assuming dirstate-v2 format, returns whether the next write should
513 513 /// append to the existing data file that contains `self.on_disk` (true),
514 514 /// or create a new data file from scratch (false).
515 515 pub(super) fn write_should_append(&self) -> bool {
516 516 let ratio = self.unreachable_bytes as f32 / self.on_disk.len() as f32;
517 517 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
518 518 }
519 519
520 520 fn get_node<'tree>(
521 521 &'tree self,
522 522 path: &HgPath,
523 523 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
524 524 let mut children = self.root.as_ref();
525 525 let mut components = path.components();
526 526 let mut component =
527 527 components.next().expect("expected at least one components");
528 528 loop {
529 529 if let Some(child) = children.get(component, self.on_disk)? {
530 530 if let Some(next_component) = components.next() {
531 531 component = next_component;
532 532 children = child.children(self.on_disk)?;
533 533 } else {
534 534 return Ok(Some(child));
535 535 }
536 536 } else {
537 537 return Ok(None);
538 538 }
539 539 }
540 540 }
541 541
542 542 /// Returns a mutable reference to the node at `path` if it exists
543 543 ///
544 544 /// This takes `root` instead of `&mut self` so that callers can mutate
545 545 /// other fields while the returned borrow is still valid
546 546 fn get_node_mut<'tree>(
547 547 on_disk: &'on_disk [u8],
548 548 unreachable_bytes: &mut u32,
549 549 root: &'tree mut ChildNodes<'on_disk>,
550 550 path: &HgPath,
551 551 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
552 552 let mut children = root;
553 553 let mut components = path.components();
554 554 let mut component =
555 555 components.next().expect("expected at least one components");
556 556 loop {
557 557 if let Some(child) = children
558 558 .make_mut(on_disk, unreachable_bytes)?
559 559 .get_mut(component)
560 560 {
561 561 if let Some(next_component) = components.next() {
562 562 component = next_component;
563 563 children = &mut child.children;
564 564 } else {
565 565 return Ok(Some(child));
566 566 }
567 567 } else {
568 568 return Ok(None);
569 569 }
570 570 }
571 571 }
572 572
573 573 pub(super) fn get_or_insert<'tree, 'path>(
574 574 &'tree mut self,
575 575 path: &HgPath,
576 576 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
577 577 Self::get_or_insert_node(
578 578 self.on_disk,
579 579 &mut self.unreachable_bytes,
580 580 &mut self.root,
581 581 path,
582 582 WithBasename::to_cow_owned,
583 583 |_| {},
584 584 )
585 585 }
586 586
587 587 fn get_or_insert_node<'tree, 'path>(
588 588 on_disk: &'on_disk [u8],
589 589 unreachable_bytes: &mut u32,
590 590 root: &'tree mut ChildNodes<'on_disk>,
591 591 path: &'path HgPath,
592 592 to_cow: impl Fn(
593 593 WithBasename<&'path HgPath>,
594 594 ) -> WithBasename<Cow<'on_disk, HgPath>>,
595 595 mut each_ancestor: impl FnMut(&mut Node),
596 596 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
597 597 let mut child_nodes = root;
598 598 let mut inclusive_ancestor_paths =
599 599 WithBasename::inclusive_ancestors_of(path);
600 600 let mut ancestor_path = inclusive_ancestor_paths
601 601 .next()
602 602 .expect("expected at least one inclusive ancestor");
603 603 loop {
604 604 let (_, child_node) = child_nodes
605 605 .make_mut(on_disk, unreachable_bytes)?
606 606 .raw_entry_mut()
607 607 .from_key(ancestor_path.base_name())
608 608 .or_insert_with(|| (to_cow(ancestor_path), Node::default()));
609 609 if let Some(next) = inclusive_ancestor_paths.next() {
610 610 each_ancestor(child_node);
611 611 ancestor_path = next;
612 612 child_nodes = &mut child_node.children;
613 613 } else {
614 614 return Ok(child_node);
615 615 }
616 616 }
617 617 }
618 618
619 619 fn reset_state(
620 620 &mut self,
621 621 filename: &HgPath,
622 622 old_entry_opt: Option<DirstateEntry>,
623 623 wc_tracked: bool,
624 624 p1_tracked: bool,
625 625 p2_info: bool,
626 626 has_meaningful_mtime: bool,
627 627 parent_file_data_opt: Option<ParentFileData>,
628 628 ) -> Result<(), DirstateError> {
629 629 let (had_entry, was_tracked) = match old_entry_opt {
630 630 Some(old_entry) => (true, old_entry.tracked()),
631 631 None => (false, false),
632 632 };
633 633 let node = Self::get_or_insert_node(
634 634 self.on_disk,
635 635 &mut self.unreachable_bytes,
636 636 &mut self.root,
637 637 filename,
638 638 WithBasename::to_cow_owned,
639 639 |ancestor| {
640 640 if !had_entry {
641 641 ancestor.descendants_with_entry_count += 1;
642 642 }
643 643 if was_tracked {
644 644 if !wc_tracked {
645 645 ancestor.tracked_descendants_count = ancestor
646 646 .tracked_descendants_count
647 647 .checked_sub(1)
648 648 .expect("tracked count to be >= 0");
649 649 }
650 650 } else {
651 651 if wc_tracked {
652 652 ancestor.tracked_descendants_count += 1;
653 653 }
654 654 }
655 655 },
656 656 )?;
657 657
658 658 let v2_data = if let Some(parent_file_data) = parent_file_data_opt {
659 659 DirstateV2Data {
660 660 wc_tracked,
661 661 p1_tracked,
662 662 p2_info,
663 663 mode_size: parent_file_data.mode_size,
664 664 mtime: if has_meaningful_mtime {
665 665 parent_file_data.mtime
666 666 } else {
667 667 None
668 668 },
669 669 ..Default::default()
670 670 }
671 671 } else {
672 672 DirstateV2Data {
673 673 wc_tracked,
674 674 p1_tracked,
675 675 p2_info,
676 676 ..Default::default()
677 677 }
678 678 };
679 679 if !had_entry {
680 680 self.nodes_with_entry_count += 1;
681 681 }
682 682 node.data = NodeData::Entry(DirstateEntry::from_v2_data(v2_data));
683 683 Ok(())
684 684 }
685 685
686 686 fn set_tracked(
687 687 &mut self,
688 688 filename: &HgPath,
689 689 old_entry_opt: Option<DirstateEntry>,
690 690 ) -> Result<bool, DirstateV2ParseError> {
691 691 let was_tracked = old_entry_opt.map_or(false, |e| e.tracked());
692 692 let had_entry = old_entry_opt.is_some();
693 693 let tracked_count_increment = if was_tracked { 0 } else { 1 };
694 694 let mut new = false;
695 695
696 696 let node = Self::get_or_insert_node(
697 697 self.on_disk,
698 698 &mut self.unreachable_bytes,
699 699 &mut self.root,
700 700 filename,
701 701 WithBasename::to_cow_owned,
702 702 |ancestor| {
703 703 if !had_entry {
704 704 ancestor.descendants_with_entry_count += 1;
705 705 }
706 706
707 707 ancestor.tracked_descendants_count += tracked_count_increment;
708 708 },
709 709 )?;
710 710 let new_entry = if let Some(old_entry) = old_entry_opt {
711 711 let mut e = old_entry.clone();
712 712 if e.tracked() {
713 713 // XXX
714 714 // This is probably overkill for more case, but we need this to
715 715 // fully replace the `normallookup` call with `set_tracked`
716 716 // one. Consider smoothing this in the future.
717 717 e.set_possibly_dirty();
718 718 } else {
719 719 new = true;
720 720 e.set_tracked();
721 721 }
722 722 e
723 723 } else {
724 724 self.nodes_with_entry_count += 1;
725 725 new = true;
726 726 DirstateEntry::new_tracked()
727 727 };
728 728 node.data = NodeData::Entry(new_entry);
729 729 Ok(new)
730 730 }
731 731
732 732 fn add_or_remove_file(
733 733 &mut self,
734 734 path: &HgPath,
735 735 old_state: Option<EntryState>,
736 736 new_entry: DirstateEntry,
737 737 ) -> Result<(), DirstateV2ParseError> {
738 738 let had_entry = old_state.is_some();
739 739 let was_tracked = old_state.map_or(false, |s| s.is_tracked());
740 740 let tracked_count_increment =
741 741 match (was_tracked, new_entry.state().is_tracked()) {
742 742 (false, true) => 1,
743 743 (true, false) => -1,
744 744 _ => 0,
745 745 };
746 746
747 747 let node = Self::get_or_insert_node(
748 748 self.on_disk,
749 749 &mut self.unreachable_bytes,
750 750 &mut self.root,
751 751 path,
752 752 WithBasename::to_cow_owned,
753 753 |ancestor| {
754 754 if !had_entry {
755 755 ancestor.descendants_with_entry_count += 1;
756 756 }
757 757
758 758 // We can’t use `+= increment` because the counter is unsigned,
759 759 // and we want debug builds to detect accidental underflow
760 760 // through zero
761 761 match tracked_count_increment {
762 762 1 => ancestor.tracked_descendants_count += 1,
763 763 -1 => ancestor.tracked_descendants_count -= 1,
764 764 _ => {}
765 765 }
766 766 },
767 767 )?;
768 768 if !had_entry {
769 769 self.nodes_with_entry_count += 1
770 770 }
771 771 node.data = NodeData::Entry(new_entry);
772 772 Ok(())
773 773 }
774 774
775 /// It is the responsibility of the caller to know that there was an entry
776 /// there before. Does not handle the removal of copy source
777 fn set_untracked(
778 &mut self,
779 filename: &HgPath,
780 old_entry: DirstateEntry,
781 ) -> Result<(), DirstateV2ParseError> {
782 let node = Self::get_or_insert_node(
783 self.on_disk,
784 &mut self.unreachable_bytes,
785 &mut self.root,
786 filename,
787 WithBasename::to_cow_owned,
788 |ancestor| {
789 ancestor.tracked_descendants_count = ancestor
790 .tracked_descendants_count
791 .checked_sub(1)
792 .expect("tracked_descendants_count should be >= 0");
793 },
794 )?;
795 let mut new_entry = old_entry.clone();
796 new_entry.set_untracked();
797 node.data = NodeData::Entry(new_entry);
798 Ok(())
799 }
800
775 801 fn set_clean(
776 802 &mut self,
777 803 filename: &HgPath,
778 804 old_entry: DirstateEntry,
779 805 mode: u32,
780 806 size: u32,
781 807 mtime: TruncatedTimestamp,
782 808 ) -> Result<(), DirstateError> {
783 809 let node = Self::get_or_insert_node(
784 810 self.on_disk,
785 811 &mut self.unreachable_bytes,
786 812 &mut self.root,
787 813 filename,
788 814 WithBasename::to_cow_owned,
789 815 |ancestor| {
790 816 if !old_entry.tracked() {
791 817 ancestor.tracked_descendants_count += 1;
792 818 }
793 819 },
794 820 )?;
795 821 let mut new_entry = old_entry.clone();
796 822 new_entry.set_clean(mode, size, mtime);
797 823 node.data = NodeData::Entry(new_entry);
798 824 Ok(())
799 825 }
800 826
801 827 fn set_possibly_dirty(
802 828 &mut self,
803 829 filename: &HgPath,
804 830 ) -> Result<(), DirstateError> {
805 831 let node = Self::get_or_insert_node(
806 832 self.on_disk,
807 833 &mut self.unreachable_bytes,
808 834 &mut self.root,
809 835 filename,
810 836 WithBasename::to_cow_owned,
811 837 |_ancestor| {},
812 838 )?;
813 839 let entry = node.data.as_entry_mut().expect("entry should exist");
814 840 entry.set_possibly_dirty();
815 841 node.data = NodeData::Entry(*entry);
816 842 Ok(())
817 843 }
818 844
819 845 fn iter_nodes<'tree>(
820 846 &'tree self,
821 847 ) -> impl Iterator<
822 848 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
823 849 > + 'tree {
824 850 // Depth first tree traversal.
825 851 //
826 852 // If we could afford internal iteration and recursion,
827 853 // this would look like:
828 854 //
829 855 // ```
830 856 // fn traverse_children(
831 857 // children: &ChildNodes,
832 858 // each: &mut impl FnMut(&Node),
833 859 // ) {
834 860 // for child in children.values() {
835 861 // traverse_children(&child.children, each);
836 862 // each(child);
837 863 // }
838 864 // }
839 865 // ```
840 866 //
841 867 // However we want an external iterator and therefore can’t use the
842 868 // call stack. Use an explicit stack instead:
843 869 let mut stack = Vec::new();
844 870 let mut iter = self.root.as_ref().iter();
845 871 std::iter::from_fn(move || {
846 872 while let Some(child_node) = iter.next() {
847 873 let children = match child_node.children(self.on_disk) {
848 874 Ok(children) => children,
849 875 Err(error) => return Some(Err(error)),
850 876 };
851 877 // Pseudo-recursion
852 878 let new_iter = children.iter();
853 879 let old_iter = std::mem::replace(&mut iter, new_iter);
854 880 stack.push((child_node, old_iter));
855 881 }
856 882 // Found the end of a `children.iter()` iterator.
857 883 if let Some((child_node, next_iter)) = stack.pop() {
858 884 // "Return" from pseudo-recursion by restoring state from the
859 885 // explicit stack
860 886 iter = next_iter;
861 887
862 888 Some(Ok(child_node))
863 889 } else {
864 890 // Reached the bottom of the stack, we’re done
865 891 None
866 892 }
867 893 })
868 894 }
869 895
870 896 fn count_dropped_path(unreachable_bytes: &mut u32, path: &Cow<HgPath>) {
871 897 if let Cow::Borrowed(path) = path {
872 898 *unreachable_bytes += path.len() as u32
873 899 }
874 900 }
875 901 }
876 902
877 903 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
878 904 ///
879 905 /// The callback is only called for incoming `Ok` values. Errors are passed
880 906 /// through as-is. In order to let it use the `?` operator the callback is
881 907 /// expected to return a `Result` of `Option`, instead of an `Option` of
882 908 /// `Result`.
883 909 fn filter_map_results<'a, I, F, A, B, E>(
884 910 iter: I,
885 911 f: F,
886 912 ) -> impl Iterator<Item = Result<B, E>> + 'a
887 913 where
888 914 I: Iterator<Item = Result<A, E>> + 'a,
889 915 F: Fn(A) -> Result<Option<B>, E> + 'a,
890 916 {
891 917 iter.filter_map(move |result| match result {
892 918 Ok(node) => f(node).transpose(),
893 919 Err(e) => Some(Err(e)),
894 920 })
895 921 }
896 922
897 923 impl OwningDirstateMap {
898 924 pub fn clear(&mut self) {
899 925 self.with_dmap_mut(|map| {
900 926 map.root = Default::default();
901 927 map.nodes_with_entry_count = 0;
902 928 map.nodes_with_copy_source_count = 0;
903 929 });
904 930 }
905 931
906 932 pub fn set_entry(
907 933 &mut self,
908 934 filename: &HgPath,
909 935 entry: DirstateEntry,
910 936 ) -> Result<(), DirstateV2ParseError> {
911 937 self.with_dmap_mut(|map| {
912 938 map.get_or_insert(&filename)?.data = NodeData::Entry(entry);
913 939 Ok(())
914 940 })
915 941 }
916 942
917 943 pub fn add_file(
918 944 &mut self,
919 945 filename: &HgPath,
920 946 entry: DirstateEntry,
921 947 ) -> Result<(), DirstateError> {
922 948 let old_state = self.get(filename)?.map(|e| e.state());
923 949 self.with_dmap_mut(|map| {
924 950 Ok(map.add_or_remove_file(filename, old_state, entry)?)
925 951 })
926 952 }
927 953
928 954 pub fn set_tracked(
929 955 &mut self,
930 956 filename: &HgPath,
931 957 ) -> Result<bool, DirstateV2ParseError> {
932 958 let old_entry_opt = self.get(filename)?;
933 959 self.with_dmap_mut(|map| map.set_tracked(filename, old_entry_opt))
934 960 }
935 961
962 pub fn set_untracked(
963 &mut self,
964 filename: &HgPath,
965 ) -> Result<bool, DirstateError> {
966 let old_entry_opt = self.get(filename)?;
967 match old_entry_opt {
968 None => Ok(false),
969 Some(old_entry) => {
970 if !old_entry.tracked() {
971 // `DirstateMap::set_untracked` is not a noop if
972 // already not tracked as it will decrement the
973 // tracked counters while going down.
974 return Ok(true);
975 }
976 if old_entry.added() {
977 // Untracking an "added" entry will just result in a
978 // worthless entry (and other parts of the code will
979 // complain about it), just drop it entirely.
980 self.drop_entry_and_copy_source(filename)?;
981 return Ok(true);
982 }
983 if !old_entry.p2_info() {
984 self.copy_map_remove(filename)?;
985 }
986
987 self.with_dmap_mut(|map| {
988 map.set_untracked(filename, old_entry)?;
989 Ok(true)
990 })
991 }
992 }
993 }
994
936 995 pub fn set_clean(
937 996 &mut self,
938 997 filename: &HgPath,
939 998 mode: u32,
940 999 size: u32,
941 1000 mtime: TruncatedTimestamp,
942 1001 ) -> Result<(), DirstateError> {
943 1002 let old_entry = match self.get(filename)? {
944 1003 None => {
945 1004 return Err(
946 1005 DirstateMapError::PathNotFound(filename.into()).into()
947 1006 )
948 1007 }
949 1008 Some(e) => e,
950 1009 };
951 1010 self.copy_map_remove(filename)?;
952 1011 self.with_dmap_mut(|map| {
953 1012 map.set_clean(filename, old_entry, mode, size, mtime)
954 1013 })
955 1014 }
956 1015
957 1016 pub fn set_possibly_dirty(
958 1017 &mut self,
959 1018 filename: &HgPath,
960 1019 ) -> Result<(), DirstateError> {
961 1020 if self.get(filename)?.is_none() {
962 1021 return Err(DirstateMapError::PathNotFound(filename.into()).into());
963 1022 }
964 1023 self.with_dmap_mut(|map| map.set_possibly_dirty(filename))
965 1024 }
966 1025
967 1026 pub fn reset_state(
968 1027 &mut self,
969 1028 filename: &HgPath,
970 1029 wc_tracked: bool,
971 1030 p1_tracked: bool,
972 1031 p2_info: bool,
973 1032 has_meaningful_mtime: bool,
974 1033 parent_file_data_opt: Option<ParentFileData>,
975 1034 ) -> Result<(), DirstateError> {
976 1035 if !(p1_tracked || p2_info || wc_tracked) {
977 1036 self.drop_entry_and_copy_source(filename)?;
978 1037 return Ok(());
979 1038 }
980 1039 self.copy_map_remove(filename)?;
981 1040 let old_entry_opt = self.get(filename)?;
982 1041 self.with_dmap_mut(|map| {
983 1042 map.reset_state(
984 1043 filename,
985 1044 old_entry_opt,
986 1045 wc_tracked,
987 1046 p1_tracked,
988 1047 p2_info,
989 1048 has_meaningful_mtime,
990 1049 parent_file_data_opt,
991 1050 )
992 1051 })
993 1052 }
994 1053
995 1054 pub fn remove_file(
996 1055 &mut self,
997 1056 filename: &HgPath,
998 1057 in_merge: bool,
999 1058 ) -> Result<(), DirstateError> {
1000 1059 let old_entry_opt = self.get(filename)?;
1001 1060 let old_state = old_entry_opt.map(|e| e.state());
1002 1061 let mut size = 0;
1003 1062 if in_merge {
1004 1063 // XXX we should not be able to have 'm' state and 'FROM_P2' if not
1005 1064 // during a merge. So I (marmoute) am not sure we need the
1006 1065 // conditionnal at all. Adding double checking this with assert
1007 1066 // would be nice.
1008 1067 if let Some(old_entry) = old_entry_opt {
1009 1068 // backup the previous state
1010 1069 if old_entry.state() == EntryState::Merged {
1011 1070 size = SIZE_NON_NORMAL;
1012 1071 } else if old_entry.state() == EntryState::Normal
1013 1072 && old_entry.size() == SIZE_FROM_OTHER_PARENT
1014 1073 {
1015 1074 // other parent
1016 1075 size = SIZE_FROM_OTHER_PARENT;
1017 1076 }
1018 1077 }
1019 1078 }
1020 1079 if size == 0 {
1021 1080 self.copy_map_remove(filename)?;
1022 1081 }
1023 1082 self.with_dmap_mut(|map| {
1024 1083 let entry = DirstateEntry::new_removed(size);
1025 1084 Ok(map.add_or_remove_file(filename, old_state, entry)?)
1026 1085 })
1027 1086 }
1028 1087
1029 1088 pub fn drop_entry_and_copy_source(
1030 1089 &mut self,
1031 1090 filename: &HgPath,
1032 1091 ) -> Result<(), DirstateError> {
1033 1092 let was_tracked = self
1034 1093 .get(filename)?
1035 1094 .map_or(false, |e| e.state().is_tracked());
1036 1095 struct Dropped {
1037 1096 was_tracked: bool,
1038 1097 had_entry: bool,
1039 1098 had_copy_source: bool,
1040 1099 }
1041 1100
1042 1101 /// If this returns `Ok(Some((dropped, removed)))`, then
1043 1102 ///
1044 1103 /// * `dropped` is about the leaf node that was at `filename`
1045 1104 /// * `removed` is whether this particular level of recursion just
1046 1105 /// removed a node in `nodes`.
1047 1106 fn recur<'on_disk>(
1048 1107 on_disk: &'on_disk [u8],
1049 1108 unreachable_bytes: &mut u32,
1050 1109 nodes: &mut ChildNodes<'on_disk>,
1051 1110 path: &HgPath,
1052 1111 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
1053 1112 let (first_path_component, rest_of_path) =
1054 1113 path.split_first_component();
1055 1114 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
1056 1115 let node = if let Some(node) = nodes.get_mut(first_path_component)
1057 1116 {
1058 1117 node
1059 1118 } else {
1060 1119 return Ok(None);
1061 1120 };
1062 1121 let dropped;
1063 1122 if let Some(rest) = rest_of_path {
1064 1123 if let Some((d, removed)) = recur(
1065 1124 on_disk,
1066 1125 unreachable_bytes,
1067 1126 &mut node.children,
1068 1127 rest,
1069 1128 )? {
1070 1129 dropped = d;
1071 1130 if dropped.had_entry {
1072 1131 node.descendants_with_entry_count = node
1073 1132 .descendants_with_entry_count
1074 1133 .checked_sub(1)
1075 1134 .expect(
1076 1135 "descendants_with_entry_count should be >= 0",
1077 1136 );
1078 1137 }
1079 1138 if dropped.was_tracked {
1080 1139 node.tracked_descendants_count = node
1081 1140 .tracked_descendants_count
1082 1141 .checked_sub(1)
1083 1142 .expect(
1084 1143 "tracked_descendants_count should be >= 0",
1085 1144 );
1086 1145 }
1087 1146
1088 1147 // Directory caches must be invalidated when removing a
1089 1148 // child node
1090 1149 if removed {
1091 1150 if let NodeData::CachedDirectory { .. } = &node.data {
1092 1151 node.data = NodeData::None
1093 1152 }
1094 1153 }
1095 1154 } else {
1096 1155 return Ok(None);
1097 1156 }
1098 1157 } else {
1099 1158 let entry = node.data.as_entry();
1100 1159 let was_tracked = entry.map_or(false, |entry| entry.tracked());
1101 1160 let had_entry = entry.is_some();
1102 1161 if had_entry {
1103 1162 node.data = NodeData::None
1104 1163 }
1105 1164 let mut had_copy_source = false;
1106 1165 if let Some(source) = &node.copy_source {
1107 1166 DirstateMap::count_dropped_path(unreachable_bytes, source);
1108 1167 had_copy_source = true;
1109 1168 node.copy_source = None
1110 1169 }
1111 1170 dropped = Dropped {
1112 1171 was_tracked,
1113 1172 had_entry,
1114 1173 had_copy_source,
1115 1174 };
1116 1175 }
1117 1176 // After recursion, for both leaf (rest_of_path is None) nodes and
1118 1177 // parent nodes, remove a node if it just became empty.
1119 1178 let remove = !node.data.has_entry()
1120 1179 && node.copy_source.is_none()
1121 1180 && node.children.is_empty();
1122 1181 if remove {
1123 1182 let (key, _) =
1124 1183 nodes.remove_entry(first_path_component).unwrap();
1125 1184 DirstateMap::count_dropped_path(
1126 1185 unreachable_bytes,
1127 1186 key.full_path(),
1128 1187 )
1129 1188 }
1130 1189 Ok(Some((dropped, remove)))
1131 1190 }
1132 1191
1133 1192 self.with_dmap_mut(|map| {
1134 1193 if let Some((dropped, _removed)) = recur(
1135 1194 map.on_disk,
1136 1195 &mut map.unreachable_bytes,
1137 1196 &mut map.root,
1138 1197 filename,
1139 1198 )? {
1140 1199 if dropped.had_entry {
1141 1200 map.nodes_with_entry_count = map
1142 1201 .nodes_with_entry_count
1143 1202 .checked_sub(1)
1144 1203 .expect("nodes_with_entry_count should be >= 0");
1145 1204 }
1146 1205 if dropped.had_copy_source {
1147 1206 map.nodes_with_copy_source_count = map
1148 1207 .nodes_with_copy_source_count
1149 1208 .checked_sub(1)
1150 1209 .expect("nodes_with_copy_source_count should be >= 0");
1151 1210 }
1152 1211 } else {
1153 1212 debug_assert!(!was_tracked);
1154 1213 }
1155 1214 Ok(())
1156 1215 })
1157 1216 }
1158 1217
1159 1218 pub fn has_tracked_dir(
1160 1219 &mut self,
1161 1220 directory: &HgPath,
1162 1221 ) -> Result<bool, DirstateError> {
1163 1222 self.with_dmap_mut(|map| {
1164 1223 if let Some(node) = map.get_node(directory)? {
1165 1224 // A node without a `DirstateEntry` was created to hold child
1166 1225 // nodes, and is therefore a directory.
1167 1226 let state = node.state()?;
1168 1227 Ok(state.is_none() && node.tracked_descendants_count() > 0)
1169 1228 } else {
1170 1229 Ok(false)
1171 1230 }
1172 1231 })
1173 1232 }
1174 1233
1175 1234 pub fn has_dir(
1176 1235 &mut self,
1177 1236 directory: &HgPath,
1178 1237 ) -> Result<bool, DirstateError> {
1179 1238 self.with_dmap_mut(|map| {
1180 1239 if let Some(node) = map.get_node(directory)? {
1181 1240 // A node without a `DirstateEntry` was created to hold child
1182 1241 // nodes, and is therefore a directory.
1183 1242 let state = node.state()?;
1184 1243 Ok(state.is_none() && node.descendants_with_entry_count() > 0)
1185 1244 } else {
1186 1245 Ok(false)
1187 1246 }
1188 1247 })
1189 1248 }
1190 1249
1191 1250 #[timed]
1192 1251 pub fn pack_v1(
1193 1252 &self,
1194 1253 parents: DirstateParents,
1195 1254 ) -> Result<Vec<u8>, DirstateError> {
1196 1255 let map = self.get_map();
1197 1256 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
1198 1257 // reallocations
1199 1258 let mut size = parents.as_bytes().len();
1200 1259 for node in map.iter_nodes() {
1201 1260 let node = node?;
1202 1261 if node.entry()?.is_some() {
1203 1262 size += packed_entry_size(
1204 1263 node.full_path(map.on_disk)?,
1205 1264 node.copy_source(map.on_disk)?,
1206 1265 );
1207 1266 }
1208 1267 }
1209 1268
1210 1269 let mut packed = Vec::with_capacity(size);
1211 1270 packed.extend(parents.as_bytes());
1212 1271
1213 1272 for node in map.iter_nodes() {
1214 1273 let node = node?;
1215 1274 if let Some(entry) = node.entry()? {
1216 1275 pack_entry(
1217 1276 node.full_path(map.on_disk)?,
1218 1277 &entry,
1219 1278 node.copy_source(map.on_disk)?,
1220 1279 &mut packed,
1221 1280 );
1222 1281 }
1223 1282 }
1224 1283 Ok(packed)
1225 1284 }
1226 1285
1227 1286 /// Returns new data and metadata together with whether that data should be
1228 1287 /// appended to the existing data file whose content is at
1229 1288 /// `map.on_disk` (true), instead of written to a new data file
1230 1289 /// (false).
1231 1290 #[timed]
1232 1291 pub fn pack_v2(
1233 1292 &self,
1234 1293 can_append: bool,
1235 1294 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool), DirstateError> {
1236 1295 let map = self.get_map();
1237 1296 on_disk::write(map, can_append)
1238 1297 }
1239 1298
1240 1299 /// `callback` allows the caller to process and do something with the
1241 1300 /// results of the status. This is needed to do so efficiently (i.e.
1242 1301 /// without cloning the `DirstateStatus` object with its paths) because
1243 1302 /// we need to borrow from `Self`.
1244 1303 pub fn with_status<R>(
1245 1304 &mut self,
1246 1305 matcher: &(dyn Matcher + Sync),
1247 1306 root_dir: PathBuf,
1248 1307 ignore_files: Vec<PathBuf>,
1249 1308 options: StatusOptions,
1250 1309 callback: impl for<'r> FnOnce(
1251 1310 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
1252 1311 ) -> R,
1253 1312 ) -> R {
1254 1313 self.with_dmap_mut(|map| {
1255 1314 callback(super::status::status(
1256 1315 map,
1257 1316 matcher,
1258 1317 root_dir,
1259 1318 ignore_files,
1260 1319 options,
1261 1320 ))
1262 1321 })
1263 1322 }
1264 1323
1265 1324 pub fn copy_map_len(&self) -> usize {
1266 1325 let map = self.get_map();
1267 1326 map.nodes_with_copy_source_count as usize
1268 1327 }
1269 1328
1270 1329 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1271 1330 let map = self.get_map();
1272 1331 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1273 1332 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1274 1333 Some((node.full_path(map.on_disk)?, source))
1275 1334 } else {
1276 1335 None
1277 1336 })
1278 1337 }))
1279 1338 }
1280 1339
1281 1340 pub fn copy_map_contains_key(
1282 1341 &self,
1283 1342 key: &HgPath,
1284 1343 ) -> Result<bool, DirstateV2ParseError> {
1285 1344 let map = self.get_map();
1286 1345 Ok(if let Some(node) = map.get_node(key)? {
1287 1346 node.has_copy_source()
1288 1347 } else {
1289 1348 false
1290 1349 })
1291 1350 }
1292 1351
1293 1352 pub fn copy_map_get(
1294 1353 &self,
1295 1354 key: &HgPath,
1296 1355 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1297 1356 let map = self.get_map();
1298 1357 if let Some(node) = map.get_node(key)? {
1299 1358 if let Some(source) = node.copy_source(map.on_disk)? {
1300 1359 return Ok(Some(source));
1301 1360 }
1302 1361 }
1303 1362 Ok(None)
1304 1363 }
1305 1364
1306 1365 pub fn copy_map_remove(
1307 1366 &mut self,
1308 1367 key: &HgPath,
1309 1368 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1310 1369 self.with_dmap_mut(|map| {
1311 1370 let count = &mut map.nodes_with_copy_source_count;
1312 1371 let unreachable_bytes = &mut map.unreachable_bytes;
1313 1372 Ok(DirstateMap::get_node_mut(
1314 1373 map.on_disk,
1315 1374 unreachable_bytes,
1316 1375 &mut map.root,
1317 1376 key,
1318 1377 )?
1319 1378 .and_then(|node| {
1320 1379 if let Some(source) = &node.copy_source {
1321 1380 *count -= 1;
1322 1381 DirstateMap::count_dropped_path(unreachable_bytes, source);
1323 1382 }
1324 1383 node.copy_source.take().map(Cow::into_owned)
1325 1384 }))
1326 1385 })
1327 1386 }
1328 1387
1329 1388 pub fn copy_map_insert(
1330 1389 &mut self,
1331 1390 key: HgPathBuf,
1332 1391 value: HgPathBuf,
1333 1392 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1334 1393 self.with_dmap_mut(|map| {
1335 1394 let node = DirstateMap::get_or_insert_node(
1336 1395 map.on_disk,
1337 1396 &mut map.unreachable_bytes,
1338 1397 &mut map.root,
1339 1398 &key,
1340 1399 WithBasename::to_cow_owned,
1341 1400 |_ancestor| {},
1342 1401 )?;
1343 1402 if node.copy_source.is_none() {
1344 1403 map.nodes_with_copy_source_count += 1
1345 1404 }
1346 1405 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
1347 1406 })
1348 1407 }
1349 1408
1350 1409 pub fn len(&self) -> usize {
1351 1410 let map = self.get_map();
1352 1411 map.nodes_with_entry_count as usize
1353 1412 }
1354 1413
1355 1414 pub fn contains_key(
1356 1415 &self,
1357 1416 key: &HgPath,
1358 1417 ) -> Result<bool, DirstateV2ParseError> {
1359 1418 Ok(self.get(key)?.is_some())
1360 1419 }
1361 1420
1362 1421 pub fn get(
1363 1422 &self,
1364 1423 key: &HgPath,
1365 1424 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1366 1425 let map = self.get_map();
1367 1426 Ok(if let Some(node) = map.get_node(key)? {
1368 1427 node.entry()?
1369 1428 } else {
1370 1429 None
1371 1430 })
1372 1431 }
1373 1432
1374 1433 pub fn iter(&self) -> StateMapIter<'_> {
1375 1434 let map = self.get_map();
1376 1435 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1377 1436 Ok(if let Some(entry) = node.entry()? {
1378 1437 Some((node.full_path(map.on_disk)?, entry))
1379 1438 } else {
1380 1439 None
1381 1440 })
1382 1441 }))
1383 1442 }
1384 1443
1385 1444 pub fn iter_tracked_dirs(
1386 1445 &mut self,
1387 1446 ) -> Result<
1388 1447 Box<
1389 1448 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1390 1449 + Send
1391 1450 + '_,
1392 1451 >,
1393 1452 DirstateError,
1394 1453 > {
1395 1454 let map = self.get_map();
1396 1455 let on_disk = map.on_disk;
1397 1456 Ok(Box::new(filter_map_results(
1398 1457 map.iter_nodes(),
1399 1458 move |node| {
1400 1459 Ok(if node.tracked_descendants_count() > 0 {
1401 1460 Some(node.full_path(on_disk)?)
1402 1461 } else {
1403 1462 None
1404 1463 })
1405 1464 },
1406 1465 )))
1407 1466 }
1408 1467
1409 1468 pub fn debug_iter(
1410 1469 &self,
1411 1470 all: bool,
1412 1471 ) -> Box<
1413 1472 dyn Iterator<
1414 1473 Item = Result<
1415 1474 (&HgPath, (u8, i32, i32, i32)),
1416 1475 DirstateV2ParseError,
1417 1476 >,
1418 1477 > + Send
1419 1478 + '_,
1420 1479 > {
1421 1480 let map = self.get_map();
1422 1481 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1423 1482 let debug_tuple = if let Some(entry) = node.entry()? {
1424 1483 entry.debug_tuple()
1425 1484 } else if !all {
1426 1485 return Ok(None);
1427 1486 } else if let Some(mtime) = node.cached_directory_mtime()? {
1428 1487 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1429 1488 } else {
1430 1489 (b' ', 0, -1, -1)
1431 1490 };
1432 1491 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1433 1492 }))
1434 1493 }
1435 1494 }
@@ -1,582 +1,592 b''
1 1 // dirstate_map.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
9 9 //! `hg-core` package.
10 10
11 11 use std::cell::{RefCell, RefMut};
12 12 use std::convert::TryInto;
13 13
14 14 use cpython::{
15 15 exc, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList, PyNone, PyObject,
16 16 PyResult, Python, PythonObject, ToPyObject, UnsafePyLeaked,
17 17 };
18 18 use hg::dirstate::{ParentFileData, TruncatedTimestamp};
19 19
20 20 use crate::{
21 21 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
22 22 dirstate::item::DirstateItem,
23 23 pybytes_deref::PyBytesDeref,
24 24 };
25 25 use hg::{
26 26 dirstate::StateMapIter,
27 27 dirstate_tree::on_disk::DirstateV2ParseError,
28 28 dirstate_tree::owning::OwningDirstateMap,
29 29 revlog::Node,
30 30 utils::files::normalize_case,
31 31 utils::hg_path::{HgPath, HgPathBuf},
32 32 DirstateEntry, DirstateError, DirstateParents, EntryState,
33 33 };
34 34
35 35 // TODO
36 36 // This object needs to share references to multiple members of its Rust
37 37 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
38 38 // Right now `CopyMap` is done, but it needs to have an explicit reference
39 39 // to `RustDirstateMap` which itself needs to have an encapsulation for
40 40 // every method in `CopyMap` (copymapcopy, etc.).
41 41 // This is ugly and hard to maintain.
42 42 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
43 43 // `py_class!` is already implemented and does not mention
44 44 // `RustDirstateMap`, rightfully so.
45 45 // All attributes also have to have a separate refcount data attribute for
46 46 // leaks, with all methods that go along for reference sharing.
47 47 py_class!(pub class DirstateMap |py| {
48 48 @shared data inner: OwningDirstateMap;
49 49
50 50 /// Returns a `(dirstate_map, parents)` tuple
51 51 @staticmethod
52 52 def new_v1(
53 53 on_disk: PyBytes,
54 54 ) -> PyResult<PyObject> {
55 55 let on_disk = PyBytesDeref::new(py, on_disk);
56 56 let (map, parents) = OwningDirstateMap::new_v1(on_disk)
57 57 .map_err(|e| dirstate_error(py, e))?;
58 58 let map = Self::create_instance(py, map)?;
59 59 let p1 = PyBytes::new(py, parents.p1.as_bytes());
60 60 let p2 = PyBytes::new(py, parents.p2.as_bytes());
61 61 let parents = (p1, p2);
62 62 Ok((map, parents).to_py_object(py).into_object())
63 63 }
64 64
65 65 /// Returns a DirstateMap
66 66 @staticmethod
67 67 def new_v2(
68 68 on_disk: PyBytes,
69 69 data_size: usize,
70 70 tree_metadata: PyBytes,
71 71 ) -> PyResult<PyObject> {
72 72 let dirstate_error = |e: DirstateError| {
73 73 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
74 74 };
75 75 let on_disk = PyBytesDeref::new(py, on_disk);
76 76 let map = OwningDirstateMap::new_v2(
77 77 on_disk, data_size, tree_metadata.data(py),
78 78 ).map_err(dirstate_error)?;
79 79 let map = Self::create_instance(py, map)?;
80 80 Ok(map.into_object())
81 81 }
82 82
83 83 def clear(&self) -> PyResult<PyObject> {
84 84 self.inner(py).borrow_mut().clear();
85 85 Ok(py.None())
86 86 }
87 87
88 88 def get(
89 89 &self,
90 90 key: PyObject,
91 91 default: Option<PyObject> = None
92 92 ) -> PyResult<Option<PyObject>> {
93 93 let key = key.extract::<PyBytes>(py)?;
94 94 match self
95 95 .inner(py)
96 96 .borrow()
97 97 .get(HgPath::new(key.data(py)))
98 98 .map_err(|e| v2_error(py, e))?
99 99 {
100 100 Some(entry) => {
101 101 Ok(Some(DirstateItem::new_as_pyobject(py, entry)?))
102 102 },
103 103 None => Ok(default)
104 104 }
105 105 }
106 106
107 107 def set_dirstate_item(
108 108 &self,
109 109 path: PyObject,
110 110 item: DirstateItem
111 111 ) -> PyResult<PyObject> {
112 112 let f = path.extract::<PyBytes>(py)?;
113 113 let filename = HgPath::new(f.data(py));
114 114 self.inner(py)
115 115 .borrow_mut()
116 116 .set_entry(filename, item.get_entry(py))
117 117 .map_err(|e| v2_error(py, e))?;
118 118 Ok(py.None())
119 119 }
120 120
121 121 def addfile(
122 122 &self,
123 123 f: PyBytes,
124 124 item: DirstateItem,
125 125 ) -> PyResult<PyNone> {
126 126 let filename = HgPath::new(f.data(py));
127 127 let entry = item.get_entry(py);
128 128 self.inner(py)
129 129 .borrow_mut()
130 130 .add_file(filename, entry)
131 131 .map_err(|e |dirstate_error(py, e))?;
132 132 Ok(PyNone)
133 133 }
134 134
135 135 def set_tracked(&self, f: PyObject) -> PyResult<PyBool> {
136 136 let bytes = f.extract::<PyBytes>(py)?;
137 137 let path = HgPath::new(bytes.data(py));
138 138 let res = self.inner(py).borrow_mut().set_tracked(path);
139 139 let was_tracked = res.or_else(|_| {
140 140 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
141 141 })?;
142 142 Ok(was_tracked.to_py_object(py))
143 143 }
144 144
145 def set_untracked(&self, f: PyObject) -> PyResult<PyBool> {
146 let bytes = f.extract::<PyBytes>(py)?;
147 let path = HgPath::new(bytes.data(py));
148 let res = self.inner(py).borrow_mut().set_untracked(path);
149 let was_tracked = res.or_else(|_| {
150 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
151 })?;
152 Ok(was_tracked.to_py_object(py))
153 }
154
145 155 def set_clean(
146 156 &self,
147 157 f: PyObject,
148 158 mode: u32,
149 159 size: u32,
150 160 mtime: (i64, u32, bool)
151 161 ) -> PyResult<PyNone> {
152 162 let (mtime_s, mtime_ns, second_ambiguous) = mtime;
153 163 let timestamp = TruncatedTimestamp::new_truncate(
154 164 mtime_s, mtime_ns, second_ambiguous
155 165 );
156 166 let bytes = f.extract::<PyBytes>(py)?;
157 167 let path = HgPath::new(bytes.data(py));
158 168 let res = self.inner(py).borrow_mut().set_clean(
159 169 path, mode, size, timestamp,
160 170 );
161 171 res.or_else(|_| {
162 172 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
163 173 })?;
164 174 Ok(PyNone)
165 175 }
166 176
167 177 def set_possibly_dirty(&self, f: PyObject) -> PyResult<PyNone> {
168 178 let bytes = f.extract::<PyBytes>(py)?;
169 179 let path = HgPath::new(bytes.data(py));
170 180 let res = self.inner(py).borrow_mut().set_possibly_dirty(path);
171 181 res.or_else(|_| {
172 182 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
173 183 })?;
174 184 Ok(PyNone)
175 185 }
176 186
177 187 def reset_state(
178 188 &self,
179 189 f: PyObject,
180 190 wc_tracked: bool,
181 191 p1_tracked: bool,
182 192 p2_info: bool,
183 193 has_meaningful_mtime: bool,
184 194 parentfiledata: Option<(u32, u32, Option<(i64, u32, bool)>)>,
185 195 ) -> PyResult<PyNone> {
186 196 let mut has_meaningful_mtime = has_meaningful_mtime;
187 197 let parent_file_data = match parentfiledata {
188 198 None => {
189 199 has_meaningful_mtime = false;
190 200 None
191 201 },
192 202 Some(data) => {
193 203 let (mode, size, mtime_info) = data;
194 204 let mtime = if let Some(mtime_info) = mtime_info {
195 205 let (mtime_s, mtime_ns, second_ambiguous) = mtime_info;
196 206 let timestamp = TruncatedTimestamp::new_truncate(
197 207 mtime_s, mtime_ns, second_ambiguous
198 208 );
199 209 Some(timestamp)
200 210 } else {
201 211 has_meaningful_mtime = false;
202 212 None
203 213 };
204 214 Some(ParentFileData {
205 215 mode_size: Some((mode, size)),
206 216 mtime,
207 217 })
208 218 }
209 219 };
210 220 let bytes = f.extract::<PyBytes>(py)?;
211 221 let path = HgPath::new(bytes.data(py));
212 222 let res = self.inner(py).borrow_mut().reset_state(
213 223 path,
214 224 wc_tracked,
215 225 p1_tracked,
216 226 p2_info,
217 227 has_meaningful_mtime,
218 228 parent_file_data,
219 229 );
220 230 res.or_else(|_| {
221 231 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
222 232 })?;
223 233 Ok(PyNone)
224 234 }
225 235
226 236 def removefile(
227 237 &self,
228 238 f: PyObject,
229 239 in_merge: PyObject
230 240 ) -> PyResult<PyObject> {
231 241 self.inner(py).borrow_mut()
232 242 .remove_file(
233 243 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
234 244 in_merge.extract::<PyBool>(py)?.is_true(),
235 245 )
236 246 .or_else(|_| {
237 247 Err(PyErr::new::<exc::OSError, _>(
238 248 py,
239 249 "Dirstate error".to_string(),
240 250 ))
241 251 })?;
242 252 Ok(py.None())
243 253 }
244 254
245 255 def drop_item_and_copy_source(
246 256 &self,
247 257 f: PyBytes,
248 258 ) -> PyResult<PyNone> {
249 259 self.inner(py)
250 260 .borrow_mut()
251 261 .drop_entry_and_copy_source(HgPath::new(f.data(py)))
252 262 .map_err(|e |dirstate_error(py, e))?;
253 263 Ok(PyNone)
254 264 }
255 265
256 266 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
257 267 let d = d.extract::<PyBytes>(py)?;
258 268 Ok(self.inner(py).borrow_mut()
259 269 .has_tracked_dir(HgPath::new(d.data(py)))
260 270 .map_err(|e| {
261 271 PyErr::new::<exc::ValueError, _>(py, e.to_string())
262 272 })?
263 273 .to_py_object(py))
264 274 }
265 275
266 276 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
267 277 let d = d.extract::<PyBytes>(py)?;
268 278 Ok(self.inner(py).borrow_mut()
269 279 .has_dir(HgPath::new(d.data(py)))
270 280 .map_err(|e| {
271 281 PyErr::new::<exc::ValueError, _>(py, e.to_string())
272 282 })?
273 283 .to_py_object(py))
274 284 }
275 285
276 286 def write_v1(
277 287 &self,
278 288 p1: PyObject,
279 289 p2: PyObject,
280 290 ) -> PyResult<PyBytes> {
281 291 let inner = self.inner(py).borrow();
282 292 let parents = DirstateParents {
283 293 p1: extract_node_id(py, &p1)?,
284 294 p2: extract_node_id(py, &p2)?,
285 295 };
286 296 let result = inner.pack_v1(parents);
287 297 match result {
288 298 Ok(packed) => Ok(PyBytes::new(py, &packed)),
289 299 Err(_) => Err(PyErr::new::<exc::OSError, _>(
290 300 py,
291 301 "Dirstate error".to_string(),
292 302 )),
293 303 }
294 304 }
295 305
296 306 /// Returns new data together with whether that data should be appended to
297 307 /// the existing data file whose content is at `self.on_disk` (True),
298 308 /// instead of written to a new data file (False).
299 309 def write_v2(
300 310 &self,
301 311 can_append: bool,
302 312 ) -> PyResult<PyObject> {
303 313 let inner = self.inner(py).borrow();
304 314 let result = inner.pack_v2(can_append);
305 315 match result {
306 316 Ok((packed, tree_metadata, append)) => {
307 317 let packed = PyBytes::new(py, &packed);
308 318 let tree_metadata = PyBytes::new(py, tree_metadata.as_bytes());
309 319 let tuple = (packed, tree_metadata, append);
310 320 Ok(tuple.to_py_object(py).into_object())
311 321 },
312 322 Err(_) => Err(PyErr::new::<exc::OSError, _>(
313 323 py,
314 324 "Dirstate error".to_string(),
315 325 )),
316 326 }
317 327 }
318 328
319 329 def filefoldmapasdict(&self) -> PyResult<PyDict> {
320 330 let dict = PyDict::new(py);
321 331 for item in self.inner(py).borrow_mut().iter() {
322 332 let (path, entry) = item.map_err(|e| v2_error(py, e))?;
323 333 if entry.state() != EntryState::Removed {
324 334 let key = normalize_case(path);
325 335 let value = path;
326 336 dict.set_item(
327 337 py,
328 338 PyBytes::new(py, key.as_bytes()).into_object(),
329 339 PyBytes::new(py, value.as_bytes()).into_object(),
330 340 )?;
331 341 }
332 342 }
333 343 Ok(dict)
334 344 }
335 345
336 346 def __len__(&self) -> PyResult<usize> {
337 347 Ok(self.inner(py).borrow().len())
338 348 }
339 349
340 350 def __contains__(&self, key: PyObject) -> PyResult<bool> {
341 351 let key = key.extract::<PyBytes>(py)?;
342 352 self.inner(py)
343 353 .borrow()
344 354 .contains_key(HgPath::new(key.data(py)))
345 355 .map_err(|e| v2_error(py, e))
346 356 }
347 357
348 358 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
349 359 let key = key.extract::<PyBytes>(py)?;
350 360 let key = HgPath::new(key.data(py));
351 361 match self
352 362 .inner(py)
353 363 .borrow()
354 364 .get(key)
355 365 .map_err(|e| v2_error(py, e))?
356 366 {
357 367 Some(entry) => {
358 368 Ok(DirstateItem::new_as_pyobject(py, entry)?)
359 369 },
360 370 None => Err(PyErr::new::<exc::KeyError, _>(
361 371 py,
362 372 String::from_utf8_lossy(key.as_bytes()),
363 373 )),
364 374 }
365 375 }
366 376
367 377 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
368 378 let leaked_ref = self.inner(py).leak_immutable();
369 379 DirstateMapKeysIterator::from_inner(
370 380 py,
371 381 unsafe { leaked_ref.map(py, |o| o.iter()) },
372 382 )
373 383 }
374 384
375 385 def items(&self) -> PyResult<DirstateMapItemsIterator> {
376 386 let leaked_ref = self.inner(py).leak_immutable();
377 387 DirstateMapItemsIterator::from_inner(
378 388 py,
379 389 unsafe { leaked_ref.map(py, |o| o.iter()) },
380 390 )
381 391 }
382 392
383 393 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
384 394 let leaked_ref = self.inner(py).leak_immutable();
385 395 DirstateMapKeysIterator::from_inner(
386 396 py,
387 397 unsafe { leaked_ref.map(py, |o| o.iter()) },
388 398 )
389 399 }
390 400
391 401 // TODO all copymap* methods, see docstring above
392 402 def copymapcopy(&self) -> PyResult<PyDict> {
393 403 let dict = PyDict::new(py);
394 404 for item in self.inner(py).borrow().copy_map_iter() {
395 405 let (key, value) = item.map_err(|e| v2_error(py, e))?;
396 406 dict.set_item(
397 407 py,
398 408 PyBytes::new(py, key.as_bytes()),
399 409 PyBytes::new(py, value.as_bytes()),
400 410 )?;
401 411 }
402 412 Ok(dict)
403 413 }
404 414
405 415 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
406 416 let key = key.extract::<PyBytes>(py)?;
407 417 match self
408 418 .inner(py)
409 419 .borrow()
410 420 .copy_map_get(HgPath::new(key.data(py)))
411 421 .map_err(|e| v2_error(py, e))?
412 422 {
413 423 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
414 424 None => Err(PyErr::new::<exc::KeyError, _>(
415 425 py,
416 426 String::from_utf8_lossy(key.data(py)),
417 427 )),
418 428 }
419 429 }
420 430 def copymap(&self) -> PyResult<CopyMap> {
421 431 CopyMap::from_inner(py, self.clone_ref(py))
422 432 }
423 433
424 434 def copymaplen(&self) -> PyResult<usize> {
425 435 Ok(self.inner(py).borrow().copy_map_len())
426 436 }
427 437 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
428 438 let key = key.extract::<PyBytes>(py)?;
429 439 self.inner(py)
430 440 .borrow()
431 441 .copy_map_contains_key(HgPath::new(key.data(py)))
432 442 .map_err(|e| v2_error(py, e))
433 443 }
434 444 def copymapget(
435 445 &self,
436 446 key: PyObject,
437 447 default: Option<PyObject>
438 448 ) -> PyResult<Option<PyObject>> {
439 449 let key = key.extract::<PyBytes>(py)?;
440 450 match self
441 451 .inner(py)
442 452 .borrow()
443 453 .copy_map_get(HgPath::new(key.data(py)))
444 454 .map_err(|e| v2_error(py, e))?
445 455 {
446 456 Some(copy) => Ok(Some(
447 457 PyBytes::new(py, copy.as_bytes()).into_object(),
448 458 )),
449 459 None => Ok(default),
450 460 }
451 461 }
452 462 def copymapsetitem(
453 463 &self,
454 464 key: PyObject,
455 465 value: PyObject
456 466 ) -> PyResult<PyObject> {
457 467 let key = key.extract::<PyBytes>(py)?;
458 468 let value = value.extract::<PyBytes>(py)?;
459 469 self.inner(py)
460 470 .borrow_mut()
461 471 .copy_map_insert(
462 472 HgPathBuf::from_bytes(key.data(py)),
463 473 HgPathBuf::from_bytes(value.data(py)),
464 474 )
465 475 .map_err(|e| v2_error(py, e))?;
466 476 Ok(py.None())
467 477 }
468 478 def copymappop(
469 479 &self,
470 480 key: PyObject,
471 481 default: Option<PyObject>
472 482 ) -> PyResult<Option<PyObject>> {
473 483 let key = key.extract::<PyBytes>(py)?;
474 484 match self
475 485 .inner(py)
476 486 .borrow_mut()
477 487 .copy_map_remove(HgPath::new(key.data(py)))
478 488 .map_err(|e| v2_error(py, e))?
479 489 {
480 490 Some(copy) => Ok(Some(
481 491 PyBytes::new(py, copy.as_bytes()).into_object(),
482 492 )),
483 493 None => Ok(default),
484 494 }
485 495 }
486 496
487 497 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
488 498 let leaked_ref = self.inner(py).leak_immutable();
489 499 CopyMapKeysIterator::from_inner(
490 500 py,
491 501 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
492 502 )
493 503 }
494 504
495 505 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
496 506 let leaked_ref = self.inner(py).leak_immutable();
497 507 CopyMapItemsIterator::from_inner(
498 508 py,
499 509 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
500 510 )
501 511 }
502 512
503 513 def tracked_dirs(&self) -> PyResult<PyList> {
504 514 let dirs = PyList::new(py, &[]);
505 515 for path in self.inner(py).borrow_mut().iter_tracked_dirs()
506 516 .map_err(|e |dirstate_error(py, e))?
507 517 {
508 518 let path = path.map_err(|e| v2_error(py, e))?;
509 519 let path = PyBytes::new(py, path.as_bytes());
510 520 dirs.append(py, path.into_object())
511 521 }
512 522 Ok(dirs)
513 523 }
514 524
515 525 def debug_iter(&self, all: bool) -> PyResult<PyList> {
516 526 let dirs = PyList::new(py, &[]);
517 527 for item in self.inner(py).borrow().debug_iter(all) {
518 528 let (path, (state, mode, size, mtime)) =
519 529 item.map_err(|e| v2_error(py, e))?;
520 530 let path = PyBytes::new(py, path.as_bytes());
521 531 let item = (path, state, mode, size, mtime);
522 532 dirs.append(py, item.to_py_object(py).into_object())
523 533 }
524 534 Ok(dirs)
525 535 }
526 536 });
527 537
528 538 impl DirstateMap {
529 539 pub fn get_inner_mut<'a>(
530 540 &'a self,
531 541 py: Python<'a>,
532 542 ) -> RefMut<'a, OwningDirstateMap> {
533 543 self.inner(py).borrow_mut()
534 544 }
535 545 fn translate_key(
536 546 py: Python,
537 547 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
538 548 ) -> PyResult<Option<PyBytes>> {
539 549 let (f, _entry) = res.map_err(|e| v2_error(py, e))?;
540 550 Ok(Some(PyBytes::new(py, f.as_bytes())))
541 551 }
542 552 fn translate_key_value(
543 553 py: Python,
544 554 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
545 555 ) -> PyResult<Option<(PyBytes, PyObject)>> {
546 556 let (f, entry) = res.map_err(|e| v2_error(py, e))?;
547 557 Ok(Some((
548 558 PyBytes::new(py, f.as_bytes()),
549 559 DirstateItem::new_as_pyobject(py, entry)?,
550 560 )))
551 561 }
552 562 }
553 563
554 564 py_shared_iterator!(
555 565 DirstateMapKeysIterator,
556 566 UnsafePyLeaked<StateMapIter<'static>>,
557 567 DirstateMap::translate_key,
558 568 Option<PyBytes>
559 569 );
560 570
561 571 py_shared_iterator!(
562 572 DirstateMapItemsIterator,
563 573 UnsafePyLeaked<StateMapIter<'static>>,
564 574 DirstateMap::translate_key_value,
565 575 Option<(PyBytes, PyObject)>
566 576 );
567 577
568 578 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<Node> {
569 579 let bytes = obj.extract::<PyBytes>(py)?;
570 580 match bytes.data(py).try_into() {
571 581 Ok(s) => Ok(s),
572 582 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
573 583 }
574 584 }
575 585
576 586 pub(super) fn v2_error(py: Python<'_>, _: DirstateV2ParseError) -> PyErr {
577 587 PyErr::new::<exc::ValueError, _>(py, "corrupted dirstate-v2")
578 588 }
579 589
580 590 fn dirstate_error(py: Python<'_>, e: DirstateError) -> PyErr {
581 591 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
582 592 }
General Comments 0
You need to be logged in to leave comments. Login now