|
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) |