##// END OF EJS Templates
widening: trust user to give full "known" set...
Martin von Zweigbergk -
r43523:a4d4e3d4 default draft
parent child Browse files
Show More
@@ -1,356 +1,354 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 ):
124 ):
125 newmatch = narrowspec.match(
125 newmatch = narrowspec.match(
126 repo.root, include=newinclude, exclude=newexclude
126 repo.root, include=newinclude, exclude=newexclude
127 )
127 )
128
128
129 common = set(common or [nullid])
129 common = set(common or [nullid])
130 # Steps:
130 # Steps:
131 # 1. Send kill for "$known & ::common"
131 # 1. Send kill for "$known & ::common"
132 #
132 #
133 # 2. Send changegroup for ::common
133 # 2. Send changegroup for ::common
134 #
134 #
135 # 3. Proceed.
135 # 3. Proceed.
136 #
136 #
137 # In the future, we can send kills for only the specific
137 # In the future, we can send kills for only the specific
138 # nodes we know should go away or change shape, and then
138 # nodes we know should go away or change shape, and then
139 # send a data stream that tells the client something like this:
139 # send a data stream that tells the client something like this:
140 #
140 #
141 # a) apply this changegroup
141 # a) apply this changegroup
142 # b) apply nodes XXX, YYY, ZZZ that you already have
142 # b) apply nodes XXX, YYY, ZZZ that you already have
143 # c) goto a
143 # c) goto a
144 #
144 #
145 # until they've built up the full new state.
145 # until they've built up the full new state.
146 # Convert to revnums and intersect with "common". The client should
146 knownrevs = {repo.changelog.rev(n) for n in known}
147 # have made it a subset of "common" already, but let's be safe.
148 known = set(repo.revs(b"%ln & ::%ln", known, common))
149 # TODO: we could send only roots() of this set, and the
147 # TODO: we could send only roots() of this set, and the
150 # list of nodes in common, and the client could work out
148 # list of nodes in common, and the client could work out
151 # what to strip, instead of us explicitly sending every
149 # what to strip, instead of us explicitly sending every
152 # single node.
150 # single node.
153 deadrevs = known
151 deadrevs = knownrevs
154
152
155 def genkills():
153 def genkills():
156 for r in deadrevs:
154 for r in deadrevs:
157 yield _KILLNODESIGNAL
155 yield _KILLNODESIGNAL
158 yield repo.changelog.node(r)
156 yield repo.changelog.node(r)
159 yield _DONESIGNAL
157 yield _DONESIGNAL
160
158
161 bundler.newpart(_CHANGESPECPART, data=genkills())
159 bundler.newpart(_CHANGESPECPART, data=genkills())
162 newvisit, newfull, newellipsis = exchange._computeellipsis(
160 newvisit, newfull, newellipsis = exchange._computeellipsis(
163 repo, set(), common, known, newmatch
161 repo, set(), common, knownrevs, newmatch
164 )
162 )
165 if newvisit:
163 if newvisit:
166 packer = changegroup.getbundler(
164 packer = changegroup.getbundler(
167 version,
165 version,
168 repo,
166 repo,
169 matcher=newmatch,
167 matcher=newmatch,
170 ellipses=True,
168 ellipses=True,
171 shallow=False,
169 shallow=False,
172 ellipsisroots=newellipsis,
170 ellipsisroots=newellipsis,
173 fullnodes=newfull,
171 fullnodes=newfull,
174 )
172 )
175 cgdata = packer.generate(common, newvisit, False, b'narrow_widen')
173 cgdata = packer.generate(common, newvisit, False, b'narrow_widen')
176
174
177 part = bundler.newpart(b'changegroup', data=cgdata)
175 part = bundler.newpart(b'changegroup', data=cgdata)
178 part.addparam(b'version', version)
176 part.addparam(b'version', version)
179 if b'treemanifest' in repo.requirements:
177 if b'treemanifest' in repo.requirements:
180 part.addparam(b'treemanifest', b'1')
178 part.addparam(b'treemanifest', b'1')
181
179
182
180
183 @bundle2.parthandler(_SPECPART, (_SPECPART_INCLUDE, _SPECPART_EXCLUDE))
181 @bundle2.parthandler(_SPECPART, (_SPECPART_INCLUDE, _SPECPART_EXCLUDE))
184 def _handlechangespec_2(op, inpart):
182 def _handlechangespec_2(op, inpart):
185 # XXX: This bundle2 handling is buggy and should be removed after hg5.2 is
183 # XXX: This bundle2 handling is buggy and should be removed after hg5.2 is
186 # released. New servers will send a mandatory bundle2 part named
184 # released. New servers will send a mandatory bundle2 part named
187 # 'Narrowspec' and will send specs as data instead of params.
185 # 'Narrowspec' and will send specs as data instead of params.
188 # Refer to issue5952 and 6019
186 # Refer to issue5952 and 6019
189 includepats = set(inpart.params.get(_SPECPART_INCLUDE, b'').splitlines())
187 includepats = set(inpart.params.get(_SPECPART_INCLUDE, b'').splitlines())
190 excludepats = set(inpart.params.get(_SPECPART_EXCLUDE, b'').splitlines())
188 excludepats = set(inpart.params.get(_SPECPART_EXCLUDE, b'').splitlines())
191 narrowspec.validatepatterns(includepats)
189 narrowspec.validatepatterns(includepats)
192 narrowspec.validatepatterns(excludepats)
190 narrowspec.validatepatterns(excludepats)
193
191
194 if not repository.NARROW_REQUIREMENT in op.repo.requirements:
192 if not repository.NARROW_REQUIREMENT in op.repo.requirements:
195 op.repo.requirements.add(repository.NARROW_REQUIREMENT)
193 op.repo.requirements.add(repository.NARROW_REQUIREMENT)
196 op.repo._writerequirements()
194 op.repo._writerequirements()
197 op.repo.setnarrowpats(includepats, excludepats)
195 op.repo.setnarrowpats(includepats, excludepats)
198 narrowspec.copytoworkingcopy(op.repo)
196 narrowspec.copytoworkingcopy(op.repo)
199
197
200
198
201 @bundle2.parthandler(_RESSPECS)
199 @bundle2.parthandler(_RESSPECS)
202 def _handlenarrowspecs(op, inpart):
200 def _handlenarrowspecs(op, inpart):
203 data = inpart.read()
201 data = inpart.read()
204 inc, exc = data.split(b'\0')
202 inc, exc = data.split(b'\0')
205 includepats = set(inc.splitlines())
203 includepats = set(inc.splitlines())
206 excludepats = set(exc.splitlines())
204 excludepats = set(exc.splitlines())
207 narrowspec.validatepatterns(includepats)
205 narrowspec.validatepatterns(includepats)
208 narrowspec.validatepatterns(excludepats)
206 narrowspec.validatepatterns(excludepats)
209
207
210 if repository.NARROW_REQUIREMENT not in op.repo.requirements:
208 if repository.NARROW_REQUIREMENT not in op.repo.requirements:
211 op.repo.requirements.add(repository.NARROW_REQUIREMENT)
209 op.repo.requirements.add(repository.NARROW_REQUIREMENT)
212 op.repo._writerequirements()
210 op.repo._writerequirements()
213 op.repo.setnarrowpats(includepats, excludepats)
211 op.repo.setnarrowpats(includepats, excludepats)
214 narrowspec.copytoworkingcopy(op.repo)
212 narrowspec.copytoworkingcopy(op.repo)
215
213
216
214
217 @bundle2.parthandler(_CHANGESPECPART)
215 @bundle2.parthandler(_CHANGESPECPART)
218 def _handlechangespec(op, inpart):
216 def _handlechangespec(op, inpart):
219 repo = op.repo
217 repo = op.repo
220 cl = repo.changelog
218 cl = repo.changelog
221
219
222 # changesets which need to be stripped entirely. either they're no longer
220 # changesets which need to be stripped entirely. either they're no longer
223 # needed in the new narrow spec, or the server is sending a replacement
221 # needed in the new narrow spec, or the server is sending a replacement
224 # in the changegroup part.
222 # in the changegroup part.
225 clkills = set()
223 clkills = set()
226
224
227 # A changespec part contains all the updates to ellipsis nodes
225 # A changespec part contains all the updates to ellipsis nodes
228 # that will happen as a result of widening or narrowing a
226 # that will happen as a result of widening or narrowing a
229 # repo. All the changes that this block encounters are ellipsis
227 # repo. All the changes that this block encounters are ellipsis
230 # nodes or flags to kill an existing ellipsis.
228 # nodes or flags to kill an existing ellipsis.
231 chunksignal = changegroup.readexactly(inpart, 4)
229 chunksignal = changegroup.readexactly(inpart, 4)
232 while chunksignal != _DONESIGNAL:
230 while chunksignal != _DONESIGNAL:
233 if chunksignal == _KILLNODESIGNAL:
231 if chunksignal == _KILLNODESIGNAL:
234 # a node used to be an ellipsis but isn't anymore
232 # a node used to be an ellipsis but isn't anymore
235 ck = changegroup.readexactly(inpart, 20)
233 ck = changegroup.readexactly(inpart, 20)
236 if cl.hasnode(ck):
234 if cl.hasnode(ck):
237 clkills.add(ck)
235 clkills.add(ck)
238 else:
236 else:
239 raise error.Abort(
237 raise error.Abort(
240 _(b'unexpected changespec node chunk type: %s') % chunksignal
238 _(b'unexpected changespec node chunk type: %s') % chunksignal
241 )
239 )
242 chunksignal = changegroup.readexactly(inpart, 4)
240 chunksignal = changegroup.readexactly(inpart, 4)
243
241
244 if clkills:
242 if clkills:
245 # preserve bookmarks that repair.strip() would otherwise strip
243 # preserve bookmarks that repair.strip() would otherwise strip
246 op._bookmarksbackup = repo._bookmarks
244 op._bookmarksbackup = repo._bookmarks
247
245
248 class dummybmstore(dict):
246 class dummybmstore(dict):
249 def applychanges(self, repo, tr, changes):
247 def applychanges(self, repo, tr, changes):
250 pass
248 pass
251
249
252 localrepo.localrepository._bookmarks.set(repo, dummybmstore())
250 localrepo.localrepository._bookmarks.set(repo, dummybmstore())
253 chgrpfile = repair.strip(
251 chgrpfile = repair.strip(
254 op.ui, repo, list(clkills), backup=True, topic=b'widen'
252 op.ui, repo, list(clkills), backup=True, topic=b'widen'
255 )
253 )
256 if chgrpfile:
254 if chgrpfile:
257 op._widen_uninterr = repo.ui.uninterruptible()
255 op._widen_uninterr = repo.ui.uninterruptible()
258 op._widen_uninterr.__enter__()
256 op._widen_uninterr.__enter__()
259 # presence of _widen_bundle attribute activates widen handler later
257 # presence of _widen_bundle attribute activates widen handler later
260 op._widen_bundle = chgrpfile
258 op._widen_bundle = chgrpfile
261 # Set the new narrowspec if we're widening. The setnewnarrowpats() method
259 # Set the new narrowspec if we're widening. The setnewnarrowpats() method
262 # will currently always be there when using the core+narrowhg server, but
260 # will currently always be there when using the core+narrowhg server, but
263 # other servers may include a changespec part even when not widening (e.g.
261 # other servers may include a changespec part even when not widening (e.g.
264 # because we're deepening a shallow repo).
262 # because we're deepening a shallow repo).
265 if util.safehasattr(repo, 'setnewnarrowpats'):
263 if util.safehasattr(repo, 'setnewnarrowpats'):
266 repo.setnewnarrowpats()
264 repo.setnewnarrowpats()
267
265
268
266
269 def handlechangegroup_widen(op, inpart):
267 def handlechangegroup_widen(op, inpart):
270 """Changegroup exchange handler which restores temporarily-stripped nodes"""
268 """Changegroup exchange handler which restores temporarily-stripped nodes"""
271 # We saved a bundle with stripped node data we must now restore.
269 # We saved a bundle with stripped node data we must now restore.
272 # This approach is based on mercurial/repair.py@6ee26a53c111.
270 # This approach is based on mercurial/repair.py@6ee26a53c111.
273 repo = op.repo
271 repo = op.repo
274 ui = op.ui
272 ui = op.ui
275
273
276 chgrpfile = op._widen_bundle
274 chgrpfile = op._widen_bundle
277 del op._widen_bundle
275 del op._widen_bundle
278 vfs = repo.vfs
276 vfs = repo.vfs
279
277
280 ui.note(_(b"adding branch\n"))
278 ui.note(_(b"adding branch\n"))
281 f = vfs.open(chgrpfile, b"rb")
279 f = vfs.open(chgrpfile, b"rb")
282 try:
280 try:
283 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
281 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
284 # silence internal shuffling chatter
282 # silence internal shuffling chatter
285 override = {(b'ui', b'quiet'): True}
283 override = {(b'ui', b'quiet'): True}
286 if ui.verbose:
284 if ui.verbose:
287 override = {}
285 override = {}
288 with ui.configoverride(override):
286 with ui.configoverride(override):
289 if isinstance(gen, bundle2.unbundle20):
287 if isinstance(gen, bundle2.unbundle20):
290 with repo.transaction(b'strip') as tr:
288 with repo.transaction(b'strip') as tr:
291 bundle2.processbundle(repo, gen, lambda: tr)
289 bundle2.processbundle(repo, gen, lambda: tr)
292 else:
290 else:
293 gen.apply(
291 gen.apply(
294 repo, b'strip', b'bundle:' + vfs.join(chgrpfile), True
292 repo, b'strip', b'bundle:' + vfs.join(chgrpfile), True
295 )
293 )
296 finally:
294 finally:
297 f.close()
295 f.close()
298
296
299 # remove undo files
297 # remove undo files
300 for undovfs, undofile in repo.undofiles():
298 for undovfs, undofile in repo.undofiles():
301 try:
299 try:
302 undovfs.unlink(undofile)
300 undovfs.unlink(undofile)
303 except OSError as e:
301 except OSError as e:
304 if e.errno != errno.ENOENT:
302 if e.errno != errno.ENOENT:
305 ui.warn(
303 ui.warn(
306 _(b'error removing %s: %s\n')
304 _(b'error removing %s: %s\n')
307 % (undovfs.join(undofile), stringutil.forcebytestr(e))
305 % (undovfs.join(undofile), stringutil.forcebytestr(e))
308 )
306 )
309
307
310 # Remove partial backup only if there were no exceptions
308 # Remove partial backup only if there were no exceptions
311 op._widen_uninterr.__exit__(None, None, None)
309 op._widen_uninterr.__exit__(None, None, None)
312 vfs.unlink(chgrpfile)
310 vfs.unlink(chgrpfile)
313
311
314
312
315 def setup():
313 def setup():
316 """Enable narrow repo support in bundle2-related extension points."""
314 """Enable narrow repo support in bundle2-related extension points."""
317 getbundleargs = wireprototypes.GETBUNDLE_ARGUMENTS
315 getbundleargs = wireprototypes.GETBUNDLE_ARGUMENTS
318
316
319 getbundleargs[b'narrow'] = b'boolean'
317 getbundleargs[b'narrow'] = b'boolean'
320 getbundleargs[b'depth'] = b'plain'
318 getbundleargs[b'depth'] = b'plain'
321 getbundleargs[b'oldincludepats'] = b'csv'
319 getbundleargs[b'oldincludepats'] = b'csv'
322 getbundleargs[b'oldexcludepats'] = b'csv'
320 getbundleargs[b'oldexcludepats'] = b'csv'
323 getbundleargs[b'known'] = b'csv'
321 getbundleargs[b'known'] = b'csv'
324
322
325 # Extend changegroup serving to handle requests from narrow clients.
323 # Extend changegroup serving to handle requests from narrow clients.
326 origcgfn = exchange.getbundle2partsmapping[b'changegroup']
324 origcgfn = exchange.getbundle2partsmapping[b'changegroup']
327
325
328 def wrappedcgfn(*args, **kwargs):
326 def wrappedcgfn(*args, **kwargs):
329 repo = args[1]
327 repo = args[1]
330 if repo.ui.has_section(_NARROWACL_SECTION):
328 if repo.ui.has_section(_NARROWACL_SECTION):
331 kwargs = exchange.applynarrowacl(repo, kwargs)
329 kwargs = exchange.applynarrowacl(repo, kwargs)
332
330
333 if kwargs.get(r'narrow', False) and repo.ui.configbool(
331 if kwargs.get(r'narrow', False) and repo.ui.configbool(
334 b'experimental', b'narrowservebrokenellipses'
332 b'experimental', b'narrowservebrokenellipses'
335 ):
333 ):
336 getbundlechangegrouppart_narrow(*args, **kwargs)
334 getbundlechangegrouppart_narrow(*args, **kwargs)
337 else:
335 else:
338 origcgfn(*args, **kwargs)
336 origcgfn(*args, **kwargs)
339
337
340 exchange.getbundle2partsmapping[b'changegroup'] = wrappedcgfn
338 exchange.getbundle2partsmapping[b'changegroup'] = wrappedcgfn
341
339
342 # Extend changegroup receiver so client can fixup after widen requests.
340 # Extend changegroup receiver so client can fixup after widen requests.
343 origcghandler = bundle2.parthandlermapping[b'changegroup']
341 origcghandler = bundle2.parthandlermapping[b'changegroup']
344
342
345 def wrappedcghandler(op, inpart):
343 def wrappedcghandler(op, inpart):
346 origcghandler(op, inpart)
344 origcghandler(op, inpart)
347 if util.safehasattr(op, '_widen_bundle'):
345 if util.safehasattr(op, '_widen_bundle'):
348 handlechangegroup_widen(op, inpart)
346 handlechangegroup_widen(op, inpart)
349 if util.safehasattr(op, '_bookmarksbackup'):
347 if util.safehasattr(op, '_bookmarksbackup'):
350 localrepo.localrepository._bookmarks.set(
348 localrepo.localrepository._bookmarks.set(
351 op.repo, op._bookmarksbackup
349 op.repo, op._bookmarksbackup
352 )
350 )
353 del op._bookmarksbackup
351 del op._bookmarksbackup
354
352
355 wrappedcghandler.params = origcghandler.params
353 wrappedcghandler.params = origcghandler.params
356 bundle2.parthandlermapping[b'changegroup'] = wrappedcghandler
354 bundle2.parthandlermapping[b'changegroup'] = wrappedcghandler
General Comments 0
You need to be logged in to leave comments. Login now