##// END OF EJS Templates
widening: duplicate generateellipsesbundle2() for widening...
Martin von Zweigbergk -
r43536:561f9bc4 default
parent child Browse files
Show More
@@ -1,360 +1,459 b''
1 # narrowbundle2.py - bundle2 extensions for narrow repository support
1 # narrowbundle2.py - bundle2 extensions for narrow repository support
2 #
2 #
3 # Copyright 2017 Google, Inc.
3 # Copyright 2017 Google, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import struct
11 import struct
12
12
13 from mercurial.i18n import _
13 from mercurial.i18n import _
14 from mercurial.node import (
14 from mercurial.node import (
15 bin,
15 bin,
16 nullid,
16 nullid,
17 )
17 )
18 from mercurial import (
18 from mercurial import (
19 bundle2,
19 bundle2,
20 changegroup,
20 changegroup,
21 error,
21 error,
22 exchange,
22 exchange,
23 localrepo,
23 localrepo,
24 narrowspec,
24 narrowspec,
25 repair,
25 repair,
26 util,
26 util,
27 wireprototypes,
27 wireprototypes,
28 )
28 )
29 from mercurial.interfaces import repository
29 from mercurial.interfaces import repository
30 from mercurial.utils import stringutil
30 from mercurial.utils import stringutil
31
31
32 _NARROWACL_SECTION = b'narrowacl'
32 _NARROWACL_SECTION = b'narrowacl'
33 _CHANGESPECPART = b'narrow:changespec'
33 _CHANGESPECPART = b'narrow:changespec'
34 _RESSPECS = b'narrow:responsespec'
34 _RESSPECS = b'narrow:responsespec'
35 _SPECPART = b'narrow:spec'
35 _SPECPART = b'narrow:spec'
36 _SPECPART_INCLUDE = b'include'
36 _SPECPART_INCLUDE = b'include'
37 _SPECPART_EXCLUDE = b'exclude'
37 _SPECPART_EXCLUDE = b'exclude'
38 _KILLNODESIGNAL = b'KILL'
38 _KILLNODESIGNAL = b'KILL'
39 _DONESIGNAL = b'DONE'
39 _DONESIGNAL = b'DONE'
40 _ELIDEDCSHEADER = b'>20s20s20sl' # cset id, p1, p2, len(text)
40 _ELIDEDCSHEADER = b'>20s20s20sl' # cset id, p1, p2, len(text)
41 _ELIDEDMFHEADER = b'>20s20s20s20sl' # manifest id, p1, p2, link id, len(text)
41 _ELIDEDMFHEADER = b'>20s20s20s20sl' # manifest id, p1, p2, link id, len(text)
42 _CSHEADERSIZE = struct.calcsize(_ELIDEDCSHEADER)
42 _CSHEADERSIZE = struct.calcsize(_ELIDEDCSHEADER)
43 _MFHEADERSIZE = struct.calcsize(_ELIDEDMFHEADER)
43 _MFHEADERSIZE = struct.calcsize(_ELIDEDMFHEADER)
44
44
45 # Serve a changegroup for a client with a narrow clone.
45 # Serve a changegroup for a client with a narrow clone.
46 def getbundlechangegrouppart_narrow(
46 def getbundlechangegrouppart_narrow(
47 bundler,
47 bundler,
48 repo,
48 repo,
49 source,
49 source,
50 bundlecaps=None,
50 bundlecaps=None,
51 b2caps=None,
51 b2caps=None,
52 heads=None,
52 heads=None,
53 common=None,
53 common=None,
54 **kwargs
54 **kwargs
55 ):
55 ):
56 assert repo.ui.configbool(b'experimental', b'narrowservebrokenellipses')
56 assert repo.ui.configbool(b'experimental', b'narrowservebrokenellipses')
57
57
58 cgversions = b2caps.get(b'changegroup')
58 cgversions = b2caps.get(b'changegroup')
59 cgversions = [
59 cgversions = [
60 v
60 v
61 for v in cgversions
61 for v in cgversions
62 if v in changegroup.supportedoutgoingversions(repo)
62 if v in changegroup.supportedoutgoingversions(repo)
63 ]
63 ]
64 if not cgversions:
64 if not cgversions:
65 raise ValueError(_(b'no common changegroup version'))
65 raise ValueError(_(b'no common changegroup version'))
66 version = max(cgversions)
66 version = max(cgversions)
67
67
68 oldinclude = sorted(filter(bool, kwargs.get(r'oldincludepats', [])))
68 oldinclude = sorted(filter(bool, kwargs.get(r'oldincludepats', [])))
69 oldexclude = sorted(filter(bool, kwargs.get(r'oldexcludepats', [])))
69 oldexclude = sorted(filter(bool, kwargs.get(r'oldexcludepats', [])))
70 newinclude = sorted(filter(bool, kwargs.get(r'includepats', [])))
70 newinclude = sorted(filter(bool, kwargs.get(r'includepats', [])))
71 newexclude = sorted(filter(bool, kwargs.get(r'excludepats', [])))
71 newexclude = sorted(filter(bool, kwargs.get(r'excludepats', [])))
72 known = {bin(n) for n in kwargs.get(r'known', [])}
72 known = {bin(n) for n in kwargs.get(r'known', [])}
73 generateellipsesbundle2(
73 generateellipsesbundle2(
74 bundler,
74 bundler,
75 repo,
75 repo,
76 oldinclude,
76 oldinclude,
77 oldexclude,
77 oldexclude,
78 newinclude,
78 newinclude,
79 newexclude,
79 newexclude,
80 version,
80 version,
81 common,
81 common,
82 heads,
82 heads,
83 known,
83 known,
84 kwargs.get(r'depth', None),
84 kwargs.get(r'depth', None),
85 )
85 )
86
86
87
87
88 def generateellipsesbundle2(
88 def generateellipsesbundle2(
89 bundler,
89 bundler,
90 repo,
90 repo,
91 oldinclude,
91 oldinclude,
92 oldexclude,
92 oldexclude,
93 newinclude,
93 newinclude,
94 newexclude,
94 newexclude,
95 version,
95 version,
96 common,
96 common,
97 heads,
97 heads,
98 known,
98 known,
99 depth,
99 depth,
100 ):
100 ):
101 newmatch = narrowspec.match(
101 newmatch = narrowspec.match(
102 repo.root, include=newinclude, exclude=newexclude
102 repo.root, include=newinclude, exclude=newexclude
103 )
103 )
104 if depth is not None:
104 if depth is not None:
105 depth = int(depth)
105 depth = int(depth)
106 if depth < 1:
106 if depth < 1:
107 raise error.Abort(_(b'depth must be positive, got %d') % depth)
107 raise error.Abort(_(b'depth must be positive, got %d') % depth)
108
108
109 heads = set(heads or repo.heads())
109 heads = set(heads or repo.heads())
110 common = set(common or [nullid])
110 common = set(common or [nullid])
111 if known and (oldinclude != newinclude or oldexclude != newexclude):
111 if known and (oldinclude != newinclude or oldexclude != newexclude):
112 # Steps:
112 # Steps:
113 # 1. Send kill for "$known & ::common"
113 # 1. Send kill for "$known & ::common"
114 #
114 #
115 # 2. Send changegroup for ::common
115 # 2. Send changegroup for ::common
116 #
116 #
117 # 3. Proceed.
117 # 3. Proceed.
118 #
118 #
119 # In the future, we can send kills for only the specific
119 # In the future, we can send kills for only the specific
120 # nodes we know should go away or change shape, and then
120 # nodes we know should go away or change shape, and then
121 # send a data stream that tells the client something like this:
121 # send a data stream that tells the client something like this:
122 #
122 #
123 # a) apply this changegroup
123 # a) apply this changegroup
124 # b) apply nodes XXX, YYY, ZZZ that you already have
124 # b) apply nodes XXX, YYY, ZZZ that you already have
125 # c) goto a
125 # c) goto a
126 #
126 #
127 # until they've built up the full new state.
127 # until they've built up the full new state.
128 # Convert to revnums and intersect with "common". The client should
128 # Convert to revnums and intersect with "common". The client should
129 # have made it a subset of "common" already, but let's be safe.
129 # have made it a subset of "common" already, but let's be safe.
130 known = set(repo.revs(b"%ln & ::%ln", known, common))
130 known = set(repo.revs(b"%ln & ::%ln", known, common))
131 # TODO: we could send only roots() of this set, and the
131 # TODO: we could send only roots() of this set, and the
132 # list of nodes in common, and the client could work out
132 # list of nodes in common, and the client could work out
133 # what to strip, instead of us explicitly sending every
133 # what to strip, instead of us explicitly sending every
134 # single node.
134 # single node.
135 deadrevs = known
135 deadrevs = known
136
136
137 def genkills():
137 def genkills():
138 for r in deadrevs:
138 for r in deadrevs:
139 yield _KILLNODESIGNAL
139 yield _KILLNODESIGNAL
140 yield repo.changelog.node(r)
140 yield repo.changelog.node(r)
141 yield _DONESIGNAL
141 yield _DONESIGNAL
142
142
143 bundler.newpart(_CHANGESPECPART, data=genkills())
143 bundler.newpart(_CHANGESPECPART, data=genkills())
144 newvisit, newfull, newellipsis = exchange._computeellipsis(
144 newvisit, newfull, newellipsis = exchange._computeellipsis(
145 repo, set(), common, known, newmatch
145 repo, set(), common, known, newmatch
146 )
146 )
147 if newvisit:
147 if newvisit:
148 packer = changegroup.getbundler(
148 packer = changegroup.getbundler(
149 version,
149 version,
150 repo,
150 repo,
151 matcher=newmatch,
151 matcher=newmatch,
152 ellipses=True,
152 ellipses=True,
153 shallow=depth is not None,
153 shallow=depth is not None,
154 ellipsisroots=newellipsis,
154 ellipsisroots=newellipsis,
155 fullnodes=newfull,
155 fullnodes=newfull,
156 )
156 )
157 cgdata = packer.generate(common, newvisit, False, b'narrow_widen')
157 cgdata = packer.generate(common, newvisit, False, b'narrow_widen')
158
158
159 part = bundler.newpart(b'changegroup', data=cgdata)
159 part = bundler.newpart(b'changegroup', data=cgdata)
160 part.addparam(b'version', version)
160 part.addparam(b'version', version)
161 if b'treemanifest' in repo.requirements:
161 if b'treemanifest' in repo.requirements:
162 part.addparam(b'treemanifest', b'1')
162 part.addparam(b'treemanifest', b'1')
163
163
164 visitnodes, relevant_nodes, ellipsisroots = exchange._computeellipsis(
164 visitnodes, relevant_nodes, ellipsisroots = exchange._computeellipsis(
165 repo, common, heads, set(), newmatch, depth=depth
165 repo, common, heads, set(), newmatch, depth=depth
166 )
166 )
167
167
168 repo.ui.debug(b'Found %d relevant revs\n' % len(relevant_nodes))
168 repo.ui.debug(b'Found %d relevant revs\n' % len(relevant_nodes))
169 if visitnodes:
169 if visitnodes:
170 packer = changegroup.getbundler(
170 packer = changegroup.getbundler(
171 version,
171 version,
172 repo,
172 repo,
173 matcher=newmatch,
173 matcher=newmatch,
174 ellipses=True,
174 ellipses=True,
175 shallow=depth is not None,
175 shallow=depth is not None,
176 ellipsisroots=ellipsisroots,
176 ellipsisroots=ellipsisroots,
177 fullnodes=relevant_nodes,
177 fullnodes=relevant_nodes,
178 )
178 )
179 cgdata = packer.generate(common, visitnodes, False, b'narrow_widen')
179 cgdata = packer.generate(common, visitnodes, False, b'narrow_widen')
180
180
181 part = bundler.newpart(b'changegroup', data=cgdata)
181 part = bundler.newpart(b'changegroup', data=cgdata)
182 part.addparam(b'version', version)
182 part.addparam(b'version', version)
183 if b'treemanifest' in repo.requirements:
183 if b'treemanifest' in repo.requirements:
184 part.addparam(b'treemanifest', b'1')
184 part.addparam(b'treemanifest', b'1')
185
185
186
186
187 def generate_ellipses_bundle2_for_widening(
188 bundler,
189 repo,
190 oldinclude,
191 oldexclude,
192 newinclude,
193 newexclude,
194 version,
195 common,
196 heads,
197 known,
198 depth,
199 ):
200 newmatch = narrowspec.match(
201 repo.root, include=newinclude, exclude=newexclude
202 )
203 if depth is not None:
204 depth = int(depth)
205 if depth < 1:
206 raise error.Abort(_(b'depth must be positive, got %d') % depth)
207
208 heads = set(heads or repo.heads())
209 common = set(common or [nullid])
210 if known and (oldinclude != newinclude or oldexclude != newexclude):
211 # Steps:
212 # 1. Send kill for "$known & ::common"
213 #
214 # 2. Send changegroup for ::common
215 #
216 # 3. Proceed.
217 #
218 # In the future, we can send kills for only the specific
219 # nodes we know should go away or change shape, and then
220 # send a data stream that tells the client something like this:
221 #
222 # a) apply this changegroup
223 # b) apply nodes XXX, YYY, ZZZ that you already have
224 # c) goto a
225 #
226 # until they've built up the full new state.
227 # Convert to revnums and intersect with "common". The client should
228 # have made it a subset of "common" already, but let's be safe.
229 known = set(repo.revs(b"%ln & ::%ln", known, common))
230 # TODO: we could send only roots() of this set, and the
231 # list of nodes in common, and the client could work out
232 # what to strip, instead of us explicitly sending every
233 # single node.
234 deadrevs = known
235
236 def genkills():
237 for r in deadrevs:
238 yield _KILLNODESIGNAL
239 yield repo.changelog.node(r)
240 yield _DONESIGNAL
241
242 bundler.newpart(_CHANGESPECPART, data=genkills())
243 newvisit, newfull, newellipsis = exchange._computeellipsis(
244 repo, set(), common, known, newmatch
245 )
246 if newvisit:
247 packer = changegroup.getbundler(
248 version,
249 repo,
250 matcher=newmatch,
251 ellipses=True,
252 shallow=depth is not None,
253 ellipsisroots=newellipsis,
254 fullnodes=newfull,
255 )
256 cgdata = packer.generate(common, newvisit, False, b'narrow_widen')
257
258 part = bundler.newpart(b'changegroup', data=cgdata)
259 part.addparam(b'version', version)
260 if b'treemanifest' in repo.requirements:
261 part.addparam(b'treemanifest', b'1')
262
263 visitnodes, relevant_nodes, ellipsisroots = exchange._computeellipsis(
264 repo, common, heads, set(), newmatch, depth=depth
265 )
266
267 repo.ui.debug(b'Found %d relevant revs\n' % len(relevant_nodes))
268 if visitnodes:
269 packer = changegroup.getbundler(
270 version,
271 repo,
272 matcher=newmatch,
273 ellipses=True,
274 shallow=depth is not None,
275 ellipsisroots=ellipsisroots,
276 fullnodes=relevant_nodes,
277 )
278 cgdata = packer.generate(common, visitnodes, False, b'narrow_widen')
279
280 part = bundler.newpart(b'changegroup', data=cgdata)
281 part.addparam(b'version', version)
282 if b'treemanifest' in repo.requirements:
283 part.addparam(b'treemanifest', b'1')
284
285
187 @bundle2.parthandler(_SPECPART, (_SPECPART_INCLUDE, _SPECPART_EXCLUDE))
286 @bundle2.parthandler(_SPECPART, (_SPECPART_INCLUDE, _SPECPART_EXCLUDE))
188 def _handlechangespec_2(op, inpart):
287 def _handlechangespec_2(op, inpart):
189 # XXX: This bundle2 handling is buggy and should be removed after hg5.2 is
288 # XXX: This bundle2 handling is buggy and should be removed after hg5.2 is
190 # released. New servers will send a mandatory bundle2 part named
289 # released. New servers will send a mandatory bundle2 part named
191 # 'Narrowspec' and will send specs as data instead of params.
290 # 'Narrowspec' and will send specs as data instead of params.
192 # Refer to issue5952 and 6019
291 # Refer to issue5952 and 6019
193 includepats = set(inpart.params.get(_SPECPART_INCLUDE, b'').splitlines())
292 includepats = set(inpart.params.get(_SPECPART_INCLUDE, b'').splitlines())
194 excludepats = set(inpart.params.get(_SPECPART_EXCLUDE, b'').splitlines())
293 excludepats = set(inpart.params.get(_SPECPART_EXCLUDE, b'').splitlines())
195 narrowspec.validatepatterns(includepats)
294 narrowspec.validatepatterns(includepats)
196 narrowspec.validatepatterns(excludepats)
295 narrowspec.validatepatterns(excludepats)
197
296
198 if not repository.NARROW_REQUIREMENT in op.repo.requirements:
297 if not repository.NARROW_REQUIREMENT in op.repo.requirements:
199 op.repo.requirements.add(repository.NARROW_REQUIREMENT)
298 op.repo.requirements.add(repository.NARROW_REQUIREMENT)
200 op.repo._writerequirements()
299 op.repo._writerequirements()
201 op.repo.setnarrowpats(includepats, excludepats)
300 op.repo.setnarrowpats(includepats, excludepats)
202 narrowspec.copytoworkingcopy(op.repo)
301 narrowspec.copytoworkingcopy(op.repo)
203
302
204
303
205 @bundle2.parthandler(_RESSPECS)
304 @bundle2.parthandler(_RESSPECS)
206 def _handlenarrowspecs(op, inpart):
305 def _handlenarrowspecs(op, inpart):
207 data = inpart.read()
306 data = inpart.read()
208 inc, exc = data.split(b'\0')
307 inc, exc = data.split(b'\0')
209 includepats = set(inc.splitlines())
308 includepats = set(inc.splitlines())
210 excludepats = set(exc.splitlines())
309 excludepats = set(exc.splitlines())
211 narrowspec.validatepatterns(includepats)
310 narrowspec.validatepatterns(includepats)
212 narrowspec.validatepatterns(excludepats)
311 narrowspec.validatepatterns(excludepats)
213
312
214 if repository.NARROW_REQUIREMENT not in op.repo.requirements:
313 if repository.NARROW_REQUIREMENT not in op.repo.requirements:
215 op.repo.requirements.add(repository.NARROW_REQUIREMENT)
314 op.repo.requirements.add(repository.NARROW_REQUIREMENT)
216 op.repo._writerequirements()
315 op.repo._writerequirements()
217 op.repo.setnarrowpats(includepats, excludepats)
316 op.repo.setnarrowpats(includepats, excludepats)
218 narrowspec.copytoworkingcopy(op.repo)
317 narrowspec.copytoworkingcopy(op.repo)
219
318
220
319
221 @bundle2.parthandler(_CHANGESPECPART)
320 @bundle2.parthandler(_CHANGESPECPART)
222 def _handlechangespec(op, inpart):
321 def _handlechangespec(op, inpart):
223 repo = op.repo
322 repo = op.repo
224 cl = repo.changelog
323 cl = repo.changelog
225
324
226 # changesets which need to be stripped entirely. either they're no longer
325 # changesets which need to be stripped entirely. either they're no longer
227 # needed in the new narrow spec, or the server is sending a replacement
326 # needed in the new narrow spec, or the server is sending a replacement
228 # in the changegroup part.
327 # in the changegroup part.
229 clkills = set()
328 clkills = set()
230
329
231 # A changespec part contains all the updates to ellipsis nodes
330 # A changespec part contains all the updates to ellipsis nodes
232 # that will happen as a result of widening or narrowing a
331 # that will happen as a result of widening or narrowing a
233 # repo. All the changes that this block encounters are ellipsis
332 # repo. All the changes that this block encounters are ellipsis
234 # nodes or flags to kill an existing ellipsis.
333 # nodes or flags to kill an existing ellipsis.
235 chunksignal = changegroup.readexactly(inpart, 4)
334 chunksignal = changegroup.readexactly(inpart, 4)
236 while chunksignal != _DONESIGNAL:
335 while chunksignal != _DONESIGNAL:
237 if chunksignal == _KILLNODESIGNAL:
336 if chunksignal == _KILLNODESIGNAL:
238 # a node used to be an ellipsis but isn't anymore
337 # a node used to be an ellipsis but isn't anymore
239 ck = changegroup.readexactly(inpart, 20)
338 ck = changegroup.readexactly(inpart, 20)
240 if cl.hasnode(ck):
339 if cl.hasnode(ck):
241 clkills.add(ck)
340 clkills.add(ck)
242 else:
341 else:
243 raise error.Abort(
342 raise error.Abort(
244 _(b'unexpected changespec node chunk type: %s') % chunksignal
343 _(b'unexpected changespec node chunk type: %s') % chunksignal
245 )
344 )
246 chunksignal = changegroup.readexactly(inpart, 4)
345 chunksignal = changegroup.readexactly(inpart, 4)
247
346
248 if clkills:
347 if clkills:
249 # preserve bookmarks that repair.strip() would otherwise strip
348 # preserve bookmarks that repair.strip() would otherwise strip
250 op._bookmarksbackup = repo._bookmarks
349 op._bookmarksbackup = repo._bookmarks
251
350
252 class dummybmstore(dict):
351 class dummybmstore(dict):
253 def applychanges(self, repo, tr, changes):
352 def applychanges(self, repo, tr, changes):
254 pass
353 pass
255
354
256 localrepo.localrepository._bookmarks.set(repo, dummybmstore())
355 localrepo.localrepository._bookmarks.set(repo, dummybmstore())
257 chgrpfile = repair.strip(
356 chgrpfile = repair.strip(
258 op.ui, repo, list(clkills), backup=True, topic=b'widen'
357 op.ui, repo, list(clkills), backup=True, topic=b'widen'
259 )
358 )
260 if chgrpfile:
359 if chgrpfile:
261 op._widen_uninterr = repo.ui.uninterruptible()
360 op._widen_uninterr = repo.ui.uninterruptible()
262 op._widen_uninterr.__enter__()
361 op._widen_uninterr.__enter__()
263 # presence of _widen_bundle attribute activates widen handler later
362 # presence of _widen_bundle attribute activates widen handler later
264 op._widen_bundle = chgrpfile
363 op._widen_bundle = chgrpfile
265 # Set the new narrowspec if we're widening. The setnewnarrowpats() method
364 # Set the new narrowspec if we're widening. The setnewnarrowpats() method
266 # will currently always be there when using the core+narrowhg server, but
365 # will currently always be there when using the core+narrowhg server, but
267 # other servers may include a changespec part even when not widening (e.g.
366 # other servers may include a changespec part even when not widening (e.g.
268 # because we're deepening a shallow repo).
367 # because we're deepening a shallow repo).
269 if util.safehasattr(repo, 'setnewnarrowpats'):
368 if util.safehasattr(repo, 'setnewnarrowpats'):
270 repo.setnewnarrowpats()
369 repo.setnewnarrowpats()
271
370
272
371
273 def handlechangegroup_widen(op, inpart):
372 def handlechangegroup_widen(op, inpart):
274 """Changegroup exchange handler which restores temporarily-stripped nodes"""
373 """Changegroup exchange handler which restores temporarily-stripped nodes"""
275 # We saved a bundle with stripped node data we must now restore.
374 # We saved a bundle with stripped node data we must now restore.
276 # This approach is based on mercurial/repair.py@6ee26a53c111.
375 # This approach is based on mercurial/repair.py@6ee26a53c111.
277 repo = op.repo
376 repo = op.repo
278 ui = op.ui
377 ui = op.ui
279
378
280 chgrpfile = op._widen_bundle
379 chgrpfile = op._widen_bundle
281 del op._widen_bundle
380 del op._widen_bundle
282 vfs = repo.vfs
381 vfs = repo.vfs
283
382
284 ui.note(_(b"adding branch\n"))
383 ui.note(_(b"adding branch\n"))
285 f = vfs.open(chgrpfile, b"rb")
384 f = vfs.open(chgrpfile, b"rb")
286 try:
385 try:
287 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
386 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
288 # silence internal shuffling chatter
387 # silence internal shuffling chatter
289 override = {(b'ui', b'quiet'): True}
388 override = {(b'ui', b'quiet'): True}
290 if ui.verbose:
389 if ui.verbose:
291 override = {}
390 override = {}
292 with ui.configoverride(override):
391 with ui.configoverride(override):
293 if isinstance(gen, bundle2.unbundle20):
392 if isinstance(gen, bundle2.unbundle20):
294 with repo.transaction(b'strip') as tr:
393 with repo.transaction(b'strip') as tr:
295 bundle2.processbundle(repo, gen, lambda: tr)
394 bundle2.processbundle(repo, gen, lambda: tr)
296 else:
395 else:
297 gen.apply(
396 gen.apply(
298 repo, b'strip', b'bundle:' + vfs.join(chgrpfile), True
397 repo, b'strip', b'bundle:' + vfs.join(chgrpfile), True
299 )
398 )
300 finally:
399 finally:
301 f.close()
400 f.close()
302
401
303 # remove undo files
402 # remove undo files
304 for undovfs, undofile in repo.undofiles():
403 for undovfs, undofile in repo.undofiles():
305 try:
404 try:
306 undovfs.unlink(undofile)
405 undovfs.unlink(undofile)
307 except OSError as e:
406 except OSError as e:
308 if e.errno != errno.ENOENT:
407 if e.errno != errno.ENOENT:
309 ui.warn(
408 ui.warn(
310 _(b'error removing %s: %s\n')
409 _(b'error removing %s: %s\n')
311 % (undovfs.join(undofile), stringutil.forcebytestr(e))
410 % (undovfs.join(undofile), stringutil.forcebytestr(e))
312 )
411 )
313
412
314 # Remove partial backup only if there were no exceptions
413 # Remove partial backup only if there were no exceptions
315 op._widen_uninterr.__exit__(None, None, None)
414 op._widen_uninterr.__exit__(None, None, None)
316 vfs.unlink(chgrpfile)
415 vfs.unlink(chgrpfile)
317
416
318
417
319 def setup():
418 def setup():
320 """Enable narrow repo support in bundle2-related extension points."""
419 """Enable narrow repo support in bundle2-related extension points."""
321 getbundleargs = wireprototypes.GETBUNDLE_ARGUMENTS
420 getbundleargs = wireprototypes.GETBUNDLE_ARGUMENTS
322
421
323 getbundleargs[b'narrow'] = b'boolean'
422 getbundleargs[b'narrow'] = b'boolean'
324 getbundleargs[b'depth'] = b'plain'
423 getbundleargs[b'depth'] = b'plain'
325 getbundleargs[b'oldincludepats'] = b'csv'
424 getbundleargs[b'oldincludepats'] = b'csv'
326 getbundleargs[b'oldexcludepats'] = b'csv'
425 getbundleargs[b'oldexcludepats'] = b'csv'
327 getbundleargs[b'known'] = b'csv'
426 getbundleargs[b'known'] = b'csv'
328
427
329 # Extend changegroup serving to handle requests from narrow clients.
428 # Extend changegroup serving to handle requests from narrow clients.
330 origcgfn = exchange.getbundle2partsmapping[b'changegroup']
429 origcgfn = exchange.getbundle2partsmapping[b'changegroup']
331
430
332 def wrappedcgfn(*args, **kwargs):
431 def wrappedcgfn(*args, **kwargs):
333 repo = args[1]
432 repo = args[1]
334 if repo.ui.has_section(_NARROWACL_SECTION):
433 if repo.ui.has_section(_NARROWACL_SECTION):
335 kwargs = exchange.applynarrowacl(repo, kwargs)
434 kwargs = exchange.applynarrowacl(repo, kwargs)
336
435
337 if kwargs.get(r'narrow', False) and repo.ui.configbool(
436 if kwargs.get(r'narrow', False) and repo.ui.configbool(
338 b'experimental', b'narrowservebrokenellipses'
437 b'experimental', b'narrowservebrokenellipses'
339 ):
438 ):
340 getbundlechangegrouppart_narrow(*args, **kwargs)
439 getbundlechangegrouppart_narrow(*args, **kwargs)
341 else:
440 else:
342 origcgfn(*args, **kwargs)
441 origcgfn(*args, **kwargs)
343
442
344 exchange.getbundle2partsmapping[b'changegroup'] = wrappedcgfn
443 exchange.getbundle2partsmapping[b'changegroup'] = wrappedcgfn
345
444
346 # Extend changegroup receiver so client can fixup after widen requests.
445 # Extend changegroup receiver so client can fixup after widen requests.
347 origcghandler = bundle2.parthandlermapping[b'changegroup']
446 origcghandler = bundle2.parthandlermapping[b'changegroup']
348
447
349 def wrappedcghandler(op, inpart):
448 def wrappedcghandler(op, inpart):
350 origcghandler(op, inpart)
449 origcghandler(op, inpart)
351 if util.safehasattr(op, '_widen_bundle'):
450 if util.safehasattr(op, '_widen_bundle'):
352 handlechangegroup_widen(op, inpart)
451 handlechangegroup_widen(op, inpart)
353 if util.safehasattr(op, '_bookmarksbackup'):
452 if util.safehasattr(op, '_bookmarksbackup'):
354 localrepo.localrepository._bookmarks.set(
453 localrepo.localrepository._bookmarks.set(
355 op.repo, op._bookmarksbackup
454 op.repo, op._bookmarksbackup
356 )
455 )
357 del op._bookmarksbackup
456 del op._bookmarksbackup
358
457
359 wrappedcghandler.params = origcghandler.params
458 wrappedcghandler.params = origcghandler.params
360 bundle2.parthandlermapping[b'changegroup'] = wrappedcghandler
459 bundle2.parthandlermapping[b'changegroup'] = wrappedcghandler
@@ -1,160 +1,160 b''
1 # narrowwirepeer.py - passes narrow spec with unbundle command
1 # narrowwirepeer.py - passes narrow spec with unbundle command
2 #
2 #
3 # Copyright 2017 Google, Inc.
3 # Copyright 2017 Google, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 from mercurial import (
10 from mercurial import (
11 bundle2,
11 bundle2,
12 error,
12 error,
13 extensions,
13 extensions,
14 hg,
14 hg,
15 narrowspec,
15 narrowspec,
16 pycompat,
16 pycompat,
17 wireprototypes,
17 wireprototypes,
18 wireprotov1peer,
18 wireprotov1peer,
19 wireprotov1server,
19 wireprotov1server,
20 )
20 )
21
21
22 from . import narrowbundle2
22 from . import narrowbundle2
23
23
24
24
25 def uisetup():
25 def uisetup():
26 wireprotov1peer.wirepeer.narrow_widen = peernarrowwiden
26 wireprotov1peer.wirepeer.narrow_widen = peernarrowwiden
27
27
28
28
29 def reposetup(repo):
29 def reposetup(repo):
30 def wirereposetup(ui, peer):
30 def wirereposetup(ui, peer):
31 def wrapped(orig, cmd, *args, **kwargs):
31 def wrapped(orig, cmd, *args, **kwargs):
32 if cmd == b'unbundle':
32 if cmd == b'unbundle':
33 # TODO: don't blindly add include/exclude wireproto
33 # TODO: don't blindly add include/exclude wireproto
34 # arguments to unbundle.
34 # arguments to unbundle.
35 include, exclude = repo.narrowpats
35 include, exclude = repo.narrowpats
36 kwargs[r"includepats"] = b','.join(include)
36 kwargs[r"includepats"] = b','.join(include)
37 kwargs[r"excludepats"] = b','.join(exclude)
37 kwargs[r"excludepats"] = b','.join(exclude)
38 return orig(cmd, *args, **kwargs)
38 return orig(cmd, *args, **kwargs)
39
39
40 extensions.wrapfunction(peer, b'_calltwowaystream', wrapped)
40 extensions.wrapfunction(peer, b'_calltwowaystream', wrapped)
41
41
42 hg.wirepeersetupfuncs.append(wirereposetup)
42 hg.wirepeersetupfuncs.append(wirereposetup)
43
43
44
44
45 @wireprotov1server.wireprotocommand(
45 @wireprotov1server.wireprotocommand(
46 b'narrow_widen',
46 b'narrow_widen',
47 b'oldincludes oldexcludes'
47 b'oldincludes oldexcludes'
48 b' newincludes newexcludes'
48 b' newincludes newexcludes'
49 b' commonheads cgversion'
49 b' commonheads cgversion'
50 b' known ellipses',
50 b' known ellipses',
51 permission=b'pull',
51 permission=b'pull',
52 )
52 )
53 def narrow_widen(
53 def narrow_widen(
54 repo,
54 repo,
55 proto,
55 proto,
56 oldincludes,
56 oldincludes,
57 oldexcludes,
57 oldexcludes,
58 newincludes,
58 newincludes,
59 newexcludes,
59 newexcludes,
60 commonheads,
60 commonheads,
61 cgversion,
61 cgversion,
62 known,
62 known,
63 ellipses,
63 ellipses,
64 ):
64 ):
65 """wireprotocol command to send data when a narrow clone is widen. We will
65 """wireprotocol command to send data when a narrow clone is widen. We will
66 be sending a changegroup here.
66 be sending a changegroup here.
67
67
68 The current set of arguments which are required:
68 The current set of arguments which are required:
69 oldincludes: the old includes of the narrow copy
69 oldincludes: the old includes of the narrow copy
70 oldexcludes: the old excludes of the narrow copy
70 oldexcludes: the old excludes of the narrow copy
71 newincludes: the new includes of the narrow copy
71 newincludes: the new includes of the narrow copy
72 newexcludes: the new excludes of the narrow copy
72 newexcludes: the new excludes of the narrow copy
73 commonheads: list of heads which are common between the server and client
73 commonheads: list of heads which are common between the server and client
74 cgversion(maybe): the changegroup version to produce
74 cgversion(maybe): the changegroup version to produce
75 known: list of nodes which are known on the client (used in ellipses cases)
75 known: list of nodes which are known on the client (used in ellipses cases)
76 ellipses: whether to send ellipses data or not
76 ellipses: whether to send ellipses data or not
77 """
77 """
78
78
79 preferuncompressed = False
79 preferuncompressed = False
80 try:
80 try:
81
81
82 def splitpaths(data):
82 def splitpaths(data):
83 # work around ''.split(',') => ['']
83 # work around ''.split(',') => ['']
84 return data.split(b',') if data else []
84 return data.split(b',') if data else []
85
85
86 oldincludes = splitpaths(oldincludes)
86 oldincludes = splitpaths(oldincludes)
87 newincludes = splitpaths(newincludes)
87 newincludes = splitpaths(newincludes)
88 oldexcludes = splitpaths(oldexcludes)
88 oldexcludes = splitpaths(oldexcludes)
89 newexcludes = splitpaths(newexcludes)
89 newexcludes = splitpaths(newexcludes)
90 # validate the patterns
90 # validate the patterns
91 narrowspec.validatepatterns(set(oldincludes))
91 narrowspec.validatepatterns(set(oldincludes))
92 narrowspec.validatepatterns(set(newincludes))
92 narrowspec.validatepatterns(set(newincludes))
93 narrowspec.validatepatterns(set(oldexcludes))
93 narrowspec.validatepatterns(set(oldexcludes))
94 narrowspec.validatepatterns(set(newexcludes))
94 narrowspec.validatepatterns(set(newexcludes))
95
95
96 common = wireprototypes.decodelist(commonheads)
96 common = wireprototypes.decodelist(commonheads)
97 known = wireprototypes.decodelist(known)
97 known = wireprototypes.decodelist(known)
98 if ellipses == b'0':
98 if ellipses == b'0':
99 ellipses = False
99 ellipses = False
100 else:
100 else:
101 ellipses = bool(ellipses)
101 ellipses = bool(ellipses)
102 cgversion = cgversion
102 cgversion = cgversion
103
103
104 bundler = bundle2.bundle20(repo.ui)
104 bundler = bundle2.bundle20(repo.ui)
105 if not ellipses:
105 if not ellipses:
106 newmatch = narrowspec.match(
106 newmatch = narrowspec.match(
107 repo.root, include=newincludes, exclude=newexcludes
107 repo.root, include=newincludes, exclude=newexcludes
108 )
108 )
109 oldmatch = narrowspec.match(
109 oldmatch = narrowspec.match(
110 repo.root, include=oldincludes, exclude=oldexcludes
110 repo.root, include=oldincludes, exclude=oldexcludes
111 )
111 )
112 bundle2.widen_bundle(
112 bundle2.widen_bundle(
113 bundler,
113 bundler,
114 repo,
114 repo,
115 oldmatch,
115 oldmatch,
116 newmatch,
116 newmatch,
117 common,
117 common,
118 known,
118 known,
119 cgversion,
119 cgversion,
120 ellipses,
120 ellipses,
121 )
121 )
122 else:
122 else:
123 narrowbundle2.generateellipsesbundle2(
123 narrowbundle2.generate_ellipses_bundle2_for_widening(
124 bundler,
124 bundler,
125 repo,
125 repo,
126 oldincludes,
126 oldincludes,
127 oldexcludes,
127 oldexcludes,
128 newincludes,
128 newincludes,
129 newexcludes,
129 newexcludes,
130 cgversion,
130 cgversion,
131 common,
131 common,
132 list(common),
132 list(common),
133 known,
133 known,
134 None,
134 None,
135 )
135 )
136 except error.Abort as exc:
136 except error.Abort as exc:
137 bundler = bundle2.bundle20(repo.ui)
137 bundler = bundle2.bundle20(repo.ui)
138 manargs = [(b'message', pycompat.bytestr(exc))]
138 manargs = [(b'message', pycompat.bytestr(exc))]
139 advargs = []
139 advargs = []
140 if exc.hint is not None:
140 if exc.hint is not None:
141 advargs.append((b'hint', exc.hint))
141 advargs.append((b'hint', exc.hint))
142 bundler.addpart(bundle2.bundlepart(b'error:abort', manargs, advargs))
142 bundler.addpart(bundle2.bundlepart(b'error:abort', manargs, advargs))
143 preferuncompressed = True
143 preferuncompressed = True
144
144
145 chunks = bundler.getchunks()
145 chunks = bundler.getchunks()
146 return wireprototypes.streamres(
146 return wireprototypes.streamres(
147 gen=chunks, prefer_uncompressed=preferuncompressed
147 gen=chunks, prefer_uncompressed=preferuncompressed
148 )
148 )
149
149
150
150
151 def peernarrowwiden(remote, **kwargs):
151 def peernarrowwiden(remote, **kwargs):
152 for ch in (r'commonheads', r'known'):
152 for ch in (r'commonheads', r'known'):
153 kwargs[ch] = wireprototypes.encodelist(kwargs[ch])
153 kwargs[ch] = wireprototypes.encodelist(kwargs[ch])
154
154
155 for ch in (r'oldincludes', r'newincludes', r'oldexcludes', r'newexcludes'):
155 for ch in (r'oldincludes', r'newincludes', r'oldexcludes', r'newexcludes'):
156 kwargs[ch] = b','.join(kwargs[ch])
156 kwargs[ch] = b','.join(kwargs[ch])
157
157
158 kwargs[r'ellipses'] = b'%i' % bool(kwargs[r'ellipses'])
158 kwargs[r'ellipses'] = b'%i' % bool(kwargs[r'ellipses'])
159 f = remote._callcompressable(b'narrow_widen', **kwargs)
159 f = remote._callcompressable(b'narrow_widen', **kwargs)
160 return bundle2.getunbundler(remote.ui, f)
160 return bundle2.getunbundler(remote.ui, f)
General Comments 0
You need to be logged in to leave comments. Login now