##// END OF EJS Templates
dirstate-map: factor out the change to _dirs and _alldirs on adding...
marmoute -
r48487:e59bd672 default
parent child Browse files
Show More
@@ -1,752 +1,756 b''
1 1 # dirstatemap.py
2 2 #
3 3 # This software may be used and distributed according to the terms of the
4 4 # GNU General Public License version 2 or any later version.
5 5
6 6 from __future__ import absolute_import
7 7
8 8 import errno
9 9
10 10 from .i18n import _
11 11
12 12 from . import (
13 13 error,
14 14 pathutil,
15 15 policy,
16 16 pycompat,
17 17 txnutil,
18 18 util,
19 19 )
20 20
21 21 from .dirstateutils import (
22 22 docket as docketmod,
23 23 )
24 24
25 25 parsers = policy.importmod('parsers')
26 26 rustmod = policy.importrust('dirstate')
27 27
28 28 propertycache = util.propertycache
29 29
30 30 DirstateItem = parsers.DirstateItem
31 31
32 32
33 33 # a special value used internally for `size` if the file come from the other parent
34 34 FROM_P2 = -2
35 35
36 36 # a special value used internally for `size` if the file is modified/merged/added
37 37 NONNORMAL = -1
38 38
39 39 # a special value used internally for `time` if the time is ambigeous
40 40 AMBIGUOUS_TIME = -1
41 41
42 42 rangemask = 0x7FFFFFFF
43 43
44 44
45 45 class dirstatemap(object):
46 46 """Map encapsulating the dirstate's contents.
47 47
48 48 The dirstate contains the following state:
49 49
50 50 - `identity` is the identity of the dirstate file, which can be used to
51 51 detect when changes have occurred to the dirstate file.
52 52
53 53 - `parents` is a pair containing the parents of the working copy. The
54 54 parents are updated by calling `setparents`.
55 55
56 56 - the state map maps filenames to tuples of (state, mode, size, mtime),
57 57 where state is a single character representing 'normal', 'added',
58 58 'removed', or 'merged'. It is read by treating the dirstate as a
59 59 dict. File state is updated by calling the `addfile`, `removefile` and
60 60 `dropfile` methods.
61 61
62 62 - `copymap` maps destination filenames to their source filename.
63 63
64 64 The dirstate also provides the following views onto the state:
65 65
66 66 - `nonnormalset` is a set of the filenames that have state other
67 67 than 'normal', or are normal but have an mtime of -1 ('normallookup').
68 68
69 69 - `otherparentset` is a set of the filenames that are marked as coming
70 70 from the second parent when the dirstate is currently being merged.
71 71
72 72 - `filefoldmap` is a dict mapping normalized filenames to the denormalized
73 73 form that they appear as in the dirstate.
74 74
75 75 - `dirfoldmap` is a dict mapping normalized directory names to the
76 76 denormalized form that they appear as in the dirstate.
77 77 """
78 78
79 79 def __init__(self, ui, opener, root, nodeconstants, use_dirstate_v2):
80 80 self._ui = ui
81 81 self._opener = opener
82 82 self._root = root
83 83 self._filename = b'dirstate'
84 84 self._nodelen = 20
85 85 self._nodeconstants = nodeconstants
86 86 assert (
87 87 not use_dirstate_v2
88 88 ), "should have detected unsupported requirement"
89 89
90 90 self._parents = None
91 91 self._dirtyparents = False
92 92
93 93 # for consistent view between _pl() and _read() invocations
94 94 self._pendingmode = None
95 95
96 96 @propertycache
97 97 def _map(self):
98 98 self._map = {}
99 99 self.read()
100 100 return self._map
101 101
102 102 @propertycache
103 103 def copymap(self):
104 104 self.copymap = {}
105 105 self._map
106 106 return self.copymap
107 107
108 108 def clear(self):
109 109 self._map.clear()
110 110 self.copymap.clear()
111 111 self.setparents(self._nodeconstants.nullid, self._nodeconstants.nullid)
112 112 util.clearcachedproperty(self, b"_dirs")
113 113 util.clearcachedproperty(self, b"_alldirs")
114 114 util.clearcachedproperty(self, b"filefoldmap")
115 115 util.clearcachedproperty(self, b"dirfoldmap")
116 116 util.clearcachedproperty(self, b"nonnormalset")
117 117 util.clearcachedproperty(self, b"otherparentset")
118 118
119 119 def items(self):
120 120 return pycompat.iteritems(self._map)
121 121
122 122 # forward for python2,3 compat
123 123 iteritems = items
124 124
125 125 debug_iter = items
126 126
127 127 def __len__(self):
128 128 return len(self._map)
129 129
130 130 def __iter__(self):
131 131 return iter(self._map)
132 132
133 133 def get(self, key, default=None):
134 134 return self._map.get(key, default)
135 135
136 136 def __contains__(self, key):
137 137 return key in self._map
138 138
139 139 def __getitem__(self, key):
140 140 return self._map[key]
141 141
142 142 def keys(self):
143 143 return self._map.keys()
144 144
145 145 def preload(self):
146 146 """Loads the underlying data, if it's not already loaded"""
147 147 self._map
148 148
149 def _dirs_incr(self, filename, old_entry=None):
150 """incremente the dirstate counter if applicable"""
151 if (
152 old_entry is None or old_entry.removed
153 ) and "_dirs" in self.__dict__:
154 self._dirs.addpath(filename)
155 if old_entry is None and "_alldirs" in self.__dict__:
156 self._alldirs.addpath(filename)
157
149 158 def addfile(
150 159 self,
151 160 f,
152 161 mode=0,
153 162 size=None,
154 163 mtime=None,
155 164 added=False,
156 165 merged=False,
157 166 from_p2=False,
158 167 possibly_dirty=False,
159 168 ):
160 169 """Add a tracked file to the dirstate."""
161 170 if added:
162 171 assert not merged
163 172 assert not possibly_dirty
164 173 assert not from_p2
165 174 state = b'a'
166 175 size = NONNORMAL
167 176 mtime = AMBIGUOUS_TIME
168 177 elif merged:
169 178 assert not possibly_dirty
170 179 assert not from_p2
171 180 state = b'm'
172 181 size = FROM_P2
173 182 mtime = AMBIGUOUS_TIME
174 183 elif from_p2:
175 184 assert not possibly_dirty
176 185 state = b'n'
177 186 size = FROM_P2
178 187 mtime = AMBIGUOUS_TIME
179 188 elif possibly_dirty:
180 189 state = b'n'
181 190 size = NONNORMAL
182 191 mtime = AMBIGUOUS_TIME
183 192 else:
184 193 assert size != FROM_P2
185 194 assert size != NONNORMAL
186 195 state = b'n'
187 196 size = size & rangemask
188 197 mtime = mtime & rangemask
189 198 assert state is not None
190 199 assert size is not None
191 200 assert mtime is not None
192 201 old_entry = self.get(f)
193 if (
194 old_entry is None or old_entry.removed
195 ) and "_dirs" in self.__dict__:
196 self._dirs.addpath(f)
197 if old_entry is None and "_alldirs" in self.__dict__:
198 self._alldirs.addpath(f)
202 self._dirs_incr(f, old_entry)
199 203 e = self._map[f] = DirstateItem(state, mode, size, mtime)
200 204 if e.dm_nonnormal:
201 205 self.nonnormalset.add(f)
202 206 if e.dm_otherparent:
203 207 self.otherparentset.add(f)
204 208
205 209 def removefile(self, f, in_merge=False):
206 210 """
207 211 Mark a file as removed in the dirstate.
208 212
209 213 The `size` parameter is used to store sentinel values that indicate
210 214 the file's previous state. In the future, we should refactor this
211 215 to be more explicit about what that state is.
212 216 """
213 217 entry = self.get(f)
214 218 size = 0
215 219 if in_merge:
216 220 # XXX we should not be able to have 'm' state and 'FROM_P2' if not
217 221 # during a merge. So I (marmoute) am not sure we need the
218 222 # conditionnal at all. Adding double checking this with assert
219 223 # would be nice.
220 224 if entry is not None:
221 225 # backup the previous state
222 226 if entry.merged: # merge
223 227 size = NONNORMAL
224 228 elif entry.from_p2:
225 229 size = FROM_P2
226 230 self.otherparentset.add(f)
227 231 if entry is not None and not (entry.merged or entry.from_p2):
228 232 self.copymap.pop(f, None)
229 233
230 234 if entry is not None and not entry.removed and "_dirs" in self.__dict__:
231 235 self._dirs.delpath(f)
232 236 if entry is None and "_alldirs" in self.__dict__:
233 237 self._alldirs.addpath(f)
234 238 if "filefoldmap" in self.__dict__:
235 239 normed = util.normcase(f)
236 240 self.filefoldmap.pop(normed, None)
237 241 self._map[f] = DirstateItem(b'r', 0, size, 0)
238 242 self.nonnormalset.add(f)
239 243
240 244 def dropfile(self, f):
241 245 """
242 246 Remove a file from the dirstate. Returns True if the file was
243 247 previously recorded.
244 248 """
245 249 old_entry = self._map.pop(f, None)
246 250 exists = False
247 251 oldstate = b'?'
248 252 if old_entry is not None:
249 253 exists = True
250 254 oldstate = old_entry.state
251 255 if exists:
252 256 if oldstate != b"r" and "_dirs" in self.__dict__:
253 257 self._dirs.delpath(f)
254 258 if "_alldirs" in self.__dict__:
255 259 self._alldirs.delpath(f)
256 260 if "filefoldmap" in self.__dict__:
257 261 normed = util.normcase(f)
258 262 self.filefoldmap.pop(normed, None)
259 263 self.nonnormalset.discard(f)
260 264 return exists
261 265
262 266 def clearambiguoustimes(self, files, now):
263 267 for f in files:
264 268 e = self.get(f)
265 269 if e is not None and e.need_delay(now):
266 270 e.set_possibly_dirty()
267 271 self.nonnormalset.add(f)
268 272
269 273 def nonnormalentries(self):
270 274 '''Compute the nonnormal dirstate entries from the dmap'''
271 275 try:
272 276 return parsers.nonnormalotherparententries(self._map)
273 277 except AttributeError:
274 278 nonnorm = set()
275 279 otherparent = set()
276 280 for fname, e in pycompat.iteritems(self._map):
277 281 if e.dm_nonnormal:
278 282 nonnorm.add(fname)
279 283 if e.from_p2:
280 284 otherparent.add(fname)
281 285 return nonnorm, otherparent
282 286
283 287 @propertycache
284 288 def filefoldmap(self):
285 289 """Returns a dictionary mapping normalized case paths to their
286 290 non-normalized versions.
287 291 """
288 292 try:
289 293 makefilefoldmap = parsers.make_file_foldmap
290 294 except AttributeError:
291 295 pass
292 296 else:
293 297 return makefilefoldmap(
294 298 self._map, util.normcasespec, util.normcasefallback
295 299 )
296 300
297 301 f = {}
298 302 normcase = util.normcase
299 303 for name, s in pycompat.iteritems(self._map):
300 304 if not s.removed:
301 305 f[normcase(name)] = name
302 306 f[b'.'] = b'.' # prevents useless util.fspath() invocation
303 307 return f
304 308
305 309 def hastrackeddir(self, d):
306 310 """
307 311 Returns True if the dirstate contains a tracked (not removed) file
308 312 in this directory.
309 313 """
310 314 return d in self._dirs
311 315
312 316 def hasdir(self, d):
313 317 """
314 318 Returns True if the dirstate contains a file (tracked or removed)
315 319 in this directory.
316 320 """
317 321 return d in self._alldirs
318 322
319 323 @propertycache
320 324 def _dirs(self):
321 325 return pathutil.dirs(self._map, b'r')
322 326
323 327 @propertycache
324 328 def _alldirs(self):
325 329 return pathutil.dirs(self._map)
326 330
327 331 def _opendirstatefile(self):
328 332 fp, mode = txnutil.trypending(self._root, self._opener, self._filename)
329 333 if self._pendingmode is not None and self._pendingmode != mode:
330 334 fp.close()
331 335 raise error.Abort(
332 336 _(b'working directory state may be changed parallelly')
333 337 )
334 338 self._pendingmode = mode
335 339 return fp
336 340
337 341 def parents(self):
338 342 if not self._parents:
339 343 try:
340 344 fp = self._opendirstatefile()
341 345 st = fp.read(2 * self._nodelen)
342 346 fp.close()
343 347 except IOError as err:
344 348 if err.errno != errno.ENOENT:
345 349 raise
346 350 # File doesn't exist, so the current state is empty
347 351 st = b''
348 352
349 353 l = len(st)
350 354 if l == self._nodelen * 2:
351 355 self._parents = (
352 356 st[: self._nodelen],
353 357 st[self._nodelen : 2 * self._nodelen],
354 358 )
355 359 elif l == 0:
356 360 self._parents = (
357 361 self._nodeconstants.nullid,
358 362 self._nodeconstants.nullid,
359 363 )
360 364 else:
361 365 raise error.Abort(
362 366 _(b'working directory state appears damaged!')
363 367 )
364 368
365 369 return self._parents
366 370
367 371 def setparents(self, p1, p2):
368 372 self._parents = (p1, p2)
369 373 self._dirtyparents = True
370 374
371 375 def read(self):
372 376 # ignore HG_PENDING because identity is used only for writing
373 377 self.identity = util.filestat.frompath(
374 378 self._opener.join(self._filename)
375 379 )
376 380
377 381 try:
378 382 fp = self._opendirstatefile()
379 383 try:
380 384 st = fp.read()
381 385 finally:
382 386 fp.close()
383 387 except IOError as err:
384 388 if err.errno != errno.ENOENT:
385 389 raise
386 390 return
387 391 if not st:
388 392 return
389 393
390 394 if util.safehasattr(parsers, b'dict_new_presized'):
391 395 # Make an estimate of the number of files in the dirstate based on
392 396 # its size. This trades wasting some memory for avoiding costly
393 397 # resizes. Each entry have a prefix of 17 bytes followed by one or
394 398 # two path names. Studies on various large-scale real-world repositories
395 399 # found 54 bytes a reasonable upper limit for the average path names.
396 400 # Copy entries are ignored for the sake of this estimate.
397 401 self._map = parsers.dict_new_presized(len(st) // 71)
398 402
399 403 # Python's garbage collector triggers a GC each time a certain number
400 404 # of container objects (the number being defined by
401 405 # gc.get_threshold()) are allocated. parse_dirstate creates a tuple
402 406 # for each file in the dirstate. The C version then immediately marks
403 407 # them as not to be tracked by the collector. However, this has no
404 408 # effect on when GCs are triggered, only on what objects the GC looks
405 409 # into. This means that O(number of files) GCs are unavoidable.
406 410 # Depending on when in the process's lifetime the dirstate is parsed,
407 411 # this can get very expensive. As a workaround, disable GC while
408 412 # parsing the dirstate.
409 413 #
410 414 # (we cannot decorate the function directly since it is in a C module)
411 415 parse_dirstate = util.nogc(parsers.parse_dirstate)
412 416 p = parse_dirstate(self._map, self.copymap, st)
413 417 if not self._dirtyparents:
414 418 self.setparents(*p)
415 419
416 420 # Avoid excess attribute lookups by fast pathing certain checks
417 421 self.__contains__ = self._map.__contains__
418 422 self.__getitem__ = self._map.__getitem__
419 423 self.get = self._map.get
420 424
421 425 def write(self, _tr, st, now):
422 426 st.write(
423 427 parsers.pack_dirstate(self._map, self.copymap, self.parents(), now)
424 428 )
425 429 st.close()
426 430 self._dirtyparents = False
427 431 self.nonnormalset, self.otherparentset = self.nonnormalentries()
428 432
429 433 @propertycache
430 434 def nonnormalset(self):
431 435 nonnorm, otherparents = self.nonnormalentries()
432 436 self.otherparentset = otherparents
433 437 return nonnorm
434 438
435 439 @propertycache
436 440 def otherparentset(self):
437 441 nonnorm, otherparents = self.nonnormalentries()
438 442 self.nonnormalset = nonnorm
439 443 return otherparents
440 444
441 445 def non_normal_or_other_parent_paths(self):
442 446 return self.nonnormalset.union(self.otherparentset)
443 447
444 448 @propertycache
445 449 def identity(self):
446 450 self._map
447 451 return self.identity
448 452
449 453 @propertycache
450 454 def dirfoldmap(self):
451 455 f = {}
452 456 normcase = util.normcase
453 457 for name in self._dirs:
454 458 f[normcase(name)] = name
455 459 return f
456 460
457 461
458 462 if rustmod is not None:
459 463
460 464 class dirstatemap(object):
461 465 def __init__(self, ui, opener, root, nodeconstants, use_dirstate_v2):
462 466 self._use_dirstate_v2 = use_dirstate_v2
463 467 self._nodeconstants = nodeconstants
464 468 self._ui = ui
465 469 self._opener = opener
466 470 self._root = root
467 471 self._filename = b'dirstate'
468 472 self._nodelen = 20 # Also update Rust code when changing this!
469 473 self._parents = None
470 474 self._dirtyparents = False
471 475 self._docket = None
472 476
473 477 # for consistent view between _pl() and _read() invocations
474 478 self._pendingmode = None
475 479
476 480 self._use_dirstate_tree = self._ui.configbool(
477 481 b"experimental",
478 482 b"dirstate-tree.in-memory",
479 483 False,
480 484 )
481 485
482 486 def addfile(
483 487 self,
484 488 f,
485 489 mode=0,
486 490 size=None,
487 491 mtime=None,
488 492 added=False,
489 493 merged=False,
490 494 from_p2=False,
491 495 possibly_dirty=False,
492 496 ):
493 497 return self._rustmap.addfile(
494 498 f,
495 499 mode,
496 500 size,
497 501 mtime,
498 502 added,
499 503 merged,
500 504 from_p2,
501 505 possibly_dirty,
502 506 )
503 507
504 508 def removefile(self, *args, **kwargs):
505 509 return self._rustmap.removefile(*args, **kwargs)
506 510
507 511 def dropfile(self, *args, **kwargs):
508 512 return self._rustmap.dropfile(*args, **kwargs)
509 513
510 514 def clearambiguoustimes(self, *args, **kwargs):
511 515 return self._rustmap.clearambiguoustimes(*args, **kwargs)
512 516
513 517 def nonnormalentries(self):
514 518 return self._rustmap.nonnormalentries()
515 519
516 520 def get(self, *args, **kwargs):
517 521 return self._rustmap.get(*args, **kwargs)
518 522
519 523 @property
520 524 def copymap(self):
521 525 return self._rustmap.copymap()
522 526
523 527 def directories(self):
524 528 return self._rustmap.directories()
525 529
526 530 def debug_iter(self):
527 531 return self._rustmap.debug_iter()
528 532
529 533 def preload(self):
530 534 self._rustmap
531 535
532 536 def clear(self):
533 537 self._rustmap.clear()
534 538 self.setparents(
535 539 self._nodeconstants.nullid, self._nodeconstants.nullid
536 540 )
537 541 util.clearcachedproperty(self, b"_dirs")
538 542 util.clearcachedproperty(self, b"_alldirs")
539 543 util.clearcachedproperty(self, b"dirfoldmap")
540 544
541 545 def items(self):
542 546 return self._rustmap.items()
543 547
544 548 def keys(self):
545 549 return iter(self._rustmap)
546 550
547 551 def __contains__(self, key):
548 552 return key in self._rustmap
549 553
550 554 def __getitem__(self, item):
551 555 return self._rustmap[item]
552 556
553 557 def __len__(self):
554 558 return len(self._rustmap)
555 559
556 560 def __iter__(self):
557 561 return iter(self._rustmap)
558 562
559 563 # forward for python2,3 compat
560 564 iteritems = items
561 565
562 566 def _opendirstatefile(self):
563 567 fp, mode = txnutil.trypending(
564 568 self._root, self._opener, self._filename
565 569 )
566 570 if self._pendingmode is not None and self._pendingmode != mode:
567 571 fp.close()
568 572 raise error.Abort(
569 573 _(b'working directory state may be changed parallelly')
570 574 )
571 575 self._pendingmode = mode
572 576 return fp
573 577
574 578 def _readdirstatefile(self, size=-1):
575 579 try:
576 580 with self._opendirstatefile() as fp:
577 581 return fp.read(size)
578 582 except IOError as err:
579 583 if err.errno != errno.ENOENT:
580 584 raise
581 585 # File doesn't exist, so the current state is empty
582 586 return b''
583 587
584 588 def setparents(self, p1, p2):
585 589 self._parents = (p1, p2)
586 590 self._dirtyparents = True
587 591
588 592 def parents(self):
589 593 if not self._parents:
590 594 if self._use_dirstate_v2:
591 595 self._parents = self.docket.parents
592 596 else:
593 597 read_len = self._nodelen * 2
594 598 st = self._readdirstatefile(read_len)
595 599 l = len(st)
596 600 if l == read_len:
597 601 self._parents = (
598 602 st[: self._nodelen],
599 603 st[self._nodelen : 2 * self._nodelen],
600 604 )
601 605 elif l == 0:
602 606 self._parents = (
603 607 self._nodeconstants.nullid,
604 608 self._nodeconstants.nullid,
605 609 )
606 610 else:
607 611 raise error.Abort(
608 612 _(b'working directory state appears damaged!')
609 613 )
610 614
611 615 return self._parents
612 616
613 617 @property
614 618 def docket(self):
615 619 if not self._docket:
616 620 if not self._use_dirstate_v2:
617 621 raise error.ProgrammingError(
618 622 b'dirstate only has a docket in v2 format'
619 623 )
620 624 self._docket = docketmod.DirstateDocket.parse(
621 625 self._readdirstatefile(), self._nodeconstants
622 626 )
623 627 return self._docket
624 628
625 629 @propertycache
626 630 def _rustmap(self):
627 631 """
628 632 Fills the Dirstatemap when called.
629 633 """
630 634 # ignore HG_PENDING because identity is used only for writing
631 635 self.identity = util.filestat.frompath(
632 636 self._opener.join(self._filename)
633 637 )
634 638
635 639 if self._use_dirstate_v2:
636 640 if self.docket.uuid:
637 641 # TODO: use mmap when possible
638 642 data = self._opener.read(self.docket.data_filename())
639 643 else:
640 644 data = b''
641 645 self._rustmap = rustmod.DirstateMap.new_v2(
642 646 data, self.docket.data_size, self.docket.tree_metadata
643 647 )
644 648 parents = self.docket.parents
645 649 else:
646 650 self._rustmap, parents = rustmod.DirstateMap.new_v1(
647 651 self._use_dirstate_tree, self._readdirstatefile()
648 652 )
649 653
650 654 if parents and not self._dirtyparents:
651 655 self.setparents(*parents)
652 656
653 657 self.__contains__ = self._rustmap.__contains__
654 658 self.__getitem__ = self._rustmap.__getitem__
655 659 self.get = self._rustmap.get
656 660 return self._rustmap
657 661
658 662 def write(self, tr, st, now):
659 663 if not self._use_dirstate_v2:
660 664 p1, p2 = self.parents()
661 665 packed = self._rustmap.write_v1(p1, p2, now)
662 666 st.write(packed)
663 667 st.close()
664 668 self._dirtyparents = False
665 669 return
666 670
667 671 # We can only append to an existing data file if there is one
668 672 can_append = self.docket.uuid is not None
669 673 packed, meta, append = self._rustmap.write_v2(now, can_append)
670 674 if append:
671 675 docket = self.docket
672 676 data_filename = docket.data_filename()
673 677 if tr:
674 678 tr.add(data_filename, docket.data_size)
675 679 with self._opener(data_filename, b'r+b') as fp:
676 680 fp.seek(docket.data_size)
677 681 assert fp.tell() == docket.data_size
678 682 written = fp.write(packed)
679 683 if written is not None: # py2 may return None
680 684 assert written == len(packed), (written, len(packed))
681 685 docket.data_size += len(packed)
682 686 docket.parents = self.parents()
683 687 docket.tree_metadata = meta
684 688 st.write(docket.serialize())
685 689 st.close()
686 690 else:
687 691 old_docket = self.docket
688 692 new_docket = docketmod.DirstateDocket.with_new_uuid(
689 693 self.parents(), len(packed), meta
690 694 )
691 695 data_filename = new_docket.data_filename()
692 696 if tr:
693 697 tr.add(data_filename, 0)
694 698 self._opener.write(data_filename, packed)
695 699 # Write the new docket after the new data file has been
696 700 # written. Because `st` was opened with `atomictemp=True`,
697 701 # the actual `.hg/dirstate` file is only affected on close.
698 702 st.write(new_docket.serialize())
699 703 st.close()
700 704 # Remove the old data file after the new docket pointing to
701 705 # the new data file was written.
702 706 if old_docket.uuid:
703 707 data_filename = old_docket.data_filename()
704 708 unlink = lambda _tr=None: self._opener.unlink(data_filename)
705 709 if tr:
706 710 category = b"dirstate-v2-clean-" + old_docket.uuid
707 711 tr.addpostclose(category, unlink)
708 712 else:
709 713 unlink()
710 714 self._docket = new_docket
711 715 # Reload from the newly-written file
712 716 util.clearcachedproperty(self, b"_rustmap")
713 717 self._dirtyparents = False
714 718
715 719 @propertycache
716 720 def filefoldmap(self):
717 721 """Returns a dictionary mapping normalized case paths to their
718 722 non-normalized versions.
719 723 """
720 724 return self._rustmap.filefoldmapasdict()
721 725
722 726 def hastrackeddir(self, d):
723 727 return self._rustmap.hastrackeddir(d)
724 728
725 729 def hasdir(self, d):
726 730 return self._rustmap.hasdir(d)
727 731
728 732 @propertycache
729 733 def identity(self):
730 734 self._rustmap
731 735 return self.identity
732 736
733 737 @property
734 738 def nonnormalset(self):
735 739 nonnorm = self._rustmap.non_normal_entries()
736 740 return nonnorm
737 741
738 742 @propertycache
739 743 def otherparentset(self):
740 744 otherparents = self._rustmap.other_parent_entries()
741 745 return otherparents
742 746
743 747 def non_normal_or_other_parent_paths(self):
744 748 return self._rustmap.non_normal_or_other_parent_paths()
745 749
746 750 @propertycache
747 751 def dirfoldmap(self):
748 752 f = {}
749 753 normcase = util.normcase
750 754 for name in self._rustmap.tracked_dirs():
751 755 f[normcase(name)] = name
752 756 return f
General Comments 0
You need to be logged in to leave comments. Login now