comparison mercurial/dirstate.py @ 42771:749ef8c31187

rust-dirstate: call rust dirstatemap from Python Since Rust-backed Python classes cannot be used as baseclasses (for rust-cpython anyway), we use composition rather than inheritance. This also allows us to keep the IO operations in the Python side, removing (for now) the need to rewrite VFS in Rust, which would be a heavy undertaking. Differential Revision: https://phab.mercurial-scm.org/D6634
author Raphaël Gomès <rgomes@octobus.net>
date Wed, 10 Jul 2019 09:57:28 +0200
parents 760a7851e9ba
children d459cd8ea42d
comparison
equal deleted inserted replaced
42770:4e8f504424f3 42771:749ef8c31187
25 scmutil, 25 scmutil,
26 txnutil, 26 txnutil,
27 util, 27 util,
28 ) 28 )
29 29
30 orig_parsers = policy.importmod(r'parsers') 30 parsers = policy.importmod(r'parsers')
31 parsers = policy.importrust(r'parsers', default=orig_parsers) 31 rustmod = policy.importrust(r'dirstate')
32 32
33 propertycache = util.propertycache 33 propertycache = util.propertycache
34 filecache = scmutil.filecache 34 filecache = scmutil.filecache
35 _rangemask = 0x7fffffff 35 _rangemask = 0x7fffffff
36 36
37 dirstatetuple = orig_parsers.dirstatetuple 37 dirstatetuple = parsers.dirstatetuple
38 38
39 class repocache(filecache): 39 class repocache(filecache):
40 """filecache for files in .hg/""" 40 """filecache for files in .hg/"""
41 def join(self, obj, fname): 41 def join(self, obj, fname):
42 return obj._opener.join(fname) 42 return obj._opener.join(fname)
650 # enough 'delaywrite' prevents 'pack_dirstate' from dropping 650 # enough 'delaywrite' prevents 'pack_dirstate' from dropping
651 # timestamp of each entries in dirstate, because of 'now > mtime' 651 # timestamp of each entries in dirstate, because of 'now > mtime'
652 delaywrite = self._ui.configint('debug', 'dirstate.delaywrite') 652 delaywrite = self._ui.configint('debug', 'dirstate.delaywrite')
653 if delaywrite > 0: 653 if delaywrite > 0:
654 # do we have any files to delay for? 654 # do we have any files to delay for?
655 for f, e in self._map.iteritems(): 655 items = self._map.iteritems()
656 for f, e in items:
656 if e[0] == 'n' and e[3] == now: 657 if e[0] == 'n' and e[3] == now:
657 import time # to avoid useless import 658 import time # to avoid useless import
658 # rather than sleep n seconds, sleep until the next 659 # rather than sleep n seconds, sleep until the next
659 # multiple of n seconds 660 # multiple of n seconds
660 clock = time.time() 661 clock = time.time()
661 start = int(clock) - (int(clock) % delaywrite) 662 start = int(clock) - (int(clock) % delaywrite)
662 end = start + delaywrite 663 end = start + delaywrite
663 time.sleep(end - clock) 664 time.sleep(end - clock)
664 now = end # trust our estimate that the end is near now 665 now = end # trust our estimate that the end is near now
665 break 666 break
667 # since the iterator is potentially not deleted,
668 # delete the iterator to release the reference for the Rust
669 # implementation.
670 # TODO make the Rust implementation behave like Python
671 # since this would not work with a non ref-counting GC.
672 del items
666 673
667 self._map.write(st, now) 674 self._map.write(st, now)
668 self._lastnormaltime = 0 675 self._lastnormaltime = 0
669 self._dirty = False 676 self._dirty = False
670 677
1514 f = {} 1521 f = {}
1515 normcase = util.normcase 1522 normcase = util.normcase
1516 for name in self._dirs: 1523 for name in self._dirs:
1517 f[normcase(name)] = name 1524 f[normcase(name)] = name
1518 return f 1525 return f
1526
1527
1528 if rustmod is not None:
1529 class dirstatemap(object):
1530 def __init__(self, ui, opener, root):
1531 self._ui = ui
1532 self._opener = opener
1533 self._root = root
1534 self._filename = 'dirstate'
1535 self._parents = None
1536 self._dirtyparents = False
1537
1538 # for consistent view between _pl() and _read() invocations
1539 self._pendingmode = None
1540
1541 def addfile(self, *args, **kwargs):
1542 return self._rustmap.addfile(*args, **kwargs)
1543
1544 def removefile(self, *args, **kwargs):
1545 return self._rustmap.removefile(*args, **kwargs)
1546
1547 def dropfile(self, *args, **kwargs):
1548 return self._rustmap.dropfile(*args, **kwargs)
1549
1550 def clearambiguoustimes(self, *args, **kwargs):
1551 return self._rustmap.clearambiguoustimes(*args, **kwargs)
1552
1553 def nonnormalentries(self):
1554 return self._rustmap.nonnormalentries()
1555
1556 def get(self, *args, **kwargs):
1557 return self._rustmap.get(*args, **kwargs)
1558
1559 @propertycache
1560 def _rustmap(self):
1561 self._rustmap = rustmod.DirstateMap(self._root)
1562 self.read()
1563 return self._rustmap
1564
1565 @property
1566 def copymap(self):
1567 return self._rustmap.copymap()
1568
1569 def preload(self):
1570 self._rustmap
1571
1572 def clear(self):
1573 self._rustmap.clear()
1574 self.setparents(nullid, nullid)
1575 util.clearcachedproperty(self, "_dirs")
1576 util.clearcachedproperty(self, "_alldirs")
1577 util.clearcachedproperty(self, "dirfoldmap")
1578
1579 def items(self):
1580 return self._rustmap.items()
1581
1582 def keys(self):
1583 return iter(self._rustmap)
1584
1585 def __contains__(self, key):
1586 return key in self._rustmap
1587
1588 def __getitem__(self, item):
1589 return self._rustmap[item]
1590
1591 def __len__(self):
1592 return len(self._rustmap)
1593
1594 def __iter__(self):
1595 return iter(self._rustmap)
1596
1597 # forward for python2,3 compat
1598 iteritems = items
1599
1600 def _opendirstatefile(self):
1601 fp, mode = txnutil.trypending(self._root, self._opener,
1602 self._filename)
1603 if self._pendingmode is not None and self._pendingmode != mode:
1604 fp.close()
1605 raise error.Abort(_('working directory state may be '
1606 'changed parallelly'))
1607 self._pendingmode = mode
1608 return fp
1609
1610 def setparents(self, p1, p2):
1611 self._rustmap.setparents(p1, p2)
1612 self._parents = (p1, p2)
1613 self._dirtyparents = True
1614
1615 def parents(self):
1616 if not self._parents:
1617 try:
1618 fp = self._opendirstatefile()
1619 st = fp.read(40)
1620 fp.close()
1621 except IOError as err:
1622 if err.errno != errno.ENOENT:
1623 raise
1624 # File doesn't exist, so the current state is empty
1625 st = ''
1626
1627 try:
1628 self._parents = self._rustmap.parents(st)
1629 except ValueError:
1630 raise error.Abort(_('working directory state appears '
1631 'damaged!'))
1632
1633 return self._parents
1634
1635 def read(self):
1636 # ignore HG_PENDING because identity is used only for writing
1637 self.identity = util.filestat.frompath(
1638 self._opener.join(self._filename))
1639
1640 try:
1641 fp = self._opendirstatefile()
1642 try:
1643 st = fp.read()
1644 finally:
1645 fp.close()
1646 except IOError as err:
1647 if err.errno != errno.ENOENT:
1648 raise
1649 return
1650 if not st:
1651 return
1652
1653 parse_dirstate = util.nogc(self._rustmap.read)
1654 parents = parse_dirstate(st)
1655 if parents and not self._dirtyparents:
1656 self.setparents(*parents)
1657
1658 def write(self, st, now):
1659 parents = self.parents()
1660 st.write(self._rustmap.write(parents[0], parents[1], now))
1661 st.close()
1662 self._dirtyparents = False
1663
1664 @propertycache
1665 def filefoldmap(self):
1666 """Returns a dictionary mapping normalized case paths to their
1667 non-normalized versions.
1668 """
1669 return self._rustmap.filefoldmapasdict()
1670
1671 def hastrackeddir(self, d):
1672 self._dirs # Trigger Python's propertycache
1673 return self._rustmap.hastrackeddir(d)
1674
1675 def hasdir(self, d):
1676 self._dirs # Trigger Python's propertycache
1677 return self._rustmap.hasdir(d)
1678
1679 @propertycache
1680 def _dirs(self):
1681 return self._rustmap.getdirs()
1682
1683 @propertycache
1684 def _alldirs(self):
1685 return self._rustmap.getalldirs()
1686
1687 @propertycache
1688 def identity(self):
1689 self._rustmap
1690 return self.identity
1691
1692 @property
1693 def nonnormalset(self):
1694 nonnorm, otherparents = self._rustmap.nonnormalentries()
1695 return nonnorm
1696
1697 @property
1698 def otherparentset(self):
1699 nonnorm, otherparents = self._rustmap.nonnormalentries()
1700 return otherparents
1701
1702 @propertycache
1703 def dirfoldmap(self):
1704 f = {}
1705 normcase = util.normcase
1706 for name in self._dirs:
1707 f[normcase(name)] = name
1708 return f