hgext/directaccess.py
changeset 1339 0e2eb196923a
child 1347 b00c2fe51ac8
equal deleted inserted replaced
1338:77cbf9121e8a 1339:0e2eb196923a
       
     1 """ This extension provides direct access
       
     2 It is the ability to refer and access hidden sha in commands provided that you 
       
     3 know their value.
       
     4 For example hg log -r xxx where xxx is a commit has should work whether xxx is
       
     5 hidden or not as we assume that the user knows what he is doing when referring
       
     6 to xxx.
       
     7 """
       
     8 from mercurial import extensions
       
     9 from mercurial import cmdutil
       
    10 from mercurial import repoview
       
    11 from mercurial import branchmap
       
    12 from mercurial import revset
       
    13 from mercurial import error
       
    14 from mercurial import commands
       
    15 from mercurial.i18n import _
       
    16 
       
    17 cmdtable = {}
       
    18 command = cmdutil.command(cmdtable)
       
    19 
       
    20 # List of commands where no warning is shown for direct access
       
    21 directaccesslevel = [
       
    22     # warning or not, extension (None if core), command name
       
    23     (False, None, 'update'), 
       
    24     (False, None, 'export'),
       
    25     (True,  'rebase', 'rebase'),
       
    26     (False,  'evolve', 'prune'),
       
    27 ]
       
    28 
       
    29 def reposetup(ui, repo):
       
    30     repo._explicitaccess = set()
       
    31 
       
    32 def _computehidden(repo):
       
    33     hidden = repoview.computehidden(repo)
       
    34     cl = repo.changelog
       
    35     dynamic = hidden & repo._explicitaccess
       
    36     if dynamic:
       
    37         blocked = cl.ancestors(dynamic, inclusive=True)
       
    38         hidden = frozenset(r for r in hidden if r not in blocked)
       
    39     return hidden
       
    40 
       
    41 def setupdirectaccess():
       
    42     """ Add two new filtername that behave like visible to provide direct access
       
    43     and direct access with warning. Wraps the commands to setup direct access """
       
    44     repoview.filtertable.update({'visible-directaccess-nowarn': _computehidden})
       
    45     repoview.filtertable.update({'visible-directaccess-warn': _computehidden})
       
    46     branchmap.subsettable['visible-directaccess-nowarn'] = 'visible'
       
    47     branchmap.subsettable['visible-directaccess-warn'] = 'visible'
       
    48 
       
    49     for warn, ext, cmd in directaccesslevel:
       
    50         cmdtable = extensions.find(ext).cmdtable if ext else commands.table
       
    51         wrapper = wrapwithwarning if warn else wrapwithoutwarning
       
    52         try:
       
    53             extensions.wrapcommand(cmdtable, cmd, wrapper)
       
    54         except error.UnknownCommand:
       
    55             pass
       
    56 
       
    57 def wrapwithoutwarning(orig, ui, repo, *args, **kwargs):
       
    58     if repo and repo.filtername == 'visible':
       
    59         repo = repo.filtered("visible-directaccess-nowarn")
       
    60     return orig(ui, repo, *args, **kwargs)
       
    61 
       
    62 def wrapwithwarning(orig, ui, repo, *args, **kwargs):
       
    63     if repo and repo.filtername == 'visible':
       
    64         repo = repo.filtered("visible-directaccess-warn")
       
    65     return orig(ui, repo, *args, **kwargs)
       
    66 
       
    67 def extsetup(ui):
       
    68     extensions.wrapfunction(revset, 'posttreebuilthook', _posttreebuilthook)
       
    69     setupdirectaccess()
       
    70 
       
    71 def gethashsymbols(tree):
       
    72     # Returns the list of symbols of the tree that look like hashes
       
    73     # for example for the revset 3::abe3ff it will return ('abe3ff')
       
    74     if not tree:
       
    75         return []
       
    76 
       
    77     if len(tree) == 2 and tree[0] == "symbol":
       
    78         try:
       
    79             int(tree[1])
       
    80             return []
       
    81         except ValueError as e:
       
    82             return [tree[1]]
       
    83     elif len(tree) == 3:
       
    84         return gethashsymbols(tree[1]) + gethashsymbols(tree[2])
       
    85     else:
       
    86         return []
       
    87 
       
    88 def _posttreebuilthook(orig, tree, repo):
       
    89     # This is use to enabled direct hash access
       
    90     # We extract the symbols that look like hashes and add them to the
       
    91     # explicitaccess set
       
    92     orig(tree, repo)
       
    93     filternm = ""
       
    94     if repo is not None:
       
    95         filternm = repo.filtername
       
    96     if filternm is not None and filternm.startswith('visible-directaccess'):
       
    97         prelength = len(repo._explicitaccess)
       
    98         accessbefore = set(repo._explicitaccess)
       
    99         repo.symbols = gethashsymbols(tree)
       
   100         cl = repo.unfiltered().changelog
       
   101         for node in repo.symbols:
       
   102             try:
       
   103                 node = cl._partialmatch(node)
       
   104             except error.LookupError:
       
   105                 node = None
       
   106             if node is not None:
       
   107                 rev = cl.rev(node)
       
   108                 if rev not in repo.changelog:
       
   109                     repo._explicitaccess.add(rev)
       
   110         if prelength != len(repo._explicitaccess):
       
   111             if repo.filtername != 'visible-directaccess-nowarn':
       
   112                 unhiddencommits = repo._explicitaccess - accessbefore
       
   113                 repo.ui.warn( _("Warning: accessing hidden changesets %s " 
       
   114                                 "for write operation\n") % 
       
   115                                 (",".join([str(repo.unfiltered()[l]) 
       
   116                                     for l in unhiddencommits])))
       
   117             repo.invalidatevolatilesets()