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