mirror of
https://github.com/facebook/sapling.git
synced 2024-10-11 09:17:30 +03:00
cc95aa58ff
Summary: Fix lint errors and make the test pass. Test Plan: arc unit Reviewers: #sourcecontrol, sid0 Reviewed By: sid0 Subscribers: mjpieters Differential Revision: https://phabricator.intern.facebook.com/D5142477 Signature: t1:5142477:1496173824:bf941d895d3259fc788c09d4fefca7dddd11a33b
201 lines
7.4 KiB
Python
201 lines
7.4 KiB
Python
""" This extension provides direct access
|
|
It is the ability to refer and access hidden sha in commands provided that you
|
|
know their value.
|
|
For example hg log -r xxx where xxx is a commit has should work whether xxx is
|
|
hidden or not as we assume that the user knows what he is doing when referring
|
|
to xxx.
|
|
"""
|
|
from mercurial import extensions
|
|
from mercurial import repoview
|
|
from mercurial import branchmap
|
|
from mercurial import registrar
|
|
from mercurial import revset
|
|
from mercurial import error
|
|
from mercurial import commands
|
|
from mercurial import hg
|
|
from mercurial import util
|
|
from mercurial.i18n import _
|
|
|
|
cmdtable = {}
|
|
|
|
if util.safehasattr(registrar, 'command'):
|
|
command = registrar.command(cmdtable)
|
|
else: # compat with hg < 4.3
|
|
from mercurial import cmdutil
|
|
command = cmdutil.command(cmdtable)
|
|
|
|
# By default, all the commands have directaccess with warnings
|
|
# List of commands that have no directaccess and directaccess with no warning
|
|
directaccesslevel = [
|
|
# Format:
|
|
# ('nowarning', 'evolve', 'prune'),
|
|
# means: no directaccess warning, for the command in evolve named prune
|
|
#
|
|
# ('error', None, 'serve'),
|
|
# means: no directaccess for the command in core named serve
|
|
#
|
|
# The list is ordered alphabetically by command names, starting with all
|
|
# the commands in core then all the commands in the extensions
|
|
#
|
|
# The general guideline is:
|
|
# - remove directaccess warnings for read only commands
|
|
# - no direct access for commands with consequences outside of the repo
|
|
# - leave directaccess warnings for all the other commands
|
|
#
|
|
('nowarning', None, 'annotate'),
|
|
('nowarning', None, 'archive'),
|
|
('nowarning', None, 'bisect'),
|
|
('nowarning', None, 'bookmarks'),
|
|
('nowarning', None, 'bundle'),
|
|
('nowarning', None, 'cat'),
|
|
('nowarning', None, 'diff'),
|
|
('nowarning', None, 'export'),
|
|
('nowarning', None, 'identify'),
|
|
('nowarning', None, 'incoming'),
|
|
('nowarning', None, 'log'),
|
|
('nowarning', None, 'manifest'),
|
|
('error', None, 'outgoing'), # confusing if push errors and not outgoing
|
|
('error', None, 'push'), # destructive
|
|
('nowarning', None, 'revert'),
|
|
('error', None, 'serve'),
|
|
('nowarning', None, 'tags'),
|
|
('nowarning', None, 'unbundle'),
|
|
('nowarning', None, 'update'),
|
|
]
|
|
|
|
def reposetup(ui, repo):
|
|
repo._explicitaccess = set()
|
|
|
|
def _computehidden(repo):
|
|
hidden = repoview.filterrevs(repo, 'visible')
|
|
cl = repo.changelog
|
|
dynamic = hidden & repo._explicitaccess
|
|
if dynamic:
|
|
blocked = cl.ancestors(dynamic, inclusive=True)
|
|
hidden = frozenset(r for r in hidden if r not in blocked)
|
|
return hidden
|
|
|
|
def setupdirectaccess():
|
|
""" Add two new filtername that behave like visible to provide direct access
|
|
and direct access with warning. Wraps the commands to setup direct access
|
|
"""
|
|
repoview.filtertable.update({'visible-directaccess-nowarn': _computehidden})
|
|
repoview.filtertable.update({'visible-directaccess-warn': _computehidden})
|
|
branchmap.subsettable['visible-directaccess-nowarn'] = 'visible'
|
|
branchmap.subsettable['visible-directaccess-warn'] = 'visible'
|
|
|
|
for warn, ext, cmd in directaccesslevel:
|
|
try:
|
|
cmdtable = extensions.find(ext).cmdtable if ext else commands.table
|
|
wrapper = wrapwitherror if warn == 'error' else wrapwithoutwarning
|
|
extensions.wrapcommand(cmdtable, cmd, wrapper)
|
|
except (error.UnknownCommand, KeyError):
|
|
pass
|
|
|
|
def wrapwitherror(orig, ui, repo, *args, **kwargs):
|
|
if repo and repo.filtername == 'visible-directaccess-warn':
|
|
repo = repo.filtered('visible')
|
|
return orig(ui, repo, *args, **kwargs)
|
|
|
|
def wrapwithoutwarning(orig, ui, repo, *args, **kwargs):
|
|
if repo and repo.filtername == 'visible-directaccess-warn':
|
|
repo = repo.filtered("visible-directaccess-nowarn")
|
|
return orig(ui, repo, *args, **kwargs)
|
|
|
|
def uisetup(ui):
|
|
""" Change ordering of extensions to ensure that directaccess extsetup comes
|
|
after the one of the extensions in the loadsafter list """
|
|
# internal config: directaccess.loadsafter
|
|
loadsafter = ui.configlist('directaccess', 'loadsafter')
|
|
order = list(extensions._order)
|
|
directaccesidx = order.index('directaccess')
|
|
|
|
# The min idx for directaccess to load after all the extensions in loadafter
|
|
minidxdirectaccess = directaccesidx
|
|
|
|
for ext in loadsafter:
|
|
try:
|
|
minidxdirectaccess = max(minidxdirectaccess, order.index(ext))
|
|
except ValueError:
|
|
pass # extension not loaded
|
|
|
|
if minidxdirectaccess > directaccesidx:
|
|
order.insert(minidxdirectaccess + 1, 'directaccess')
|
|
order.remove('directaccess')
|
|
extensions._order = order
|
|
|
|
def _repository(orig, *args, **kwargs):
|
|
"""Make visible-directaccess-warn the default filter for new repos"""
|
|
repo = orig(*args, **kwargs)
|
|
return repo.filtered("visible-directaccess-warn")
|
|
|
|
def extsetup(ui):
|
|
extensions.wrapfunction(revset, 'posttreebuilthook', _posttreebuilthook)
|
|
extensions.wrapfunction(hg, 'repository', _repository)
|
|
setupdirectaccess()
|
|
|
|
hashre = util.re.compile('[0-9a-fA-F]{1,40}')
|
|
|
|
_listtuple = ('symbol', '_list')
|
|
|
|
def _ishashsymbol(symbol, maxrev):
|
|
# Returns true if symbol looks like a hash
|
|
try:
|
|
n = int(symbol)
|
|
if n <= maxrev:
|
|
# It's a rev number
|
|
return False
|
|
except ValueError:
|
|
pass
|
|
return hashre.match(symbol)
|
|
|
|
def gethashsymbols(tree, maxrev):
|
|
# Returns the list of symbols of the tree that look like hashes
|
|
# for example for the revset 3::abe3ff it will return ('abe3ff')
|
|
if not tree:
|
|
return []
|
|
|
|
results = []
|
|
if len(tree) == 2 and tree[0] == "symbol":
|
|
results.append(tree[1])
|
|
elif tree[0] == "func" and tree[1] == _listtuple:
|
|
# the optimiser will group sequence of hash request
|
|
results += tree[2][1].split('\0')
|
|
elif len(tree) >= 3:
|
|
for subtree in tree[1:]:
|
|
results += gethashsymbols(subtree, maxrev)
|
|
# return directly, we don't need to filter symbols again
|
|
return results
|
|
return [s for s in results if _ishashsymbol(s, maxrev)]
|
|
|
|
def _posttreebuilthook(orig, tree, repo):
|
|
# This is use to enabled direct hash access
|
|
# We extract the symbols that look like hashes and add them to the
|
|
# explicitaccess set
|
|
orig(tree, repo)
|
|
filternm = ""
|
|
if repo is not None:
|
|
filternm = repo.filtername
|
|
if filternm is not None and filternm.startswith('visible-directaccess'):
|
|
prelength = len(repo._explicitaccess)
|
|
accessbefore = set(repo._explicitaccess)
|
|
cl = repo.unfiltered().changelog
|
|
repo.symbols = gethashsymbols(tree, len(cl))
|
|
for node in repo.symbols:
|
|
try:
|
|
node = cl._partialmatch(node)
|
|
except error.LookupError:
|
|
node = None
|
|
if node is not None:
|
|
rev = cl.rev(node)
|
|
if rev not in repo.changelog:
|
|
repo._explicitaccess.add(rev)
|
|
if prelength != len(repo._explicitaccess):
|
|
if repo.filtername != 'visible-directaccess-nowarn':
|
|
unhiddencommits = repo._explicitaccess - accessbefore
|
|
repo.ui.warn(_("Warning: accessing hidden changesets %s "
|
|
"for write operation\n") %
|
|
(",".join([str(repo.unfiltered()[l])
|
|
for l in unhiddencommits])))
|
|
repo.invalidatevolatilesets()
|