Mercurial > hg-stable
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 |