Example #1
0
    def testwalk(self):
        treepath = os.path.join(testtmp, "walk")
        tree = treestate.treestate(treepath, 0)
        count = 5000
        files = list(itertools.islice(genfiles(), count))
        expected = {}
        for path, bits, mode, size, mtime, copied in files:
            tree.insert(path, bits, mode, size, mtime, copied)
            expected[path] = (bits, mode, size, mtime, copied)

        def walk(setbits, unsetbits):
            return sorted(
                k
                for k, v in pycompat.iteritems(expected)
                if ((v[0] & unsetbits) == 0 and (v[0] & setbits) == setbits)
            )

        def check(setbits, unsetbits):
            self.assertEqual(
                walk(setbits, unsetbits), sorted(tree.walk(setbits, unsetbits))
            )

        for i in ["in-memory", "flushed"]:
            for bit in [treestate.IGNORED, treestate.COPIED]:
                check(0, bit)
                check(bit, 0)
            check(treestate.EXIST_P1, treestate.EXIST_P2)
            rootid = tree.flush()
            tree = treestate.treestate(treepath, rootid)
Example #2
0
    def clear(self):
        self._threshold = 0
        self._rootid = 0
        self._parents = (node.nullid, node.nullid)

        # use a new file
        path = self._setfilename()
        self._tree = treestate.treestate(path, self._rootid)
Example #3
0
 def testfiltered(self):
     treepath = os.path.join(testtmp, "filtered")
     tree = treestate.treestate(treepath, 0)
     tree.insert("a/B/c", 1, 2, 3, 4, None)
     filtered = tree.getfiltered("A/B/C", lambda x: x.upper(), 1)
     self.assertEqual(filtered, ["a/B/c"])
     filtered = tree.getfiltered("A/B/C", lambda x: x, 2)
     self.assertEqual(filtered, [])
Example #4
0
    def testsaveas(self):
        treepath = os.path.join(testtmp, "saveas")
        tree = treestate.treestate(treepath, 0)
        tree.insert("a", 1, 2, 3, 4, None)
        tree.setmetadata(b"1")
        tree.flush()

        tree.insert("b", 1, 2, 3, 4, None)
        tree.remove("a")
        treepath = "%s-savedas" % treepath
        tree.setmetadata(b"2")
        rootid = tree.saveas(treepath)

        tree = treestate.treestate(treepath, rootid)
        self.assertFalse("a" in tree)
        self.assertTrue("b" in tree)
        self.assertEqual(tree.getmetadata(), b"2")
Example #5
0
 def testdirfilter(self):
     treepath = os.path.join(testtmp, "walk")
     tree = treestate.treestate(treepath, 0)
     files = ["a/b", "a/b/c", "b/c", "c/d"]
     for path in files:
         tree.insert(path, 1, 2, 3, 4, None)
     self.assertEqual(tree.walk(1, 0, None), files)
     self.assertEqual(tree.walk(1, 0, lambda dir: dir in {"a/b/", "c/"}),
                      ["a/b", "b/c"])
     self.assertEqual(tree.walk(1, 0, lambda dir: True), [])
Example #6
0
 def testinsert(self):
     tree = treestate.treestate(os.path.join(testtmp, "insert"), 0)
     count = 5000
     files = list(itertools.islice(genfiles(), count))
     expected = {}
     for path, bits, mode, size, mtime, copied in files:
         tree.insert(path, bits, mode, size, mtime, copied)
         expected[path] = (bits, mode, size, mtime, copied)
     self.assertEqual(len(tree), len(expected))
     for path in tree.walk(0, 0):
         self.assertTrue(tree.hasdir(os.path.dirname(path) + "/"))
         self.assertEqual(tree.get(path, None), expected[path])
Example #7
0
    def testflush(self):
        treepath = os.path.join(testtmp, "flush")
        tree = treestate.treestate(treepath, 0)
        tree.insert("a", 1, 2, 3, 4, None)
        tree.setmetadata(b"1")
        rootid1 = tree.flush()

        tree.remove("a")
        tree.insert("b", 1, 2, 3, 4, None)
        tree.setmetadata(b"2")
        rootid2 = tree.flush()

        tree = treestate.treestate(treepath, rootid1)
        self.assertTrue("a" in tree)
        self.assertFalse("b" in tree)
        self.assertEqual(tree.getmetadata(), b"1")

        tree = treestate.treestate(treepath, rootid2)
        self.assertFalse("a" in tree)
        self.assertTrue("b" in tree)
        self.assertEqual(tree.getmetadata(), b"2")
Example #8
0
 def testempty(self):
     tree = treestate.treestate(os.path.join(testtmp, "empty"), 0)
     self.assertEqual(len(tree), 0)
     self.assertEqual(tree.getmetadata(), "")
     self.assertEqual(tree.walk(0, 0), [])
     self.assertTrue(tree.hasdir("/"))
     for path in ["", "a", "/", "b/c", "d/"]:
         self.assertFalse(path in tree)
         if path and path != "/":
             self.assertFalse(tree.hasdir(path))
         if path != "/":
             self.assertIsNone(tree.get(path, None))
Example #9
0
 def testgetdir(self):
     treepath = os.path.join(testtmp, "filtered")
     tree = treestate.treestate(treepath, 0)
     tree.insert("a/b/c", 3, 0, 0, 0, None)
     tree.insert("a/d", 5, 0, 0, 0, None)
     self.assertEqual(tree.getdir("/"), (3 | 5, 3 & 5))
     self.assertEqual(tree.getdir("a/"), (3 | 5, 3 & 5))
     self.assertEqual(tree.getdir("a/b/"), (3, 3))
     self.assertIsNone(tree.getdir("a/b/c/"))
     tree.insert("a/e/f", 10, 0, 0, 0, None)
     self.assertEqual(tree.getdir("a/"), (3 | 5 | 10, 3 & 5 & 10))
     tree.remove("a/e/f")
     self.assertEqual(tree.getdir("a/"), (3 | 5, 3 & 5))
Example #10
0
 def testsubdirquery(self):
     treepath = os.path.join(testtmp, "subdir")
     tree = treestate.treestate(treepath, 0)
     paths = ["a/b/c", "a/b/d", "a/c", "de"]
     for path in paths:
         tree.insert(path, 1, 2, 3, 4, None)
     self.assertEqual(tree.tracked(""), paths)
     self.assertEqual(tree.tracked("de"), ["de"])
     self.assertEqual(tree.tracked("a"), [])
     self.assertEqual(tree.tracked("a/"), ["a/b/c", "a/b/d", "a/c"])
     self.assertEqual(tree.tracked("a/b/"), ["a/b/c", "a/b/d"])
     self.assertEqual(tree.tracked("a/b"), [])
     self.assertEqual(tree.tracked("a/c/"), [])
     self.assertEqual(tree.tracked("a/c"), ["a/c"])
Example #11
0
    def testpathcomplete(self):
        treepath = os.path.join(testtmp, "pathcomplete")
        tree = treestate.treestate(treepath, 0)
        paths = ["a/b/c", "a/b/d", "a/c", "de"]
        for path in paths:
            tree.insert(path, 1, 2, 3, 4, None)

        def complete(prefix, fullpath=False):
            completed = []
            tree.pathcomplete(prefix, 0, 0, completed.append, fullpath)
            return completed

        self.assertEqual(complete(""), ["a/", "de"])
        self.assertEqual(complete("d"), ["de"])
        self.assertEqual(complete("a/"), ["a/b/", "a/c"])
        self.assertEqual(complete("a/b/"), ["a/b/c", "a/b/d"])
        self.assertEqual(complete("a/b/c"), ["a/b/c"])
        self.assertEqual(complete("", True), paths)
Example #12
0
    def _read(self):
        """Read every metadata automatically"""
        content = b""
        try:
            fp, _mode = txnutil.trypending(self._root, self._vfs, "dirstate")
            with fp:
                content = fp.read()
        except IOError as ex:
            if ex.errno != errno.ENOENT:
                raise
        p1, p2, filename, rootid, threshold = self._parsedirstate(content)

        self._parents = (p1, p2)
        self._threshold = threshold
        self._rootid = rootid

        path = self._setfilename(filename)
        try:
            tree = treestate.treestate(path, rootid)
        except IOError:
            if not rootid:
                # treestate.treestate is read-only if rootid is not None.
                # If rootid is None, treestate transparently creates an empty
                # tree (ex. right after "hg init").  IOError can happen if
                # treestate cannot write such an empty tree. It's hard to make
                # the Rust land support read-only operation in this case. So
                # just use a read-only, empty tree.
                tree = emptytree()
            else:
                raise

        # Double check p1 p2 against metadata stored in the tree. This is
        # redundant but many things depend on "dirstate" file format.
        # The metadata here contains (watchman) "clock" which does not exist
        # in "dirstate".
        metadata = _unpackmetadata(tree.getmetadata())
        if metadata:
            if metadata.get("p1", node.nullhex) != node.hex(p1) or metadata.get(
                "p2", node.nullhex
            ) != node.hex(p2):
                raise error.Abort(
                    _("working directory state appears damaged (metadata mismatch)!")
                )
        self._tree = tree
Example #13
0
def repairtreestate(ui, vfs, root, cl):
    """Attempt to fix treestate:

    - Fix the dirstate pointer to a valid treestate root node.
    - Fix the dirstate node to point to a valid changelog node.
    """
    if not vfs.exists("treestate"):
        return
    needrebuild = False
    try:
        tmap = treestate.treestatemap(ui, vfs, root)
        p1node = tmap.parents()[0]
        if p1node not in cl.nodemap:
            needrebuild = True
    except Exception:
        needrebuild = True
    if not needrebuild:
        return

    nodemap = cl.nodemap

    def stat(name):
        return vfs.stat("treestate/%s" % name)

    def rebuild(filename, rootpos, p1hex):
        meta = {"p1": p1hex, "filename": filename, "rootid": rootpos}
        p1bin = bin(p1hex)
        with vfs.open("dirstate", "w", atomictemp=True) as f:
            # see treestate.py:treestatemap.write
            f.write(p1bin)
            f.write(nullid)
            f.write(treestate.HEADER)
            f.write(treestate._packmetadata(meta))
        ui.write_err(_("treestate: repaired\n"))

    # Find a recent treestate (name, root) pair.
    for filename in sorted(vfs.listdir("treestate"),
                           key=lambda n: -(stat(n).st_mtime)):
        data = vfs.read("treestate/%s" % filename)
        path = vfs.join("treestate/%s" % filename)

        end = len(data)
        while True:
            # Find the offset of "p1=".
            p1pos = data.rfind(b"p1=", 0, end)
            if p1pos < 0:
                break

            # Find a near root offset. A root offset has the property:
            # - It's before a "p1=" offset (if "p1=" is part of the root metadata,
            #   "p1=" can also be part of a filename or other things).
            # - It starts with "\0".
            # See treestate/src/serialization.rs for details.
            searchrange = 300
            end = max(p1pos, searchrange) - searchrange
            for rootpos in range(end, p1pos):
                # The first byte of the Root entry is "version", b"\0".
                # No need to try otherwise.
                if data[rootpos:rootpos + 1] != b"\0":
                    continue
                try:
                    rawtree = rawtreestate.treestate(path, rootpos)
                except Exception:
                    # Root deserialization failed xxhash check. Try next.
                    continue
                else:
                    meta = treestate._unpackmetadata(rawtree.getmetadata())
                    p1hex = meta.get("p1")
                    p2hex = meta.get("p2", nullhex)
                    if p2hex != nullhex:
                        # Do not restore to a merge commit.
                        continue
                    if p1hex is None or bin(p1hex) not in nodemap:
                        # Try next - p1 not in changelog.
                        continue
                    rebuild(filename, rootpos, p1hex)
                    return

    ui.write_err(
        _("treestate: cannot fix automatically (consider create a new workdir)\n"
          ))