sapling/edenscm/hgext/edrecord.py
Jun Wu 9dc21f8d0b codemod: import from the edenscm package
Summary:
D13853115 adds `edenscm/` to `sys.path` and code still uses `import mercurial`.
That has nasty problems if both `import mercurial` and
`import edenscm.mercurial` are used, because Python would think `mercurial.foo`
and `edenscm.mercurial.foo` are different modules so code like
`try: ... except mercurial.error.Foo: ...`, or `isinstance(x, mercurial.foo.Bar)`
would fail to handle the `edenscm.mercurial` version. There are also some
module-level states (ex. `extensions._extensions`) that would cause trouble if
they have multiple versions in a single process.

Change imports to use the `edenscm` so ideally the `mercurial` is no longer
imported at all. Add checks in extensions.py to catch unexpected extensions
importing modules from the old (wrong) locations when running tests.

Reviewed By: phillco

Differential Revision: D13868981

fbshipit-source-id: f4e2513766957fd81d85407994f7521a08e4de48
2019-01-29 17:25:32 -08:00

121 lines
3.8 KiB
Python

# edrecord.py
#
# Copyright 2017 Facebook, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from edenscm.mercurial import cmdutil, crecord as crecordmod, patch as patchmod, util
from edenscm.mercurial.i18n import _
testedwith = "ships-with-fb-hgext"
stringio = util.stringio
originaldorecord = cmdutil.dorecord
originalrecordfilter = cmdutil.recordfilter
def uisetup(ui):
# "editor" is otherwise not allowed as a valid option for "ui.interface"
class edrecordui(ui.__class__):
def interface(self, feature):
if feature == "chunkselector":
configvalue = self.config("ui", "interface.%s" % feature)
if configvalue == "editor":
return "editor"
elif configvalue is None:
if self.config("ui", "interface") == "editor":
return "editor"
return super(edrecordui, self).interface(feature)
ui.__class__ = edrecordui
cmdutil.recordfilter = recordfilter
cmdutil.dorecord = dorecord
def dorecord(ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts):
if ui.interface("chunkselector") != "editor":
return originaldorecord(
ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts
)
overrides = {("ui", "interactive"): True}
with ui.configoverride(overrides, "edrecord"):
originaldorecord(
ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts
)
def recordfilter(ui, headers, operation=None):
if ui.interface("chunkselector") != "editor":
return originalrecordfilter(ui, headers, operation)
# construct diff string from headers
if len(headers) == 0:
return [], {}
patch = stringio()
patch.write(crecordmod.diffhelptext)
specials = {}
for header in headers:
patch.write("#\n")
if header.special():
# this is useful for special changes, we are able to get away with
# only including the parts of headers that offer useful info
specials[header.filename()] = header
for h in header.header:
if h.startswith("index "):
# starting at 'index', the headers for binary files tend to
# stop offering useful info for the viewer
patch.write(
_(
"""\
# this modifies a binary file (all or nothing)
"""
)
)
break
if not h.startswith("diff "):
# For specials, we only care about the filename header.
# The rest can be displayed as comments
patch.write("# ")
patch.write(h)
else:
header.write(patch)
for hunk in header.hunks:
hunk.write(patch)
patcheditor = ui.config("ui", "editor.chunkselector")
if patcheditor is not None:
override = {("ui", "editor"): patcheditor}
else:
override = {}
with ui.configoverride(override):
patch = ui.edit(patch.getvalue(), "", action=(operation or "edit"))
# remove comments from patch
# if there's an empty line, add a space to it
patch = [
(line if len(line) > 0 else " ") + "\n"
for line in patch.splitlines()
if not line.startswith("#")
]
headers = patchmod.parsepatch(patch)
applied = {}
for h in headers:
if h.filename() in specials:
h = specials[h.filename()]
applied[h.filename()] = [h] + h.hunks
return (
sum([i for i in applied.itervalues() if i[0].special() or len(i) > 1], []),
{},
)