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