Patchwork D9480: node: import symbols explicitly

login
register
mail settings
Submitter phabricator
Date Dec. 1, 2020, 9:12 p.m.
Message ID <differential-rev-PHID-DREV-vzjmg56c3kc42j7gw2pk-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/47762/
State Superseded
Headers show

Comments

phabricator - Dec. 1, 2020, 9:12 p.m.
joerg.sonnenberger created this revision.
Herald added a reviewer: durin42.
Herald added a reviewer: durin42.
Herald added a reviewer: martinvonz.
Herald added a reviewer: martinvonz.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  There is no point in lazy importing mercurial.node, it is used all over
  the place anyway. So consistently import the used symbols directly.
  Fix one file using symbols indirectly via mercurial.revlog.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D9480

AFFECTED FILES
  contrib/dumprevlog
  contrib/undumprevlog
  hgext/absorb.py
  hgext/convert/git.py
  hgext/convert/hg.py
  hgext/fastannotate/context.py
  hgext/fastannotate/formatter.py
  hgext/git/dirstate.py
  hgext/git/gitlog.py
  hgext/git/index.py
  hgext/gpg.py
  hgext/histedit.py
  hgext/infinitepush/bundleparts.py
  hgext/infinitepush/store.py
  hgext/journal.py
  hgext/largefiles/lfcommands.py
  hgext/largefiles/lfutil.py
  hgext/lfs/__init__.py
  hgext/lfs/blobstore.py
  hgext/narrow/narrowcommands.py
  hgext/patchbomb.py
  hgext/rebase.py
  hgext/releasenotes.py
  hgext/remotefilelog/__init__.py
  hgext/remotefilelog/basepack.py
  hgext/remotefilelog/debugcommands.py
  hgext/remotefilelog/fileserverclient.py
  hgext/remotefilelog/shallowutil.py
  hgext/transplant.py
  hgext/uncommit.py
  mercurial/bundle2.py
  mercurial/bundlerepo.py
  mercurial/chgserver.py
  mercurial/copies.py
  mercurial/dagop.py
  mercurial/hg.py
  mercurial/keepalive.py
  mercurial/metadata.py
  mercurial/obsolete.py
  mercurial/obsutil.py
  mercurial/revlogutils/nodemap.py
  mercurial/revset.py
  mercurial/revsetlang.py
  mercurial/rewriteutil.py
  mercurial/shelve.py
  mercurial/simplemerge.py
  mercurial/sslutil.py
  mercurial/store.py
  mercurial/strip.py
  mercurial/subrepo.py
  mercurial/tagmerge.py
  mercurial/templatefilters.py
  mercurial/util.py
  tests/drawdag.py
  tests/test-parseindex2.py
  tests/test-revlog-raw.py
  tests/test-rust-ancestor.py
  tests/testlib/ext-sidedata.py

CHANGE DETAILS




To: joerg.sonnenberger, durin42, martinvonz, #hg-reviewers
Cc: mercurial-patches, mercurial-devel

Patch

diff --git a/tests/testlib/ext-sidedata.py b/tests/testlib/ext-sidedata.py
--- a/tests/testlib/ext-sidedata.py
+++ b/tests/testlib/ext-sidedata.py
@@ -10,9 +10,12 @@ 
 import hashlib
 import struct
 
+from mercurial.node import (
+    nullid,
+    nullrev,
+)
 from mercurial import (
     extensions,
-    node,
     requirements,
     revlog,
     upgrade,
@@ -40,7 +43,7 @@ 
     text = orig(self, nodeorrev, *args, **kwargs)
     if getattr(self, 'sidedatanocheck', False):
         return text
-    if nodeorrev != node.nullrev and nodeorrev != node.nullid:
+    if nodeorrev != nullrev and nodeorrev != nullid:
         sd = self.sidedata(nodeorrev)
         if len(text) != struct.unpack('>I', sd[sidedata.SD_TEST1])[0]:
             raise RuntimeError('text size mismatch')
diff --git a/tests/test-rust-ancestor.py b/tests/test-rust-ancestor.py
--- a/tests/test-rust-ancestor.py
+++ b/tests/test-rust-ancestor.py
@@ -2,10 +2,8 @@ 
 import sys
 import unittest
 
-from mercurial import (
-    error,
-    node,
-)
+from mercurial.node import wdirrev
+from mercurial import error
 
 from mercurial.testing import revlog as revlogtesting
 
@@ -150,7 +148,7 @@ 
         # WdirUnsupported directly
         idx = self.parseindex()
         with self.assertRaises(error.WdirUnsupported):
-            list(AncestorsIterator(idx, [node.wdirrev], -1, False))
+            list(AncestorsIterator(idx, [wdirrev], -1, False))
 
     def testheadrevs(self):
         idx = self.parseindex()
diff --git a/tests/test-revlog-raw.py b/tests/test-revlog-raw.py
--- a/tests/test-revlog-raw.py
+++ b/tests/test-revlog-raw.py
@@ -6,9 +6,9 @@ 
 import hashlib
 import sys
 
+from mercurial.node import nullid
 from mercurial import (
     encoding,
-    node,
     revlog,
     transaction,
     vfs,
@@ -93,7 +93,7 @@ 
     """
     nextrev = len(rlog)
     p1 = rlog.node(nextrev - 1)
-    p2 = node.nullid
+    p2 = nullid
     if isext:
         flags = revlog.REVIDX_EXTSTORED
     else:
@@ -127,7 +127,7 @@ 
     class dummychangegroup(object):
         @staticmethod
         def deltachunk(pnode):
-            pnode = pnode or node.nullid
+            pnode = pnode or nullid
             parentrev = rlog.rev(pnode)
             r = parentrev + 1
             if r >= len(rlog):
@@ -142,7 +142,7 @@ 
             return {
                 b'node': rlog.node(r),
                 b'p1': pnode,
-                b'p2': node.nullid,
+                b'p2': nullid,
                 b'cs': rlog.node(rlog.linkrev(r)),
                 b'flags': rlog.flags(r),
                 b'deltabase': rlog.node(deltaparent),
@@ -181,7 +181,7 @@ 
     dlog = newrevlog(destname, recreate=True)
     for r in rlog:
         p1 = rlog.node(r - 1)
-        p2 = node.nullid
+        p2 = nullid
         if r == 0 or (rlog.flags(r) & revlog.REVIDX_EXTSTORED):
             text = rlog.rawdata(r)
             cachedelta = None
diff --git a/tests/test-parseindex2.py b/tests/test-parseindex2.py
--- a/tests/test-parseindex2.py
+++ b/tests/test-parseindex2.py
@@ -12,11 +12,12 @@ 
 import unittest
 
 from mercurial.node import (
+    bin,
+    hex,
     nullid,
     nullrev,
 )
 from mercurial import (
-    node as nodemod,
     policy,
     pycompat,
 )
@@ -232,7 +233,7 @@ 
                 self.assertEqual(
                     ix[r[7]],
                     i,
-                    'Reverse lookup inconsistent for %r' % nodemod.hex(r[7]),
+                    'Reverse lookup inconsistent for %r' % hex(r[7]),
                 )
             except TypeError:
                 # pure version doesn't support this
@@ -255,7 +256,7 @@ 
             if rev == nullrev:
                 return b'\xff\xff\xff\xff'
             else:
-                return nodemod.bin('%08x' % rev)
+                return bin('%08x' % rev)
 
         def appendrev(p1, p2=nullrev):
             # node won't matter for this test, let's just make sure
diff --git a/tests/drawdag.py b/tests/drawdag.py
--- a/tests/drawdag.py
+++ b/tests/drawdag.py
@@ -86,11 +86,11 @@ 
 import itertools
 import re
 
+from mercurial.node import nullid
 from mercurial.i18n import _
 from mercurial import (
     context,
     error,
-    node,
     obsolete,
     pycompat,
     registrar,
@@ -299,7 +299,7 @@ 
         self._added = added
         self._parents = parentctxs
         while len(self._parents) < 2:
-            self._parents.append(repo[node.nullid])
+            self._parents.append(repo[nullid])
 
     def filectx(self, key):
         return simplefilectx(key, self._added[key])
@@ -388,7 +388,7 @@ 
         content = content.replace(br'\n', b'\n').replace(br'\1', b'\1')
         files[name][path] = content
 
-    committed = {None: node.nullid}  # {name: node}
+    committed = {None: nullid}  # {name: node}
 
     # for leaf nodes, try to find existing nodes in repo
     for name, parents in edges.items():
diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -42,12 +42,12 @@ 
     open,
     setattr,
 )
+from .node import hex
 from hgdemandimport import tracing
 from . import (
     encoding,
     error,
     i18n,
-    node as nodemod,
     policy,
     pycompat,
     urllibcompat,
@@ -254,7 +254,7 @@ 
     def __getitem__(self, key):
         if key not in DIGESTS:
             raise error.Abort(_(b'unknown digest type: %s') % k)
-        return nodemod.hex(self._hashes[key].digest())
+        return hex(self._hashes[key].digest())
 
     def __iter__(self):
         return iter(self._hashes)
diff --git a/mercurial/templatefilters.py b/mercurial/templatefilters.py
--- a/mercurial/templatefilters.py
+++ b/mercurial/templatefilters.py
@@ -12,10 +12,10 @@ 
 import time
 
 from .i18n import _
+from .node import hex
 from . import (
     encoding,
     error,
-    node,
     pycompat,
     registrar,
     smartset,
@@ -280,7 +280,7 @@ 
     """Any text. Convert a binary Mercurial node identifier into
     its long hexadecimal representation.
     """
-    return node.hex(text)
+    return hex(text)
 
 
 @templatefilter(b'hgdate', intype=templateutil.date)
diff --git a/mercurial/tagmerge.py b/mercurial/tagmerge.py
--- a/mercurial/tagmerge.py
+++ b/mercurial/tagmerge.py
@@ -76,6 +76,7 @@ 
 from .i18n import _
 from .node import (
     hex,
+    nullhex,
     nullid,
 )
 from . import (
@@ -83,8 +84,6 @@ 
     util,
 )
 
-hexnullid = hex(nullid)
-
 
 def readtagsformerge(ui, repo, lines, fn=b'', keeplinenums=False):
     """read the .hgtags file into a structure that is suitable for merging
@@ -246,8 +245,8 @@ 
         pnlosttagset = basetagset - pntagset
         for t in pnlosttagset:
             pntags[t] = basetags[t]
-            if pntags[t][-1][0] != hexnullid:
-                pntags[t].append([hexnullid, None])
+            if pntags[t][-1][0] != nullhex:
+                pntags[t].append([nullhex, None])
 
     conflictedtags = []  # for reporting purposes
     mergedtags = util.sortdict(p1tags)
diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py
--- a/mercurial/subrepo.py
+++ b/mercurial/subrepo.py
@@ -18,6 +18,12 @@ 
 import xml.dom.minidom
 
 from .i18n import _
+from .node import (
+    bin,
+    hex,
+    nullid,
+    short,
+)
 from . import (
     cmdutil,
     encoding,
@@ -26,7 +32,6 @@ 
     logcmdutil,
     match as matchmod,
     merge as merge,
-    node,
     pathutil,
     phases,
     pycompat,
@@ -61,7 +66,7 @@ 
 
 def _getstorehashcachename(remotepath):
     '''get a unique filename for the store hash cache of a remote repository'''
-    return node.hex(hashutil.sha1(_expandedabspath(remotepath)).digest())[0:12]
+    return hex(hashutil.sha1(_expandedabspath(remotepath)).digest())[0:12]
 
 
 class SubrepoAbort(error.Abort):
@@ -508,7 +513,7 @@ 
         yield b'# %s\n' % _expandedabspath(remotepath)
         vfs = self._repo.vfs
         for relname in filelist:
-            filehash = node.hex(hashutil.sha1(vfs.tryread(relname)).digest())
+            filehash = hex(hashutil.sha1(vfs.tryread(relname)).digest())
             yield b'%s = %s\n' % (relname, filehash)
 
     @propertycache
@@ -601,11 +606,11 @@ 
     @annotatesubrepoerror
     def diff(self, ui, diffopts, node2, match, prefix, **opts):
         try:
-            node1 = node.bin(self._state[1])
+            node1 = bin(self._state[1])
             # We currently expect node2 to come from substate and be
             # in hex format
             if node2 is not None:
-                node2 = node.bin(node2)
+                node2 = bin(node2)
             logcmdutil.diffordiffstat(
                 ui,
                 self._repo,
@@ -669,7 +674,7 @@ 
         n = self._repo.commit(text, user, date)
         if not n:
             return self._repo[b'.'].hex()  # different version checked out
-        return node.hex(n)
+        return hex(n)
 
     @annotatesubrepoerror
     def phase(self, state):
@@ -680,7 +685,7 @@ 
         # we can't fully delete the repository as it may contain
         # local-only history
         self.ui.note(_(b'removing subrepo %s\n') % subrelpath(self))
-        hg.clean(self._repo, node.nullid, False)
+        hg.clean(self._repo, nullid, False)
 
     def _get(self, state):
         source, revision, kind = state
@@ -1019,7 +1024,7 @@ 
                 # explicit warning.
                 msg = _(b"subrepo '%s' is hidden in revision %s") % (
                     self._relpath,
-                    node.short(self._ctx.node()),
+                    short(self._ctx.node()),
                 )
 
                 if onpush:
@@ -1032,7 +1037,7 @@ 
             # don't treat this as an error for `hg verify`.
             msg = _(b"subrepo '%s' not found in revision %s") % (
                 self._relpath,
-                node.short(self._ctx.node()),
+                short(self._ctx.node()),
             )
 
             if onpush:
diff --git a/mercurial/strip.py b/mercurial/strip.py
--- a/mercurial/strip.py
+++ b/mercurial/strip.py
@@ -2,6 +2,7 @@ 
 
 from .i18n import _
 from .pycompat import getattr
+from .node import nullid
 from . import (
     bookmarks as bookmarksmod,
     cmdutil,
@@ -9,7 +10,6 @@ 
     hg,
     lock as lockmod,
     mergestate as mergestatemod,
-    node as nodemod,
     pycompat,
     registrar,
     repair,
@@ -17,7 +17,6 @@ 
     util,
 )
 
-nullid = nodemod.nullid
 release = lockmod.release
 
 cmdtable = {}
diff --git a/mercurial/store.py b/mercurial/store.py
--- a/mercurial/store.py
+++ b/mercurial/store.py
@@ -14,11 +14,11 @@ 
 
 from .i18n import _
 from .pycompat import getattr
+from .node import hex
 from . import (
     changelog,
     error,
     manifest,
-    node,
     policy,
     pycompat,
     util,
@@ -273,7 +273,7 @@ 
 
 
 def _hashencode(path, dotencode):
-    digest = node.hex(hashutil.sha1(path).digest())
+    digest = hex(hashutil.sha1(path).digest())
     le = lowerencode(path[5:]).split(b'/')  # skips prefix 'data/' or 'meta/'
     parts = _auxencode(le, dotencode)
     basename = parts[-1]
diff --git a/mercurial/sslutil.py b/mercurial/sslutil.py
--- a/mercurial/sslutil.py
+++ b/mercurial/sslutil.py
@@ -16,10 +16,10 @@ 
 
 from .i18n import _
 from .pycompat import getattr
+from .node import hex
 from . import (
     encoding,
     error,
-    node,
     pycompat,
     util,
 )
@@ -762,9 +762,9 @@ 
     # If a certificate fingerprint is pinned, use it and only it to
     # validate the remote cert.
     peerfingerprints = {
-        b'sha1': node.hex(hashutil.sha1(peercert).digest()),
-        b'sha256': node.hex(hashlib.sha256(peercert).digest()),
-        b'sha512': node.hex(hashlib.sha512(peercert).digest()),
+        b'sha1': hex(hashutil.sha1(peercert).digest()),
+        b'sha256': hex(hashlib.sha256(peercert).digest()),
+        b'sha512': hex(hashlib.sha512(peercert).digest()),
     }
 
     def fmtfingerprint(s):
diff --git a/mercurial/simplemerge.py b/mercurial/simplemerge.py
--- a/mercurial/simplemerge.py
+++ b/mercurial/simplemerge.py
@@ -19,10 +19,10 @@ 
 from __future__ import absolute_import
 
 from .i18n import _
+from .node import nullid
 from . import (
     error,
     mdiff,
-    node as nodemod,
     pycompat,
     util,
 )
@@ -452,7 +452,7 @@ 
 def is_not_null(ctx):
     if not util.safehasattr(ctx, "node"):
         return False
-    return ctx.node() != nodemod.nullid
+    return ctx.node() != nullid
 
 
 def simplemerge(ui, localctx, basectx, otherctx, **opts):
diff --git a/mercurial/shelve.py b/mercurial/shelve.py
--- a/mercurial/shelve.py
+++ b/mercurial/shelve.py
@@ -29,6 +29,12 @@ 
 
 from .i18n import _
 from .pycompat import open
+from .node import (
+    bin,
+    hex,
+    nullid,
+    nullrev,
+)
 from . import (
     bookmarks,
     bundle2,
@@ -43,7 +49,6 @@ 
     mdiff,
     merge,
     mergestate as mergestatemod,
-    node as nodemod,
     patch,
     phases,
     pycompat,
@@ -196,11 +201,11 @@ 
     def _verifyandtransform(cls, d):
         """Some basic shelvestate syntactic verification and transformation"""
         try:
-            d[b'originalwctx'] = nodemod.bin(d[b'originalwctx'])
-            d[b'pendingctx'] = nodemod.bin(d[b'pendingctx'])
-            d[b'parents'] = [nodemod.bin(h) for h in d[b'parents'].split(b' ')]
+            d[b'originalwctx'] = bin(d[b'originalwctx'])
+            d[b'pendingctx'] = bin(d[b'pendingctx'])
+            d[b'parents'] = [bin(h) for h in d[b'parents'].split(b' ')]
             d[b'nodestoremove'] = [
-                nodemod.bin(h) for h in d[b'nodestoremove'].split(b' ')
+                bin(h) for h in d[b'nodestoremove'].split(b' ')
             ]
         except (ValueError, TypeError, KeyError) as err:
             raise error.CorruptedState(pycompat.bytestr(err))
@@ -296,13 +301,13 @@ 
     ):
         info = {
             b"name": name,
-            b"originalwctx": nodemod.hex(originalwctx.node()),
-            b"pendingctx": nodemod.hex(pendingctx.node()),
+            b"originalwctx": hex(originalwctx.node()),
+            b"pendingctx": hex(pendingctx.node()),
             b"parents": b' '.join(
-                [nodemod.hex(p) for p in repo.dirstate.parents()]
+                [hex(p) for p in repo.dirstate.parents()]
             ),
             b"nodestoremove": b' '.join(
-                [nodemod.hex(n) for n in nodestoremove]
+                [hex(n) for n in nodestoremove]
             ),
             b"branchtorestore": branchtorestore,
             b"keep": cls._keep if keep else cls._nokeep,
@@ -400,7 +405,7 @@ 
     """return all mutable ancestors for ctx (included)
 
     Much faster than the revset ancestors(ctx) & draft()"""
-    seen = {nodemod.nullrev}
+    seen = {nullrev}
     visit = collections.deque()
     visit.append(ctx)
     while visit:
@@ -464,7 +469,7 @@ 
 
 
 def _shelvecreatedcommit(repo, node, name, match):
-    info = {b'node': nodemod.hex(node)}
+    info = {b'node': hex(node)}
     shelvedfile(repo, name, b'shelve').writeinfo(info)
     bases = list(mutableancestors(repo[node]))
     shelvedfile(repo, name, b'hg').writebundle(bases, node)
@@ -501,7 +506,7 @@ 
     parent = parents[0]
     origbranch = wctx.branch()
 
-    if parent.node() != nodemod.nullid:
+    if parent.node() != nullid:
         desc = b"changes to: %s" % parent.description().split(b'\n', 1)[0]
     else:
         desc = b'(changes in empty repository)'
@@ -816,7 +821,7 @@ 
         pendingctx = state.pendingctx
 
         with repo.dirstate.parentchange():
-            repo.setparents(state.pendingctx.node(), nodemod.nullid)
+            repo.setparents(state.pendingctx.node(), nullid)
             repo.dirstate.write(repo.currenttransaction())
 
         targetphase = phases.internal
@@ -825,7 +830,7 @@ 
         overrides = {(b'phases', b'new-commit'): targetphase}
         with repo.ui.configoverride(overrides, b'unshelve'):
             with repo.dirstate.parentchange():
-                repo.setparents(state.parents[0], nodemod.nullid)
+                repo.setparents(state.parents[0], nullid)
                 newnode, ispartialunshelve = _createunshelvectx(
                     ui, repo, shelvectx, basename, interactive, opts
                 )
@@ -901,7 +906,7 @@ 
         # We might not strip the unbundled changeset, so we should keep track of
         # the unshelve node in case we need to reuse it (eg: unshelve --keep)
         if node is None:
-            info = {b'node': nodemod.hex(shelvectx.node())}
+            info = {b'node': hex(shelvectx.node())}
             shelvedfile(repo, basename, b'shelve').writeinfo(info)
     else:
         shelvectx = repo[node]
@@ -1020,7 +1025,7 @@ 
             raise error.ConflictResolutionRequired(b'unshelve')
 
         with repo.dirstate.parentchange():
-            repo.setparents(tmpwctx.node(), nodemod.nullid)
+            repo.setparents(tmpwctx.node(), nullid)
             newnode, ispartialunshelve = _createunshelvectx(
                 ui, repo, shelvectx, basename, interactive, opts
             )
diff --git a/mercurial/rewriteutil.py b/mercurial/rewriteutil.py
--- a/mercurial/rewriteutil.py
+++ b/mercurial/rewriteutil.py
@@ -10,10 +10,13 @@ 
 import re
 
 from .i18n import _
+from .node import (
+    hex,
+    nullrev,
+)
 
 from . import (
     error,
-    node,
     obsolete,
     obsutil,
     revset,
@@ -30,7 +33,7 @@ 
 
     Make sure this function is called after taking the lock.
     """
-    if node.nullrev in revs:
+    if nullrev in revs:
         msg = _(b"cannot %s null changeset") % action
         hint = _(b"no changeset checked out")
         raise error.InputError(msg, hint=hint)
@@ -113,7 +116,7 @@ 
         if len(successors) == 1 and len(successors[0]) == 1:
             successor = successors[0][0]
             if successor is not None:
-                newhash = node.hex(successor)
+                newhash = hex(successor)
                 commitmsg = commitmsg.replace(h, newhash[: len(h)])
             else:
                 repo.ui.note(
diff --git a/mercurial/revsetlang.py b/mercurial/revsetlang.py
--- a/mercurial/revsetlang.py
+++ b/mercurial/revsetlang.py
@@ -11,9 +11,9 @@ 
 
 from .i18n import _
 from .pycompat import getattr
+from .node import hex
 from . import (
     error,
-    node,
     parser,
     pycompat,
     smartset,
@@ -687,7 +687,7 @@ 
         parse(arg)  # make sure syntax errors are confined
         return b'(%s)' % arg
     elif c == b'n':
-        return _quote(node.hex(arg))
+        return _quote(hex(arg))
     elif c == b'b':
         try:
             return _quote(arg.branch())
@@ -707,7 +707,7 @@ 
     elif t == b's':
         return b"_list(%s)" % _quote(b"\0".join(s))
     elif t == b'n':
-        return b"_hexlist('%s')" % b"\0".join(node.hex(a) for a in s)
+        return b"_hexlist('%s')" % b"\0".join(hex(a) for a in s)
     elif t == b'b':
         try:
             return b"_list('%s')" % b"\0".join(a.branch() for a in s)
diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -11,6 +11,11 @@ 
 
 from .i18n import _
 from .pycompat import getattr
+from .node import (
+    bin,
+    nullrev,
+    wdirrev,
+)
 from . import (
     dagop,
     destutil,
@@ -20,7 +25,6 @@ 
     grep as grepmod,
     hbisect,
     match as matchmod,
-    node,
     obsolete as obsmod,
     obsutil,
     pathutil,
@@ -55,7 +59,7 @@ 
 fullreposet = smartset.fullreposet
 
 # revisions not included in all(), but populated if specified
-_virtualrevs = (node.nullrev, node.wdirrev)
+_virtualrevs = (nullrev, wdirrev)
 
 # Constants for ordering requirement, used in getset():
 #
@@ -177,9 +181,9 @@ 
 def _makerangeset(repo, subset, m, n, order):
     if m == n:
         r = baseset([m])
-    elif n == node.wdirrev:
+    elif n == wdirrev:
         r = spanset(repo, m, len(repo)) + baseset([n])
-    elif m == node.wdirrev:
+    elif m == wdirrev:
         r = baseset([m]) + spanset(repo, repo.changelog.tiprev(), n - 1)
     elif m < n:
         r = spanset(repo, m, n + 1)
@@ -597,7 +601,7 @@ 
                 bms.add(repo[bmrev].rev())
     else:
         bms = {repo[r].rev() for r in repo._bookmarks.values()}
-    bms -= {node.nullrev}
+    bms -= {nullrev}
     return subset & bms
 
 
@@ -722,7 +726,6 @@ 
     cs = set()
     pr = repo.changelog.parentrevs
     minrev = parentset.min()
-    nullrev = node.nullrev
     for r in subset:
         if r <= minrev:
             continue
@@ -1396,7 +1399,7 @@ 
                     b'_matchfiles expected at most one revision'
                 )
             if value == b'':  # empty means working directory
-                rev = node.wdirrev
+                rev = wdirrev
             else:
                 rev = value
         elif prefix == b'd:':
@@ -1416,7 +1419,6 @@ 
     # This directly read the changelog data as creating changectx for all
     # revisions is quite expensive.
     getfiles = repo.changelog.readfiles
-    wdirrev = node.wdirrev
 
     def matches(x):
         if x == wdirrev:
@@ -1490,15 +1492,15 @@ 
         order = followorder
     inputset = getset(repo, fullreposet(repo), x, order=order)
     wdirparents = None
-    if node.wdirrev in inputset:
+    if wdirrev in inputset:
         # a bit slower, but not common so good enough for now
         wdirparents = [p.rev() for p in repo[None].parents()]
         inputset = set(inputset)
-        inputset.discard(node.wdirrev)
+        inputset.discard(wdirrev)
     heads = repo.changelog.headrevs(inputset)
     if wdirparents is not None:
         heads.difference_update(wdirparents)
-        heads.add(node.wdirrev)
+        heads.add(wdirrev)
     heads = baseset(heads)
     return subset & heads
 
@@ -1598,7 +1600,6 @@ 
     # i18n: "merge" is a keyword
     getargs(x, 0, 0, _(b"merge takes no arguments"))
     cl = repo.changelog
-    nullrev = node.nullrev
 
     def ismerge(r):
         try:
@@ -1692,7 +1693,7 @@ 
             if name not in ns.deprecated:
                 names.update(repo[n].rev() for n in ns.nodes(repo, name))
 
-    names -= {node.nullrev}
+    names -= {nullrev}
     return subset & names
 
 
@@ -1705,9 +1706,9 @@ 
     n = getstring(l[0], _(b"id requires a string"))
     if len(n) == 40:
         try:
-            rn = repo.changelog.rev(node.bin(n))
+            rn = repo.changelog.rev(bin(n))
         except error.WdirUnsupported:
-            rn = node.wdirrev
+            rn = wdirrev
         except (LookupError, TypeError):
             rn = None
     else:
@@ -1719,7 +1720,7 @@ 
         except LookupError:
             pass
         except error.WdirUnsupported:
-            rn = node.wdirrev
+            rn = wdirrev
 
     if rn is None:
         return baseset()
@@ -1864,7 +1865,7 @@ 
             ps.add(cl.parentrevs(r)[0])
         except error.WdirUnsupported:
             ps.add(repo[r].p1().rev())
-    ps -= {node.nullrev}
+    ps -= {nullrev}
     # XXX we should turn this into a baseset instead of a set, smartset may do
     # some optimizations from the fact this is a baseset.
     return subset & ps
@@ -1892,7 +1893,7 @@ 
             parents = repo[r].parents()
             if len(parents) == 2:
                 ps.add(parents[1])
-    ps -= {node.nullrev}
+    ps -= {nullrev}
     # XXX we should turn this into a baseset instead of a set, smartset may do
     # some optimizations from the fact this is a baseset.
     return subset & ps
@@ -1919,7 +1920,7 @@ 
                 up(parentrevs(r))
             except error.WdirUnsupported:
                 up(p.rev() for p in repo[r].parents())
-    ps -= {node.nullrev}
+    ps -= {nullrev}
     return subset & ps
 
 
@@ -1994,7 +1995,7 @@ 
         else:
             try:
                 parents = cl.parentrevs(r)
-                if parents[1] != node.nullrev:
+                if parents[1] != nullrev:
                     ps.add(parents[1])
             except error.WdirUnsupported:
                 parents = repo[r].parents()
@@ -2567,8 +2568,8 @@ 
     """Working directory. (EXPERIMENTAL)"""
     # i18n: "wdir" is a keyword
     getargs(x, 0, 0, _(b"wdir takes no arguments"))
-    if node.wdirrev in subset or isinstance(subset, fullreposet):
-        return baseset([node.wdirrev])
+    if wdirrev in subset or isinstance(subset, fullreposet):
+        return baseset([wdirrev])
     return baseset()
 
 
@@ -2638,7 +2639,7 @@ 
     if not s:
         return baseset()
     cl = repo.changelog
-    ls = [cl.rev(node.bin(r)) for r in s.split(b'\0')]
+    ls = [cl.rev(bin(r)) for r in s.split(b'\0')]
     s = subset
     return baseset([r for r in ls if r in s])
 
diff --git a/mercurial/revlogutils/nodemap.py b/mercurial/revlogutils/nodemap.py
--- a/mercurial/revlogutils/nodemap.py
+++ b/mercurial/revlogutils/nodemap.py
@@ -14,10 +14,10 @@ 
 import struct
 
 from ..i18n import _
+from ..node import hex
 
 from .. import (
     error,
-    node as nodemod,
     util,
 )
 
@@ -276,7 +276,7 @@ 
     """return a new unique identifier.
 
     The identifier is random and composed of ascii characters."""
-    return nodemod.hex(os.urandom(ID_SIZE))
+    return hex(os.urandom(ID_SIZE))
 
 
 class NodeMapDocket(object):
@@ -459,8 +459,8 @@ 
     """
     root = Block()
     for rev in range(len(index)):
-        hex = nodemod.hex(index[rev][7])
-        _insert_into_block(index, 0, root, rev, hex)
+        current_hex = hex(index[rev][7])
+        _insert_into_block(index, 0, root, rev, current_hex)
     return root
 
 
@@ -468,8 +468,8 @@ 
     """consume"""
     changed = 0
     for rev in range(last_rev + 1, len(index)):
-        hex = nodemod.hex(index[rev][7])
-        changed += _insert_into_block(index, 0, root, rev, hex)
+        current_hex = hex(index[rev][7])
+        changed += _insert_into_block(index, 0, root, rev, current_hex)
     return changed, root
 
 
@@ -498,7 +498,7 @@ 
     else:
         # collision with a previously unique prefix, inserting new
         # vertices to fit both entry.
-        other_hex = nodemod.hex(index[entry][7])
+        other_hex = hex(index[entry][7])
         other_rev = entry
         new = Block()
         block[hex_digit] = new
@@ -602,7 +602,7 @@ 
             ret = 1
         else:
             all_revs.remove(r)
-        nm_rev = _find_node(root, nodemod.hex(index[r][7]))
+        nm_rev = _find_node(root, hex(index[r][7]))
         if nm_rev is None:
             msg = b"  revision node does not match any entries: %d\n" % r
             ui.write_err(msg)
diff --git a/mercurial/obsutil.py b/mercurial/obsutil.py
--- a/mercurial/obsutil.py
+++ b/mercurial/obsutil.py
@@ -10,11 +10,14 @@ 
 import re
 
 from .i18n import _
+from .node import (
+    hex,
+    short,
+)
 from . import (
     diffutil,
     encoding,
     error,
-    node as nodemod,
     phases,
     pycompat,
     util,
@@ -957,13 +960,13 @@ 
     elif fate == b'diverged':
         return filteredmsgtable[b'diverged'] % changeid
     elif fate == b'superseded':
-        single_successor = nodemod.short(successors[0][0])
+        single_successor = short(successors[0][0])
         return filteredmsgtable[b'superseded'] % (changeid, single_successor)
     elif fate == b'superseded_split':
 
         succs = []
         for node_id in successors[0]:
-            succs.append(nodemod.short(node_id))
+            succs.append(short(node_id))
 
         if len(succs) <= 2:
             fmtsuccs = b', '.join(succs)
@@ -1040,7 +1043,7 @@ 
                     b'instability': b'content-divergent',
                     b'divergentnodes': divnodes,
                     b'reason': b'predecessor',
-                    b'node': nodemod.hex(dset[b'commonpredecessor']),
+                    b'node': hex(dset[b'commonpredecessor']),
                 }
             )
     return result
diff --git a/mercurial/obsolete.py b/mercurial/obsolete.py
--- a/mercurial/obsolete.py
+++ b/mercurial/obsolete.py
@@ -74,10 +74,14 @@ 
 
 from .i18n import _
 from .pycompat import getattr
+from .node import (
+    bin,
+    hex,
+    nullid,
+)
 from . import (
     encoding,
     error,
-    node,
     obsutil,
     phases,
     policy,
@@ -235,7 +239,7 @@ 
             parents = ()
         if parents is not None:
             try:
-                parents = tuple(node.bin(p) for p in parents)
+                parents = tuple(bin(p) for p in parents)
                 # if parent content is not a nodeid, drop the data
                 for p in parents:
                     if len(p) != 20:
@@ -262,7 +266,7 @@ 
             # mark that we explicitly recorded no parents
             metadata[b'p0'] = b''
         for i, p in enumerate(parents, 1):
-            metadata[b'p%i' % i] = node.hex(p)
+            metadata[b'p%i' % i] = hex(p)
     metadata = _fm0encodemeta(metadata)
     numsuc = len(sucs)
     format = _fm0fixed + (_fm0node * numsuc)
@@ -529,7 +533,7 @@ 
     subtle handling.
     """
     for mark in markers:
-        if node.nullid in mark[1]:
+        if nullid in mark[1]:
             raise error.Abort(
                 _(
                     b'bad obsolescence marker detected: '
@@ -639,7 +643,7 @@ 
                     raise ValueError(succ)
         if prec in succs:
             raise ValueError(
-                'in-marker cycle with %s' % pycompat.sysstr(node.hex(prec))
+                'in-marker cycle with %s' % pycompat.sysstr(hex(prec))
             )
 
         metadata = tuple(sorted(pycompat.iteritems(metadata)))
@@ -1031,7 +1035,7 @@ 
         folddigest.update(p.node())
     # Since fold only has to compete against fold for the same successors, it
     # seems fine to use a small ID. Smaller ID save space.
-    return node.hex(folddigest.digest())[:8]
+    return hex(folddigest.digest())[:8]
 
 
 def createmarkers(
diff --git a/mercurial/metadata.py b/mercurial/metadata.py
--- a/mercurial/metadata.py
+++ b/mercurial/metadata.py
@@ -11,9 +11,12 @@ 
 import multiprocessing
 import struct
 
+from .node import (
+    nullid,
+    nullrev,
+)
 from . import (
     error,
-    node,
     pycompat,
     util,
 )
@@ -239,11 +242,11 @@ 
     """compute the files changed by a revision"""
     p1 = ctx.p1()
     p2 = ctx.p2()
-    if p1.rev() == node.nullrev and p2.rev() == node.nullrev:
+    if p1.rev() == nullrev and p2.rev() == nullrev:
         return _process_root(ctx)
-    elif p1.rev() != node.nullrev and p2.rev() == node.nullrev:
+    elif p1.rev() != nullrev and p2.rev() == nullrev:
         return _process_linear(p1, ctx)
-    elif p1.rev() == node.nullrev and p2.rev() != node.nullrev:
+    elif p1.rev() == nullrev and p2.rev() != nullrev:
         # In the wild, one can encounter changeset where p1 is null but p2 is not
         return _process_linear(p1, ctx, parent=2)
     elif p1.rev() == p2.rev():
@@ -423,7 +426,7 @@ 
         p1_ctx.node(), p2_ctx.node()
     )
     if not cahs:
-        cahs = [node.nullrev]
+        cahs = [nullrev]
     mas = [ctx.repo()[r].manifest() for r in cahs]
 
     copy_candidates = []
@@ -560,7 +563,7 @@ 
         p2n = p2.node()
         cahs = ctx.repo().changelog.commonancestorsheads(p1n, p2n)
         if not cahs:
-            cahs = [node.nullrev]
+            cahs = [nullrev]
         return [ctx.repo()[r].manifest() for r in cahs]
 
     def deletionfromparent(f):
@@ -597,7 +600,7 @@ 
         if f in ctx:
             fctx = ctx[f]
             parents = fctx._filelog.parents(fctx._filenode)
-            if parents[1] != node.nullid:
+            if parents[1] != nullid:
                 merged.append(f)
     return merged
 
diff --git a/mercurial/keepalive.py b/mercurial/keepalive.py
--- a/mercurial/keepalive.py
+++ b/mercurial/keepalive.py
@@ -93,8 +93,8 @@ 
 
 from .i18n import _
 from .pycompat import getattr
+from .node import hex
 from . import (
-    node,
     pycompat,
     urllibcompat,
     util,
@@ -723,7 +723,7 @@ 
     foo = fo.read()
     fo.close()
     m = md5(foo)
-    print(format % (b'normal urllib', node.hex(m.digest())))
+    print(format % (b'normal urllib', hex(m.digest())))
 
     # now install the keepalive handler and try again
     opener = urlreq.buildopener(HTTPHandler())
@@ -733,7 +733,7 @@ 
     foo = fo.read()
     fo.close()
     m = md5(foo)
-    print(format % (b'keepalive read', node.hex(m.digest())))
+    print(format % (b'keepalive read', hex(m.digest())))
 
     fo = urlreq.urlopen(url)
     foo = b''
@@ -745,7 +745,7 @@ 
             break
     fo.close()
     m = md5(foo)
-    print(format % (b'keepalive readline', node.hex(m.digest())))
+    print(format % (b'keepalive readline', hex(m.digest())))
 
 
 def comp(N, url):
diff --git a/mercurial/hg.py b/mercurial/hg.py
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -14,7 +14,12 @@ 
 import stat
 
 from .i18n import _
-from .node import nullid
+from .node import (
+    hex,
+    nullhex,
+    nullid,
+    short,
+)
 from .pycompat import getattr
 
 from . import (
@@ -35,7 +40,6 @@ 
     merge as mergemod,
     mergestate as mergestatemod,
     narrowspec,
-    node,
     phases,
     pycompat,
     requirements,
@@ -108,7 +112,7 @@ 
                 raise error.Abort(_(b"dirstate branch not accessible"))
             branch = lrepo.dirstate.branch()
         if branch in branchmap:
-            revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
+            revs.extend(hex(r) for r in reversed(branchmap[branch]))
             return True
         else:
             return False
@@ -762,8 +766,8 @@ 
                         },
                     ).result()
 
-                if rootnode != node.nullid:
-                    sharepath = os.path.join(sharepool, node.hex(rootnode))
+                if rootnode != nullid:
+                    sharepath = os.path.join(sharepool, hex(rootnode))
                 else:
                     ui.status(
                         _(
@@ -780,7 +784,7 @@ 
                 )
         elif sharenamemode == b'remote':
             sharepath = os.path.join(
-                sharepool, node.hex(hashutil.sha1(source).digest())
+                sharepool, hex(hashutil.sha1(source).digest())
             )
         else:
             raise error.Abort(
@@ -873,7 +877,7 @@ 
             # into it
             destpeer = peer(srcrepo, peeropts, dest)
             srcrepo.hook(
-                b'outgoing', source=b'clone', node=node.hex(node.nullid)
+                b'outgoing', source=b'clone', node=nullhex
             )
         else:
             try:
@@ -1379,7 +1383,7 @@ 
             except Exception:
                 repo.ui.warn(
                     _(b'.hgsubstate is corrupt in revision %s\n')
-                    % node.short(ctx.node())
+                    % short(ctx.node())
                 )
 
     return ret
diff --git a/mercurial/dagop.py b/mercurial/dagop.py
--- a/mercurial/dagop.py
+++ b/mercurial/dagop.py
@@ -11,10 +11,10 @@ 
 
 from .node import nullrev
 from .thirdparty import attr
+from .node import nullrev
 from . import (
     error,
     mdiff,
-    node,
     patch,
     pycompat,
     scmutil,
@@ -77,7 +77,7 @@ 
         pdepth = curdepth + 1
         if foundnew and pdepth < stopdepth:
             for prev in pfunc(currev):
-                if prev != node.nullrev:
+                if prev != nullrev:
                     heapq.heappush(pendingheap, (heapsign * prev, pdepth))
 
 
@@ -183,7 +183,6 @@ 
 
     cl = repo.changelog
     first = revs.min()
-    nullrev = node.nullrev
     if first == nullrev:
         # Are there nodes with a null first parent and a non-null
         # second one? Maybe. Do we care? Probably not.
@@ -206,7 +205,6 @@ 
 def _builddescendantsmap(repo, startrev, followfirst):
     """Build map of 'rev -> child revs', offset from startrev"""
     cl = repo.changelog
-    nullrev = node.nullrev
     descmap = [[] for _rev in pycompat.xrange(startrev, len(cl))]
     for currev in cl.revs(startrev + 1):
         p1rev, p2rev = cl.parentrevs(currev)
@@ -971,7 +969,7 @@ 
             if rev == currentrev:  # only display stuff in rev
                 gr[0].append(rev)
             gr[1].remove(rev)
-            parents = [p for p in parentsfunc(rev) if p > node.nullrev]
+            parents = [p for p in parentsfunc(rev) if p > nullrev]
             gr[1].update(parents)
             for p in parents:
                 if p not in pendingset:
@@ -1030,7 +1028,7 @@ 
     the input set.
     """
     headrevs = set(revs)
-    parents = {node.nullrev}
+    parents = {nullrev}
     up = parents.update
 
     for rev in revs:
@@ -1104,7 +1102,7 @@ 
             visit.append(-rev - 1)
 
             for prev in parentsfn(rev):
-                if prev == node.nullrev or prev not in revs or prev in finished:
+                if prev == nullrev or prev not in revs or prev in finished:
                     continue
 
                 visit.append(prev)
diff --git a/mercurial/copies.py b/mercurial/copies.py
--- a/mercurial/copies.py
+++ b/mercurial/copies.py
@@ -11,11 +11,13 @@ 
 import os
 
 from .i18n import _
-
+from .node import (
+    nullid,
+    nullrev,
+)
 
 from . import (
     match as matchmod,
-    node,
     pathutil,
     policy,
     pycompat,
@@ -144,7 +146,7 @@ 
     # optimization, since the ctx.files() for a merge commit is not correct for
     # this comparison.
     forwardmissingmatch = match
-    if b.p1() == a and b.p2().node() == node.nullid:
+    if b.p1() == a and b.p2().node() == nullid:
         filesmatcher = matchmod.exact(b.files())
         forwardmissingmatch = matchmod.intersectmatchers(match, filesmatcher)
     missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
@@ -225,7 +227,7 @@ 
         if flags(rev) & HASCOPIESINFO:
             changes = changelogrevision(rev).changes
         value = (p1, p2, changes)
-        if p1 != node.nullrev and p2 != node.nullrev:
+        if p1 != nullrev and p2 != nullrev:
             # XXX some case we over cache, IGNORE
             merge_caches[rev] = value
         return value
@@ -252,7 +254,7 @@ 
 
 
 def _changesetforwardcopies(a, b, match):
-    if a.rev() in (node.nullrev, b.rev()):
+    if a.rev() in (nullrev, b.rev()):
         return {}
 
     repo = a.repo().unfiltered()
@@ -265,7 +267,7 @@ 
     roots = set()
     for r in missingrevs:
         for p in cl.parentrevs(r):
-            if p == node.nullrev:
+            if p == nullrev:
                 continue
             if p not in children:
                 children[p] = [r]
@@ -464,7 +466,7 @@ 
             parents = fctx._filelog.parents(fctx._filenode)
             nb_parents = 0
             for n in parents:
-                if n != node.nullid:
+                if n != nullid:
                     nb_parents += 1
             return nb_parents >= 2
 
@@ -638,7 +640,7 @@ 
         if debug:
             repo.ui.debug(b'debug.copies: search mode: combined\n')
         base = None
-        if a.rev() != node.nullrev:
+        if a.rev() != nullrev:
             base = x
         copies = _chain(
             _backwardrenames(x, a, match=match),
diff --git a/mercurial/chgserver.py b/mercurial/chgserver.py
--- a/mercurial/chgserver.py
+++ b/mercurial/chgserver.py
@@ -54,13 +54,13 @@ 
     getattr,
     setattr,
 )
+from .node import hex
 
 from . import (
     commandserver,
     encoding,
     error,
     extensions,
-    node,
     pycompat,
     util,
 )
@@ -74,7 +74,7 @@ 
 
 def _hashlist(items):
     """return sha1 hexdigest for a list"""
-    return node.hex(hashutil.sha1(stringutil.pprint(items)).digest())
+    return hex(hashutil.sha1(stringutil.pprint(items)).digest())
 
 
 # sensitive config sections affecting confighash
diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py
--- a/mercurial/bundlerepo.py
+++ b/mercurial/bundlerepo.py
@@ -17,7 +17,11 @@ 
 import shutil
 
 from .i18n import _
-from .node import nullid, nullrev
+from .node import (
+    hex,
+    nullid,
+    nullrev,
+)
 
 from . import (
     bundle2,
@@ -32,7 +36,6 @@ 
     localrepo,
     manifest,
     mdiff,
-    node as nodemod,
     pathutil,
     phases,
     pycompat,
@@ -437,9 +440,9 @@ 
         p2rev = self.changelog.rev(p2)
         msg = _(b"setting parent to node %s that only exists in the bundle\n")
         if self.changelog.repotiprev < p1rev:
-            self.ui.warn(msg % nodemod.hex(p1))
+            self.ui.warn(msg % hex(p1))
         if self.changelog.repotiprev < p2rev:
-            self.ui.warn(msg % nodemod.hex(p2))
+            self.ui.warn(msg % hex(p2))
         return super(bundlerepository, self).setparents(p1, p2)
 
 
diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
--- a/mercurial/bundle2.py
+++ b/mercurial/bundle2.py
@@ -156,12 +156,16 @@ 
 import sys
 
 from .i18n import _
+from .node import (
+    hex,
+    nullid,
+    short,
+)
 from . import (
     bookmarks,
     changegroup,
     encoding,
     error,
-    node as nodemod,
     obsolete,
     phases,
     pushkey,
@@ -2131,14 +2135,14 @@ 
         currentnode = op.repo._bookmarks.get(book)
         if currentnode != node:
             if node is None:
-                finalmsg = msgexist % (book, nodemod.short(currentnode))
+                finalmsg = msgexist % (book, short(currentnode))
             elif currentnode is None:
-                finalmsg = msgmissing % (book, nodemod.short(node))
+                finalmsg = msgmissing % (book, short(node))
             else:
                 finalmsg = msgstandard % (
                     book,
-                    nodemod.short(node),
-                    nodemod.short(currentnode),
+                    short(node),
+                    short(currentnode),
                 )
             raise error.PushRaced(finalmsg)
 
@@ -2215,7 +2219,7 @@ 
             actualphase = phasecache.phase(unfi, cl.rev(n))
             if actualphase != expectedphase:
                 finalmsg = msg % (
-                    nodemod.short(n),
+                    short(n),
                     phases.phasenames[actualphase],
                     phases.phasenames[expectedphase],
                 )
@@ -2360,8 +2364,8 @@ 
                 hookargs[b'pushkeycompat'] = b'1'
                 hookargs[b'namespace'] = b'bookmarks'
                 hookargs[b'key'] = book
-                hookargs[b'old'] = nodemod.hex(bookstore.get(book, b''))
-                hookargs[b'new'] = nodemod.hex(
+                hookargs[b'old'] = hex(bookstore.get(book, b''))
+                hookargs[b'new'] = hex(
                     node if node is not None else b''
                 )
                 allhooks.append(hookargs)
@@ -2569,7 +2573,7 @@ 
             fullnodes=commonnodes,
         )
         cgdata = packer.generate(
-            {nodemod.nullid},
+            {nullid},
             list(commonnodes),
             False,
             b'narrow_widen',
diff --git a/hgext/uncommit.py b/hgext/uncommit.py
--- a/hgext/uncommit.py
+++ b/hgext/uncommit.py
@@ -20,6 +20,7 @@ 
 from __future__ import absolute_import
 
 from mercurial.i18n import _
+from mercurial.node import nullid
 
 from mercurial import (
     cmdutil,
@@ -27,7 +28,6 @@ 
     context,
     copies as copiesmod,
     error,
-    node,
     obsutil,
     pathutil,
     pycompat,
@@ -113,7 +113,7 @@ 
 
     new = context.memctx(
         repo,
-        parents=[base.node(), node.nullid],
+        parents=[base.node(), nullid],
         text=message,
         files=files,
         filectxfn=filectxfn,
diff --git a/hgext/transplant.py b/hgext/transplant.py
--- a/hgext/transplant.py
+++ b/hgext/transplant.py
@@ -19,6 +19,12 @@ 
 
 from mercurial.i18n import _
 from mercurial.pycompat import open
+from mercurial.node import (
+    bin,
+    hex,
+    nullid,
+    short,
+)
 from mercurial import (
     bundlerepo,
     cmdutil,
@@ -28,7 +34,6 @@ 
     logcmdutil,
     match,
     merge,
-    node as nodemod,
     patch,
     pycompat,
     registrar,
@@ -95,7 +100,7 @@ 
         abspath = os.path.join(self.path, self.transplantfile)
         if self.transplantfile and os.path.exists(abspath):
             for line in self.opener.read(self.transplantfile).splitlines():
-                lnode, rnode = map(revlog.bin, line.split(b':'))
+                lnode, rnode = map(bin, line.split(b':'))
                 list = self.transplants.setdefault(rnode, [])
                 list.append(transplantentry(lnode, rnode))
 
@@ -106,7 +111,7 @@ 
             fp = self.opener(self.transplantfile, b'w')
             for list in pycompat.itervalues(self.transplants):
                 for t in list:
-                    l, r = map(nodemod.hex, (t.lnode, t.rnode))
+                    l, r = map(hex, (t.lnode, t.rnode))
                     fp.write(l + b':' + r + b'\n')
             fp.close()
         self.dirty = False
@@ -183,7 +188,7 @@ 
             tr = repo.transaction(b'transplant')
             for rev in revs:
                 node = revmap[rev]
-                revstr = b'%d:%s' % (rev, nodemod.short(node))
+                revstr = b'%d:%s' % (rev, short(node))
 
                 if self.applied(repo, node, p1):
                     self.ui.warn(
@@ -216,11 +221,11 @@ 
                         exchange.pull(repo, source.peer(), heads=[node])
 
                 skipmerge = False
-                if parents[1] != revlog.nullid:
+                if parents[1] != nullid:
                     if not opts.get(b'parent'):
                         self.ui.note(
                             _(b'skipping merge changeset %d:%s\n')
-                            % (rev, nodemod.short(node))
+                            % (rev, short(node))
                         )
                         skipmerge = True
                     else:
@@ -228,7 +233,7 @@ 
                         if parent not in parents:
                             raise error.Abort(
                                 _(b'%s is not a parent of %s')
-                                % (nodemod.short(parent), nodemod.short(node))
+                                % (short(parent), short(node))
                             )
                 else:
                     parent = parents[0]
@@ -264,12 +269,12 @@ 
                         if n and domerge:
                             self.ui.status(
                                 _(b'%s merged at %s\n')
-                                % (revstr, nodemod.short(n))
+                                % (revstr, short(n))
                             )
                         elif n:
                             self.ui.status(
                                 _(b'%s transplanted to %s\n')
-                                % (nodemod.short(node), nodemod.short(n))
+                                % (short(node), short(n))
                             )
                     finally:
                         if patchfile:
@@ -309,7 +314,7 @@ 
                 ),
                 environ={
                     b'HGUSER': changelog[1],
-                    b'HGREVISION': nodemod.hex(node),
+                    b'HGREVISION': hex(node),
                 },
                 onerr=error.Abort,
                 errprefix=_(b'filter failed'),
@@ -333,9 +338,9 @@ 
 
         if log:
             # we don't translate messages inserted into commits
-            message += b'\n(transplanted from %s)' % nodemod.hex(node)
+            message += b'\n(transplanted from %s)' % hex(node)
 
-        self.ui.status(_(b'applying %s\n') % nodemod.short(node))
+        self.ui.status(_(b'applying %s\n') % short(node))
         self.ui.note(b'%s %s\n%s\n' % (user, date, message))
 
         if not patchfile and not merge:
@@ -378,7 +383,7 @@ 
         )
         if not n:
             self.ui.warn(
-                _(b'skipping emptied changeset %s\n') % nodemod.short(node)
+                _(b'skipping emptied changeset %s\n') % short(node)
             )
             return None
         if not merge:
@@ -396,12 +401,12 @@ 
             if n:
                 self.ui.status(
                     _(b'%s transplanted as %s\n')
-                    % (nodemod.short(node), nodemod.short(n))
+                    % (short(node), short(n))
                 )
             else:
                 self.ui.status(
                     _(b'%s skipped due to empty diff\n')
-                    % (nodemod.short(node),)
+                    % (short(node),)
                 )
         seriespath = os.path.join(self.path, b'series')
         if not os.path.exists(seriespath):
@@ -430,7 +435,7 @@ 
                 if parent not in parents:
                     raise error.Abort(
                         _(b'%s is not a parent of %s')
-                        % (nodemod.short(parent), nodemod.short(node))
+                        % (short(parent), short(node))
                     )
             else:
                 merge = True
@@ -441,7 +446,7 @@ 
             if p1 != parent:
                 raise error.Abort(
                     _(b'working directory not at transplant parent %s')
-                    % nodemod.hex(parent)
+                    % hex(parent)
                 )
             if merge:
                 repo.setparents(p1, parents[1])
@@ -494,7 +499,7 @@ 
             if line.startswith(b'# Merges'):
                 cur = merges
                 continue
-            cur.append(revlog.bin(line))
+            cur.append(bin(line))
 
         return (nodes, merges)
 
@@ -506,17 +511,17 @@ 
             os.mkdir(self.path)
         series = self.opener(b'series', b'w')
         for rev in sorted(revmap):
-            series.write(nodemod.hex(revmap[rev]) + b'\n')
+            series.write(hex(revmap[rev]) + b'\n')
         if merges:
             series.write(b'# Merges\n')
             for m in merges:
-                series.write(nodemod.hex(m) + b'\n')
+                series.write(hex(m) + b'\n')
         series.close()
 
     def parselog(self, fp):
         parents = []
         message = []
-        node = revlog.nullid
+        node = nullid
         inmsg = False
         user = None
         date = None
@@ -528,9 +533,9 @@ 
             elif line.startswith(b'# Date '):
                 date = line[7:]
             elif line.startswith(b'# Node ID '):
-                node = revlog.bin(line[10:])
+                node = bin(line[10:])
             elif line.startswith(b'# Parent '):
-                parents.append(revlog.bin(line[9:]))
+                parents.append(bin(line[9:]))
             elif not line.startswith(b'# '):
                 inmsg = True
                 message.append(line)
@@ -548,10 +553,10 @@ 
         fp = self.opener(b'journal', b'w')
         fp.write(b'# User %s\n' % user)
         fp.write(b'# Date %s\n' % date)
-        fp.write(b'# Node ID %s\n' % nodemod.hex(p2))
-        fp.write(b'# Parent ' + nodemod.hex(p1) + b'\n')
+        fp.write(b'# Node ID %s\n' % hex(p2))
+        fp.write(b'# Parent ' + hex(p1) + b'\n')
         if merge:
-            fp.write(b'# Parent ' + nodemod.hex(p2) + b'\n')
+            fp.write(b'# Parent ' + hex(p2) + b'\n')
         fp.write(message.rstrip() + b'\n')
         fp.close()
 
@@ -568,7 +573,7 @@ 
         def matchfn(node):
             if self.applied(repo, node, root):
                 return False
-            if source.changelog.parents(node)[1] != revlog.nullid:
+            if source.changelog.parents(node)[1] != nullid:
                 return False
             extra = source.changelog.read(node)[5]
             cnode = extra.get(b'transplant_source')
@@ -804,7 +809,7 @@ 
     tp = transplanter(ui, repo, opts)
 
     p1 = repo.dirstate.p1()
-    if len(repo) > 0 and p1 == revlog.nullid:
+    if len(repo) > 0 and p1 == nullid:
         raise error.Abort(_(b'no revision checked out'))
     if opts.get(b'continue'):
         if not tp.canresume():
@@ -909,7 +914,7 @@ 
     changeset if any."""
     ctx = context.resource(mapping, b'ctx')
     n = ctx.extra().get(b'transplant_source')
-    return n and nodemod.hex(n) or b''
+    return n and hex(n) or b''
 
 
 def extsetup(ui):
diff --git a/hgext/remotefilelog/shallowutil.py b/hgext/remotefilelog/shallowutil.py
--- a/hgext/remotefilelog/shallowutil.py
+++ b/hgext/remotefilelog/shallowutil.py
@@ -15,9 +15,9 @@ 
 
 from mercurial.i18n import _
 from mercurial.pycompat import open
+from mercurial.node import hex
 from mercurial import (
     error,
-    node,
     pycompat,
     revlog,
     util,
@@ -39,12 +39,12 @@ 
 
 
 def getcachekey(reponame, file, id):
-    pathhash = node.hex(hashutil.sha1(file).digest())
+    pathhash = hex(hashutil.sha1(file).digest())
     return os.path.join(reponame, pathhash[:2], pathhash[2:], id)
 
 
 def getlocalkey(file, id):
-    pathhash = node.hex(hashutil.sha1(file).digest())
+    pathhash = hex(hashutil.sha1(file).digest())
     return os.path.join(pathhash, id)
 
 
diff --git a/hgext/remotefilelog/fileserverclient.py b/hgext/remotefilelog/fileserverclient.py
--- a/hgext/remotefilelog/fileserverclient.py
+++ b/hgext/remotefilelog/fileserverclient.py
@@ -17,7 +17,6 @@ 
 from mercurial.node import bin, hex, nullid
 from mercurial import (
     error,
-    node,
     pycompat,
     revlog,
     sshpeer,
@@ -47,12 +46,12 @@ 
 
 
 def getcachekey(reponame, file, id):
-    pathhash = node.hex(hashutil.sha1(file).digest())
+    pathhash = hex(hashutil.sha1(file).digest())
     return os.path.join(reponame, pathhash[:2], pathhash[2:], id)
 
 
 def getlocalkey(file, id):
-    pathhash = node.hex(hashutil.sha1(file).digest())
+    pathhash = hex(hashutil.sha1(file).digest())
     return os.path.join(pathhash, id)
 
 
diff --git a/hgext/remotefilelog/debugcommands.py b/hgext/remotefilelog/debugcommands.py
--- a/hgext/remotefilelog/debugcommands.py
+++ b/hgext/remotefilelog/debugcommands.py
@@ -9,14 +9,18 @@ 
 import os
 import zlib
 
-from mercurial.node import bin, hex, nullid, short
+from mercurial.node import (
+    bin,
+    hex,
+    nullid,
+    short,
+)
 from mercurial.i18n import _
 from mercurial.pycompat import open
 from mercurial import (
     error,
     filelog,
     lock as lockmod,
-    node as nodemod,
     pycompat,
     revlog,
 )
@@ -61,7 +65,7 @@ 
 
 def buildtemprevlog(repo, file):
     # get filename key
-    filekey = nodemod.hex(hashutil.sha1(file).digest())
+    filekey = hex(hashutil.sha1(file).digest())
     filedir = os.path.join(repo.path, b'store/data', filekey)
 
     # sort all entries based on linkrev
@@ -421,7 +425,7 @@ 
             % (
                 hashformatter(node),
                 hashformatter(deltabasenode),
-                nodemod.hex(hashutil.sha1(delta).digest()),
+                hex(hashutil.sha1(delta).digest()),
                 len(delta),
             )
         )
diff --git a/hgext/remotefilelog/basepack.py b/hgext/remotefilelog/basepack.py
--- a/hgext/remotefilelog/basepack.py
+++ b/hgext/remotefilelog/basepack.py
@@ -12,8 +12,8 @@ 
     getattr,
     open,
 )
+from mercurial.node import hex
 from mercurial import (
-    node as nodemod,
     policy,
     pycompat,
     util,
@@ -429,7 +429,7 @@ 
             return
 
         try:
-            sha = nodemod.hex(self.sha.digest())
+            sha = hex(self.sha.digest())
             self.packfp.close()
             self.writeindex()
 
diff --git a/hgext/remotefilelog/__init__.py b/hgext/remotefilelog/__init__.py
--- a/hgext/remotefilelog/__init__.py
+++ b/hgext/remotefilelog/__init__.py
@@ -130,7 +130,10 @@ 
 import time
 import traceback
 
-from mercurial.node import hex
+from mercurial.node import (
+    hex,
+    wdirrev,
+)
 from mercurial.i18n import _
 from mercurial.pycompat import open
 from mercurial import (
@@ -150,7 +153,6 @@ 
     match as matchmod,
     merge,
     mergestate as mergestatemod,
-    node as nodemod,
     patch,
     pycompat,
     registrar,
@@ -1091,7 +1093,7 @@ 
     if isenabled(repo):
         allfiles = []
         for rev, match in revmatches:
-            if rev == nodemod.wdirrev or rev is None:
+            if rev == wdirrev or rev is None:
                 continue
             ctx = repo[rev]
             mf = ctx.manifest()
diff --git a/hgext/releasenotes.py b/hgext/releasenotes.py
--- a/hgext/releasenotes.py
+++ b/hgext/releasenotes.py
@@ -19,12 +19,12 @@ 
 
 from mercurial.i18n import _
 from mercurial.pycompat import open
+from mercurial.node import hex
 from mercurial import (
     cmdutil,
     config,
     error,
     minirst,
-    node,
     pycompat,
     registrar,
     scmutil,
@@ -381,7 +381,7 @@ 
             if not paragraphs:
                 repo.ui.warn(
                     _(b"error parsing releasenotes for revision: '%s'\n")
-                    % node.hex(ctx.node())
+                    % hex(ctx.node())
                 )
             if title:
                 notes.addtitleditem(directive, title, paragraphs)
diff --git a/hgext/rebase.py b/hgext/rebase.py
--- a/hgext/rebase.py
+++ b/hgext/rebase.py
@@ -23,6 +23,7 @@ 
 from mercurial.node import (
     nullrev,
     short,
+    wdirrev,
 )
 from mercurial.pycompat import open
 from mercurial import (
@@ -37,7 +38,6 @@ 
     merge as mergemod,
     mergestate as mergestatemod,
     mergeutil,
-    node as nodemod,
     obsolete,
     obsutil,
     patch,
@@ -1369,7 +1369,7 @@ 
                 )
             return None
 
-    if nodemod.wdirrev in rebaseset:
+    if wdirrev in rebaseset:
         raise error.Abort(_(b'cannot rebase the working copy'))
     rebasingwcp = repo[b'.'].rev() in rebaseset
     ui.log(
diff --git a/hgext/patchbomb.py b/hgext/patchbomb.py
--- a/hgext/patchbomb.py
+++ b/hgext/patchbomb.py
@@ -83,6 +83,7 @@ 
 
 from mercurial.i18n import _
 from mercurial.pycompat import open
+from mercurial.node import bin
 from mercurial import (
     cmdutil,
     commands,
@@ -91,7 +92,6 @@ 
     formatter,
     hg,
     mail,
-    node as nodemod,
     patch,
     pycompat,
     registrar,
@@ -306,7 +306,7 @@ 
         p = mail.mimetextpatch(
             b'\n'.join(patchlines), 'x-patch', opts.get(b'test')
         )
-        binnode = nodemod.bin(node)
+        binnode = bin(node)
         # if node is mq patch, it will have the patch file's name as a tag
         if not patchname:
             patchtags = [
diff --git a/hgext/narrow/narrowcommands.py b/hgext/narrow/narrowcommands.py
--- a/hgext/narrow/narrowcommands.py
+++ b/hgext/narrow/narrowcommands.py
@@ -10,6 +10,11 @@ 
 import os
 
 from mercurial.i18n import _
+from mercurial.node import (
+    hex,
+    nullid,
+    short,
+)
 from mercurial import (
     bundle2,
     cmdutil,
@@ -21,7 +26,6 @@ 
     extensions,
     hg,
     narrowspec,
-    node,
     pathutil,
     pycompat,
     registrar,
@@ -184,9 +188,9 @@ 
     # we have all the nodes
     if wireprototypes.ELLIPSESCAP1 in pullop.remote.capabilities():
         kwargs[b'known'] = [
-            node.hex(ctx.node())
+            hex(ctx.node())
             for ctx in repo.set(b'::%ln', pullop.common)
-            if ctx.node() != node.nullid
+            if ctx.node() != nullid
         ]
         if not kwargs[b'known']:
             # Mercurial serializes an empty list as '' and deserializes it as
@@ -239,10 +243,10 @@ 
         maxnodes = 10
         if ui.verbose or len(visibletostrip) <= maxnodes:
             for n in visibletostrip:
-                ui.status(b'%s\n' % node.short(n))
+                ui.status(b'%s\n' % short(n))
         else:
             for n in visibletostrip[:maxnodes]:
-                ui.status(b'%s\n' % node.short(n))
+                ui.status(b'%s\n' % short(n))
             ui.status(
                 _(b'...and %d more, use --verbose to list all\n')
                 % (len(visibletostrip) - maxnodes)
@@ -362,7 +366,7 @@ 
             ds = repo.dirstate
             p1, p2 = ds.p1(), ds.p2()
             with ds.parentchange():
-                ds.setparents(node.nullid, node.nullid)
+                ds.setparents(nullid, nullid)
         if isoldellipses:
             with wrappedextraprepare:
                 exchange.pull(repo, remote, heads=common)
@@ -372,7 +376,7 @@ 
                 known = [
                     ctx.node()
                     for ctx in repo.set(b'::%ln', common)
-                    if ctx.node() != node.nullid
+                    if ctx.node() != nullid
                 ]
             with remote.commandexecutor() as e:
                 bundle = e.callcommand(
diff --git a/hgext/lfs/blobstore.py b/hgext/lfs/blobstore.py
--- a/hgext/lfs/blobstore.py
+++ b/hgext/lfs/blobstore.py
@@ -17,12 +17,12 @@ 
 
 from mercurial.i18n import _
 from mercurial.pycompat import getattr
+from mercurial.node import hex
 
 from mercurial import (
     encoding,
     error,
     httpconnection as httpconnectionmod,
-    node,
     pathutil,
     pycompat,
     url as urlmod,
@@ -173,7 +173,7 @@ 
                 )
                 raise LfsRemoteError(_(msg) % (size, int(content_length)))
 
-            realoid = node.hex(sha256.digest())
+            realoid = hex(sha256.digest())
             if realoid != oid:
                 raise LfsCorruptionError(
                     _(b'corrupt remote lfs object: %s') % oid
@@ -224,7 +224,7 @@ 
             # Don't abort if corruption is detected, because `hg verify` will
             # give more useful info about the corruption- simply don't add the
             # hardlink.
-            if verify or node.hex(hashlib.sha256(blob).digest()) == oid:
+            if verify or hex(hashlib.sha256(blob).digest()) == oid:
                 self.ui.note(_(b'lfs: found %s in the usercache\n') % oid)
                 lfutil.link(self.cachevfs.join(oid), self.vfs.join(oid))
         else:
@@ -248,7 +248,7 @@ 
             for chunk in util.filechunkiter(fp, size=1048576):
                 sha256.update(chunk)
 
-        return oid == node.hex(sha256.digest())
+        return oid == hex(sha256.digest())
 
     def has(self, oid):
         """Returns True if the local blobstore contains the requested blob,
@@ -706,7 +706,7 @@ 
 
 
 def _verify(oid, content):
-    realoid = node.hex(hashlib.sha256(content).digest())
+    realoid = hex(hashlib.sha256(content).digest())
     if realoid != oid:
         raise LfsCorruptionError(
             _(b'detected corrupt lfs object: %s') % oid,
diff --git a/hgext/lfs/__init__.py b/hgext/lfs/__init__.py
--- a/hgext/lfs/__init__.py
+++ b/hgext/lfs/__init__.py
@@ -125,6 +125,7 @@ 
 import sys
 
 from mercurial.i18n import _
+from mercurial.node import bin
 
 from mercurial import (
     bundlecaches,
@@ -137,7 +138,6 @@ 
     filesetlang,
     localrepo,
     minifileset,
-    node,
     pycompat,
     revlog,
     scmutil,
@@ -260,7 +260,7 @@ 
                 return 0
 
             last = kwargs.get('node_last')
-            _bin = node.bin
+            _bin = bin
             if last:
                 s = repo.set(b'%n:%n', _bin(kwargs['node']), _bin(last))
             else:
diff --git a/hgext/largefiles/lfutil.py b/hgext/largefiles/lfutil.py
--- a/hgext/largefiles/lfutil.py
+++ b/hgext/largefiles/lfutil.py
@@ -15,7 +15,10 @@ 
 import stat
 
 from mercurial.i18n import _
-from mercurial.node import hex
+from mercurial.node import (
+    hex,
+    nullid,
+)
 from mercurial.pycompat import open
 
 from mercurial import (
@@ -24,7 +27,6 @@ 
     error,
     httpconnection,
     match as matchmod,
-    node,
     pycompat,
     scmutil,
     sparse,
@@ -610,7 +612,7 @@ 
     ) as progress:
         for i, n in enumerate(missing):
             progress.update(i)
-            parents = [p for p in repo[n].parents() if p != node.nullid]
+            parents = [p for p in repo[n].parents() if p != nullid]
 
             with lfstatus(repo, value=False):
                 ctx = repo[n]
diff --git a/hgext/largefiles/lfcommands.py b/hgext/largefiles/lfcommands.py
--- a/hgext/largefiles/lfcommands.py
+++ b/hgext/largefiles/lfcommands.py
@@ -14,6 +14,11 @@ 
 import shutil
 
 from mercurial.i18n import _
+from mercurial.node import (
+    bin,
+    hex,
+    nullid,
+)
 
 from mercurial import (
     cmdutil,
@@ -23,7 +28,6 @@ 
     hg,
     lock,
     match as matchmod,
-    node,
     pycompat,
     scmutil,
     util,
@@ -111,7 +115,7 @@ 
             rsrc[ctx]
             for ctx in rsrc.changelog.nodesbetween(None, rsrc.heads())[0]
         )
-        revmap = {node.nullid: node.nullid}
+        revmap = {nullid: nullid}
         if tolfile:
             # Lock destination to prevent modification while it is converted to.
             # Don't need to lock src because we are just reading from its
@@ -275,7 +279,7 @@ 
                 # largefile was modified, update standins
                 m = hashutil.sha1(b'')
                 m.update(ctx[f].data())
-                hash = node.hex(m.digest())
+                hash = hex(m.digest())
                 if f not in lfiletohash or lfiletohash[f] != hash:
                     rdst.wwrite(f, ctx[f].data(), ctx[f].flags())
                     executable = b'x' in ctx[f].flags()
@@ -336,7 +340,7 @@ 
 # Generate list of changed files
 def _getchangedfiles(ctx, parents):
     files = set(ctx.files())
-    if node.nullid not in parents:
+    if nullid not in parents:
         mc = ctx.manifest()
         for pctx in ctx.parents():
             for fn in pctx.manifest().diff(mc):
@@ -350,7 +354,7 @@ 
     for p in ctx.parents():
         parents.append(revmap[p.node()])
     while len(parents) < 2:
-        parents.append(node.nullid)
+        parents.append(nullid)
     return parents
 
 
@@ -380,12 +384,12 @@ 
             ui.warn(_(b'skipping incorrectly formatted tag %s\n') % line)
             continue
         try:
-            newid = node.bin(id)
+            newid = bin(id)
         except TypeError:
             ui.warn(_(b'skipping incorrectly formatted id %s\n') % id)
             continue
         try:
-            newdata.append(b'%s %s\n' % (node.hex(revmap[newid]), name))
+            newdata.append(b'%s %s\n' % (hex(revmap[newid]), name))
         except KeyError:
             ui.warn(_(b'no mapping for id %s\n') % id)
             continue
diff --git a/hgext/journal.py b/hgext/journal.py
--- a/hgext/journal.py
+++ b/hgext/journal.py
@@ -19,6 +19,11 @@ 
 import weakref
 
 from mercurial.i18n import _
+from mercurial.node import (
+    bin,
+    hex,
+    nullid,
+)
 
 from mercurial import (
     bookmarks,
@@ -31,7 +36,6 @@ 
     localrepo,
     lock,
     logcmdutil,
-    node,
     pycompat,
     registrar,
     util,
@@ -113,8 +117,8 @@ 
     new = list(new)
     if util.safehasattr(dirstate, 'journalstorage'):
         # only record two hashes if there was a merge
-        oldhashes = old[:1] if old[1] == node.nullid else old
-        newhashes = new[:1] if new[1] == node.nullid else new
+        oldhashes = old[:1] if old[1] == nullid else old
+        newhashes = new[:1] if new[1] == nullid else new
         dirstate.journalstorage.record(
             wdirparenttype, b'.', oldhashes, newhashes
         )
@@ -127,7 +131,7 @@ 
     if util.safehasattr(repo, 'journal'):
         oldmarks = bookmarks.bmstore(repo)
         for mark, value in pycompat.iteritems(store):
-            oldvalue = oldmarks.get(mark, node.nullid)
+            oldvalue = oldmarks.get(mark, nullid)
             if value != oldvalue:
                 repo.journal.record(bookmarktype, mark, oldvalue, value)
     return orig(store, fp)
@@ -248,8 +252,8 @@ 
         ) = line.split(b'\n')
         timestamp, tz = time.split()
         timestamp, tz = float(timestamp), int(tz)
-        oldhashes = tuple(node.bin(hash) for hash in oldhashes.split(b','))
-        newhashes = tuple(node.bin(hash) for hash in newhashes.split(b','))
+        oldhashes = tuple(bin(hash) for hash in oldhashes.split(b','))
+        newhashes = tuple(bin(hash) for hash in newhashes.split(b','))
         return cls(
             (timestamp, tz),
             user,
@@ -263,8 +267,8 @@ 
     def __bytes__(self):
         """bytes representation for storage"""
         time = b' '.join(map(pycompat.bytestr, self.timestamp))
-        oldhashes = b','.join([node.hex(hash) for hash in self.oldhashes])
-        newhashes = b','.join([node.hex(hash) for hash in self.newhashes])
+        oldhashes = b','.join([hex(hash) for hash in self.oldhashes])
+        newhashes = b','.join([hex(hash) for hash in self.newhashes])
         return b'\n'.join(
             (
                 time,
diff --git a/hgext/infinitepush/store.py b/hgext/infinitepush/store.py
--- a/hgext/infinitepush/store.py
+++ b/hgext/infinitepush/store.py
@@ -9,11 +9,9 @@ 
 import os
 import subprocess
 
+from mercurial.node import hex
 from mercurial.pycompat import open
-from mercurial import (
-    node,
-    pycompat,
-)
+from mercurial import pycompat
 from mercurial.utils import (
     hashutil,
     procutil,
@@ -86,7 +84,7 @@ 
         return os.path.join(self._dirpath(filename), filename)
 
     def write(self, data):
-        filename = node.hex(hashutil.sha1(data).digest())
+        filename = hex(hashutil.sha1(data).digest())
         dirpath = self._dirpath(filename)
 
         if not os.path.exists(dirpath):
diff --git a/hgext/infinitepush/bundleparts.py b/hgext/infinitepush/bundleparts.py
--- a/hgext/infinitepush/bundleparts.py
+++ b/hgext/infinitepush/bundleparts.py
@@ -6,13 +6,13 @@ 
 from __future__ import absolute_import
 
 from mercurial.i18n import _
+from mercurial.node import hex
 
 from mercurial import (
     bundle2,
     changegroup,
     error,
     extensions,
-    node as nodemod,
     pycompat,
     revsetlang,
     util,
@@ -54,7 +54,7 @@ 
         params[b'bookprevnode'] = b''
         bookmarks = repo._bookmarks
         if bookmark in bookmarks:
-            params[b'bookprevnode'] = nodemod.hex(bookmarks[bookmark])
+            params[b'bookprevnode'] = hex(bookmarks[bookmark])
 
     # Do not send pushback bundle2 part with bookmarks if remotenames extension
     # is enabled. It will be handled manually in `_push()`
diff --git a/hgext/histedit.py b/hgext/histedit.py
--- a/hgext/histedit.py
+++ b/hgext/histedit.py
@@ -209,6 +209,11 @@ 
     getattr,
     open,
 )
+from mercurial.node import (
+    bin,
+    hex,
+    short,
+)
 from mercurial import (
     bundle2,
     cmdutil,
@@ -225,7 +230,6 @@ 
     merge as mergemod,
     mergestate as mergestatemod,
     mergeutil,
-    node,
     obsolete,
     pycompat,
     registrar,
@@ -404,8 +408,8 @@ 
 
     def _write(self, fp):
         fp.write(b'v1\n')
-        fp.write(b'%s\n' % node.hex(self.parentctxnode))
-        fp.write(b'%s\n' % node.hex(self.topmost))
+        fp.write(b'%s\n' % hex(self.parentctxnode))
+        fp.write(b'%s\n' % hex(self.topmost))
         fp.write(b'%s\n' % (b'True' if self.keep else b'False'))
         fp.write(b'%d\n' % len(self.actions))
         for action in self.actions:
@@ -415,8 +419,8 @@ 
             fp.write(
                 b'%s%s\n'
                 % (
-                    node.hex(replacement[0]),
-                    b''.join(node.hex(r) for r in replacement[1]),
+                    hex(replacement[0]),
+                    b''.join(hex(r) for r in replacement[1]),
                 )
             )
         backupfile = self.backupfile
@@ -432,10 +436,10 @@ 
         lines[index]  # version number
         index += 1
 
-        parentctxnode = node.bin(lines[index])
+        parentctxnode = bin(lines[index])
         index += 1
 
-        topmost = node.bin(lines[index])
+        topmost = bin(lines[index])
         index += 1
 
         keep = lines[index] == b'True'
@@ -458,9 +462,9 @@ 
         index += 1
         for i in pycompat.xrange(replacementlen):
             replacement = lines[index]
-            original = node.bin(replacement[:40])
+            original = bin(replacement[:40])
             succ = [
-                node.bin(replacement[i : i + 40])
+                bin(replacement[i : i + 40])
                 for i in range(40, len(replacement), 40)
             ]
             replacements.append((original, succ))
@@ -494,12 +498,12 @@ 
         # ruleid can be anything from rev numbers, hashes, "bookmarks" etc
         # Check for validation of rule ids and get the rulehash
         try:
-            rev = node.bin(ruleid)
+            rev = bin(ruleid)
         except TypeError:
             try:
                 _ctx = scmutil.revsingle(state.repo, ruleid)
                 rulehash = _ctx.hex()
-                rev = node.bin(rulehash)
+                rev = bin(rulehash)
             except error.RepoLookupError:
                 raise error.ParseError(_(b"invalid changeset %s") % ruleid)
         return cls(state, rev)
@@ -507,7 +511,7 @@ 
     def verify(self, prev, expected, seen):
         """ Verifies semantic correctness of the rule"""
         repo = self.repo
-        ha = node.hex(self.node)
+        ha = hex(self.node)
         self.node = scmutil.resolvehexnodeidprefix(repo, ha)
         if self.node is None:
             raise error.ParseError(_(b'unknown changeset %s listed') % ha[:12])
@@ -518,14 +522,14 @@ 
         if self.node not in expected:
             raise error.ParseError(
                 _(b'%s "%s" changeset was not a candidate')
-                % (self.verb, node.short(self.node)),
+                % (self.verb, short(self.node)),
                 hint=_(b'only use listed changesets'),
             )
         # and only one command per node
         if self.node in seen:
             raise error.ParseError(
                 _(b'duplicated command for changeset %s')
-                % node.short(self.node)
+                % short(self.node)
             )
 
     def torule(self):
@@ -557,7 +561,7 @@ 
         """Print an action in format used by histedit state files
         (the first line is a verb, the remainder is the second)
         """
-        return b"%s\n%s" % (self.verb, node.hex(self.node))
+        return b"%s\n%s" % (self.verb, hex(self.node))
 
     def run(self):
         """Runs the action. The default behavior is simply apply the action's
@@ -579,7 +583,7 @@ 
         if stats.unresolvedcount:
             raise error.InterventionRequired(
                 _(b'Fix up the change (%s %s)')
-                % (self.verb, node.short(self.node)),
+                % (self.verb, short(self.node)),
                 hint=_(b'hg histedit --continue to resume'),
             )
 
@@ -615,7 +619,7 @@ 
         if ctx.node() == self.state.parentctxnode:
             self.repo.ui.warn(
                 _(b'%s: skipping changeset (no changes)\n')
-                % node.short(self.node)
+                % short(self.node)
             )
             return ctx, [(self.node, tuple())]
         if ctx.node() == self.node:
@@ -684,7 +688,7 @@ 
     for c in ctxs:
         if not c.mutable():
             raise error.ParseError(
-                _(b"cannot fold into public change %s") % node.short(c.node())
+                _(b"cannot fold into public change %s") % short(c.node())
             )
     base = firstctx.p1()
 
@@ -786,7 +790,7 @@ 
     def run(self):
         rulectx = self.repo[self.node]
         if rulectx.p1().node() == self.state.parentctxnode:
-            self.repo.ui.debug(b'node %s unchanged\n' % node.short(self.node))
+            self.repo.ui.debug(b'node %s unchanged\n' % short(self.node))
             return rulectx, []
 
         return super(pick, self).run()
@@ -801,7 +805,7 @@ 
         applychanges(repo.ui, repo, rulectx, {})
         raise error.InterventionRequired(
             _(b'Editing (%s), you may commit or record as needed now.')
-            % node.short(self.node),
+            % short(self.node),
             hint=_(b'hg histedit --continue to resume'),
         )
 
@@ -823,7 +827,7 @@ 
             c = repo[prev.node]
         if not c.mutable():
             raise error.ParseError(
-                _(b"cannot fold into public change %s") % node.short(c.node())
+                _(b"cannot fold into public change %s") % short(c.node())
             )
 
     def continuedirty(self):
@@ -832,7 +836,7 @@ 
 
         commit = commitfuncfor(repo, rulectx)
         commit(
-            text=b'fold-temp-revision %s' % node.short(self.node),
+            text=b'fold-temp-revision %s' % short(self.node),
             user=rulectx.user(),
             date=rulectx.date(),
             extra=rulectx.extra(),
@@ -844,7 +848,7 @@ 
         rulectx = repo[self.node]
         parentctxnode = self.state.parentctxnode
         if ctx.node() == parentctxnode:
-            repo.ui.warn(_(b'%s: empty changeset\n') % node.short(self.node))
+            repo.ui.warn(_(b'%s: empty changeset\n') % short(self.node))
             return ctx, [(self.node, (parentctxnode,))]
 
         parentctx = repo[parentctxnode]
@@ -858,7 +862,7 @@ 
                     b'%s: cannot fold - working copy is not a '
                     b'descendant of previous commit %s\n'
                 )
-                % (node.short(self.node), node.short(parentctxnode))
+                % (short(self.node), short(parentctxnode))
             )
             return ctx, [(self.node, (ctx.node(),))]
 
@@ -972,7 +976,7 @@ 
         if self.node in expected:
             msg = _(b'%s "%s" changeset was an edited list candidate')
             raise error.ParseError(
-                msg % (self.verb, node.short(self.node)),
+                msg % (self.verb, short(self.node)),
                 hint=_(b'base must only use unlisted changesets'),
             )
 
@@ -1717,7 +1721,7 @@ 
         if not revs:
             raise error.Abort(
                 _(b'%s is not an ancestor of working directory')
-                % node.short(root)
+                % short(root)
             )
 
         ctxs = []
@@ -2070,16 +2074,16 @@ 
     if mapping:
         for prec, succs in pycompat.iteritems(mapping):
             if not succs:
-                ui.debug(b'histedit: %s is dropped\n' % node.short(prec))
+                ui.debug(b'histedit: %s is dropped\n' % short(prec))
             else:
                 ui.debug(
                     b'histedit: %s is replaced by %s\n'
-                    % (node.short(prec), node.short(succs[0]))
+                    % (short(prec), short(succs[0]))
                 )
                 if len(succs) > 1:
                     m = b'histedit:                            %s'
                     for n in succs[1:]:
-                        ui.debug(m % node.short(n))
+                        ui.debug(m % short(n))
 
     if not state.keep:
         if mapping:
@@ -2124,7 +2128,7 @@ 
     try:
         state.read()
         __, leafs, tmpnodes, __ = processreplacement(state)
-        ui.debug(b'restore wc to old parent %s\n' % node.short(state.topmost))
+        ui.debug(b'restore wc to old parent %s\n' % short(state.topmost))
 
         # Recover our old commits if necessary
         if not state.topmost in repo and state.backupfile:
@@ -2178,7 +2182,7 @@ 
     state.read()
     if not rules:
         comment = geteditcomment(
-            ui, node.short(state.parentctxnode), node.short(state.topmost)
+            ui, short(state.parentctxnode), short(state.topmost)
         )
         rules = ruleeditor(repo, ui, state.actions, comment)
     else:
@@ -2219,7 +2223,7 @@ 
     revs = between(repo, root, topmost, state.keep)
     if not revs:
         raise error.Abort(
-            _(b'%s is not an ancestor of working directory') % node.short(root)
+            _(b'%s is not an ancestor of working directory') % short(root)
         )
 
     ctxs = [repo[r] for r in revs]
@@ -2256,7 +2260,7 @@ 
             )
 
     if not rules:
-        comment = geteditcomment(ui, node.short(root), node.short(topmost))
+        comment = geteditcomment(ui, short(root), short(topmost))
         actions = [pick(state, r) for r in revs]
         rules = ruleeditor(repo, ui, actions, comment)
     else:
@@ -2460,12 +2464,12 @@ 
         actions[:0] = drops
     elif missing:
         raise error.ParseError(
-            _(b'missing rules for changeset %s') % node.short(missing[0]),
+            _(b'missing rules for changeset %s') % short(missing[0]),
             hint=_(
                 b'use "drop %s" to discard, see also: '
                 b"'hg help -e histedit.config'"
             )
-            % node.short(missing[0]),
+            % short(missing[0]),
         )
 
 
@@ -2619,7 +2623,7 @@ 
         if common_nodes:
             raise error.Abort(
                 _(b"histedit in progress, can't strip %s")
-                % b', '.join(node.short(x) for x in common_nodes)
+                % b', '.join(short(x) for x in common_nodes)
             )
     return orig(ui, repo, nodelist, *args, **kwargs)
 
diff --git a/hgext/gpg.py b/hgext/gpg.py
--- a/hgext/gpg.py
+++ b/hgext/gpg.py
@@ -11,12 +11,17 @@ 
 import os
 
 from mercurial.i18n import _
+from mercurial.node import (
+    bin,
+    hex,
+    nullid,
+    short,
+)
 from mercurial import (
     cmdutil,
     error,
     help,
     match,
-    node as hgnode,
     pycompat,
     registrar,
 )
@@ -155,7 +160,7 @@ 
     # read the heads
     fl = repo.file(b".hgsigs")
     for r in reversed(fl.heads()):
-        fn = b".hgsigs|%s" % hgnode.short(r)
+        fn = b".hgsigs|%s" % short(r)
         for item in parsefile(fl.read(r).splitlines(), fn):
             yield item
     try:
@@ -172,7 +177,7 @@ 
     fn, ln = context
     node, version, sig = sigdata
     prefix = b"%s:%d" % (fn, ln)
-    node = hgnode.bin(node)
+    node = bin(node)
 
     data = node2txt(repo, node, version)
     sig = binascii.a2b_base64(sig)
@@ -223,7 +228,7 @@ 
         revs[r].extend(keys)
     for rev in sorted(revs, reverse=True):
         for k in revs[rev]:
-            r = b"%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev)))
+            r = b"%5d:%s" % (rev, hex(repo.changelog.node(rev)))
             ui.write(b"%-30s %s\n" % (keystr(ui, k), r))
 
 
@@ -232,7 +237,7 @@ 
     """verify all the signatures there may be for a particular revision"""
     mygpg = newgpg(ui)
     rev = repo.lookup(rev)
-    hexrev = hgnode.hex(rev)
+    hexrev = hex(rev)
     keys = []
 
     for data, context in sigwalk(repo):
@@ -243,11 +248,11 @@ 
                 keys.extend(k)
 
     if not keys:
-        ui.write(_(b"no valid signature for %s\n") % hgnode.short(rev))
+        ui.write(_(b"no valid signature for %s\n") % short(rev))
         return
 
     # print summary
-    ui.write(_(b"%s is signed by:\n") % hgnode.short(rev))
+    ui.write(_(b"%s is signed by:\n") % short(rev))
     for key in keys:
         ui.write(b" %s\n" % keystr(ui, key))
 
@@ -310,7 +315,7 @@ 
         nodes = [repo.lookup(n) for n in revs]
     else:
         nodes = [
-            node for node in repo.dirstate.parents() if node != hgnode.nullid
+            node for node in repo.dirstate.parents() if node != nullid
         ]
         if len(nodes) > 1:
             raise error.Abort(
@@ -320,9 +325,9 @@ 
             nodes = [repo.changelog.tip()]
 
     for n in nodes:
-        hexnode = hgnode.hex(n)
+        hexnode = hex(n)
         ui.write(
-            _(b"signing %d:%s\n") % (repo.changelog.rev(n), hgnode.short(n))
+            _(b"signing %d:%s\n") % (repo.changelog.rev(n), short(n))
         )
         # build data
         data = node2txt(repo, n, sigver)
@@ -361,7 +366,7 @@ 
         # we don't translate commit messages
         message = b"\n".join(
             [
-                b"Added signature for changeset %s" % hgnode.short(n)
+                b"Added signature for changeset %s" % short(n)
                 for n in nodes
             ]
         )
@@ -379,7 +384,7 @@ 
 def node2txt(repo, node, ver):
     """map a manifest into some text"""
     if ver == b"0":
-        return b"%s\n" % hgnode.hex(node)
+        return b"%s\n" % hex(node)
     else:
         raise error.Abort(_(b"unknown signature version"))
 
diff --git a/hgext/git/index.py b/hgext/git/index.py
--- a/hgext/git/index.py
+++ b/hgext/git/index.py
@@ -5,11 +5,14 @@ 
 import sqlite3
 
 from mercurial.i18n import _
+from mercurial.node import (
+    nullhex,
+    nullid,
+)
 
 from mercurial import (
     encoding,
     error,
-    node as nodemod,
     pycompat,
 )
 
@@ -278,7 +281,7 @@ 
     for pos, commit in enumerate(walker):
         if prog is not None:
             prog.update(pos)
-        p1 = p2 = nodemod.nullhex
+        p1 = p2 = nullhex
         if len(commit.parents) > 2:
             raise error.ProgrammingError(
                 (
@@ -317,7 +320,7 @@ 
             files = {
                 nf.path: nf.id.hex
                 for nf in new_files
-                if nf.id.raw != nodemod.nullid
+                if nf.id.raw != nullid
             }
             for p, n in files.items():
                 # We intentionally set NULLs for any file parentage
diff --git a/hgext/git/gitlog.py b/hgext/git/gitlog.py
--- a/hgext/git/gitlog.py
+++ b/hgext/git/gitlog.py
@@ -2,6 +2,13 @@ 
 
 from mercurial.i18n import _
 
+from mercurial.node import (
+    bin,
+    nullhex,
+    nullid,
+    nullrev,
+    wdirhex,
+)
 from mercurial import (
     ancestor,
     changelog as hgchangelog,
@@ -9,7 +16,6 @@ 
     encoding,
     error,
     manifest,
-    node as nodemod,
     pycompat,
 )
 from mercurial.interfaces import (
@@ -39,7 +45,7 @@ 
         )
 
     def rev(self, n):
-        if n == nodemod.nullid:
+        if n == nullid:
             return -1
         t = self._db.execute(
             'SELECT rev FROM changelog WHERE node = ?', (gitutil.togitnode(n),)
@@ -49,14 +55,14 @@ 
         return t[0]
 
     def node(self, r):
-        if r == nodemod.nullrev:
-            return nodemod.nullid
+        if r == nullrev:
+            return nullid
         t = self._db.execute(
             'SELECT node FROM changelog WHERE rev = ?', (r,)
         ).fetchone()
         if t is None:
             raise error.LookupError(r, b'00changelog.i', _(b'no node'))
-        return nodemod.bin(t[0])
+        return bin(t[0])
 
     def hasnode(self, n):
         t = self._db.execute(
@@ -123,10 +129,10 @@ 
     @property
     def nodemap(self):
         r = {
-            nodemod.bin(v[0]): v[1]
+            bin(v[0]): v[1]
             for v in self._db.execute('SELECT node, rev FROM changelog')
         }
-        r[nodemod.nullid] = nodemod.nullrev
+        r[nullid] = nullrev
         return r
 
     def tip(self):
@@ -134,8 +140,8 @@ 
             'SELECT node FROM changelog ORDER BY rev DESC LIMIT 1'
         ).fetchone()
         if t:
-            return nodemod.bin(t[0])
-        return nodemod.nullid
+            return bin(t[0])
+        return nullid
 
     def revs(self, start=0, stop=None):
         if stop is None:
@@ -155,16 +161,16 @@ 
         return next(t)
 
     def _partialmatch(self, id):
-        if nodemod.wdirhex.startswith(id):
+        if wdirhex.startswith(id):
             raise error.WdirUnsupported
         candidates = [
-            nodemod.bin(x[0])
+            bin(x[0])
             for x in self._db.execute(
                 'SELECT node FROM changelog WHERE node LIKE ?', (id + b'%',)
             )
         ]
-        if nodemod.nullhex.startswith(id):
-            candidates.append(nodemod.nullid)
+        if nullhex.startswith(id):
+            candidates.append(nullid)
         if len(candidates) > 1:
             raise error.AmbiguousPrefixLookupError(
                 id, b'00changelog.i', _(b'ambiguous identifier')
@@ -177,7 +183,7 @@ 
         return 0
 
     def shortest(self, node, minlength=1):
-        nodehex = nodemod.hex(node)
+        nodehex = hex(node)
         for attempt in pycompat.xrange(minlength, len(nodehex) + 1):
             candidate = nodehex[:attempt]
             matches = int(
@@ -209,7 +215,7 @@ 
         else:
             n = nodeorrev
         # handle looking up nullid
-        if n == nodemod.nullid:
+        if n == nullid:
             return hgchangelog._changelogrevision(extra={})
         hn = gitutil.togitnode(n)
         # We've got a real commit!
@@ -226,7 +232,7 @@ 
             for r in self._db.execute(
                 'SELECT filename FROM changedfiles '
                 'WHERE node = ? and filenode = ?',
-                (hn, nodemod.nullhex),
+                (hn, nullhex),
             )
         ]
         c = self.gitrepo[hn]
@@ -267,7 +273,7 @@ 
         nullrev.
         """
         if common is None:
-            common = [nodemod.nullrev]
+            common = [nullrev]
 
         return ancestor.incrementalmissingancestors(self.parentrevs, common)
 
@@ -287,7 +293,7 @@ 
         not supplied, uses all of the revlog's heads.  If common is not
         supplied, uses nullid."""
         if common is None:
-            common = [nodemod.nullid]
+            common = [nullid]
         if heads is None:
             heads = self.heads()
 
@@ -302,12 +308,12 @@ 
         c = []
         p = self.rev(node)
         for r in self.revs(start=p + 1):
-            prevs = [pr for pr in self.parentrevs(r) if pr != nodemod.nullrev]
+            prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
             if prevs:
                 for pr in prevs:
                     if pr == p:
                         c.append(self.node(r))
-            elif p == nodemod.nullrev:
+            elif p == nullrev:
                 c.append(self.node(r))
         return c
 
@@ -323,7 +329,7 @@ 
 
     # Cleanup opportunity: this is *identical* to the revlog.py version
     def isancestorrev(self, a, b):
-        if a == nodemod.nullrev:
+        if a == nullrev:
             return True
         elif a == b:
             return True
@@ -337,8 +343,8 @@ 
         if hn != gitutil.nullgit:
             c = self.gitrepo[hn]
         else:
-            return nodemod.nullrev, nodemod.nullrev
-        p1 = p2 = nodemod.nullrev
+            return nullrev, nullrev
+        p1 = p2 = nullrev
         if c.parents:
             p1 = self.rev(c.parents[0].id.raw)
             if len(c.parents) > 2:
@@ -386,9 +392,9 @@ 
     ):
         parents = []
         hp1, hp2 = gitutil.togitnode(p1), gitutil.togitnode(p2)
-        if p1 != nodemod.nullid:
+        if p1 != nullid:
             parents.append(hp1)
-        if p2 and p2 != nodemod.nullid:
+        if p2 and p2 != nullid:
             parents.append(hp2)
         assert date is not None
         timestamp, tz = date
@@ -419,7 +425,7 @@ 
         return self.get(b'', node)
 
     def get(self, relpath, node):
-        if node == nodemod.nullid:
+        if node == nullid:
             # TODO: this should almost certainly be a memgittreemanifestctx
             return manifest.memtreemanifestctx(self, relpath)
         commit = self.gitrepo[gitutil.togitnode(node)]
@@ -440,7 +446,7 @@ 
         self.path = path
 
     def read(self, node):
-        if node == nodemod.nullid:
+        if node == nullid:
             return b''
         return self.gitrepo[gitutil.togitnode(node)].data
 
@@ -450,7 +456,7 @@ 
         if isinstance(node, int):
             assert False, b'todo revnums for nodes'
         if len(node) == 40:
-            node = nodemod.bin(node)
+            node = bin(node)
         hnode = gitutil.togitnode(node)
         if hnode in self.gitrepo:
             return node
@@ -500,7 +506,7 @@ 
         ).fetchone()
         if maybe is None:
             raise IndexError('gitlog %r out of range %d' % (self.path, rev))
-        return nodemod.bin(maybe[0])
+        return bin(maybe[0])
 
     def parents(self, node):
         gn = gitutil.togitnode(node)
@@ -525,7 +531,7 @@ 
                 index.fill_in_filelog(self.gitrepo, self._db, commit, gp, gn)
                 return self.parents(node)
             else:
-                ps.append(nodemod.bin(p))
+                ps.append(bin(p))
         return ps
 
     def renamed(self, node):
diff --git a/hgext/git/dirstate.py b/hgext/git/dirstate.py
--- a/hgext/git/dirstate.py
+++ b/hgext/git/dirstate.py
@@ -4,11 +4,11 @@ 
 import errno
 import os
 
+from mercurial.node import nullid
 from mercurial import (
     error,
     extensions,
     match as matchmod,
-    node as nodemod,
     pycompat,
     scmutil,
     util,
@@ -81,14 +81,14 @@ 
         except pygit2.GitError:
             # Typically happens when peeling HEAD fails, as in an
             # empty repository.
-            return nodemod.nullid
+            return nullid
 
     def p2(self):
         # TODO: MERGE_HEAD? something like that, right?
-        return nodemod.nullid
+        return nullid
 
-    def setparents(self, p1, p2=nodemod.nullid):
-        assert p2 == nodemod.nullid, b'TODO merging support'
+    def setparents(self, p1, p2=nullid):
+        assert p2 == nullid, b'TODO merging support'
         self.git.head.set_target(gitutil.togitnode(p1))
 
     @util.propertycache
@@ -102,7 +102,7 @@ 
 
     def parents(self):
         # TODO how on earth do we find p2 if a merge is in flight?
-        return self.p1(), nodemod.nullid
+        return self.p1(), nullid
 
     def __iter__(self):
         return (pycompat.fsencode(f.path) for f in self.git.index)
diff --git a/hgext/fastannotate/formatter.py b/hgext/fastannotate/formatter.py
--- a/hgext/fastannotate/formatter.py
+++ b/hgext/fastannotate/formatter.py
@@ -6,9 +6,12 @@ 
 # GNU General Public License version 2 or any later version.
 from __future__ import absolute_import
 
+from mercurial.node import (
+    hex,
+    short,
+)
 from mercurial import (
     encoding,
-    node,
     pycompat,
     templatefilters,
     util,
@@ -116,9 +119,9 @@ 
     @util.propertycache
     def _hexfunc(self):
         if self.ui.debugflag or self.opts.get(b'long_hash'):
-            return node.hex
+            return hex
         else:
-            return node.short
+            return short
 
     def end(self):
         pass
@@ -168,7 +171,7 @@ 
 
     @util.propertycache
     def _hexfunc(self):
-        return node.hex
+        return hex
 
     def end(self):
         self.ui.write(b'\n]\n')
diff --git a/hgext/fastannotate/context.py b/hgext/fastannotate/context.py
--- a/hgext/fastannotate/context.py
+++ b/hgext/fastannotate/context.py
@@ -17,12 +17,16 @@ 
     open,
     setattr,
 )
+from mercurial.node import (
+    bin,
+    hex,
+    short,
+)
 from mercurial import (
     error,
     linelog as linelogmod,
     lock as lockmod,
     mdiff,
-    node,
     pycompat,
     scmutil,
     util,
@@ -150,7 +154,7 @@ 
     diffoptstr = stringutil.pprint(
         sorted((k, getattr(diffopts, k)) for k in mdiff.diffopts.defaults)
     )
-    return node.hex(hashutil.sha1(diffoptstr).digest())[:6]
+    return hex(hashutil.sha1(diffoptstr).digest())[:6]
 
 
 _defaultdiffopthash = hashdiffopts(mdiff.defaultopts)
@@ -308,7 +312,7 @@ 
         # command could give us a revision number even if the user passes a
         # commit hash.
         if isinstance(rev, int):
-            rev = node.hex(self.repo.changelog.node(rev))
+            rev = hex(self.repo.changelog.node(rev))
 
         # fast path: if rev is in the main branch already
         directly, revfctx = self.canannotatedirectly(rev)
@@ -493,7 +497,7 @@ 
         result = True
         f = None
         if not isinstance(rev, int) and rev is not None:
-            hsh = {20: bytes, 40: node.bin}.get(len(rev), lambda x: None)(rev)
+            hsh = {20: bytes, 40: bin}.get(len(rev), lambda x: None)(rev)
             if hsh is not None and (hsh, self.path) in self.revmap:
                 f = hsh
         if f is None:
@@ -598,7 +602,7 @@ 
                             self.ui.debug(
                                 b'fastannotate: reading %s line #%d '
                                 b'to resolve lines %r\n'
-                                % (node.short(hsh), linenum, idxs)
+                                % (short(hsh), linenum, idxs)
                             )
                         fctx = self._resolvefctx(hsh, revmap.rev2path(rev))
                         lines = mdiff.splitnewlines(fctx.data())
@@ -611,7 +615,7 @@ 
             # run the annotate and the lines should match to the file content
             self.ui.debug(
                 b'fastannotate: annotate %s to resolve lines\n'
-                % node.short(hsh)
+                % short(hsh)
             )
             linelog.annotate(rev)
             fctx = self._resolvefctx(hsh, revmap.rev2path(rev))
@@ -641,11 +645,11 @@ 
         llrev = self.revmap.hsh2rev(hsh)
         if not llrev:
             raise faerror.CorruptedFileError(
-                b'%s is not in revmap' % node.hex(hsh)
+                b'%s is not in revmap' % hex(hsh)
             )
         if (self.revmap.rev2flag(llrev) & revmapmod.sidebranchflag) != 0:
             raise faerror.CorruptedFileError(
-                b'%s is not in revmap mainbranch' % node.hex(hsh)
+                b'%s is not in revmap mainbranch' % hex(hsh)
             )
         self.linelog.annotate(llrev)
         result = [
diff --git a/hgext/convert/hg.py b/hgext/convert/hg.py
--- a/hgext/convert/hg.py
+++ b/hgext/convert/hg.py
@@ -24,6 +24,12 @@ 
 
 from mercurial.i18n import _
 from mercurial.pycompat import open
+from mercurial.node import (
+    bin,
+    hex,
+    nullhex,
+    nullid,
+)
 from mercurial import (
     bookmarks,
     context,
@@ -32,7 +38,6 @@ 
     hg,
     lock as lockmod,
     merge as mergemod,
-    node as nodemod,
     phases,
     pycompat,
     scmutil,
@@ -155,7 +160,7 @@ 
                 continue
             revid = revmap.get(source.lookuprev(s[0]))
             if not revid:
-                if s[0] == nodemod.nullhex:
+                if s[0] == nullhex:
                     revid = s[0]
                 else:
                     # missing, but keep for hash stability
@@ -174,7 +179,7 @@ 
 
             revid = s[0]
             subpath = s[1]
-            if revid != nodemod.nullhex:
+            if revid != nullhex:
                 revmap = self.subrevmaps.get(subpath)
                 if revmap is None:
                     revmap = mapfile(
@@ -295,13 +300,13 @@ 
         parents = pl
         nparents = len(parents)
         if self.filemapmode and nparents == 1:
-            m1node = self.repo.changelog.read(nodemod.bin(parents[0]))[0]
+            m1node = self.repo.changelog.read(bin(parents[0]))[0]
             parent = parents[0]
 
         if len(parents) < 2:
-            parents.append(nodemod.nullid)
+            parents.append(nullid)
         if len(parents) < 2:
-            parents.append(nodemod.nullid)
+            parents.append(nullid)
         p2 = parents.pop(0)
 
         text = commit.desc
@@ -332,12 +337,12 @@ 
 
             # Only transplant stores its reference in binary
             if label == b'transplant_source':
-                node = nodemod.hex(node)
+                node = hex(node)
 
             newrev = revmap.get(node)
             if newrev is not None:
                 if label == b'transplant_source':
-                    newrev = nodemod.bin(newrev)
+                    newrev = bin(newrev)
 
                 extra[label] = newrev
 
@@ -351,7 +356,7 @@ 
             p2 = parents.pop(0)
             p1ctx = self.repo[p1]
             p2ctx = None
-            if p2 != nodemod.nullid:
+            if p2 != nullid:
                 p2ctx = self.repo[p2]
             fileset = set(files)
             if full:
@@ -389,7 +394,7 @@ 
                     origctx = commit.ctx
                 else:
                     origctx = None
-                node = nodemod.hex(self.repo.commitctx(ctx, origctx=origctx))
+                node = hex(self.repo.commitctx(ctx, origctx=origctx))
 
                 # If the node value has changed, but the phase is lower than
                 # draft, set it back to draft since it hasn't been exposed
@@ -406,7 +411,7 @@ 
 
         if self.filemapmode and nparents == 1:
             man = self.repo.manifestlog.getstorage(b'')
-            mnode = self.repo.changelog.read(nodemod.bin(p2))[0]
+            mnode = self.repo.changelog.read(bin(p2))[0]
             closed = b'close' in commit.extra
             if not closed and not man.cmp(m1node, man.revision(mnode)):
                 self.ui.status(_(b"filtering out empty revision\n"))
@@ -416,7 +421,7 @@ 
 
     def puttags(self, tags):
         tagparent = self.repo.branchtip(self.tagsbranch, ignoremissing=True)
-        tagparent = tagparent or nodemod.nullid
+        tagparent = tagparent or nullid
 
         oldlines = set()
         for branch, heads in pycompat.iteritems(self.repo.branchmap()):
@@ -468,7 +473,7 @@ 
             extra,
         )
         node = self.repo.commitctx(ctx)
-        return nodemod.hex(node), nodemod.hex(tagparent)
+        return hex(node), hex(tagparent)
 
     def setfilemapmode(self, active):
         self.filemapmode = active
@@ -484,7 +489,7 @@ 
             self.ui.status(_(b"updating bookmarks\n"))
             destmarks = self.repo._bookmarks
             changes = [
-                (bookmark, nodemod.bin(updatedbookmark[bookmark]))
+                (bookmark, bin(updatedbookmark[bookmark]))
                 for bookmark in updatedbookmark
             ]
             destmarks.applychanges(self.repo, tr, changes)
@@ -577,7 +582,7 @@ 
         return [p for p in ctx.parents() if p and self.keep(p.node())]
 
     def getheads(self):
-        return [nodemod.hex(h) for h in self._heads if self.keep(h)]
+        return [hex(h) for h in self._heads if self.keep(h)]
 
     def getfile(self, name, rev):
         try:
@@ -679,7 +684,7 @@ 
             if self.repo.tagtype(t[0]) == b'global'
         ]
         return {
-            name: nodemod.hex(node) for name, node in tags if self.keep(node)
+            name: hex(node) for name, node in tags if self.keep(node)
         }
 
     def getchangedfiles(self, rev, i):
@@ -718,7 +723,7 @@ 
 
     def lookuprev(self, rev):
         try:
-            return nodemod.hex(self.repo.lookup(rev))
+            return hex(self.repo.lookup(rev))
         except (error.RepoError, error.LookupError):
             return None
 
diff --git a/hgext/convert/git.py b/hgext/convert/git.py
--- a/hgext/convert/git.py
+++ b/hgext/convert/git.py
@@ -9,10 +9,10 @@ 
 import os
 
 from mercurial.i18n import _
+from mercurial.node import nullhex
 from mercurial import (
     config,
     error,
-    node as nodemod,
     pycompat,
 )
 
@@ -192,7 +192,7 @@ 
         return heads
 
     def catfile(self, rev, ftype):
-        if rev == nodemod.nullhex:
+        if rev == nullhex:
             raise IOError
         self.catfilepipe[0].write(rev + b'\n')
         self.catfilepipe[0].flush()
@@ -214,7 +214,7 @@ 
         return data
 
     def getfile(self, name, rev):
-        if rev == nodemod.nullhex:
+        if rev == nullhex:
             return None, None
         if name == b'.hgsub':
             data = b'\n'.join([m.hgsub() for m in self.submoditer()])
@@ -228,7 +228,7 @@ 
         return data, mode
 
     def submoditer(self):
-        null = nodemod.nullhex
+        null = nullhex
         for m in sorted(self.submodules, key=lambda p: p.path):
             if m.node != null:
                 yield m
@@ -316,7 +316,7 @@ 
                 subexists[0] = True
                 if entry[4] == b'D' or renamesource:
                     subdeleted[0] = True
-                    changes.append((b'.hgsub', nodemod.nullhex))
+                    changes.append((b'.hgsub', nullhex))
                 else:
                     changes.append((b'.hgsub', b''))
             elif entry[1] == b'160000' or entry[0] == b':160000':
@@ -324,7 +324,7 @@ 
                     subexists[0] = True
             else:
                 if renamesource:
-                    h = nodemod.nullhex
+                    h = nullhex
                 self.modecache[(f, h)] = (p and b"x") or (s and b"l") or b""
                 changes.append((f, h))
 
@@ -361,7 +361,7 @@ 
 
         if subexists[0]:
             if subdeleted[0]:
-                changes.append((b'.hgsubstate', nodemod.nullhex))
+                changes.append((b'.hgsubstate', nullhex))
             else:
                 self.retrievegitmodules(version)
                 changes.append((b'.hgsubstate', b''))
diff --git a/hgext/absorb.py b/hgext/absorb.py
--- a/hgext/absorb.py
+++ b/hgext/absorb.py
@@ -36,6 +36,11 @@ 
 import collections
 
 from mercurial.i18n import _
+from mercurial.node import (
+    hex,
+    nullid,
+    short,
+)
 from mercurial import (
     cmdutil,
     commands,
@@ -44,7 +49,6 @@ 
     error,
     linelog,
     mdiff,
-    node,
     obsolete,
     patch,
     phases,
@@ -102,7 +106,7 @@ 
         return b''
 
     def node(self):
-        return node.nullid
+        return nullid
 
 
 def uniq(lst):
@@ -367,7 +371,7 @@ 
                 idx = (max(rev - 1, 0)) // 2
                 self.ui.write(
                     _(b'%s: chunk %d:%d -> %d lines\n')
-                    % (node.short(self.fctxs[idx].node()), a1, a2, len(blines))
+                    % (short(self.fctxs[idx].node()), a1, a2, len(blines))
                 )
             self.linelog.replacelines(rev, a1, a2, b1, b2)
         if self.opts.get(b'edit_lines', False):
@@ -486,7 +490,7 @@ 
             editortext += _(b'HG: %s/%s %s %s\n') % (
                 b'|' * i,
                 b'-' * (len(visiblefctxs) - i + 1),
-                node.short(f.node()),
+                short(f.node()),
                 f.description().split(b'\n', 1)[0],
             )
         editortext += _(b'HG: %s\n') % (b'|' * len(visiblefctxs))
@@ -816,7 +820,7 @@ 
         if self.ui.debugflag:
             return b'%d:%s' % (ctx.rev(), ctx.hex())
         else:
-            return b'%d:%s' % (ctx.rev(), node.short(ctx.node()))
+            return b'%d:%s' % (ctx.rev(), short(ctx.node()))
 
     def _getnewfilecontents(self, ctx):
         """(ctx) -> {path: str}
@@ -849,7 +853,7 @@ 
                 changes.append((name, hsh))
                 if self.ui.verbose:
                     self.ui.write(
-                        _(b'moving bookmark %s to %s\n') % (name, node.hex(hsh))
+                        _(b'moving bookmark %s to %s\n') % (name, hex(hsh))
                     )
             else:
                 changes.append((name, None))
@@ -920,7 +924,7 @@ 
         the commit is a clone from ctx, with a (optionally) different p1, and
         different file contents replaced by memworkingcopy.
         """
-        parents = p1 and (p1, node.nullid)
+        parents = p1 and (p1, nullid)
         extra = ctx.extra()
         if self._useobsolete and self.ui.configbool(b'absorb', b'add-noise'):
             extra[b'absorb_source'] = ctx.hex()
diff --git a/contrib/undumprevlog b/contrib/undumprevlog
--- a/contrib/undumprevlog
+++ b/contrib/undumprevlog
@@ -6,9 +6,9 @@ 
 from __future__ import absolute_import, print_function
 
 import sys
+from mercurial.node import bin
 from mercurial import (
     encoding,
-    node,
     revlog,
     transaction,
     vfs as vfsmod,
@@ -31,13 +31,13 @@ 
         r = revlog.revlog(opener, f)
         procutil.stdout.write(b'%s\n' % f)
     elif l.startswith("node:"):
-        n = node.bin(l[6:-1])
+        n = bin(l[6:-1])
     elif l.startswith("linkrev:"):
         lr = int(l[9:-1])
     elif l.startswith("parents:"):
         p = l[9:-1].split()
-        p1 = node.bin(p[0])
-        p2 = node.bin(p[1])
+        p1 = bin(p[0])
+        p2 = bin(p[1])
     elif l.startswith("length:"):
         length = int(l[8:-1])
         sys.stdin.readline()  # start marker
diff --git a/contrib/dumprevlog b/contrib/dumprevlog
--- a/contrib/dumprevlog
+++ b/contrib/dumprevlog
@@ -5,9 +5,9 @@ 
 from __future__ import absolute_import, print_function
 
 import sys
+from mercurial.node import hex
 from mercurial import (
     encoding,
-    node,
     pycompat,
     revlog,
 )
@@ -38,9 +38,9 @@ 
         n = r.node(i)
         p = r.parents(n)
         d = r.revision(n)
-        printb(b"node: %s" % node.hex(n))
+        printb(b"node: %s" % hex(n))
         printb(b"linkrev: %d" % r.linkrev(i))
-        printb(b"parents: %s %s" % (node.hex(p[0]), node.hex(p[1])))
+        printb(b"parents: %s %s" % (hex(p[0]), hex(p[1])))
         printb(b"length: %d" % len(d))
         printb(b"-start-")
         printb(d)