##// END OF EJS Templates
narrow: drop the bundle2 capability since we have server capabilities (BC)...
Pulkit Goyal -
r40786:e3792741 default
parent child Browse files
Show More
@@ -1,286 +1,276 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 extensions,
24 narrowspec,
23 narrowspec,
25 repair,
24 repair,
26 repository,
25 repository,
27 util,
26 util,
28 wireprototypes,
27 wireprototypes,
29 )
28 )
30 from mercurial.utils import (
29 from mercurial.utils import (
31 stringutil,
30 stringutil,
32 )
31 )
33
32
34 NARROWCAP = 'narrow'
35 _NARROWACL_SECTION = 'narrowhgacl'
33 _NARROWACL_SECTION = 'narrowhgacl'
36 _CHANGESPECPART = NARROWCAP + ':changespec'
34 _CHANGESPECPART = 'narrow:changespec'
37 _SPECPART = NARROWCAP + ':spec'
35 _SPECPART = 'narrow:spec'
38 _SPECPART_INCLUDE = 'include'
36 _SPECPART_INCLUDE = 'include'
39 _SPECPART_EXCLUDE = 'exclude'
37 _SPECPART_EXCLUDE = 'exclude'
40 _KILLNODESIGNAL = 'KILL'
38 _KILLNODESIGNAL = 'KILL'
41 _DONESIGNAL = 'DONE'
39 _DONESIGNAL = 'DONE'
42 _ELIDEDCSHEADER = '>20s20s20sl' # cset id, p1, p2, len(text)
40 _ELIDEDCSHEADER = '>20s20s20sl' # cset id, p1, p2, len(text)
43 _ELIDEDMFHEADER = '>20s20s20s20sl' # manifest id, p1, p2, link id, len(text)
41 _ELIDEDMFHEADER = '>20s20s20s20sl' # manifest id, p1, p2, link id, len(text)
44 _CSHEADERSIZE = struct.calcsize(_ELIDEDCSHEADER)
42 _CSHEADERSIZE = struct.calcsize(_ELIDEDCSHEADER)
45 _MFHEADERSIZE = struct.calcsize(_ELIDEDMFHEADER)
43 _MFHEADERSIZE = struct.calcsize(_ELIDEDMFHEADER)
46
44
47 # When advertising capabilities, always include narrow clone support.
48 def getrepocaps_narrow(orig, repo, **kwargs):
49 caps = orig(repo, **kwargs)
50 caps[NARROWCAP] = ['v0']
51 return caps
52
53 # Serve a changegroup for a client with a narrow clone.
45 # Serve a changegroup for a client with a narrow clone.
54 def getbundlechangegrouppart_narrow(bundler, repo, source,
46 def getbundlechangegrouppart_narrow(bundler, repo, source,
55 bundlecaps=None, b2caps=None, heads=None,
47 bundlecaps=None, b2caps=None, heads=None,
56 common=None, **kwargs):
48 common=None, **kwargs):
57 assert repo.ui.configbool('experimental', 'narrowservebrokenellipses')
49 assert repo.ui.configbool('experimental', 'narrowservebrokenellipses')
58
50
59 cgversions = b2caps.get('changegroup')
51 cgversions = b2caps.get('changegroup')
60 if cgversions: # 3.1 and 3.2 ship with an empty value
52 if cgversions: # 3.1 and 3.2 ship with an empty value
61 cgversions = [v for v in cgversions
53 cgversions = [v for v in cgversions
62 if v in changegroup.supportedoutgoingversions(repo)]
54 if v in changegroup.supportedoutgoingversions(repo)]
63 if not cgversions:
55 if not cgversions:
64 raise ValueError(_('no common changegroup version'))
56 raise ValueError(_('no common changegroup version'))
65 version = max(cgversions)
57 version = max(cgversions)
66 else:
58 else:
67 raise ValueError(_("server does not advertise changegroup version,"
59 raise ValueError(_("server does not advertise changegroup version,"
68 " can't negotiate support for ellipsis nodes"))
60 " can't negotiate support for ellipsis nodes"))
69
61
70 include = sorted(filter(bool, kwargs.get(r'includepats', [])))
62 include = sorted(filter(bool, kwargs.get(r'includepats', [])))
71 exclude = sorted(filter(bool, kwargs.get(r'excludepats', [])))
63 exclude = sorted(filter(bool, kwargs.get(r'excludepats', [])))
72 newmatch = narrowspec.match(repo.root, include=include, exclude=exclude)
64 newmatch = narrowspec.match(repo.root, include=include, exclude=exclude)
73
65
74 depth = kwargs.get(r'depth', None)
66 depth = kwargs.get(r'depth', None)
75 if depth is not None:
67 if depth is not None:
76 depth = int(depth)
68 depth = int(depth)
77 if depth < 1:
69 if depth < 1:
78 raise error.Abort(_('depth must be positive, got %d') % depth)
70 raise error.Abort(_('depth must be positive, got %d') % depth)
79
71
80 heads = set(heads or repo.heads())
72 heads = set(heads or repo.heads())
81 common = set(common or [nullid])
73 common = set(common or [nullid])
82 oldinclude = sorted(filter(bool, kwargs.get(r'oldincludepats', [])))
74 oldinclude = sorted(filter(bool, kwargs.get(r'oldincludepats', [])))
83 oldexclude = sorted(filter(bool, kwargs.get(r'oldexcludepats', [])))
75 oldexclude = sorted(filter(bool, kwargs.get(r'oldexcludepats', [])))
84 known = {bin(n) for n in kwargs.get(r'known', [])}
76 known = {bin(n) for n in kwargs.get(r'known', [])}
85 if known and (oldinclude != include or oldexclude != exclude):
77 if known and (oldinclude != include or oldexclude != exclude):
86 # Steps:
78 # Steps:
87 # 1. Send kill for "$known & ::common"
79 # 1. Send kill for "$known & ::common"
88 #
80 #
89 # 2. Send changegroup for ::common
81 # 2. Send changegroup for ::common
90 #
82 #
91 # 3. Proceed.
83 # 3. Proceed.
92 #
84 #
93 # In the future, we can send kills for only the specific
85 # In the future, we can send kills for only the specific
94 # nodes we know should go away or change shape, and then
86 # nodes we know should go away or change shape, and then
95 # send a data stream that tells the client something like this:
87 # send a data stream that tells the client something like this:
96 #
88 #
97 # a) apply this changegroup
89 # a) apply this changegroup
98 # b) apply nodes XXX, YYY, ZZZ that you already have
90 # b) apply nodes XXX, YYY, ZZZ that you already have
99 # c) goto a
91 # c) goto a
100 #
92 #
101 # until they've built up the full new state.
93 # until they've built up the full new state.
102 # Convert to revnums and intersect with "common". The client should
94 # Convert to revnums and intersect with "common". The client should
103 # have made it a subset of "common" already, but let's be safe.
95 # have made it a subset of "common" already, but let's be safe.
104 known = set(repo.revs("%ln & ::%ln", known, common))
96 known = set(repo.revs("%ln & ::%ln", known, common))
105 # TODO: we could send only roots() of this set, and the
97 # TODO: we could send only roots() of this set, and the
106 # list of nodes in common, and the client could work out
98 # list of nodes in common, and the client could work out
107 # what to strip, instead of us explicitly sending every
99 # what to strip, instead of us explicitly sending every
108 # single node.
100 # single node.
109 deadrevs = known
101 deadrevs = known
110 def genkills():
102 def genkills():
111 for r in deadrevs:
103 for r in deadrevs:
112 yield _KILLNODESIGNAL
104 yield _KILLNODESIGNAL
113 yield repo.changelog.node(r)
105 yield repo.changelog.node(r)
114 yield _DONESIGNAL
106 yield _DONESIGNAL
115 bundler.newpart(_CHANGESPECPART, data=genkills())
107 bundler.newpart(_CHANGESPECPART, data=genkills())
116 newvisit, newfull, newellipsis = exchange._computeellipsis(
108 newvisit, newfull, newellipsis = exchange._computeellipsis(
117 repo, set(), common, known, newmatch)
109 repo, set(), common, known, newmatch)
118 if newvisit:
110 if newvisit:
119 packer = changegroup.getbundler(version, repo,
111 packer = changegroup.getbundler(version, repo,
120 matcher=newmatch,
112 matcher=newmatch,
121 ellipses=True,
113 ellipses=True,
122 shallow=depth is not None,
114 shallow=depth is not None,
123 ellipsisroots=newellipsis,
115 ellipsisroots=newellipsis,
124 fullnodes=newfull)
116 fullnodes=newfull)
125 cgdata = packer.generate(common, newvisit, False, 'narrow_widen')
117 cgdata = packer.generate(common, newvisit, False, 'narrow_widen')
126
118
127 part = bundler.newpart('changegroup', data=cgdata)
119 part = bundler.newpart('changegroup', data=cgdata)
128 part.addparam('version', version)
120 part.addparam('version', version)
129 if 'treemanifest' in repo.requirements:
121 if 'treemanifest' in repo.requirements:
130 part.addparam('treemanifest', '1')
122 part.addparam('treemanifest', '1')
131
123
132 visitnodes, relevant_nodes, ellipsisroots = exchange._computeellipsis(
124 visitnodes, relevant_nodes, ellipsisroots = exchange._computeellipsis(
133 repo, common, heads, set(), newmatch, depth=depth)
125 repo, common, heads, set(), newmatch, depth=depth)
134
126
135 repo.ui.debug('Found %d relevant revs\n' % len(relevant_nodes))
127 repo.ui.debug('Found %d relevant revs\n' % len(relevant_nodes))
136 if visitnodes:
128 if visitnodes:
137 packer = changegroup.getbundler(version, repo,
129 packer = changegroup.getbundler(version, repo,
138 matcher=newmatch,
130 matcher=newmatch,
139 ellipses=True,
131 ellipses=True,
140 shallow=depth is not None,
132 shallow=depth is not None,
141 ellipsisroots=ellipsisroots,
133 ellipsisroots=ellipsisroots,
142 fullnodes=relevant_nodes)
134 fullnodes=relevant_nodes)
143 cgdata = packer.generate(common, visitnodes, False, 'narrow_widen')
135 cgdata = packer.generate(common, visitnodes, False, 'narrow_widen')
144
136
145 part = bundler.newpart('changegroup', data=cgdata)
137 part = bundler.newpart('changegroup', data=cgdata)
146 part.addparam('version', version)
138 part.addparam('version', version)
147 if 'treemanifest' in repo.requirements:
139 if 'treemanifest' in repo.requirements:
148 part.addparam('treemanifest', '1')
140 part.addparam('treemanifest', '1')
149
141
150 @bundle2.parthandler(_SPECPART, (_SPECPART_INCLUDE, _SPECPART_EXCLUDE))
142 @bundle2.parthandler(_SPECPART, (_SPECPART_INCLUDE, _SPECPART_EXCLUDE))
151 def _handlechangespec_2(op, inpart):
143 def _handlechangespec_2(op, inpart):
152 includepats = set(inpart.params.get(_SPECPART_INCLUDE, '').splitlines())
144 includepats = set(inpart.params.get(_SPECPART_INCLUDE, '').splitlines())
153 excludepats = set(inpart.params.get(_SPECPART_EXCLUDE, '').splitlines())
145 excludepats = set(inpart.params.get(_SPECPART_EXCLUDE, '').splitlines())
154 narrowspec.validatepatterns(includepats)
146 narrowspec.validatepatterns(includepats)
155 narrowspec.validatepatterns(excludepats)
147 narrowspec.validatepatterns(excludepats)
156
148
157 if not repository.NARROW_REQUIREMENT in op.repo.requirements:
149 if not repository.NARROW_REQUIREMENT in op.repo.requirements:
158 op.repo.requirements.add(repository.NARROW_REQUIREMENT)
150 op.repo.requirements.add(repository.NARROW_REQUIREMENT)
159 op.repo._writerequirements()
151 op.repo._writerequirements()
160 op.repo.setnarrowpats(includepats, excludepats)
152 op.repo.setnarrowpats(includepats, excludepats)
161
153
162 @bundle2.parthandler(_CHANGESPECPART)
154 @bundle2.parthandler(_CHANGESPECPART)
163 def _handlechangespec(op, inpart):
155 def _handlechangespec(op, inpart):
164 repo = op.repo
156 repo = op.repo
165 cl = repo.changelog
157 cl = repo.changelog
166
158
167 # changesets which need to be stripped entirely. either they're no longer
159 # changesets which need to be stripped entirely. either they're no longer
168 # needed in the new narrow spec, or the server is sending a replacement
160 # needed in the new narrow spec, or the server is sending a replacement
169 # in the changegroup part.
161 # in the changegroup part.
170 clkills = set()
162 clkills = set()
171
163
172 # A changespec part contains all the updates to ellipsis nodes
164 # A changespec part contains all the updates to ellipsis nodes
173 # that will happen as a result of widening or narrowing a
165 # that will happen as a result of widening or narrowing a
174 # repo. All the changes that this block encounters are ellipsis
166 # repo. All the changes that this block encounters are ellipsis
175 # nodes or flags to kill an existing ellipsis.
167 # nodes or flags to kill an existing ellipsis.
176 chunksignal = changegroup.readexactly(inpart, 4)
168 chunksignal = changegroup.readexactly(inpart, 4)
177 while chunksignal != _DONESIGNAL:
169 while chunksignal != _DONESIGNAL:
178 if chunksignal == _KILLNODESIGNAL:
170 if chunksignal == _KILLNODESIGNAL:
179 # a node used to be an ellipsis but isn't anymore
171 # a node used to be an ellipsis but isn't anymore
180 ck = changegroup.readexactly(inpart, 20)
172 ck = changegroup.readexactly(inpart, 20)
181 if cl.hasnode(ck):
173 if cl.hasnode(ck):
182 clkills.add(ck)
174 clkills.add(ck)
183 else:
175 else:
184 raise error.Abort(
176 raise error.Abort(
185 _('unexpected changespec node chunk type: %s') % chunksignal)
177 _('unexpected changespec node chunk type: %s') % chunksignal)
186 chunksignal = changegroup.readexactly(inpart, 4)
178 chunksignal = changegroup.readexactly(inpart, 4)
187
179
188 if clkills:
180 if clkills:
189 # preserve bookmarks that repair.strip() would otherwise strip
181 # preserve bookmarks that repair.strip() would otherwise strip
190 bmstore = repo._bookmarks
182 bmstore = repo._bookmarks
191 class dummybmstore(dict):
183 class dummybmstore(dict):
192 def applychanges(self, repo, tr, changes):
184 def applychanges(self, repo, tr, changes):
193 pass
185 pass
194 def recordchange(self, tr): # legacy version
186 def recordchange(self, tr): # legacy version
195 pass
187 pass
196 repo._bookmarks = dummybmstore()
188 repo._bookmarks = dummybmstore()
197 chgrpfile = repair.strip(op.ui, repo, list(clkills), backup=True,
189 chgrpfile = repair.strip(op.ui, repo, list(clkills), backup=True,
198 topic='widen')
190 topic='widen')
199 repo._bookmarks = bmstore
191 repo._bookmarks = bmstore
200 if chgrpfile:
192 if chgrpfile:
201 op._widen_uninterr = repo.ui.uninterruptable()
193 op._widen_uninterr = repo.ui.uninterruptable()
202 op._widen_uninterr.__enter__()
194 op._widen_uninterr.__enter__()
203 # presence of _widen_bundle attribute activates widen handler later
195 # presence of _widen_bundle attribute activates widen handler later
204 op._widen_bundle = chgrpfile
196 op._widen_bundle = chgrpfile
205 # Set the new narrowspec if we're widening. The setnewnarrowpats() method
197 # Set the new narrowspec if we're widening. The setnewnarrowpats() method
206 # will currently always be there when using the core+narrowhg server, but
198 # will currently always be there when using the core+narrowhg server, but
207 # other servers may include a changespec part even when not widening (e.g.
199 # other servers may include a changespec part even when not widening (e.g.
208 # because we're deepening a shallow repo).
200 # because we're deepening a shallow repo).
209 if util.safehasattr(repo, 'setnewnarrowpats'):
201 if util.safehasattr(repo, 'setnewnarrowpats'):
210 repo.setnewnarrowpats()
202 repo.setnewnarrowpats()
211
203
212 def handlechangegroup_widen(op, inpart):
204 def handlechangegroup_widen(op, inpart):
213 """Changegroup exchange handler which restores temporarily-stripped nodes"""
205 """Changegroup exchange handler which restores temporarily-stripped nodes"""
214 # We saved a bundle with stripped node data we must now restore.
206 # We saved a bundle with stripped node data we must now restore.
215 # This approach is based on mercurial/repair.py@6ee26a53c111.
207 # This approach is based on mercurial/repair.py@6ee26a53c111.
216 repo = op.repo
208 repo = op.repo
217 ui = op.ui
209 ui = op.ui
218
210
219 chgrpfile = op._widen_bundle
211 chgrpfile = op._widen_bundle
220 del op._widen_bundle
212 del op._widen_bundle
221 vfs = repo.vfs
213 vfs = repo.vfs
222
214
223 ui.note(_("adding branch\n"))
215 ui.note(_("adding branch\n"))
224 f = vfs.open(chgrpfile, "rb")
216 f = vfs.open(chgrpfile, "rb")
225 try:
217 try:
226 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
218 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
227 if not ui.verbose:
219 if not ui.verbose:
228 # silence internal shuffling chatter
220 # silence internal shuffling chatter
229 ui.pushbuffer()
221 ui.pushbuffer()
230 if isinstance(gen, bundle2.unbundle20):
222 if isinstance(gen, bundle2.unbundle20):
231 with repo.transaction('strip') as tr:
223 with repo.transaction('strip') as tr:
232 bundle2.processbundle(repo, gen, lambda: tr)
224 bundle2.processbundle(repo, gen, lambda: tr)
233 else:
225 else:
234 gen.apply(repo, 'strip', 'bundle:' + vfs.join(chgrpfile), True)
226 gen.apply(repo, 'strip', 'bundle:' + vfs.join(chgrpfile), True)
235 if not ui.verbose:
227 if not ui.verbose:
236 ui.popbuffer()
228 ui.popbuffer()
237 finally:
229 finally:
238 f.close()
230 f.close()
239
231
240 # remove undo files
232 # remove undo files
241 for undovfs, undofile in repo.undofiles():
233 for undovfs, undofile in repo.undofiles():
242 try:
234 try:
243 undovfs.unlink(undofile)
235 undovfs.unlink(undofile)
244 except OSError as e:
236 except OSError as e:
245 if e.errno != errno.ENOENT:
237 if e.errno != errno.ENOENT:
246 ui.warn(_('error removing %s: %s\n') %
238 ui.warn(_('error removing %s: %s\n') %
247 (undovfs.join(undofile), stringutil.forcebytestr(e)))
239 (undovfs.join(undofile), stringutil.forcebytestr(e)))
248
240
249 # Remove partial backup only if there were no exceptions
241 # Remove partial backup only if there were no exceptions
250 op._widen_uninterr.__exit__(None, None, None)
242 op._widen_uninterr.__exit__(None, None, None)
251 vfs.unlink(chgrpfile)
243 vfs.unlink(chgrpfile)
252
244
253 def setup():
245 def setup():
254 """Enable narrow repo support in bundle2-related extension points."""
246 """Enable narrow repo support in bundle2-related extension points."""
255 extensions.wrapfunction(bundle2, 'getrepocaps', getrepocaps_narrow)
256
257 getbundleargs = wireprototypes.GETBUNDLE_ARGUMENTS
247 getbundleargs = wireprototypes.GETBUNDLE_ARGUMENTS
258
248
259 getbundleargs['narrow'] = 'boolean'
249 getbundleargs['narrow'] = 'boolean'
260 getbundleargs['depth'] = 'plain'
250 getbundleargs['depth'] = 'plain'
261 getbundleargs['oldincludepats'] = 'csv'
251 getbundleargs['oldincludepats'] = 'csv'
262 getbundleargs['oldexcludepats'] = 'csv'
252 getbundleargs['oldexcludepats'] = 'csv'
263 getbundleargs['known'] = 'csv'
253 getbundleargs['known'] = 'csv'
264
254
265 # Extend changegroup serving to handle requests from narrow clients.
255 # Extend changegroup serving to handle requests from narrow clients.
266 origcgfn = exchange.getbundle2partsmapping['changegroup']
256 origcgfn = exchange.getbundle2partsmapping['changegroup']
267 def wrappedcgfn(*args, **kwargs):
257 def wrappedcgfn(*args, **kwargs):
268 repo = args[1]
258 repo = args[1]
269 if repo.ui.has_section(_NARROWACL_SECTION):
259 if repo.ui.has_section(_NARROWACL_SECTION):
270 kwargs = exchange.applynarrowacl(repo, kwargs)
260 kwargs = exchange.applynarrowacl(repo, kwargs)
271
261
272 if (kwargs.get(r'narrow', False) and
262 if (kwargs.get(r'narrow', False) and
273 repo.ui.configbool('experimental', 'narrowservebrokenellipses')):
263 repo.ui.configbool('experimental', 'narrowservebrokenellipses')):
274 getbundlechangegrouppart_narrow(*args, **kwargs)
264 getbundlechangegrouppart_narrow(*args, **kwargs)
275 else:
265 else:
276 origcgfn(*args, **kwargs)
266 origcgfn(*args, **kwargs)
277 exchange.getbundle2partsmapping['changegroup'] = wrappedcgfn
267 exchange.getbundle2partsmapping['changegroup'] = wrappedcgfn
278
268
279 # Extend changegroup receiver so client can fixup after widen requests.
269 # Extend changegroup receiver so client can fixup after widen requests.
280 origcghandler = bundle2.parthandlermapping['changegroup']
270 origcghandler = bundle2.parthandlermapping['changegroup']
281 def wrappedcghandler(op, inpart):
271 def wrappedcghandler(op, inpart):
282 origcghandler(op, inpart)
272 origcghandler(op, inpart)
283 if util.safehasattr(op, '_widen_bundle'):
273 if util.safehasattr(op, '_widen_bundle'):
284 handlechangegroup_widen(op, inpart)
274 handlechangegroup_widen(op, inpart)
285 wrappedcghandler.params = origcghandler.params
275 wrappedcghandler.params = origcghandler.params
286 bundle2.parthandlermapping['changegroup'] = wrappedcghandler
276 bundle2.parthandlermapping['changegroup'] = wrappedcghandler
@@ -1,68 +1,67 b''
1 Test attempting a narrow clone against a server that doesn't support narrowhg.
1 Test attempting a narrow clone against a server that doesn't support narrowhg.
2
2
3 $ . "$TESTDIR/narrow-library.sh"
3 $ . "$TESTDIR/narrow-library.sh"
4
4
5 $ hg init master
5 $ hg init master
6 $ cd master
6 $ cd master
7
7
8 $ for x in `$TESTDIR/seq.py 10`; do
8 $ for x in `$TESTDIR/seq.py 10`; do
9 > echo $x > "f$x"
9 > echo $x > "f$x"
10 > hg add "f$x"
10 > hg add "f$x"
11 > hg commit -m "Add $x"
11 > hg commit -m "Add $x"
12 > done
12 > done
13
13
14 $ hg serve -a localhost -p $HGPORT1 --config extensions.narrow=! -d \
14 $ hg serve -a localhost -p $HGPORT1 --config extensions.narrow=! -d \
15 > --pid-file=hg.pid
15 > --pid-file=hg.pid
16 $ cat hg.pid >> "$DAEMON_PIDS"
16 $ cat hg.pid >> "$DAEMON_PIDS"
17 $ hg serve -a localhost -p $HGPORT2 -d --pid-file=hg.pid
17 $ hg serve -a localhost -p $HGPORT2 -d --pid-file=hg.pid
18 $ cat hg.pid >> "$DAEMON_PIDS"
18 $ cat hg.pid >> "$DAEMON_PIDS"
19
19
20 Verify that narrow is advertised in the bundle2 capabilities:
20 Verify that narrow is advertised in the bundle2 capabilities:
21
21
22 $ cat >> unquote.py <<EOF
22 $ cat >> unquote.py <<EOF
23 > from __future__ import print_function
23 > from __future__ import print_function
24 > import sys
24 > import sys
25 > if sys.version[0] == '3':
25 > if sys.version[0] == '3':
26 > import urllib.parse as up
26 > import urllib.parse as up
27 > unquote = up.unquote_plus
27 > unquote = up.unquote_plus
28 > else:
28 > else:
29 > import urllib
29 > import urllib
30 > unquote = urllib.unquote_plus
30 > unquote = urllib.unquote_plus
31 > print(unquote(list(sys.stdin)[1]))
31 > print(unquote(list(sys.stdin)[1]))
32 > EOF
32 > EOF
33 $ echo hello | hg -R . serve --stdio | \
33 $ echo hello | hg -R . serve --stdio | \
34 > "$PYTHON" unquote.py | tr ' ' '\n' | grep narrow
34 > "$PYTHON" unquote.py | tr ' ' '\n' | grep narrow
35 narrow=v0
36 exp-narrow-1
35 exp-narrow-1
37
36
38 $ cd ..
37 $ cd ..
39
38
40 $ hg clone --narrow --include f1 http://localhost:$HGPORT1/ narrowclone
39 $ hg clone --narrow --include f1 http://localhost:$HGPORT1/ narrowclone
41 requesting all changes
40 requesting all changes
42 abort: server does not support narrow clones
41 abort: server does not support narrow clones
43 [255]
42 [255]
44
43
45 Make a narrow clone (via HGPORT2), then try to narrow and widen
44 Make a narrow clone (via HGPORT2), then try to narrow and widen
46 into it (from HGPORT1) to prove that narrowing is fine and widening fails
45 into it (from HGPORT1) to prove that narrowing is fine and widening fails
47 gracefully:
46 gracefully:
48 $ hg clone -r 0 --narrow --include f1 http://localhost:$HGPORT2/ narrowclone
47 $ hg clone -r 0 --narrow --include f1 http://localhost:$HGPORT2/ narrowclone
49 adding changesets
48 adding changesets
50 adding manifests
49 adding manifests
51 adding file changes
50 adding file changes
52 added 1 changesets with 1 changes to 1 files
51 added 1 changesets with 1 changes to 1 files
53 new changesets * (glob)
52 new changesets * (glob)
54 updating to branch default
53 updating to branch default
55 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
54 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
56 $ cd narrowclone
55 $ cd narrowclone
57 $ hg tracked --addexclude f2 http://localhost:$HGPORT1/
56 $ hg tracked --addexclude f2 http://localhost:$HGPORT1/
58 comparing with http://localhost:$HGPORT1/
57 comparing with http://localhost:$HGPORT1/
59 searching for changes
58 searching for changes
60 looking for local changes to affected paths
59 looking for local changes to affected paths
61
60
62 $ hg tracked --addinclude f1 http://localhost:$HGPORT1/
61 $ hg tracked --addinclude f1 http://localhost:$HGPORT1/
63 nothing to widen or narrow
62 nothing to widen or narrow
64
63
65 $ hg tracked --addinclude f9 http://localhost:$HGPORT1/
64 $ hg tracked --addinclude f9 http://localhost:$HGPORT1/
66 comparing with http://localhost:$HGPORT1/
65 comparing with http://localhost:$HGPORT1/
67 abort: server does not support narrow clones
66 abort: server does not support narrow clones
68 [255]
67 [255]
General Comments 0
You need to be logged in to leave comments. Login now