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