# HG changeset patch # User Joerg Sonnenberger # Date 2021-01-13 15:14:58 # Node ID 6266d19556ad186f81caf3e892148a993428c407 # Parent 49fd21f32695d885d730b56d91707c9059c6bd54 node: introduce nodeconstants class In preparing for moving from SHA1 hashes to a modern hash function, place nullid and other constant magic vules in a class. Provide the active set of constants in the repository and push it down. Provide nullid directly in strategic places like the repository as it is accessed very often. This changeset introduces the API change, but not the mechanical replacement of the node.py attributes itself. Differential Revision: https://phab.mercurial-scm.org/D9750 diff --git a/contrib/perf.py b/contrib/perf.py --- a/contrib/perf.py +++ b/contrib/perf.py @@ -3672,7 +3672,7 @@ def perfloadmarkers(ui, repo): Result is the number of markers in the repo.""" timer, fm = gettimer(ui) svfs = getsvfs(repo) - timer(lambda: len(obsolete.obsstore(svfs))) + timer(lambda: len(obsolete.obsstore(repo, svfs))) fm.end() diff --git a/hgext/absorb.py b/hgext/absorb.py --- a/hgext/absorb.py +++ b/hgext/absorb.py @@ -102,6 +102,9 @@ class nullui(object): class emptyfilecontext(object): """minimal filecontext representing an empty file""" + def __init__(self, repo): + self._repo = repo + def data(self): return b'' @@ -212,7 +215,7 @@ def getfilestack(stack, path, seenfctxs= if path in pctx: fctxs.append(pctx[path]) else: - fctxs.append(emptyfilecontext()) + fctxs.append(emptyfilecontext(pctx.repo())) fctxs.reverse() # note: we rely on a property of hg: filerev is not reused for linear diff --git a/hgext/git/gitlog.py b/hgext/git/gitlog.py --- a/hgext/git/gitlog.py +++ b/hgext/git/gitlog.py @@ -8,6 +8,7 @@ from mercurial.node import ( nullhex, nullid, nullrev, + sha1nodeconstants, wdirhex, ) from mercurial import ( @@ -422,6 +423,8 @@ class changelog(baselog): class manifestlog(baselog): + nodeconstants = sha1nodeconstants + def __getitem__(self, node): return self.get(b'', node) diff --git a/hgext/largefiles/lfutil.py b/hgext/largefiles/lfutil.py --- a/hgext/largefiles/lfutil.py +++ b/hgext/largefiles/lfutil.py @@ -206,6 +206,7 @@ def openlfdirstate(ui, repo, create=True repo.root, repo.dirstate._validate, lambda: sparse.matcher(repo), + repo.nodeconstants, ) # If the largefiles dirstate does not exist, populate and create diff --git a/hgext/sqlitestore.py b/hgext/sqlitestore.py --- a/hgext/sqlitestore.py +++ b/hgext/sqlitestore.py @@ -54,6 +54,7 @@ from mercurial.i18n import _ from mercurial.node import ( nullid, nullrev, + sha1nodeconstants, short, ) from mercurial.thirdparty import attr @@ -305,6 +306,7 @@ class sqlitefilestore(object): """Implements storage for an individual tracked path.""" def __init__(self, db, path, compression): + self.nullid = sha1nodeconstants.nullid self._db = db self._path = path diff --git a/mercurial/bookmarks.py b/mercurial/bookmarks.py --- a/mercurial/bookmarks.py +++ b/mercurial/bookmarks.py @@ -623,7 +623,7 @@ def unhexlifybookmarks(marks): _binaryentry = struct.Struct(b'>20sH') -def binaryencode(bookmarks): +def binaryencode(repo, bookmarks): """encode a '(bookmark, node)' iterable into a binary stream the binary format is: @@ -645,7 +645,7 @@ def binaryencode(bookmarks): return b''.join(binarydata) -def binarydecode(stream): +def binarydecode(repo, stream): """decode a binary stream into an '(bookmark, node)' iterable the binary format is: diff --git a/mercurial/branchmap.py b/mercurial/branchmap.py --- a/mercurial/branchmap.py +++ b/mercurial/branchmap.py @@ -97,7 +97,7 @@ class BranchMapCache(object): revs.extend(r for r in extrarevs if r <= bcache.tiprev) else: # nothing to fall back on, start empty. - bcache = branchcache() + bcache = branchcache(repo) revs.extend(cl.revs(start=bcache.tiprev + 1)) if revs: @@ -129,6 +129,7 @@ class BranchMapCache(object): if rbheads: rtiprev = max((int(clrev(node)) for node in rbheads)) cache = branchcache( + repo, remotebranchmap, repo[rtiprev].node(), rtiprev, @@ -184,6 +185,7 @@ class branchcache(object): def __init__( self, + repo, entries=(), tipnode=nullid, tiprev=nullrev, @@ -195,6 +197,7 @@ class branchcache(object): """hasnode is a function which can be used to verify whether changelog has a given node or not. If it's not provided, we assume that every node we have exists in changelog""" + self._repo = repo self.tipnode = tipnode self.tiprev = tiprev self.filteredhash = filteredhash @@ -280,6 +283,7 @@ class branchcache(object): if len(cachekey) > 2: filteredhash = bin(cachekey[2]) bcache = cls( + repo, tipnode=last, tiprev=lrev, filteredhash=filteredhash, @@ -388,6 +392,7 @@ class branchcache(object): def copy(self): """return an deep copy of the branchcache object""" return type(self)( + self._repo, self._entries, self.tipnode, self.tiprev, diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -2146,7 +2146,7 @@ def handlecheckbookmarks(op, inpart): contains binary encoded (bookmark, node) tuple. If the local state does not marks the one in the part, a PushRaced exception is raised """ - bookdata = bookmarks.binarydecode(inpart) + bookdata = bookmarks.binarydecode(op.repo, inpart) msgstandard = ( b'remote repository changed while pushing - please try again ' @@ -2376,7 +2376,7 @@ def handlebookmark(op, inpart): When mode is 'records', the information is recorded into the 'bookmarks' records of the bundle operation. This behavior is suitable for pulling. """ - changes = bookmarks.binarydecode(inpart) + changes = bookmarks.binarydecode(op.repo, inpart) pushkeycompat = op.repo.ui.configbool( b'server', b'bookmarks-pushkey-compat' diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py +++ b/mercurial/bundlerepo.py @@ -175,9 +175,15 @@ class bundlechangelog(bundlerevlog, chan class bundlemanifest(bundlerevlog, manifest.manifestrevlog): def __init__( - self, opener, cgunpacker, linkmapper, dirlogstarts=None, dir=b'' + self, + nodeconstants, + opener, + cgunpacker, + linkmapper, + dirlogstarts=None, + dir=b'', ): - manifest.manifestrevlog.__init__(self, opener, tree=dir) + manifest.manifestrevlog.__init__(self, nodeconstants, opener, tree=dir) bundlerevlog.__init__( self, opener, self.indexfile, cgunpacker, linkmapper ) @@ -192,6 +198,7 @@ class bundlemanifest(bundlerevlog, manif if d in self._dirlogstarts: self.bundle.seek(self._dirlogstarts[d]) return bundlemanifest( + self.nodeconstants, self.opener, self.bundle, self._linkmapper, @@ -368,7 +375,9 @@ class bundlerepository(object): # consume the header if it exists self._cgunpacker.manifestheader() linkmapper = self.unfiltered().changelog.rev - rootstore = bundlemanifest(self.svfs, self._cgunpacker, linkmapper) + rootstore = bundlemanifest( + self.nodeconstants, self.svfs, self._cgunpacker, linkmapper + ) self.filestart = self._cgunpacker.tell() return manifest.manifestlog( diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -662,7 +662,7 @@ class headerlessfixup(object): return readexactly(self._fh, n) -def _revisiondeltatochunks(delta, headerfn): +def _revisiondeltatochunks(repo, delta, headerfn): """Serialize a revisiondelta to changegroup chunks.""" # The captured revision delta may be encoded as a delta against @@ -1065,7 +1065,9 @@ class cgpacker(object): sidedata_helpers=sidedata_helpers, ) for delta in deltas: - for chunk in _revisiondeltatochunks(delta, self._builddeltaheader): + for chunk in _revisiondeltatochunks( + self._repo, delta, self._builddeltaheader + ): size += len(chunk) yield chunk @@ -1121,7 +1123,9 @@ class cgpacker(object): yield chunk for delta in deltas: - chunks = _revisiondeltatochunks(delta, self._builddeltaheader) + chunks = _revisiondeltatochunks( + self._repo, delta, self._builddeltaheader + ) for chunk in chunks: size += len(chunk) yield chunk @@ -1160,7 +1164,9 @@ class cgpacker(object): yield h for delta in deltas: - chunks = _revisiondeltatochunks(delta, self._builddeltaheader) + chunks = _revisiondeltatochunks( + self._repo, delta, self._builddeltaheader + ) for chunk in chunks: size += len(chunk) yield chunk diff --git a/mercurial/changelog.py b/mercurial/changelog.py --- a/mercurial/changelog.py +++ b/mercurial/changelog.py @@ -191,7 +191,7 @@ class _changelogrevision(object): # Extensions might modify _defaultextra, so let the constructor below pass # it in extra = attr.ib() - manifest = attr.ib(default=nullid) + manifest = attr.ib() user = attr.ib(default=b'') date = attr.ib(default=(0, 0)) files = attr.ib(default=attr.Factory(list)) @@ -219,9 +219,9 @@ class changelogrevision(object): '_changes', ) - def __new__(cls, text, sidedata, cpsd): + def __new__(cls, cl, text, sidedata, cpsd): if not text: - return _changelogrevision(extra=_defaultextra) + return _changelogrevision(extra=_defaultextra, manifest=nullid) self = super(changelogrevision, cls).__new__(cls) # We could return here and implement the following as an __init__. @@ -526,7 +526,7 @@ class changelog(revlog.revlog): """ d, s = self._revisiondata(nodeorrev) c = changelogrevision( - d, s, self._copiesstorage == b'changeset-sidedata' + self, d, s, self._copiesstorage == b'changeset-sidedata' ) return (c.manifest, c.user, c.date, c.files, c.description, c.extra) @@ -534,7 +534,7 @@ class changelog(revlog.revlog): """Obtain a ``changelogrevision`` for a node or revision.""" text, sidedata = self._revisiondata(nodeorrev) return changelogrevision( - text, sidedata, self._copiesstorage == b'changeset-sidedata' + self, text, sidedata, self._copiesstorage == b'changeset-sidedata' ) def readfiles(self, nodeorrev): diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -73,13 +73,16 @@ def _getfsnow(vfs): @interfaceutil.implementer(intdirstate.idirstate) class dirstate(object): - def __init__(self, opener, ui, root, validate, sparsematchfn): + def __init__( + self, opener, ui, root, validate, sparsematchfn, nodeconstants + ): """Create a new dirstate object. opener is an open()-like callable that can be used to open the dirstate file; root is the root of the directory tracked by the dirstate. """ + self._nodeconstants = nodeconstants self._opener = opener self._validate = validate self._root = root @@ -136,7 +139,9 @@ class dirstate(object): @propertycache def _map(self): """Return the dirstate contents (see documentation for dirstatemap).""" - self._map = self._mapcls(self._ui, self._opener, self._root) + self._map = self._mapcls( + self._ui, self._opener, self._root, self._nodeconstants + ) return self._map @property @@ -1420,12 +1425,13 @@ class dirstatemap(object): denormalized form that they appear as in the dirstate. """ - def __init__(self, ui, opener, root): + def __init__(self, ui, opener, root, nodeconstants): self._ui = ui self._opener = opener self._root = root self._filename = b'dirstate' self._nodelen = 20 + self._nodeconstants = nodeconstants self._parents = None self._dirtyparents = False @@ -1724,7 +1730,8 @@ class dirstatemap(object): if rustmod is not None: class dirstatemap(object): - def __init__(self, ui, opener, root): + def __init__(self, ui, opener, root, nodeconstants): + self._nodeconstants = nodeconstants self._ui = ui self._opener = opener self._root = root diff --git a/mercurial/discovery.py b/mercurial/discovery.py --- a/mercurial/discovery.py +++ b/mercurial/discovery.py @@ -270,9 +270,12 @@ def _headssummary(pushop): # C. Update newmap with outgoing changes. # This will possibly add new heads and remove existing ones. newmap = branchmap.remotebranchcache( - (branch, heads[1]) - for branch, heads in pycompat.iteritems(headssum) - if heads[0] is not None + repo, + ( + (branch, heads[1]) + for branch, heads in pycompat.iteritems(headssum) + if heads[0] is not None + ), ) newmap.update(repo, (ctx.rev() for ctx in missingctx)) for branch, newheads in pycompat.iteritems(newmap): diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -827,7 +827,7 @@ def _pushb2checkbookmarks(pushop, bundle data = [] for book, old, new in pushop.outbookmarks: data.append((book, old)) - checkdata = bookmod.binaryencode(data) + checkdata = bookmod.binaryencode(pushop.repo, data) bundler.newpart(b'check:bookmarks', data=checkdata) @@ -1027,7 +1027,7 @@ def _pushb2bookmarkspart(pushop, bundler _abortonsecretctx(pushop, new, book) data.append((book, new)) allactions.append((book, _bmaction(old, new))) - checkdata = bookmod.binaryencode(data) + checkdata = bookmod.binaryencode(pushop.repo, data) bundler.newpart(b'bookmarks', data=checkdata) def handlereply(op): @@ -2455,7 +2455,7 @@ def _getbundlebookmarkpart( if not b2caps or b'bookmarks' not in b2caps: raise error.Abort(_(b'no common bookmarks exchange method')) books = bookmod.listbinbookmarks(repo) - data = bookmod.binaryencode(books) + data = bookmod.binaryencode(repo, books) if data: bundler.newpart(b'bookmarks', data=data) diff --git a/mercurial/filelog.py b/mercurial/filelog.py --- a/mercurial/filelog.py +++ b/mercurial/filelog.py @@ -33,6 +33,7 @@ class filelog(object): # Used by LFS. self._revlog.filename = path self._revlog.revlog_kind = b'filelog' + self.nullid = self._revlog.nullid def __len__(self): return len(self._revlog) diff --git a/mercurial/interfaces/dirstate.py b/mercurial/interfaces/dirstate.py --- a/mercurial/interfaces/dirstate.py +++ b/mercurial/interfaces/dirstate.py @@ -8,7 +8,7 @@ from . import util as interfaceutil class idirstate(interfaceutil.Interface): - def __init__(opener, ui, root, validate, sparsematchfn): + def __init__(opener, ui, root, validate, sparsematchfn, nodeconstants): """Create a new dirstate object. opener is an open()-like callable that can be used to open the diff --git a/mercurial/interfaces/repository.py b/mercurial/interfaces/repository.py --- a/mercurial/interfaces/repository.py +++ b/mercurial/interfaces/repository.py @@ -523,6 +523,10 @@ class ifileindex(interfaceutil.Interface * Metadata to facilitate storage. """ + nullid = interfaceutil.Attribute( + """node for the null revision for use as delta base.""" + ) + def __len__(): """Obtain the number of revisions stored for this file.""" @@ -1143,6 +1147,10 @@ class imanifestrevisionwritable(imanifes class imanifeststorage(interfaceutil.Interface): """Storage interface for manifest data.""" + nodeconstants = interfaceutil.Attribute( + """nodeconstants used by the current repository.""" + ) + tree = interfaceutil.Attribute( """The path to the directory this manifest tracks. @@ -1366,6 +1374,10 @@ class imanifestlog(interfaceutil.Interfa tree manifests. """ + nodeconstants = interfaceutil.Attribute( + """nodeconstants used by the current repository.""" + ) + def __getitem__(node): """Obtain a manifest instance for a given binary node. @@ -1434,6 +1446,13 @@ class ilocalrepositorymain(interfaceutil This currently captures the reality of things - not how things should be. """ + nodeconstants = interfaceutil.Attribute( + """Constant nodes matching the hash function used by the repository.""" + ) + nullid = interfaceutil.Attribute( + """null revision for the hash function used by the repository.""" + ) + supportedformats = interfaceutil.Attribute( """Set of requirements that apply to stream clone. diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -21,6 +21,7 @@ from .node import ( hex, nullid, nullrev, + sha1nodeconstants, short, ) from .pycompat import ( @@ -1330,6 +1331,8 @@ class localrepository(object): self.vfs = hgvfs self.path = hgvfs.base self.requirements = requirements + self.nodeconstants = sha1nodeconstants + self.nullid = self.nodeconstants.nullid self.supported = supportedrequirements self.sharedpath = sharedpath self.store = store @@ -1676,7 +1679,12 @@ class localrepository(object): sparsematchfn = lambda: sparse.matcher(self) return dirstate.dirstate( - self.vfs, self.ui, self.root, self._dirstatevalidate, sparsematchfn + self.vfs, + self.ui, + self.root, + self._dirstatevalidate, + sparsematchfn, + self.nodeconstants, ) def _dirstatevalidate(self, node): diff --git a/mercurial/manifest.py b/mercurial/manifest.py --- a/mercurial/manifest.py +++ b/mercurial/manifest.py @@ -792,8 +792,9 @@ def _splittopdir(f): @interfaceutil.implementer(repository.imanifestdict) class treemanifest(object): - def __init__(self, dir=b'', text=b''): + def __init__(self, nodeconstants, dir=b'', text=b''): self._dir = dir + self.nodeconstants = nodeconstants self._node = nullid self._loadfunc = _noop self._copyfunc = _noop @@ -1051,7 +1052,9 @@ class treemanifest(object): if dir: self._loadlazy(dir) if dir not in self._dirs: - self._dirs[dir] = treemanifest(self._subpath(dir)) + self._dirs[dir] = treemanifest( + self.nodeconstants, self._subpath(dir) + ) self._dirs[dir].__setitem__(subpath, n) else: # manifest nodes are either 20 bytes or 32 bytes, @@ -1078,14 +1081,16 @@ class treemanifest(object): if dir: self._loadlazy(dir) if dir not in self._dirs: - self._dirs[dir] = treemanifest(self._subpath(dir)) + self._dirs[dir] = treemanifest( + self.nodeconstants, self._subpath(dir) + ) self._dirs[dir].setflag(subpath, flags) else: self._flags[f] = flags self._dirty = True def copy(self): - copy = treemanifest(self._dir) + copy = treemanifest(self.nodeconstants, self._dir) copy._node = self._node copy._dirty = self._dirty if self._copyfunc is _noop: @@ -1215,7 +1220,7 @@ class treemanifest(object): visit = match.visitchildrenset(self._dir[:-1]) if visit == b'all': return self.copy() - ret = treemanifest(self._dir) + ret = treemanifest(self.nodeconstants, self._dir) if not visit: return ret @@ -1272,7 +1277,7 @@ class treemanifest(object): m2 = m2._matches(match) return m1.diff(m2, clean=clean) result = {} - emptytree = treemanifest() + emptytree = treemanifest(self.nodeconstants) def _iterativediff(t1, t2, stack): """compares two tree manifests and append new tree-manifests which @@ -1368,7 +1373,7 @@ class treemanifest(object): self._load() # for consistency; should never have any effect here m1._load() m2._load() - emptytree = treemanifest() + emptytree = treemanifest(self.nodeconstants) def getnode(m, d): ld = m._lazydirs.get(d) @@ -1551,6 +1556,7 @@ class manifestrevlog(object): def __init__( self, + nodeconstants, opener, tree=b'', dirlogcache=None, @@ -1567,6 +1573,7 @@ class manifestrevlog(object): option takes precedence, so if it is set to True, we ignore whatever value is passed in to the constructor. """ + self.nodeconstants = nodeconstants # During normal operations, we expect to deal with not more than four # revs at a time (such as during commit --amend). When rebasing large # stacks of commits, the number can go up, hence the config knob below. @@ -1654,7 +1661,11 @@ class manifestrevlog(object): assert self._treeondisk if d not in self._dirlogcache: mfrevlog = manifestrevlog( - self.opener, d, self._dirlogcache, treemanifest=self._treeondisk + self.nodeconstants, + self.opener, + d, + self._dirlogcache, + treemanifest=self._treeondisk, ) self._dirlogcache[d] = mfrevlog return self._dirlogcache[d] @@ -1917,6 +1928,7 @@ class manifestlog(object): they receive (i.e. tree or flat or lazily loaded, etc).""" def __init__(self, opener, repo, rootstore, narrowmatch): + self.nodeconstants = repo.nodeconstants usetreemanifest = False cachesize = 4 @@ -1955,7 +1967,7 @@ class manifestlog(object): if not self._narrowmatch.always(): if not self._narrowmatch.visitdir(tree[:-1]): - return excludeddirmanifestctx(tree, node) + return excludeddirmanifestctx(self.nodeconstants, tree, node) if tree: if self._rootstore._treeondisk: if verify: @@ -2118,7 +2130,7 @@ class memtreemanifestctx(object): def __init__(self, manifestlog, dir=b''): self._manifestlog = manifestlog self._dir = dir - self._treemanifest = treemanifest() + self._treemanifest = treemanifest(manifestlog.nodeconstants) def _storage(self): return self._manifestlog.getstorage(b'') @@ -2168,17 +2180,19 @@ class treemanifestctx(object): narrowmatch = self._manifestlog._narrowmatch if not narrowmatch.always(): if not narrowmatch.visitdir(self._dir[:-1]): - return excludedmanifestrevlog(self._dir) + return excludedmanifestrevlog( + self._manifestlog.nodeconstants, self._dir + ) return self._manifestlog.getstorage(self._dir) def read(self): if self._data is None: store = self._storage() if self._node == nullid: - self._data = treemanifest() + self._data = treemanifest(self._manifestlog.nodeconstants) # TODO accessing non-public API elif store._treeondisk: - m = treemanifest(dir=self._dir) + m = treemanifest(self._manifestlog.nodeconstants, dir=self._dir) def gettext(): return store.revision(self._node) @@ -2198,7 +2212,9 @@ class treemanifestctx(object): text = store.revision(self._node) arraytext = bytearray(text) store.fulltextcache[self._node] = arraytext - self._data = treemanifest(dir=self._dir, text=text) + self._data = treemanifest( + self._manifestlog.nodeconstants, dir=self._dir, text=text + ) return self._data @@ -2235,7 +2251,7 @@ class treemanifestctx(object): r0 = store.deltaparent(store.rev(self._node)) m0 = self._manifestlog.get(self._dir, store.node(r0)).read() m1 = self.read() - md = treemanifest(dir=self._dir) + md = treemanifest(self._manifestlog.nodeconstants, dir=self._dir) for f, ((n0, fl0), (n1, fl1)) in pycompat.iteritems(m0.diff(m1)): if n1: md[f] = n1 @@ -2278,8 +2294,8 @@ class excludeddir(treemanifest): whose contents are unknown. """ - def __init__(self, dir, node): - super(excludeddir, self).__init__(dir) + def __init__(self, nodeconstants, dir, node): + super(excludeddir, self).__init__(nodeconstants, dir) self._node = node # Add an empty file, which will be included by iterators and such, # appearing as the directory itself (i.e. something like "dir/") @@ -2298,12 +2314,13 @@ class excludeddir(treemanifest): class excludeddirmanifestctx(treemanifestctx): """context wrapper for excludeddir - see that docstring for rationale""" - def __init__(self, dir, node): + def __init__(self, nodeconstants, dir, node): + self.nodeconstants = nodeconstants self._dir = dir self._node = node def read(self): - return excludeddir(self._dir, self._node) + return excludeddir(self.nodeconstants, self._dir, self._node) def readfast(self, shallow=False): # special version of readfast since we don't have underlying storage @@ -2325,7 +2342,8 @@ class excludedmanifestrevlog(manifestrev outside the narrowspec. """ - def __init__(self, dir): + def __init__(self, nodeconstants, dir): + self.nodeconstants = nodeconstants self._dir = dir def __len__(self): diff --git a/mercurial/node.py b/mercurial/node.py --- a/mercurial/node.py +++ b/mercurial/node.py @@ -21,29 +21,48 @@ def bin(s): raise TypeError(e) -nullrev = -1 -# In hex, this is '0000000000000000000000000000000000000000' -nullid = b"\0" * 20 -nullhex = hex(nullid) +def short(node): + return hex(node[:6]) + -# Phony node value to stand-in for new files in some uses of -# manifests. -# In hex, this is '2121212121212121212121212121212121212121' -newnodeid = b'!!!!!!!!!!!!!!!!!!!!' -# In hex, this is '3030303030303030303030303030306164646564' -addednodeid = b'000000000000000added' -# In hex, this is '3030303030303030303030306d6f646966696564' -modifiednodeid = b'000000000000modified' +nullrev = -1 -wdirfilenodeids = {newnodeid, addednodeid, modifiednodeid} - -# pseudo identifiers for working directory -# (they are experimental, so don't add too many dependencies on them) +# pseudo identifier for working directory +# (experimental, so don't add too many dependencies on it) wdirrev = 0x7FFFFFFF -# In hex, this is 'ffffffffffffffffffffffffffffffffffffffff' -wdirid = b"\xff" * 20 -wdirhex = hex(wdirid) -def short(node): - return hex(node[:6]) +class sha1nodeconstants(object): + nodelen = 20 + + # In hex, this is '0000000000000000000000000000000000000000' + nullid = b"\0" * nodelen + nullhex = hex(nullid) + + # Phony node value to stand-in for new files in some uses of + # manifests. + # In hex, this is '2121212121212121212121212121212121212121' + newnodeid = b'!!!!!!!!!!!!!!!!!!!!' + # In hex, this is '3030303030303030303030303030306164646564' + addednodeid = b'000000000000000added' + # In hex, this is '3030303030303030303030306d6f646966696564' + modifiednodeid = b'000000000000modified' + + wdirfilenodeids = {newnodeid, addednodeid, modifiednodeid} + + # pseudo identifier for working directory + # (experimental, so don't add too many dependencies on it) + # In hex, this is 'ffffffffffffffffffffffffffffffffffffffff' + wdirid = b"\xff" * nodelen + wdirhex = hex(wdirid) + + +# legacy starting point for porting modules +nullid = sha1nodeconstants.nullid +nullhex = sha1nodeconstants.nullhex +newnodeid = sha1nodeconstants.newnodeid +addednodeid = sha1nodeconstants.addednodeid +modifiednodeid = sha1nodeconstants.modifiednodeid +wdirfilenodeids = sha1nodeconstants.wdirfilenodeids +wdirid = sha1nodeconstants.wdirid +wdirhex = sha1nodeconstants.wdirhex diff --git a/mercurial/obsolete.py b/mercurial/obsolete.py --- a/mercurial/obsolete.py +++ b/mercurial/obsolete.py @@ -560,10 +560,11 @@ class obsstore(object): # parents: (tuple of nodeid) or None, parents of predecessors # None is used when no data has been recorded - def __init__(self, svfs, defaultformat=_fm1version, readonly=False): + def __init__(self, repo, svfs, defaultformat=_fm1version, readonly=False): # caches for various obsolescence related cache self.caches = {} self.svfs = svfs + self.repo = repo self._defaultformat = defaultformat self._readonly = readonly @@ -806,7 +807,7 @@ def makestore(ui, repo): if defaultformat is not None: kwargs['defaultformat'] = defaultformat readonly = not isenabled(repo, createmarkersopt) - store = obsstore(repo.svfs, readonly=readonly, **kwargs) + store = obsstore(repo, repo.svfs, readonly=readonly, **kwargs) if store and readonly: ui.warn( _(b'obsolete feature not enabled but %i markers found!\n') diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -28,6 +28,7 @@ from .node import ( nullhex, nullid, nullrev, + sha1nodeconstants, short, wdirfilenodeids, wdirhex, @@ -651,6 +652,10 @@ class revlog(object): raise error.RevlogError( _(b'unknown version (%d) in revlog %s') % (fmt, self.indexfile) ) + + self.nodeconstants = sha1nodeconstants + self.nullid = self.nodeconstants.nullid + # sparse-revlog can't be on without general-delta (issue6056) if not self._generaldelta: self._sparserevlog = False diff --git a/mercurial/statichttprepo.py b/mercurial/statichttprepo.py --- a/mercurial/statichttprepo.py +++ b/mercurial/statichttprepo.py @@ -12,6 +12,7 @@ from __future__ import absolute_import import errno from .i18n import _ +from .node import sha1nodeconstants from . import ( branchmap, changelog, @@ -198,6 +199,8 @@ class statichttprepository( requirements, supportedrequirements ) localrepo.ensurerequirementscompatible(ui, requirements) + self.nodeconstants = sha1nodeconstants + self.nullid = self.nodeconstants.nullid # setup store self.store = localrepo.makestore(requirements, self.path, vfsclass) @@ -207,7 +210,7 @@ class statichttprepository( self._filecache = {} self.requirements = requirements - rootmanifest = manifest.manifestrevlog(self.svfs) + rootmanifest = manifest.manifestrevlog(self.nodeconstants, self.svfs) self.manifestlog = manifest.manifestlog( self.svfs, self, rootmanifest, self.narrowmatch() ) diff --git a/mercurial/store.py b/mercurial/store.py --- a/mercurial/store.py +++ b/mercurial/store.py @@ -441,7 +441,7 @@ class basicstore(object): ) def manifestlog(self, repo, storenarrowmatch): - rootstore = manifest.manifestrevlog(self.vfs) + rootstore = manifest.manifestrevlog(repo.nodeconstants, self.vfs) return manifest.manifestlog(self.vfs, repo, rootstore, storenarrowmatch) def datafiles(self, matcher=None): diff --git a/mercurial/unionrepo.py b/mercurial/unionrepo.py --- a/mercurial/unionrepo.py +++ b/mercurial/unionrepo.py @@ -153,9 +153,9 @@ class unionchangelog(unionrevlog, change class unionmanifest(unionrevlog, manifest.manifestrevlog): - def __init__(self, opener, opener2, linkmapper): - manifest.manifestrevlog.__init__(self, opener) - manifest2 = manifest.manifestrevlog(opener2) + def __init__(self, nodeconstants, opener, opener2, linkmapper): + manifest.manifestrevlog.__init__(self, nodeconstants, opener) + manifest2 = manifest.manifestrevlog(nodeconstants, opener2) unionrevlog.__init__( self, opener, self.indexfile, manifest2, linkmapper ) @@ -205,7 +205,10 @@ class unionrepository(object): @localrepo.unfilteredpropertycache def manifestlog(self): rootstore = unionmanifest( - self.svfs, self.repo2.svfs, self.unfiltered()._clrev + self.nodeconstants, + self.svfs, + self.repo2.svfs, + self.unfiltered()._clrev, ) return manifest.manifestlog( self.svfs, self, rootstore, self.narrowmatch() diff --git a/mercurial/upgrade_utils/engine.py b/mercurial/upgrade_utils/engine.py --- a/mercurial/upgrade_utils/engine.py +++ b/mercurial/upgrade_utils/engine.py @@ -36,7 +36,9 @@ def _revlogfrompath(repo, path): return changelog.changelog(repo.svfs) elif path.endswith(b'00manifest.i'): mandir = path[: -len(b'00manifest.i')] - return manifest.manifestrevlog(repo.svfs, tree=mandir) + return manifest.manifestrevlog( + repo.nodeconstants, repo.svfs, tree=mandir + ) else: # reverse of "/".join(("data", path + ".i")) return filelog.filelog(repo.svfs, path[5:-2]) diff --git a/relnotes/next b/relnotes/next --- a/relnotes/next +++ b/relnotes/next @@ -43,3 +43,7 @@ now get a revision number as argument instead of a node. * revlog.addrevision returns the revision number instead of the node. + + * `nodes.nullid` and related constants are being phased out as part of + the deprecation of SHA1. Repository instances and related classes + provide access via `nodeconstants` and in some cases `nullid` attributes. diff --git a/tests/simplestorerepo.py b/tests/simplestorerepo.py --- a/tests/simplestorerepo.py +++ b/tests/simplestorerepo.py @@ -106,7 +106,9 @@ class filestorage(object): _flagserrorclass = simplestoreerror - def __init__(self, svfs, path): + def __init__(self, repo, svfs, path): + self.nullid = repo.nullid + self._repo = repo self._svfs = svfs self._path = path @@ -689,7 +691,7 @@ def reposetup(ui, repo): class simplestorerepo(repo.__class__): def file(self, f): - return filestorage(self.svfs, f) + return filestorage(repo, self.svfs, f) repo.__class__ = simplestorerepo diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py --- a/tests/test-check-interfaces.py +++ b/tests/test-check-interfaces.py @@ -248,7 +248,10 @@ def main(): # Conforms to imanifestlog. ml = manifest.manifestlog( - vfs, repo, manifest.manifestrevlog(repo.svfs), repo.narrowmatch() + vfs, + repo, + manifest.manifestrevlog(repo.nodeconstants, repo.svfs), + repo.narrowmatch(), ) checkzobject(ml) checkzobject(repo.manifestlog) @@ -263,7 +266,7 @@ def main(): # Conforms to imanifestdict. checkzobject(mctx.read()) - mrl = manifest.manifestrevlog(vfs) + mrl = manifest.manifestrevlog(repo.nodeconstants, vfs) checkzobject(mrl) ziverify.verifyClass(repository.irevisiondelta, revlog.revlogrevisiondelta) diff --git a/tests/test-manifest.py b/tests/test-manifest.py --- a/tests/test-manifest.py +++ b/tests/test-manifest.py @@ -6,6 +6,8 @@ import silenttestrunner import unittest import zlib +from mercurial.node import sha1nodeconstants + from mercurial import ( manifest as manifestmod, match as matchmod, @@ -436,7 +438,7 @@ class testmanifestdict(unittest.TestCase class testtreemanifest(unittest.TestCase, basemanifesttests): def parsemanifest(self, text): - return manifestmod.treemanifest(b'', text) + return manifestmod.treemanifest(sha1nodeconstants, b'', text) def testWalkSubtrees(self): m = self.parsemanifest(A_DEEPER_MANIFEST)