sapling/eden/scm/edenscm/hgext/gitrevset.py
Jun Wu e5de1683e4 gitnode: try using commit extra first
Summary:
There are 3 extensions that can provide the `{gitnode}` template: "gitrevset",
"fbscmquery", "hggit". The last can read from commit extras. Fix the first two
to also read commit extras. At some point we need to unify the implementations.

Reviewed By: farnz

Differential Revision: D19198979

fbshipit-source-id: 24a0df0df4ceb7a9af9236a7f70babfd54e9802b
2019-12-20 16:14:25 -08:00

118 lines
3.5 KiB
Python

# Copyright (c) Facebook, Inc. and its affiliates.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2.
"""map a git hash to a Mercurial hash:
$ hg log -r "gitnode($HASH)"
$ hg id -r "gitnode($HASH)"
short version:
$ hg log -r "g$HASH"
$ hg id -r "g$HASH"
"""
from __future__ import absolute_import
import re
from edenscm.mercurial import error, extensions, hg, namespaces, registrar, revset
from edenscm.mercurial.i18n import _
namespacepredicate = registrar.namespacepredicate()
revsetpredicate = registrar.revsetpredicate()
githashre = re.compile("g([0-9a-fA-F]{40})")
templatekeyword = registrar.templatekeyword()
@templatekeyword("gitnode")
def showgitnode(repo, ctx, templ, **args):
"""Return the git revision corresponding to a given hg rev"""
# Try reading from commit extra first.
extra = ctx.extra()
if "hg-git-rename-source" in extra:
hexnode = extra.get("convert_revision")
if hexnode:
return hexnode
binnode = _lookup_node(repo, ctx.hex(), from_scm_type="hg")
# templates are expected to return an empty string when no
# data exists
return binnode.encode("hex") if binnode else ""
@revsetpredicate("gitnode(id)")
def gitnode(repo, subset, x):
"""``gitnode(id)``
Return the hg revision corresponding to a given git rev."""
l = revset.getargs(x, 1, 1, _("id requires one argument"))
n = revset.getstring(l[0], _("id requires a string"))
hexhgnode = _lookup_node(repo, n, from_scm_type="git")
if not hexhgnode:
raise error.RepoLookupError(_("unknown revision '%s'") % n)
rev = repo[hexhgnode].rev()
return subset.filter(lambda r: r == rev)
def _lookup_node(repo, hexnode, from_scm_type):
gitlookupnode = "_gitlookup_%s_%s" % (from_scm_type, hexnode)
# ui.expandpath('default') returns 'default' if there is no default
# path. This can be the case when command is ran on the server.
# In that case let's run lookup() command locally.
try:
result = repo.lookup(gitlookupnode)
except error.RepoLookupError:
# Note: RepoLookupError is caught here because repo.lookup()
# can throw only this exception.
peerpath = repo.ui.expandpath("default")
# sshing can cause junk 'remote: ...' output to stdout, so we need to
# redirect it temporarily so automation can parse the result easily.
oldfout = repo.ui.fout
try:
repo.baseui.fout = repo.ui.ferr
remoterepo = hg.peer(repo, {}, peerpath)
result = remoterepo.lookup(gitlookupnode)
except error.RepoError:
# Note: RepoError can be thrown by hg.peer(), RepoLookupError
# can be thrown by remoterepo.lookup(). RepoLookupError is a
# subclass of RepoError so catching just error.RepoError is enough.
return None
finally:
repo.baseui.fout = oldfout
# Sanity check - result must be 20 chars
if len(result) != 20:
return None
else:
return result
@namespacepredicate("gitrev", priority=75)
def _getnamespace(_repo):
return namespaces.namespace(
listnames=lambda repo: [], namemap=_gitlookup, nodemap=lambda repo, node: []
)
def _gitlookup(repo, gitrev):
cl = repo.changelog
tonode = cl.node
def _gittohg(githash):
return [tonode(rev) for rev in repo.revs("gitnode(%s)" % githash)]
m = githashre.match(gitrev)
if m is not None:
return _gittohg(m.group(1))
else:
return []