hgext/inhibit.py
changeset 1224 859a854cedc3
child 1225 577f5340be6f
equal deleted inserted replaced
1223:4e7da688a066 1224:859a854cedc3
       
     1 """Reduce the changesets evolution feature scope for early and noob friendly UI
       
     2 
       
     3 The full scale changeset evolution have some massive bleeding edge and it is
       
     4 very easy for people not very intimate with the concept to end up in intricate
       
     5 situation. In order to get some of the benefit sooner, this extension is
       
     6 disabling some of the less polished aspect of evolution. It should gradually
       
     7 get thinner and thinner as changeset evolution will get more polished. This
       
     8 extension is only recommended for large scale organisations. Individual user
       
     9 should probably stick on using Evolution in its current state, understand its
       
    10 concept and provide feedback
       
    11 
       
    12 The first feature provided by this extension is the ability to "inhibit"
       
    13 obsolescence markers. Obsolete revision can be cheaply brought back to life
       
    14 that way. However as the inhibitor are not fitting in an append only model,
       
    15 this is incompatible with sharing mutable history.
       
    16 """
       
    17 from mercurial import localrepo
       
    18 from mercurial import obsolete
       
    19 from mercurial import extensions
       
    20 from mercurial import cmdutil
       
    21 from mercurial import scmutil
       
    22 
       
    23 cmdtable = {}
       
    24 command = cmdutil.command(cmdtable)
       
    25 
       
    26 def reposetup(ui, repo):
       
    27 
       
    28     class obsinhibitedrepo(repo.__class__):
       
    29 
       
    30         @localrepo.storecache('obsinhibit')
       
    31         def _obsinhibit(self):
       
    32             # XXX we should make sure it is invalidated by transaction failure
       
    33             obsinhibit = set()
       
    34             raw = self.sopener.tryread('obsinhibit')
       
    35             for i in xrange(0, len(raw), 20):
       
    36                 obsinhibit.add(raw[i:i+20])
       
    37             return obsinhibit
       
    38 
       
    39     repo.__class__ = obsinhibitedrepo
       
    40 
       
    41 # obsolescence inhibitor
       
    42 ########################
       
    43 
       
    44 def _schedulewrite(tr, obsinhibit):
       
    45     """Make sure on disk content will be updated on transaction commit"""
       
    46     def writer(fp):
       
    47         """Serialize the inhibited list to disk.
       
    48         """
       
    49         raw = ''.join(obsinhibit)
       
    50         fp.write(raw)
       
    51     tr.addfilegenerator('obsinhibit', ('obsinhibit',), writer)
       
    52     tr.hookargs['obs_inbihited'] = '1'
       
    53 
       
    54 def _inhibitmarkers(repo, nodes):
       
    55     """add marker inhibitor for all obsolete revision under <nodes>
       
    56 
       
    57     Content of <nodes> and all mutable ancestors are considered. Marker for
       
    58     obsolete revision only are created.
       
    59     """
       
    60     newinhibit = repo.set('::%ln and obsolete()', nodes)
       
    61     if newinhibit:
       
    62         tr = repo.transaction('obsinhibit')
       
    63         try:
       
    64             repo._obsinhibit.update(c.node() for c in newinhibit)
       
    65             _schedulewrite(tr, repo._obsinhibit)
       
    66             repo.invalidatevolatilesets()
       
    67             tr.close()
       
    68         finally:
       
    69             tr.release()
       
    70 
       
    71 def _deinhibitmarkers(repo, nodes):
       
    72     """lift obsolescence inhibition on a set of nodes
       
    73 
       
    74     This will be triggered when inhibited nodes received new obsolescence
       
    75     markers. Otherwise the new obsolescence markers would also be inhibited.
       
    76     """
       
    77     deinhibited = repo._obsinhibit & set(nodes)
       
    78     if deinhibited:
       
    79         tr = repo.transaction('obsinhibit')
       
    80         try:
       
    81             repo._obsinhibit -= deinhibited
       
    82             _schedulewrite(tr, repo._obsinhibit)
       
    83             repo.invalidatevolatilesets()
       
    84             tr.close()
       
    85         finally:
       
    86             tr.release()
       
    87 
       
    88 def _createmarkers(orig, repo, relations, flag=0, date=None, metadata=None):
       
    89     """wrap markers create to make sure we de-inhibit target nodes"""
       
    90     # wrapping transactio to unify the one in each function
       
    91     tr = repo.transaction('add-obsolescence-marker')
       
    92     try:
       
    93         orig(repo, relations, flag, date, metadata)
       
    94         precs = (r[0].node() for r in relations)
       
    95         _deinhibitmarkers(repo, precs)
       
    96         tr.close()
       
    97     finally:
       
    98         tr.release()
       
    99 
       
   100 def extsetup(ui):
       
   101     # lets wrap the computation of the obsolete set
       
   102     # We apply inhibition there
       
   103     obsfunc = obsolete.cachefuncs['obsolete']
       
   104     def _computeobsoleteset(repo):
       
   105         """remove any inhibited nodes from the obsolete set
       
   106 
       
   107         This will trickle down to other part of mercurial (hidden, log, etc)"""
       
   108         obs = obsfunc(repo)
       
   109         getrev = repo.changelog.nodemap.get
       
   110         for n in repo._obsinhibit:
       
   111             obs.discard(getrev(n))
       
   112         return obs
       
   113     obsolete.cachefuncs['obsolete'] = _computeobsoleteset
       
   114     # drop divergence computation since it is incompatible with "light revive"
       
   115     obsolete.cachefuncs['divergent'] = lambda repo: set()
       
   116     # drop bumped computation since it is incompatible with "light revive"
       
   117     obsolete.cachefuncs['bumped'] = lambda repo: set()
       
   118     # wrap create marker to make it able to lift the inhibition
       
   119     extensions.wrapfunction(obsolete, 'createmarkers', _createmarkers)
       
   120 
       
   121 @command('debugobsinhibit', [], '')
       
   122 def cmddebugobsinhibit(ui, repo, *revs):
       
   123     """inhibit obsolescence markers effect on a set of revs"""
       
   124     nodes = (repo[r].node() for r in scmutil.revrange(repo, revs))
       
   125     _inhibitmarkers(repo, nodes)