manifest: use Rust Tree Manifest by default

Summary:
The Rust Manifest is deployed practically everywhere.
Removing the C++ code from being compiled in Mercurial along with all of the cstore extension.

Reviewed By: quark-zju

Differential Revision: D19543248

fbshipit-source-id: d632e171175e6866563c1aa0808a099b67bd937d
This commit is contained in:
Stefan Filip 2020-01-23 16:04:55 -08:00 committed by Facebook Github Bot
parent f5280b75e9
commit 2dc14cf9a6
7 changed files with 46 additions and 1139 deletions

View File

@ -1117,9 +1117,9 @@ def _getmanifest(op, rev):
else:
store = repo.manifestlog.datastore
# pyre-fixme[21]: Could not find `edenscmnative`.
from edenscmnative import cstore
from bindings import manifest
m = cstore.treemanifest(store, rev.manifestnode())
m = manifest.treemanifest(store, rev.manifestnode())
if store.getmissing([("", rev.manifestnode())]):
raise error.Abort(
_(

View File

@ -135,14 +135,6 @@ trees for new draft roots added to the repository.
[treemanifest]
prefetchdraftparents = True
`treemanifest.rustmanifest` causes treemanifest to use the Rust
implementation rather than the C++ one.
::
[treemanifest]
rustmanifest = True
`treemanifest.bfsprefetch` causes the client to perform a BFS over the
tree to be prefetched and manually request all missing nodes from the
server, rather than relying on the server to perform this computation.
@ -201,9 +193,6 @@ from edenscm.mercurial.i18n import _, _n
from edenscm.mercurial.node import bin, hex, nullid, short
from edenscm.mercurial.pycompat import range
# pyre-fixme[21]: Could not find `edenscmnative`.
from edenscmnative import cstore
from ..extutil import flock
from ..remotefilelog import (
cmdtable as remotefilelogcmdtable,
@ -242,9 +231,6 @@ configitem("treemanifest", "stickypushpath", default=True)
configitem("treemanifest", "treeonly", default=True)
configitem("treemanifest", "usehttp", default=False)
configitem("treemanifest", "prefetchdraftparents", default=True)
configitem("treemanifest", "rustmanifest", default=False)
nativemanifesttype = (cstore.treemanifest, rustmanifest.treemanifest)
PACK_CATEGORY = "manifests"
@ -1079,18 +1065,11 @@ def _usehttp(ui):
return edenapi.enabled(ui) and ui.configbool("treemanifest", "usehttp")
def _userustmanifest(manifestlog):
return manifestlog.ui.configbool("treemanifest", "rustmanifest")
def _buildtree(manifestlog, node=None):
# this code seems to belong in manifestlog but I have no idea how
# manifestlog objects work
store = manifestlog.datastore
if _userustmanifest(manifestlog):
initfn = rustmanifest.treemanifest
else:
initfn = cstore.treemanifest
initfn = rustmanifest.treemanifest
if node is not None and node != nullid:
return initfn(store, node)
else:
@ -1504,10 +1483,7 @@ def _converttotree(tr, mfl, tmfl, mfctx, linkrev=None, torevlog=False):
_("unable to find tree parent nodes %s %s") % (hex(p1node), hex(p2node))
)
else:
if _userustmanifest(mfl):
parenttree = rustmanifest.treemanifest(tmfl.datastore)
else:
parenttree = cstore.treemanifest(tmfl.datastore)
parenttree = rustmanifest.treemanifest(tmfl.datastore)
added, removed = _getflatdiff(mfl, mfctx)
newtree = _getnewtree(parenttree, added, removed)
@ -2054,11 +2030,7 @@ def _registerbundle2parts():
for node in rootnodes:
p1, p2, linknode, copyfrom = wirepackstore.getnodeinfo("", node)
userustmanifest = mfl.ui.configbool("treemanifest", "rustmanifest")
if userustmanifest:
newtree = rustmanifest.treemanifest(datastore, node)
else:
newtree = cstore.treemanifest(datastore, node)
newtree = rustmanifest.treemanifest(datastore, node)
mfl.add(mfl.ui, newtree, p1, p2, linknode, tr=tr)
return
@ -2483,16 +2455,8 @@ def _generatepackstream(
# Only use the first two base trees, since the current tree
# implementation cannot handle more yet.
userustmanifest = repo.ui.configbool("treemanifest", "rustmanifest")
if userustmanifest:
basenodes = [mybasenode for (_path, mybasenode) in basetrees]
subtrees = rustmanifest.subdirdiff(
datastore, rootdir, node, basenodes, depth
)
else:
subtrees = cstore.treemanifest.walksubdirtrees(
(rootdir, node), datastore, comparetrees=basetrees[:2], depth=depth
)
basenodes = [mybasenode for (_path, mybasenode) in basetrees]
subtrees = rustmanifest.subdirdiff(datastore, rootdir, node, basenodes, depth)
rootlinknode = None
if linknodefixup is not None:
validlinknodes, linknodemap = linknodefixup
@ -2663,7 +2627,7 @@ class ondemandtreedatastore(generatingdatastore):
def _debugcmdfindtreemanifest(orig, ctx):
manifest = ctx.manifest()
# Check if the manifest we have is a treemanifest.
if isinstance(manifest, nativemanifesttype):
if isinstance(manifest, rustmanifest.treemanifest):
return manifest
try:
# Look up the treemanifest in the treemanifestlog. There might not be

View File

@ -1606,42 +1606,6 @@ extmodules = [
sources=["edenscm/hgext/extlib/pywatchman/bser.c"],
include_dirs=include_dirs,
),
Extension(
"edenscmnative.cstore",
sources=[
"edenscm/hgext/extlib/cstore/datapackstore.cpp",
"edenscm/hgext/extlib/cstore/deltachain.cpp",
"edenscm/hgext/extlib/cstore/py-cstore.cpp",
"edenscm/hgext/extlib/cstore/pythonutil.cpp",
"edenscm/hgext/extlib/cstore/pythondatastore.cpp",
"edenscm/hgext/extlib/cstore/uniondatapackstore.cpp",
"edenscm/hgext/extlib/ctreemanifest/manifest.cpp",
"edenscm/hgext/extlib/ctreemanifest/manifest_entry.cpp",
"edenscm/hgext/extlib/ctreemanifest/manifest_fetcher.cpp",
"edenscm/hgext/extlib/ctreemanifest/manifest_ptr.cpp",
"edenscm/hgext/extlib/ctreemanifest/treemanifest.cpp",
],
depends=[
"edenscm/hgext/extlib/cstore/datapackstore.h",
"edenscm/hgext/extlib/cstore/datastore.h",
"edenscm/hgext/extlib/cstore/deltachain.h",
"edenscm/hgext/extlib/cstore/key.h",
"edenscm/hgext/extlib/cstore/match.h",
"edenscm/hgext/extlib/cstore/py-datapackstore.h",
"edenscm/hgext/extlib/cstore/py-structs.h",
"edenscm/hgext/extlib/cstore/py-treemanifest.h",
"edenscm/hgext/extlib/cstore/pythondatastore.h",
"edenscm/hgext/extlib/cstore/pythonkeyiterator.h",
"edenscm/hgext/extlib/cstore/pythonutil.h",
"edenscm/hgext/extlib/cstore/store.h",
"edenscm/hgext/extlib/cstore/uniondatapackstore.h",
"edenscm/hgext/extlib/cstore/util.h",
],
include_dirs=include_dirs,
library_dirs=["build/" + distutils_dir_name("lib")] + library_dirs,
libraries=["datapack", "lz4", "mpatch", SHA1_LIBRARY],
extra_compile_args=filter(None, [STDCPP0X, WALL] + cflags),
),
]

View File

@ -32,5 +32,3 @@
> EOF
tests/infinitepush/library.sh is not an executable but does have #!
tests/stresstest-atomicreplace.py is not an executable but does have #!
tests/test-fb-hgext-cstore-treemanifest.py is a Python script but does not have Python interpreter specified
tests/test-fb-hgext-cstore-uniondatapackstore.py is a Python script but does not have Python interpreter specified

View File

@ -1,775 +0,0 @@
#!/usr/bin/env python2.7
from __future__ import absolute_import
import random
import unittest
import silenttestrunner
from edenscm.mercurial import manifest, match as matchmod
from edenscm.mercurial.node import hex, nullid
from edenscmnative import cstore
class FakeDataStore(object):
def __init__(self):
self._data = {}
def get(self, path, node):
return self._data[(path, node)]
def add(self, path, node, deltabase, value):
self._data[(path, node)] = value
class FakeHistoryStore(object):
def __init__(self):
self._data = {}
def getancestors(self, queryname, querynode):
results = {}
queue = [(queryname, querynode)]
while queue:
name, node = queue.pop()
p1, p2, linknode, copyfrom = self._data[(name, node)]
results[node] = (p1, p2, linknode, copyfrom)
if p1 != nullid:
queue.append((copyfrom or name, p1))
if p2 != nullid:
queue.append((name, p2))
return results
def add(self, path, node, p1, p2, linknode, copyfrom):
self._data[(path, node)] = (p1, p2, linknode, copyfrom)
def getvalidflag():
# t is reserved as a directory entry, so don't go around setting that as the
# flag.
while True:
r = random.randint(0, 255)
if r != ord("t"):
return chr(r)
def hashflags(requireflag=False):
h = "".join([chr(random.randint(0, 255)) for x in range(20)])
if random.randint(0, 1) == 0 and requireflag is False:
f = ""
else:
f = getvalidflag()
return h, f
class ctreemanifesttests(unittest.TestCase):
def setUp(self):
random.seed(0)
def testInitialization(self):
cstore.treemanifest(FakeDataStore())
def testEmptyFlag(self):
a = cstore.treemanifest(FakeDataStore())
h, f = hashflags()[0], ""
a.set("abc", h, f)
out = a.find("abc")
self.assertEquals((h, f), out)
def testNullFlag(self):
a = cstore.treemanifest(FakeDataStore())
h, f = hashflags()[0], "\0"
a.set("abc", h, f)
out = a.find("abc")
self.assertEquals((h, f), out)
def testSetGet(self):
a = cstore.treemanifest(FakeDataStore())
h, f = hashflags()
a.set("abc", h, f)
out = a.find("abc")
self.assertEquals((h, f), out)
def testUpdate(self):
a = cstore.treemanifest(FakeDataStore())
h, f = hashflags()
a.set("abc", h, f)
out = a.find("abc")
self.assertEquals((h, f), out)
h, f = hashflags()
a.set("abc", h, f)
out = a.find("abc")
self.assertEquals((h, f), out)
def testDirAfterFile(self):
a = cstore.treemanifest(FakeDataStore())
file_h, file_f = hashflags()
a.set("abc", file_h, file_f)
out = a.find("abc")
self.assertEquals((file_h, file_f), out)
dir_h, dir_f = hashflags()
a.set("abc/def", dir_h, dir_f)
out = a.find("abc/def")
self.assertEquals((dir_h, dir_f), out)
out = a.find("abc")
self.assertEquals((file_h, file_f), out)
def testFileAfterDir(self):
a = cstore.treemanifest(FakeDataStore())
dir_h, dir_f = hashflags()
a.set("abc/def", dir_h, dir_f)
out = a.find("abc/def")
self.assertEquals((dir_h, dir_f), out)
file_h, file_f = hashflags()
a.set("abc", file_h, file_f)
out = a.find("abc")
self.assertEquals((file_h, file_f), out)
out = a.find("abc/def")
self.assertEquals((dir_h, dir_f), out)
def testDeeplyNested(self):
a = cstore.treemanifest(FakeDataStore())
h, f = hashflags()
a.set("abc/def/ghi/jkl", h, f)
out = a.find("abc/def/ghi/jkl")
self.assertEquals((h, f), out)
h, f = hashflags()
a.set("abc/def/ghi/jkl2", h, f)
out = a.find("abc/def/ghi/jkl2")
self.assertEquals((h, f), out)
def testBushyTrees(self):
a = cstore.treemanifest(FakeDataStore())
nodes = {}
for ix in range(111):
h, f = hashflags()
nodes["abc/def/ghi/jkl%d" % ix] = (h, f)
for fp, (h, f) in nodes.items():
a.set(fp, h, f)
for fp, (h, f) in nodes.items():
out = a.find(fp)
self.assertEquals((h, f), out)
def testFlagChanges(self):
a = cstore.treemanifest(FakeDataStore())
# go from no flags to with flags, back to no flags.
h, f = hashflags(requireflag=True)
self.assertEquals(len(f), 1)
a.set("abc", h, "")
out = a.find("abc")
self.assertEquals(h, out[0])
self.assertEquals("", out[1])
a.set("abc", h, f)
out = a.find("abc")
self.assertEquals(h, out[0])
self.assertEquals(f, out[1])
a.set("abc", h, "")
out = a.find("abc")
self.assertEquals(h, out[0])
self.assertEquals("", out[1])
def testSetRemove(self):
a = cstore.treemanifest(FakeDataStore())
h, f = hashflags()
a.set("abc", h, f)
out = a.find("abc")
self.assertEquals((h, f), out)
a.set("abc", None, None)
try:
out = a.find("abc")
raise RuntimeError("set should've removed file abc")
except KeyError:
pass
def testCleanupAfterRemove(self):
a = cstore.treemanifest(FakeDataStore())
h, f = hashflags()
a.set("abc/def/ghi", h, f)
out = a.find("abc/def/ghi")
self.assertEquals((h, f), out)
a.set("abc/def/ghi", None, None)
h, f = hashflags()
a.set("abc", h, f)
out = a.find("abc")
self.assertEquals((h, f), out)
def testIterOrder(self):
a = cstore.treemanifest(FakeDataStore())
h, f = hashflags()
a.set("abc/def/ghi", h, f)
a.set("abc/def.ghi", h, f)
results = [fp for fp in a]
self.assertEquals(results[0], "abc/def.ghi")
self.assertEquals(results[1], "abc/def/ghi")
def testIterOrderSigned(self):
a = cstore.treemanifest(FakeDataStore())
h, f = hashflags()
a.set("abc/def/\xe6\xe9", h, f)
a.set("abc/def/gh", h, f)
results = [fp for fp in a]
self.assertEquals(results[0], "abc/def/gh")
self.assertEquals(results[1], "abc/def/\xe6\xe9")
def testWrite(self):
a = cstore.treemanifest(FakeDataStore())
a.set("abc/def/x", *hashflags())
a.set("abc/def/y", *hashflags())
a.set("abc/z", *hashflags())
alinknode = hashflags()[0]
dstore = FakeDataStore()
hstore = FakeHistoryStore()
for name, node, text, p1text, p1, p2 in a.finalize():
dstore.add(name, node, nullid, text)
hstore.add(name, node, p1, p2, alinknode, "")
if not name:
anode = node
a2 = cstore.treemanifest(dstore, anode)
self.assertEquals(list(a.iterentries()), list(a2.iterentries()))
self.assertEquals(
hstore.getancestors("", anode), {anode: (nullid, nullid, alinknode, "")}
)
b = a2.copy()
b.set("lmn/v", *hashflags())
b.set("abc/z", *hashflags())
blinknode = hashflags()[0]
for name, node, text, p1text, p1, p2 in b.finalize(a2):
dstore.add(name, node, nullid, text)
hstore.add(name, node, p1, p2, blinknode, "")
if not name:
bnode = node
b2 = cstore.treemanifest(dstore, bnode)
self.assertEquals(list(b.iterentries()), list(b2.iterentries()))
self.assertEquals(
hstore.getancestors("", bnode),
{
bnode: (anode, nullid, blinknode, ""),
anode: (nullid, nullid, alinknode, ""),
},
)
def testWriteNoChange(self):
"""Tests that making a change to a tree, then making a second change
such that the result is a no-op, doesn't serialize that subtree. It
should only serialize the root node, because we're giving the root node
a new parent.
"""
a = cstore.treemanifest(FakeDataStore())
xhashflags = hashflags()
a.set("abc/def/x", *xhashflags)
a.set("abc/z", *hashflags())
alinknode = hashflags()[0]
dstore = FakeDataStore()
hstore = FakeHistoryStore()
for name, node, text, p1text, p1, p2 in a.finalize():
dstore.add(name, node, nullid, text)
hstore.add(name, node, p1, p2, alinknode, "")
if not name:
anode = node
a2 = cstore.treemanifest(dstore, anode)
b = a2.copy()
b.set("abc/def/x", *hashflags())
b.set("abc/def/x", *xhashflags)
blinknode = hashflags()[0]
newtrees = set()
for name, node, text, p1text, p1, p2 in b.finalize(a2):
newtrees.add((name, node))
dstore.add(name, node, nullid, text)
hstore.add(name, node, p1, p2, blinknode, "")
self.assertEquals(newtrees, set([("", node)]))
def testWriteReplaceFile(self):
"""Tests writing a manifest which replaces a file with a directory."""
a = cstore.treemanifest(FakeDataStore())
a.set("abc/a", *hashflags())
a.set("abc/z", *hashflags())
alinknode = hashflags()[0]
dstore = FakeDataStore()
hstore = FakeHistoryStore()
for name, node, text, p1text, p1, p2 in a.finalize():
dstore.add(name, node, nullid, text)
hstore.add(name, node, p1, p2, alinknode, "")
if not name:
anode = node
b = a.copy()
b.set("abc/a", None, None)
b.set("abc/a/foo", *hashflags())
blinknode = hashflags()[0]
for name, node, text, p1text, p1, p2 in b.finalize(a):
dstore.add(name, node, nullid, text)
hstore.add(name, node, p1, p2, blinknode, "")
if not name:
bnode = node
b2 = cstore.treemanifest(dstore, bnode)
self.assertEquals(list(b.iterentries()), list(b2.iterentries()))
self.assertEquals(
hstore.getancestors("", bnode),
{
bnode: (anode, nullid, blinknode, ""),
anode: (nullid, nullid, alinknode, ""),
},
)
def testGet(self):
a = cstore.treemanifest(FakeDataStore())
zflags = hashflags()
a.set("abc/z", *zflags)
self.assertEquals(a.get("abc/z"), zflags[0])
self.assertEquals(a.get("abc/x"), None)
self.assertEquals(a.get("abc"), None)
def testFind(self):
a = cstore.treemanifest(FakeDataStore())
zflags = hashflags()
a.set("abc/z", *zflags)
self.assertEquals(a.find("abc/z"), zflags)
try:
a.find("abc/x")
raise RuntimeError("find for non-existent file should throw")
except KeyError:
pass
def testSetFlag(self):
a = cstore.treemanifest(FakeDataStore())
zflags = hashflags()
a.set("abc/z", *zflags)
a.setflag("abc/z", "")
self.assertEquals(a.flags("abc/z"), "")
a.setflag("abc/z", "d")
self.assertEquals(a.flags("abc/z"), "d")
try:
a.setflag("foo", "d")
raise RuntimeError("setflag should throw")
except KeyError:
pass
def testSetItem(self):
a = cstore.treemanifest(FakeDataStore())
zflags = hashflags(requireflag=True)
a.set("abc/z", *zflags)
fooflags = hashflags()
a["foo"] = fooflags[0]
self.assertEquals(a.find("foo"), (fooflags[0], ""))
newnode = hashflags()[0]
a["abc/z"] = newnode
self.assertEquals(a.find("abc/z"), (newnode, zflags[1]))
def testText(self):
a = cstore.treemanifest(FakeDataStore())
zflags = hashflags(requireflag=True)
a.set("abc/z", *zflags)
treetext = a.text()
treetextv2 = a.text()
b = manifest.manifestdict()
b["abc/z"] = zflags[0]
b.setflag("abc/z", zflags[1])
fulltext = b.text()
fulltextv2 = b.text()
self.assertEquals(treetext, fulltext)
self.assertEquals(treetextv2, fulltextv2)
def testDiff(self):
a = cstore.treemanifest(FakeDataStore())
zflags = hashflags()
mflags = hashflags()
a.set("abc/z", *zflags)
a.set("xyz/m", *mflags)
alinknode = hashflags()[0]
b = cstore.treemanifest(FakeDataStore())
b.set("abc/z", *zflags)
b.set("xyz/m", *mflags)
blinknode = hashflags()[0]
# Diff matching trees
# - uncommitted trees
diff = a.diff(b)
self.assertEquals(diff, {})
# - committed trees
dstore = FakeDataStore()
hstore = FakeHistoryStore()
for name, node, text, p1text, p1, p2 in a.finalize():
dstore.add(name, node, nullid, text)
hstore.add(name, node, p1, p2, alinknode, "")
for name, node, text, p1text, p1, p2 in b.finalize():
dstore.add(name, node, nullid, text)
hstore.add(name, node, p1, p2, blinknode, "")
diff = a.diff(b)
self.assertEquals(diff, {})
b2 = b.copy()
# Diff with modifications
newfileflags = hashflags()
newzflags = hashflags()
b2.set("newfile", *newfileflags)
b2.set("abc/z", *newzflags)
# - uncommitted trees
diff = a.diff(b2)
self.assertEquals(
diff, {"newfile": ((None, ""), newfileflags), "abc/z": (zflags, newzflags)}
)
# - uncommitted trees with matcher
matcher = matchmod.match("/", "/", ["abc/*"])
diff = a.diff(b2, matcher=matcher)
self.assertEquals(diff, {"abc/z": (zflags, newzflags)})
matcher = matchmod.match("/", "/", ["newfile"])
diff = a.diff(b2, matcher=matcher)
self.assertEquals(diff, {"newfile": ((None, ""), newfileflags)})
# - committed trees
for name, node, text, p1text, p1, p2 in b2.finalize(a):
dstore.add(name, node, nullid, text)
hstore.add(name, node, p1, p2, blinknode, "")
diff = a.diff(b2)
self.assertEquals(
diff, {"newfile": ((None, ""), newfileflags), "abc/z": (zflags, newzflags)}
)
# Diff with clean
diff = a.diff(b2, clean=True)
self.assertEquals(
diff,
{
"newfile": ((None, ""), newfileflags),
"abc/z": (zflags, newzflags),
"xyz/m": None,
},
)
def testFilesNotIn(self):
a = cstore.treemanifest(FakeDataStore())
zflags = hashflags()
mflags = hashflags()
a.set("abc/z", *zflags)
a.set("xyz/m", *mflags)
alinknode = hashflags()[0]
b = cstore.treemanifest(FakeDataStore())
b.set("abc/z", *zflags)
b.set("xyz/m", *mflags)
blinknode = hashflags()[0]
# filesnotin matching trees
# - uncommitted trees
diff = a.filesnotin(b)
self.assertEquals(diff, set())
# - committed trees
dstore = FakeDataStore()
hstore = FakeHistoryStore()
for name, node, text, p1text, p1, p2 in a.finalize():
dstore.add(name, node, nullid, text)
hstore.add(name, node, p1, p2, alinknode, "")
for name, node, text, p1text, p1, p2 in b.finalize():
dstore.add(name, node, nullid, text)
hstore.add(name, node, p1, p2, blinknode, "")
diff = a.filesnotin(b)
self.assertEquals(diff, set())
# filesnotin with modifications
newfileflags = hashflags()
newzflags = hashflags()
b.set("newfile", *newfileflags)
b.set("abc/z", *newzflags)
# In 'a' and not in 'b'
files = a.filesnotin(b)
self.assertEquals(files, set())
# In 'b' and not in 'a'
files = b.filesnotin(a)
self.assertEquals(files, set(["newfile"]))
# With dir matcher
match = matchmod.match("/", "/", ["abc/*"])
files = b.filesnotin(a, matcher=match)
self.assertEquals(files, set())
# With file matcher
match = matchmod.match("/", "/", ["newfile"])
files = b.filesnotin(a, matcher=match)
self.assertEquals(files, set(["newfile"]))
# With no matches
match = matchmod.match("/", "/", ["xxx"])
files = b.filesnotin(a, matcher=match)
self.assertEquals(files, set([]))
def testHasDir(self):
a = cstore.treemanifest(FakeDataStore())
zflags = hashflags()
a.set("abc/z", *zflags)
self.assertFalse(a.hasdir("abc/z"))
self.assertTrue(a.hasdir("abc"))
self.assertFalse(a.hasdir("xyz"))
def testListDir(self):
a = cstore.treemanifest(FakeDataStore())
zflags = hashflags()
a.set("a/b/x", *zflags)
a.set("a/b/y/_", *zflags)
a.set("a/c/z", *zflags)
a.set("1", *zflags)
self.assertEquals(a.listdir(""), ["1", "a"])
self.assertEquals(a.listdir("a"), ["b", "c"])
self.assertEquals(a.listdir("a/b"), ["x", "y"])
self.assertEquals(a.listdir("a/b/"), ["x", "y"])
self.assertEquals(a.listdir("a/b/y"), ["_"])
self.assertEquals(a.listdir("a/c"), ["z"])
self.assertEquals(a.listdir("foo"), None)
self.assertEquals(a.listdir("1"), None)
self.assertEquals(a.listdir("1/"), None)
self.assertEquals(a.listdir("a/b/y/_"), None)
def testContains(self):
a = cstore.treemanifest(FakeDataStore())
zflags = hashflags()
a.set("abc/z", *zflags)
self.assertTrue("abc/z" in a)
self.assertFalse("abc" in a)
self.assertFalse(None in a)
def testNonZero(self):
a = cstore.treemanifest(FakeDataStore())
self.assertFalse(bool(a))
zflags = hashflags()
a.set("abc/z", *zflags)
self.assertTrue(bool(a))
def testFlags(self):
a = cstore.treemanifest(FakeDataStore())
zflags = hashflags(requireflag=True)
a.set("abc/z", *zflags)
self.assertEquals(a.flags("x"), "")
self.assertEquals(a.flags("x", default="z"), "z")
self.assertEquals(a.flags("abc/z"), zflags[1])
def testMatches(self):
a = cstore.treemanifest(FakeDataStore())
zflags = hashflags()
a.set("abc/z", *zflags)
a.set("foo", *hashflags())
match = matchmod.match("/", "/", ["abc/z"])
result = a.matches(match)
self.assertEquals(list(result.iterentries()), [("abc/z", zflags[0], zflags[1])])
match = matchmod.match("/", "/", ["x"])
result = a.matches(match)
self.assertEquals(list(result.iterentries()), [])
def testKeys(self):
a = cstore.treemanifest(FakeDataStore())
self.assertEquals(a.keys(), [])
zflags = hashflags()
a.set("abc/z", *zflags)
a.set("foo", *hashflags())
self.assertEquals(a.keys(), ["abc/z", "foo"])
def testIterItems(self):
a = cstore.treemanifest(FakeDataStore())
self.assertEquals(list(a.iteritems()), [])
zflags = hashflags()
fooflags = hashflags()
a.set("abc/z", *zflags)
a.set("foo", *fooflags)
self.assertEquals(
list(a.iteritems()), [("abc/z", zflags[0]), ("foo", fooflags[0])]
)
def testWalkSubtrees(self):
a = cstore.treemanifest(FakeDataStore())
zflags = hashflags()
fooflags = hashflags()
a.set("abc/z", *zflags)
a.set("foo", *fooflags)
# Walk over inmemory tree
subtrees = list(a.walksubtrees())
self.assertEquals(
subtrees,
[
(
"abc",
nullid,
"z\0%s%s\n" % (hex(zflags[0]), zflags[1]),
"",
nullid,
nullid,
),
(
"",
nullid,
"abc\0%st\nfoo\0%s%s\n"
% (hex(nullid), hex(fooflags[0]), fooflags[1]),
"",
nullid,
nullid,
),
],
)
# Walk over finalized tree
dstore = FakeDataStore()
hstore = FakeHistoryStore()
for name, node, text, p1text, p1, p2 in a.finalize():
dstore.add(name, node, nullid, text)
hstore.add(name, node, p1, p2, nullid, "")
if name == "":
rootnode = node
if name == "abc":
abcnode = node
subtrees = list(a.walksubtrees())
self.assertEquals(
subtrees,
[
(
"abc",
abcnode,
"z\0%s%s\n" % (hex(zflags[0]), zflags[1]),
"",
nullid,
nullid,
),
(
"",
rootnode,
"abc\0%st\nfoo\0%s%s\n"
% (hex(abcnode), hex(fooflags[0]), fooflags[1]),
"",
nullid,
nullid,
),
],
)
def testWalkSubdirtrees(self):
a = cstore.treemanifest(FakeDataStore())
zflags = hashflags()
qflags = hashflags()
fooflags = hashflags()
a.set("abc/def/z", *zflags)
a.set("abc/xyz/q", *qflags)
a.set("mno/foo", *fooflags)
# Walk over finalized tree
dstore = FakeDataStore()
hstore = FakeHistoryStore()
for name, node, text, p1text, p1, p2 in a.finalize():
dstore.add(name, node, nullid, text)
hstore.add(name, node, p1, p2, nullid, "")
if name == "abc":
abcnode = node
if name == "abc/xyz":
abcxyznode = node
if name == "abc/def":
abcdefnode = node
subtrees = list(
cstore.treemanifest.walksubdirtrees(("abc/def", abcdefnode), dstore)
)
self.assertEquals(
subtrees,
[
(
"abc/def",
abcdefnode,
"z\0%s%s\n" % (hex(zflags[0]), zflags[1]),
"",
nullid,
nullid,
)
],
)
subtrees = list(cstore.treemanifest.walksubdirtrees(("abc", abcnode), dstore))
self.assertEquals(
subtrees,
[
(
"abc/def",
abcdefnode,
"z\0%s%s\n" % (hex(zflags[0]), zflags[1]),
"",
nullid,
nullid,
),
(
"abc/xyz",
abcxyznode,
"q\0%s%s\n" % (hex(qflags[0]), qflags[1]),
"",
nullid,
nullid,
),
(
"abc",
abcnode,
"def\0%st\n" % (hex(abcdefnode),)
+ "xyz\0%st\n" % (hex(abcxyznode),),
"",
nullid,
nullid,
),
],
)
if __name__ == "__main__":
silenttestrunner.main(__name__)

View File

@ -1,231 +0,0 @@
#!/usr/bin/env python2.7
from __future__ import absolute_import
import hashlib
import random
import shutil
import tempfile
import unittest
import edenscm.mercurial.ui as uimod
import silenttestrunner
from bindings import revisionstore
from edenscm.mercurial import mdiff
from edenscm.mercurial.node import nullid
from edenscmnative.cstore import datapackstore, uniondatapackstore
class uniondatapackstoretests(unittest.TestCase):
def setUp(self):
random.seed(0)
self.tempdirs = []
def tearDown(self):
for d in self.tempdirs:
shutil.rmtree(d)
def makeTempDir(self):
tempdir = tempfile.mkdtemp()
self.tempdirs.append(tempdir)
return tempdir
def getHash(self, content):
return hashlib.sha1(content).digest()
def getFakeHash(self):
return "".join(chr(random.randint(0, 255)) for _ in range(20))
def createPackStore(self, packdir, revisions=None):
if revisions is None:
revisions = [("filename", self.getFakeHash(), nullid, "content")]
packer = revisionstore.mutabledeltastore(packfilepath=packdir)
for filename, node, base, content in revisions:
packer.add(filename, node, base, content)
packer.flush()
return datapackstore(packdir)
def testGetFromSingleDelta(self):
packdir = self.makeTempDir()
revisions = [("foo", self.getFakeHash(), nullid, "content")]
store = self.createPackStore(packdir, revisions=revisions)
unionstore = uniondatapackstore([store])
text = unionstore.get(revisions[0][0], revisions[0][1])
self.assertEquals("content", text)
def testGetFromChainDeltas(self):
packdir = self.makeTempDir()
rev1 = "content"
rev2 = "content2"
firsthash = self.getFakeHash()
revisions = [
("foo", firsthash, nullid, rev1),
("foo", self.getFakeHash(), firsthash, mdiff.textdiff(rev1, rev2)),
]
store = self.createPackStore(packdir, revisions=revisions)
unionstore = uniondatapackstore([store])
text = unionstore.get(revisions[1][0], revisions[1][1])
self.assertEquals(rev2, text)
def testGetDeltaChainSingleRev(self):
"""Test getting a 1-length delta chain."""
packdir = self.makeTempDir()
revisions = [("foo", self.getFakeHash(), nullid, "content")]
store = self.createPackStore(packdir, revisions=revisions)
unionstore = uniondatapackstore([store])
chain = unionstore.getdeltachain(revisions[0][0], revisions[0][1])
self.assertEquals(1, len(chain))
self.assertEquals("content", chain[0][4])
def testGetDeltaChainMultiRev(self):
"""Test getting a 2-length delta chain."""
packdir = self.makeTempDir()
firsthash = self.getFakeHash()
revisions = [
("foo", firsthash, nullid, "content"),
("foo", self.getFakeHash(), firsthash, "content2"),
]
store = self.createPackStore(packdir, revisions=revisions)
unionstore = uniondatapackstore([store])
chain = unionstore.getdeltachain(revisions[1][0], revisions[1][1])
self.assertEquals(2, len(chain))
self.assertEquals("content2", chain[0][4])
self.assertEquals("content", chain[1][4])
def testGetDeltaChainMultiPack(self):
"""Test getting chains from multiple packs."""
packdir = self.makeTempDir()
revisions1 = [("foo", self.getFakeHash(), nullid, "content")]
store = self.createPackStore(packdir, revisions=revisions1)
revisions2 = [("foo", self.getFakeHash(), revisions1[0][1], "content2")]
store = self.createPackStore(packdir, revisions=revisions2)
unionstore = uniondatapackstore([store])
chain = unionstore.getdeltachain(revisions2[0][0], revisions2[0][1])
self.assertEquals(2, len(chain))
self.assertEquals("content2", chain[0][4])
self.assertEquals("content", chain[1][4])
def testGetMissing(self):
packdir = self.makeTempDir()
revisions = [("foo", self.getFakeHash(), nullid, "content")]
store = self.createPackStore(packdir, revisions=revisions)
unionstore = uniondatapackstore([store])
missinghash1 = self.getFakeHash()
missinghash2 = self.getFakeHash()
missing = unionstore.getmissing(
[
(revisions[0][0], revisions[0][1]),
("foo", missinghash1),
("foo2", missinghash2),
]
)
self.assertEquals(2, len(missing))
self.assertEquals(
set([("foo", missinghash1), ("foo2", missinghash2)]), set(missing)
)
def testAddRemoveStore(self):
packdir = self.makeTempDir()
revisions = [("foo", self.getFakeHash(), nullid, "content")]
store = self.createPackStore(packdir, revisions=revisions)
packdir2 = self.makeTempDir()
revisions2 = [("foo2", self.getFakeHash(), nullid, "content2")]
store2 = self.createPackStore(packdir2, revisions=revisions2)
unionstore = uniondatapackstore([store])
unionstore.addstore(store2)
# Fetch from store2
result = unionstore.get("foo2", revisions2[0][1])
self.assertEquals(result, revisions2[0][3])
# Drop the store
unionstore.removestore(store2)
# Fetch from store1
result = unionstore.get("foo", revisions[0][1])
self.assertEquals(result, revisions[0][3])
# Fetch from missing store2
try:
unionstore.get("foo2", revisions2[0][1])
self.asserFalse(True, "get should've thrown")
except KeyError:
pass
class uniondatastorepythontests(uniondatapackstoretests):
def createPackStore(self, packdir, revisions=None):
if revisions is None:
revisions = [("filename", self.getFakeHash(), nullid, "content")]
packer = revisionstore.mutabledeltastore(packfilepath=packdir)
for filename, node, base, content in revisions:
packer.add(filename, node, base, content)
path = packer.flush()
return revisionstore.datapack(path)
def testGetDeltaChainMultiPack(self):
"""Test getting chains from multiple packs."""
packdir = self.makeTempDir()
revisions1 = [("foo", self.getFakeHash(), nullid, "content")]
pack1 = self.createPackStore(packdir, revisions=revisions1)
revisions2 = [("foo", self.getFakeHash(), revisions1[0][1], "content2")]
pack2 = self.createPackStore(packdir, revisions=revisions2)
unionstore = uniondatapackstore([pack1, pack2])
chain = unionstore.getdeltachain(revisions2[0][0], revisions2[0][1])
self.assertEquals(2, len(chain))
self.assertEquals("content2", chain[0][4])
self.assertEquals("content", chain[1][4])
def testGetDeltaChainMultiPackPyAndC(self):
"""Test getting chains from multiple packs."""
packdir1 = self.makeTempDir()
packdir2 = self.makeTempDir()
revisions1 = [("foo", self.getFakeHash(), nullid, "content")]
store = super(uniondatastorepythontests, self).createPackStore(
packdir1, revisions=revisions1
)
revisions2 = [("foo", self.getFakeHash(), revisions1[0][1], "content2")]
pack = self.createPackStore(packdir2, revisions=revisions2)
unionstore = uniondatapackstore([pack, store])
chain = unionstore.getdeltachain(revisions2[0][0], revisions2[0][1])
self.assertEquals(2, len(chain))
self.assertEquals("content2", chain[0][4])
self.assertEquals("content", chain[1][4])
if __name__ == "__main__":
silenttestrunner.main(__name__)

View File

@ -17,60 +17,47 @@ sh % "setconfig 'rebase.experimental.inmemory=1'"
sh % "setconfig 'rebase.experimental.inmemory.nomergedriver=False'"
sh % "setconfig 'rebase.experimental.inmemorywarning=rebasing in-memory!'"
sh.cd(testtmp.TESTTMP)
for userustmanifest in [True, False]:
sh.cd(testtmp.TESTTMP)
sh.setconfig("treemanifest.rustmanifest=%s" % int(userustmanifest))
# Create a commit with a move + content change:
sh % "newrepo"
sh % "echo 'original content'" > "file"
sh % "hg add -q"
sh % "hg commit -q -m base"
sh % "echo 'new content'" > "file"
sh % "hg mv file file_new"
sh % "hg commit -m a"
sh % "hg book -r . a"
# Create a commit with a move + content change:
sh % "newrepo"
sh % "echo 'original content'" > "file"
sh % "hg add -q"
sh % "hg commit -q -m base"
sh % "echo 'new content'" > "file"
sh % "hg mv file file_new"
sh % "hg commit -m a"
sh % "hg book -r . a"
# Recreate the same commit:
sh % "hg up -q '.~1'"
sh % "echo 'new content'" > "file"
sh % "hg mv file file_new"
sh % "hg commit -m b"
sh % "hg book -r . b"
# Recreate the same commit:
sh % "hg up -q '.~1'"
sh % "echo 'new content'" > "file"
sh % "hg mv file file_new"
sh % "hg commit -m b"
sh % "hg book -r . b"
reponame = "../without-imm"
sh.cp("-R", ".", reponame)
reponame = "../without-imm-%s" % userustmanifest
sh.cp("-R", ".", reponame)
# Rebase one version onto the other, confirm it gets rebased out:
sh % "hg rebase -r b -d a" == r"""
rebasing in-memory!
rebasing 811ec875201f "b" (b)
note: rebase of 2:811ec875201f created no changes to commit"""
# Rebase one version onto the other, confirm it gets rebased out:
sh % "hg rebase -r b -d a" == r"""
rebasing in-memory!
rebasing 811ec875201f "b" (b)
note: rebase of 2:811ec875201f created no changes to commit"""
# Without IMM, this behavior is semi-broken: the commit is not rebased out and the
# created commit is empty. (D8676355)
sh.cd(reponame)
sh % "setconfig 'rebase.experimental.inmemory=0'"
sh % "hg rebase -r b -d a" == r"""
rebasing 811ec875201f "b" (b)
warning: can't find ancestor for 'file_new' copied from 'file'!"""
# Without IMM, this behavior is semi-broken: the commit is not rebased out and the
# created commit is empty. (D8676355)
sh.cd(reponame)
sh % "setconfig 'rebase.experimental.inmemory=0'"
sh % "hg rebase -r b -d a" == r"""
rebasing 811ec875201f "b" (b)
warning: can't find ancestor for 'file_new' copied from 'file'!"""
if userustmanifest:
sh % "hg export tip" == r"""
# HG changeset patch
# User test
# Date 0 0
# Thu Jan 01 00:00:00 1970 +0000
# Node ID 0fe513c05d7fe2819c3ceccb072e74940604af36
# Parent 24483d5afe6cb1a13b3642b4d8622e91f4d1bec1
b"""
else:
sh % "hg export tip" == r"""
# HG changeset patch
# User test
# Date 0 0
# Thu Jan 01 00:00:00 1970 +0000
# Node ID 7552e6b0bc4ab4ac16175ced4f08a54c31faf706
# Parent 24483d5afe6cb1a13b3642b4d8622e91f4d1bec1
b"""
sh % "hg export tip" == r"""
# HG changeset patch
# User test
# Date 0 0
# Thu Jan 01 00:00:00 1970 +0000
# Node ID 0fe513c05d7fe2819c3ceccb072e74940604af36
# Parent 24483d5afe6cb1a13b3642b4d8622e91f4d1bec1
b"""