comparison hgext/hgfastobs.py @ 814:e629a4f9d498

fastobs: move the extension to the hgext dir
author Pierre-Yves David <pierre-yves.david@fb.com>
date Thu, 27 Feb 2014 17:11:09 -0800
parents hgfastobs.py@ad2060da7ffa
children 916bebf91c41
comparison
equal deleted inserted replaced
813:b49a9276ec8e 814:e629a4f9d498
1 """Extension to try and speed up transfer of obsolete markers.
2
3 Mercurial 2.6 transfers obsolete markers in the dumbest way possible:
4 it simply transfers all of them to the server on every
5 operation. While this /works/, it's not ideal because it's a large
6 amount of extra data for users to pull down (1.9M for the 17k obsolete
7 markers in hg-crew as of this writing in late July 2013). It's also
8 frustrating because this transfer takes a nontrivial amount of time.
9
10 You can specify a strategy with the config knob
11 obsolete.syncstrategy. Current strategies are "stock" and
12 "boxfill". Default strategy is presently boxfill.
13
14 :stock: use the default strategy of mercurial explaned above
15
16 :boxfill: transmit obsolete markers which list any of transmitted changesets as
17 a successor (transitively), as well as any kill markers for dead
18 nodes descended from any of the precursors of outgoing.missing.
19
20 TODO(durin42): consider better names for sync strategies.
21 """
22 import sys
23
24 from mercurial import base85
25 from mercurial import commands
26 from mercurial import extensions
27 from mercurial import node
28 from mercurial import obsolete
29 from mercurial import exchange
30 from mercurial import revset
31 from mercurial.i18n import _
32
33 _strategies = {
34 'stock': exchange._pushobsolete,
35 }
36
37 def _strategy(name, default=False):
38 def inner(func):
39 _strategies[name] = func
40 if default:
41 _strategies[None] = func
42 return func
43 return inner
44
45 def _pushobsoletewrapper(orig, pushop):
46 stratfn = _strategies[pushop.repo.ui.config('obsolete', 'syncstrategy')]
47 return stratfn(pushop)
48
49 extensions.wrapfunction(exchange, '_pushobsolete', _pushobsoletewrapper)
50
51 def _precursors(repo, s):
52 """Precursor of a changeset"""
53 cs = set()
54 nm = repo.changelog.nodemap
55 markerbysubj = repo.obsstore.precursors
56 for r in s:
57 for p in markerbysubj.get(repo[r].node(), ()):
58 pr = nm.get(p[0])
59 if pr is not None:
60 cs.add(pr)
61 return cs
62
63 def _revsetprecursors(repo, subset, x):
64 s = revset.getset(repo, revset.baseset(range(len(repo))), x)
65 cs = _precursors(repo, s)
66 return revset.baseset([r for r in subset if r in cs])
67
68 revset.symbols['_fastobs_precursors'] = _revsetprecursors
69
70
71 @_strategy('boxfill', default=True)
72 def boxfill(pushop):
73 """The "fill in the box" strategy from the 2.6 sprint.
74
75 See the notes[0] from the 2.6 sprint for what "fill in the box"
76 means here. It's a fairly subtle algorithm, which may have
77 surprising behavior at times, but was the least-bad option
78 proposed at the sprint.
79
80 [0]: https://bitbucket.org/durin42/2.6sprint-notes/src/tip/mercurial26-obsstore-rev.1398.txt
81 """
82 repo = pushop.repo
83 remote = pushop.remote
84 outgoing = pushop.outgoing
85 urepo = pushop.repo.unfiltered()
86 # need to collect obsolete markers which list any of
87 # outgoing.missing as a successor (transitively), as well as any
88 # kill markers for dead nodes descended from any of the precursors
89 # of outgoing.missing.
90 boxedges = urepo.revs(
91 '(descendants(_fastobs_precursors(%ln)) or '
92 ' descendants(%ln)) and hidden()',
93 outgoing.missing, outgoing.missing)
94 transmit = []
95 for node in outgoing.missing:
96 transmit.extend(obsolete.precursormarkers(urepo[node]))
97 for node in boxedges:
98 transmit.extend(obsolete.successormarkers(urepo[node]))
99 transmit = list(set(transmit))
100 xmit, total = len(transmit), len(repo.obsstore._all)
101 repo.ui.status(
102 'boxpush: about to transmit %d obsolete markers (%d markers total)\n'
103 % (xmit, total))
104 parts, size, chunk = [], 0, 0
105 def transmitmarks():
106 repo.ui.note(
107 'boxpush: sending a chunk of obsolete markers\n')
108 data = ''.join([obsolete._pack('>B', obsolete._fmversion)] + parts)
109 remote.pushkey('obsolete', 'dump-%d' % chunk, '',
110 base85.b85encode(data))
111
112 for marker in transmit:
113 enc = obsolete._encodeonemarker(_markertuple(marker))
114 parts.append(enc)
115 size += len(enc)
116 if size > obsolete._maxpayload:
117 transmitmarks()
118 parts, size = [], 0
119 chunk += 1
120 if parts:
121 transmitmarks()
122
123 def _markertuple(marker):
124 return marker._data