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