##// END OF EJS Templates
narrow: use diffmatcher to send only new filelogs in non-ellipses widening...
Pulkit Goyal -
r39701:c73c7653 default
parent child Browse files
Show More
@@ -1,335 +1,339 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,
23 extensions,
24 match as matchmod,
24 narrowspec,
25 narrowspec,
25 repair,
26 repair,
26 repository,
27 repository,
27 util,
28 util,
28 wireprototypes,
29 wireprototypes,
29 )
30 )
30 from mercurial.utils import (
31 from mercurial.utils import (
31 stringutil,
32 stringutil,
32 )
33 )
33
34
34 NARROWCAP = 'narrow'
35 NARROWCAP = 'narrow'
35 _NARROWACL_SECTION = 'narrowhgacl'
36 _NARROWACL_SECTION = 'narrowhgacl'
36 _CHANGESPECPART = NARROWCAP + ':changespec'
37 _CHANGESPECPART = NARROWCAP + ':changespec'
37 _SPECPART = NARROWCAP + ':spec'
38 _SPECPART = NARROWCAP + ':spec'
38 _SPECPART_INCLUDE = 'include'
39 _SPECPART_INCLUDE = 'include'
39 _SPECPART_EXCLUDE = 'exclude'
40 _SPECPART_EXCLUDE = 'exclude'
40 _KILLNODESIGNAL = 'KILL'
41 _KILLNODESIGNAL = 'KILL'
41 _DONESIGNAL = 'DONE'
42 _DONESIGNAL = 'DONE'
42 _ELIDEDCSHEADER = '>20s20s20sl' # cset id, p1, p2, len(text)
43 _ELIDEDCSHEADER = '>20s20s20sl' # cset id, p1, p2, len(text)
43 _ELIDEDMFHEADER = '>20s20s20s20sl' # manifest id, p1, p2, link id, len(text)
44 _ELIDEDMFHEADER = '>20s20s20s20sl' # manifest id, p1, p2, link id, len(text)
44 _CSHEADERSIZE = struct.calcsize(_ELIDEDCSHEADER)
45 _CSHEADERSIZE = struct.calcsize(_ELIDEDCSHEADER)
45 _MFHEADERSIZE = struct.calcsize(_ELIDEDMFHEADER)
46 _MFHEADERSIZE = struct.calcsize(_ELIDEDMFHEADER)
46
47
47 # When advertising capabilities, always include narrow clone support.
48 # When advertising capabilities, always include narrow clone support.
48 def getrepocaps_narrow(orig, repo, **kwargs):
49 def getrepocaps_narrow(orig, repo, **kwargs):
49 caps = orig(repo, **kwargs)
50 caps = orig(repo, **kwargs)
50 caps[NARROWCAP] = ['v0']
51 caps[NARROWCAP] = ['v0']
51 return caps
52 return caps
52
53
53 def getbundlechangegrouppart_widen(bundler, repo, source, bundlecaps=None,
54 def getbundlechangegrouppart_widen(bundler, repo, source, bundlecaps=None,
54 b2caps=None, heads=None, common=None,
55 b2caps=None, heads=None, common=None,
55 **kwargs):
56 **kwargs):
56 """Handling changegroup changegroup generation on the server when user
57 """Handling changegroup changegroup generation on the server when user
57 is widening their narrowspec"""
58 is widening their narrowspec"""
58
59
59 cgversions = b2caps.get('changegroup')
60 cgversions = b2caps.get('changegroup')
60 if cgversions: # 3.1 and 3.2 ship with an empty value
61 if cgversions: # 3.1 and 3.2 ship with an empty value
61 cgversions = [v for v in cgversions
62 cgversions = [v for v in cgversions
62 if v in changegroup.supportedoutgoingversions(repo)]
63 if v in changegroup.supportedoutgoingversions(repo)]
63 if not cgversions:
64 if not cgversions:
64 raise ValueError(_('no common changegroup version'))
65 raise ValueError(_('no common changegroup version'))
65 version = max(cgversions)
66 version = max(cgversions)
66 else:
67 else:
67 raise ValueError(_("server does not advertise changegroup version,"
68 raise ValueError(_("server does not advertise changegroup version,"
68 " can't negotiate support for ellipsis nodes"))
69 " can't negotiate support for ellipsis nodes"))
69
70
70 include = sorted(filter(bool, kwargs.get(r'includepats', [])))
71 include = sorted(filter(bool, kwargs.get(r'includepats', [])))
71 exclude = sorted(filter(bool, kwargs.get(r'excludepats', [])))
72 exclude = sorted(filter(bool, kwargs.get(r'excludepats', [])))
72 newmatch = narrowspec.match(repo.root, include=include, exclude=exclude)
73 newmatch = narrowspec.match(repo.root, include=include, exclude=exclude)
73 oldinclude = sorted(filter(bool, kwargs.get(r'oldincludepats', [])))
74 oldinclude = sorted(filter(bool, kwargs.get(r'oldincludepats', [])))
74 oldexclude = sorted(filter(bool, kwargs.get(r'oldexcludepats', [])))
75 oldexclude = sorted(filter(bool, kwargs.get(r'oldexcludepats', [])))
76 oldmatch = narrowspec.match(repo.root, include=oldinclude,
77 exclude=oldexclude)
78 diffmatch = matchmod.differencematcher(newmatch, oldmatch)
75 common = set(common or [nullid])
79 common = set(common or [nullid])
76
80
77 if (oldinclude != include or oldexclude != exclude):
81 if (oldinclude != include or oldexclude != exclude):
78 common = repo.revs("::%ln", common)
82 common = repo.revs("::%ln", common)
79 commonnodes = set()
83 commonnodes = set()
80 cl = repo.changelog
84 cl = repo.changelog
81 for c in common:
85 for c in common:
82 commonnodes.add(cl.node(c))
86 commonnodes.add(cl.node(c))
83 if commonnodes:
87 if commonnodes:
84 # XXX: we should only send the filelogs (and treemanifest). user
88 # XXX: we should only send the filelogs (and treemanifest). user
85 # already has the changelog and manifest
89 # already has the changelog and manifest
86 packer = changegroup.getbundler(version, repo,
90 packer = changegroup.getbundler(version, repo,
87 filematcher=newmatch,
91 filematcher=diffmatch,
88 fullnodes=commonnodes)
92 fullnodes=commonnodes)
89 cgdata = packer.generate(set([nullid]), list(commonnodes), False,
93 cgdata = packer.generate(set([nullid]), list(commonnodes), False,
90 source)
94 source)
91
95
92 part = bundler.newpart('changegroup', data=cgdata)
96 part = bundler.newpart('changegroup', data=cgdata)
93 part.addparam('version', version)
97 part.addparam('version', version)
94 if 'treemanifest' in repo.requirements:
98 if 'treemanifest' in repo.requirements:
95 part.addparam('treemanifest', '1')
99 part.addparam('treemanifest', '1')
96
100
97 # Serve a changegroup for a client with a narrow clone.
101 # Serve a changegroup for a client with a narrow clone.
98 def getbundlechangegrouppart_narrow(bundler, repo, source,
102 def getbundlechangegrouppart_narrow(bundler, repo, source,
99 bundlecaps=None, b2caps=None, heads=None,
103 bundlecaps=None, b2caps=None, heads=None,
100 common=None, **kwargs):
104 common=None, **kwargs):
101 assert repo.ui.configbool('experimental', 'narrowservebrokenellipses')
105 assert repo.ui.configbool('experimental', 'narrowservebrokenellipses')
102
106
103 cgversions = b2caps.get('changegroup')
107 cgversions = b2caps.get('changegroup')
104 if cgversions: # 3.1 and 3.2 ship with an empty value
108 if cgversions: # 3.1 and 3.2 ship with an empty value
105 cgversions = [v for v in cgversions
109 cgversions = [v for v in cgversions
106 if v in changegroup.supportedoutgoingversions(repo)]
110 if v in changegroup.supportedoutgoingversions(repo)]
107 if not cgversions:
111 if not cgversions:
108 raise ValueError(_('no common changegroup version'))
112 raise ValueError(_('no common changegroup version'))
109 version = max(cgversions)
113 version = max(cgversions)
110 else:
114 else:
111 raise ValueError(_("server does not advertise changegroup version,"
115 raise ValueError(_("server does not advertise changegroup version,"
112 " can't negotiate support for ellipsis nodes"))
116 " can't negotiate support for ellipsis nodes"))
113
117
114 include = sorted(filter(bool, kwargs.get(r'includepats', [])))
118 include = sorted(filter(bool, kwargs.get(r'includepats', [])))
115 exclude = sorted(filter(bool, kwargs.get(r'excludepats', [])))
119 exclude = sorted(filter(bool, kwargs.get(r'excludepats', [])))
116 newmatch = narrowspec.match(repo.root, include=include, exclude=exclude)
120 newmatch = narrowspec.match(repo.root, include=include, exclude=exclude)
117
121
118 depth = kwargs.get(r'depth', None)
122 depth = kwargs.get(r'depth', None)
119 if depth is not None:
123 if depth is not None:
120 depth = int(depth)
124 depth = int(depth)
121 if depth < 1:
125 if depth < 1:
122 raise error.Abort(_('depth must be positive, got %d') % depth)
126 raise error.Abort(_('depth must be positive, got %d') % depth)
123
127
124 heads = set(heads or repo.heads())
128 heads = set(heads or repo.heads())
125 common = set(common or [nullid])
129 common = set(common or [nullid])
126 oldinclude = sorted(filter(bool, kwargs.get(r'oldincludepats', [])))
130 oldinclude = sorted(filter(bool, kwargs.get(r'oldincludepats', [])))
127 oldexclude = sorted(filter(bool, kwargs.get(r'oldexcludepats', [])))
131 oldexclude = sorted(filter(bool, kwargs.get(r'oldexcludepats', [])))
128 known = {bin(n) for n in kwargs.get(r'known', [])}
132 known = {bin(n) for n in kwargs.get(r'known', [])}
129 if known and (oldinclude != include or oldexclude != exclude):
133 if known and (oldinclude != include or oldexclude != exclude):
130 # Steps:
134 # Steps:
131 # 1. Send kill for "$known & ::common"
135 # 1. Send kill for "$known & ::common"
132 #
136 #
133 # 2. Send changegroup for ::common
137 # 2. Send changegroup for ::common
134 #
138 #
135 # 3. Proceed.
139 # 3. Proceed.
136 #
140 #
137 # In the future, we can send kills for only the specific
141 # In the future, we can send kills for only the specific
138 # nodes we know should go away or change shape, and then
142 # nodes we know should go away or change shape, and then
139 # send a data stream that tells the client something like this:
143 # send a data stream that tells the client something like this:
140 #
144 #
141 # a) apply this changegroup
145 # a) apply this changegroup
142 # b) apply nodes XXX, YYY, ZZZ that you already have
146 # b) apply nodes XXX, YYY, ZZZ that you already have
143 # c) goto a
147 # c) goto a
144 #
148 #
145 # until they've built up the full new state.
149 # until they've built up the full new state.
146 # Convert to revnums and intersect with "common". The client should
150 # Convert to revnums and intersect with "common". The client should
147 # have made it a subset of "common" already, but let's be safe.
151 # have made it a subset of "common" already, but let's be safe.
148 known = set(repo.revs("%ln & ::%ln", known, common))
152 known = set(repo.revs("%ln & ::%ln", known, common))
149 # TODO: we could send only roots() of this set, and the
153 # TODO: we could send only roots() of this set, and the
150 # list of nodes in common, and the client could work out
154 # list of nodes in common, and the client could work out
151 # what to strip, instead of us explicitly sending every
155 # what to strip, instead of us explicitly sending every
152 # single node.
156 # single node.
153 deadrevs = known
157 deadrevs = known
154 def genkills():
158 def genkills():
155 for r in deadrevs:
159 for r in deadrevs:
156 yield _KILLNODESIGNAL
160 yield _KILLNODESIGNAL
157 yield repo.changelog.node(r)
161 yield repo.changelog.node(r)
158 yield _DONESIGNAL
162 yield _DONESIGNAL
159 bundler.newpart(_CHANGESPECPART, data=genkills())
163 bundler.newpart(_CHANGESPECPART, data=genkills())
160 newvisit, newfull, newellipsis = exchange._computeellipsis(
164 newvisit, newfull, newellipsis = exchange._computeellipsis(
161 repo, set(), common, known, newmatch)
165 repo, set(), common, known, newmatch)
162 if newvisit:
166 if newvisit:
163 packer = changegroup.getbundler(version, repo,
167 packer = changegroup.getbundler(version, repo,
164 filematcher=newmatch,
168 filematcher=newmatch,
165 ellipses=True,
169 ellipses=True,
166 shallow=depth is not None,
170 shallow=depth is not None,
167 ellipsisroots=newellipsis,
171 ellipsisroots=newellipsis,
168 fullnodes=newfull)
172 fullnodes=newfull)
169 cgdata = packer.generate(common, newvisit, False, source)
173 cgdata = packer.generate(common, newvisit, False, source)
170
174
171 part = bundler.newpart('changegroup', data=cgdata)
175 part = bundler.newpart('changegroup', data=cgdata)
172 part.addparam('version', version)
176 part.addparam('version', version)
173 if 'treemanifest' in repo.requirements:
177 if 'treemanifest' in repo.requirements:
174 part.addparam('treemanifest', '1')
178 part.addparam('treemanifest', '1')
175
179
176 visitnodes, relevant_nodes, ellipsisroots = exchange._computeellipsis(
180 visitnodes, relevant_nodes, ellipsisroots = exchange._computeellipsis(
177 repo, common, heads, set(), newmatch, depth=depth)
181 repo, common, heads, set(), newmatch, depth=depth)
178
182
179 repo.ui.debug('Found %d relevant revs\n' % len(relevant_nodes))
183 repo.ui.debug('Found %d relevant revs\n' % len(relevant_nodes))
180 if visitnodes:
184 if visitnodes:
181 packer = changegroup.getbundler(version, repo,
185 packer = changegroup.getbundler(version, repo,
182 filematcher=newmatch,
186 filematcher=newmatch,
183 ellipses=True,
187 ellipses=True,
184 shallow=depth is not None,
188 shallow=depth is not None,
185 ellipsisroots=ellipsisroots,
189 ellipsisroots=ellipsisroots,
186 fullnodes=relevant_nodes)
190 fullnodes=relevant_nodes)
187 cgdata = packer.generate(common, visitnodes, False, source)
191 cgdata = packer.generate(common, visitnodes, False, source)
188
192
189 part = bundler.newpart('changegroup', data=cgdata)
193 part = bundler.newpart('changegroup', data=cgdata)
190 part.addparam('version', version)
194 part.addparam('version', version)
191 if 'treemanifest' in repo.requirements:
195 if 'treemanifest' in repo.requirements:
192 part.addparam('treemanifest', '1')
196 part.addparam('treemanifest', '1')
193
197
194 @bundle2.parthandler(_SPECPART, (_SPECPART_INCLUDE, _SPECPART_EXCLUDE))
198 @bundle2.parthandler(_SPECPART, (_SPECPART_INCLUDE, _SPECPART_EXCLUDE))
195 def _handlechangespec_2(op, inpart):
199 def _handlechangespec_2(op, inpart):
196 includepats = set(inpart.params.get(_SPECPART_INCLUDE, '').splitlines())
200 includepats = set(inpart.params.get(_SPECPART_INCLUDE, '').splitlines())
197 excludepats = set(inpart.params.get(_SPECPART_EXCLUDE, '').splitlines())
201 excludepats = set(inpart.params.get(_SPECPART_EXCLUDE, '').splitlines())
198 narrowspec.validatepatterns(includepats)
202 narrowspec.validatepatterns(includepats)
199 narrowspec.validatepatterns(excludepats)
203 narrowspec.validatepatterns(excludepats)
200
204
201 if not repository.NARROW_REQUIREMENT in op.repo.requirements:
205 if not repository.NARROW_REQUIREMENT in op.repo.requirements:
202 op.repo.requirements.add(repository.NARROW_REQUIREMENT)
206 op.repo.requirements.add(repository.NARROW_REQUIREMENT)
203 op.repo._writerequirements()
207 op.repo._writerequirements()
204 op.repo.setnarrowpats(includepats, excludepats)
208 op.repo.setnarrowpats(includepats, excludepats)
205
209
206 @bundle2.parthandler(_CHANGESPECPART)
210 @bundle2.parthandler(_CHANGESPECPART)
207 def _handlechangespec(op, inpart):
211 def _handlechangespec(op, inpart):
208 repo = op.repo
212 repo = op.repo
209 cl = repo.changelog
213 cl = repo.changelog
210
214
211 # changesets which need to be stripped entirely. either they're no longer
215 # changesets which need to be stripped entirely. either they're no longer
212 # needed in the new narrow spec, or the server is sending a replacement
216 # needed in the new narrow spec, or the server is sending a replacement
213 # in the changegroup part.
217 # in the changegroup part.
214 clkills = set()
218 clkills = set()
215
219
216 # A changespec part contains all the updates to ellipsis nodes
220 # A changespec part contains all the updates to ellipsis nodes
217 # that will happen as a result of widening or narrowing a
221 # that will happen as a result of widening or narrowing a
218 # repo. All the changes that this block encounters are ellipsis
222 # repo. All the changes that this block encounters are ellipsis
219 # nodes or flags to kill an existing ellipsis.
223 # nodes or flags to kill an existing ellipsis.
220 chunksignal = changegroup.readexactly(inpart, 4)
224 chunksignal = changegroup.readexactly(inpart, 4)
221 while chunksignal != _DONESIGNAL:
225 while chunksignal != _DONESIGNAL:
222 if chunksignal == _KILLNODESIGNAL:
226 if chunksignal == _KILLNODESIGNAL:
223 # a node used to be an ellipsis but isn't anymore
227 # a node used to be an ellipsis but isn't anymore
224 ck = changegroup.readexactly(inpart, 20)
228 ck = changegroup.readexactly(inpart, 20)
225 if cl.hasnode(ck):
229 if cl.hasnode(ck):
226 clkills.add(ck)
230 clkills.add(ck)
227 else:
231 else:
228 raise error.Abort(
232 raise error.Abort(
229 _('unexpected changespec node chunk type: %s') % chunksignal)
233 _('unexpected changespec node chunk type: %s') % chunksignal)
230 chunksignal = changegroup.readexactly(inpart, 4)
234 chunksignal = changegroup.readexactly(inpart, 4)
231
235
232 if clkills:
236 if clkills:
233 # preserve bookmarks that repair.strip() would otherwise strip
237 # preserve bookmarks that repair.strip() would otherwise strip
234 bmstore = repo._bookmarks
238 bmstore = repo._bookmarks
235 class dummybmstore(dict):
239 class dummybmstore(dict):
236 def applychanges(self, repo, tr, changes):
240 def applychanges(self, repo, tr, changes):
237 pass
241 pass
238 def recordchange(self, tr): # legacy version
242 def recordchange(self, tr): # legacy version
239 pass
243 pass
240 repo._bookmarks = dummybmstore()
244 repo._bookmarks = dummybmstore()
241 chgrpfile = repair.strip(op.ui, repo, list(clkills), backup=True,
245 chgrpfile = repair.strip(op.ui, repo, list(clkills), backup=True,
242 topic='widen')
246 topic='widen')
243 repo._bookmarks = bmstore
247 repo._bookmarks = bmstore
244 if chgrpfile:
248 if chgrpfile:
245 op._widen_uninterr = repo.ui.uninterruptable()
249 op._widen_uninterr = repo.ui.uninterruptable()
246 op._widen_uninterr.__enter__()
250 op._widen_uninterr.__enter__()
247 # presence of _widen_bundle attribute activates widen handler later
251 # presence of _widen_bundle attribute activates widen handler later
248 op._widen_bundle = chgrpfile
252 op._widen_bundle = chgrpfile
249 # Set the new narrowspec if we're widening. The setnewnarrowpats() method
253 # Set the new narrowspec if we're widening. The setnewnarrowpats() method
250 # will currently always be there when using the core+narrowhg server, but
254 # will currently always be there when using the core+narrowhg server, but
251 # other servers may include a changespec part even when not widening (e.g.
255 # other servers may include a changespec part even when not widening (e.g.
252 # because we're deepening a shallow repo).
256 # because we're deepening a shallow repo).
253 if util.safehasattr(repo, 'setnewnarrowpats'):
257 if util.safehasattr(repo, 'setnewnarrowpats'):
254 repo.setnewnarrowpats()
258 repo.setnewnarrowpats()
255
259
256 def handlechangegroup_widen(op, inpart):
260 def handlechangegroup_widen(op, inpart):
257 """Changegroup exchange handler which restores temporarily-stripped nodes"""
261 """Changegroup exchange handler which restores temporarily-stripped nodes"""
258 # We saved a bundle with stripped node data we must now restore.
262 # We saved a bundle with stripped node data we must now restore.
259 # This approach is based on mercurial/repair.py@6ee26a53c111.
263 # This approach is based on mercurial/repair.py@6ee26a53c111.
260 repo = op.repo
264 repo = op.repo
261 ui = op.ui
265 ui = op.ui
262
266
263 chgrpfile = op._widen_bundle
267 chgrpfile = op._widen_bundle
264 del op._widen_bundle
268 del op._widen_bundle
265 vfs = repo.vfs
269 vfs = repo.vfs
266
270
267 ui.note(_("adding branch\n"))
271 ui.note(_("adding branch\n"))
268 f = vfs.open(chgrpfile, "rb")
272 f = vfs.open(chgrpfile, "rb")
269 try:
273 try:
270 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
274 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
271 if not ui.verbose:
275 if not ui.verbose:
272 # silence internal shuffling chatter
276 # silence internal shuffling chatter
273 ui.pushbuffer()
277 ui.pushbuffer()
274 if isinstance(gen, bundle2.unbundle20):
278 if isinstance(gen, bundle2.unbundle20):
275 with repo.transaction('strip') as tr:
279 with repo.transaction('strip') as tr:
276 bundle2.processbundle(repo, gen, lambda: tr)
280 bundle2.processbundle(repo, gen, lambda: tr)
277 else:
281 else:
278 gen.apply(repo, 'strip', 'bundle:' + vfs.join(chgrpfile), True)
282 gen.apply(repo, 'strip', 'bundle:' + vfs.join(chgrpfile), True)
279 if not ui.verbose:
283 if not ui.verbose:
280 ui.popbuffer()
284 ui.popbuffer()
281 finally:
285 finally:
282 f.close()
286 f.close()
283
287
284 # remove undo files
288 # remove undo files
285 for undovfs, undofile in repo.undofiles():
289 for undovfs, undofile in repo.undofiles():
286 try:
290 try:
287 undovfs.unlink(undofile)
291 undovfs.unlink(undofile)
288 except OSError as e:
292 except OSError as e:
289 if e.errno != errno.ENOENT:
293 if e.errno != errno.ENOENT:
290 ui.warn(_('error removing %s: %s\n') %
294 ui.warn(_('error removing %s: %s\n') %
291 (undovfs.join(undofile), stringutil.forcebytestr(e)))
295 (undovfs.join(undofile), stringutil.forcebytestr(e)))
292
296
293 # Remove partial backup only if there were no exceptions
297 # Remove partial backup only if there were no exceptions
294 op._widen_uninterr.__exit__(None, None, None)
298 op._widen_uninterr.__exit__(None, None, None)
295 vfs.unlink(chgrpfile)
299 vfs.unlink(chgrpfile)
296
300
297 def setup():
301 def setup():
298 """Enable narrow repo support in bundle2-related extension points."""
302 """Enable narrow repo support in bundle2-related extension points."""
299 extensions.wrapfunction(bundle2, 'getrepocaps', getrepocaps_narrow)
303 extensions.wrapfunction(bundle2, 'getrepocaps', getrepocaps_narrow)
300
304
301 getbundleargs = wireprototypes.GETBUNDLE_ARGUMENTS
305 getbundleargs = wireprototypes.GETBUNDLE_ARGUMENTS
302
306
303 getbundleargs['narrow'] = 'boolean'
307 getbundleargs['narrow'] = 'boolean'
304 getbundleargs['widen'] = 'boolean'
308 getbundleargs['widen'] = 'boolean'
305 getbundleargs['depth'] = 'plain'
309 getbundleargs['depth'] = 'plain'
306 getbundleargs['oldincludepats'] = 'csv'
310 getbundleargs['oldincludepats'] = 'csv'
307 getbundleargs['oldexcludepats'] = 'csv'
311 getbundleargs['oldexcludepats'] = 'csv'
308 getbundleargs['includepats'] = 'csv'
312 getbundleargs['includepats'] = 'csv'
309 getbundleargs['excludepats'] = 'csv'
313 getbundleargs['excludepats'] = 'csv'
310 getbundleargs['known'] = 'csv'
314 getbundleargs['known'] = 'csv'
311
315
312 # Extend changegroup serving to handle requests from narrow clients.
316 # Extend changegroup serving to handle requests from narrow clients.
313 origcgfn = exchange.getbundle2partsmapping['changegroup']
317 origcgfn = exchange.getbundle2partsmapping['changegroup']
314 def wrappedcgfn(*args, **kwargs):
318 def wrappedcgfn(*args, **kwargs):
315 repo = args[1]
319 repo = args[1]
316 if repo.ui.has_section(_NARROWACL_SECTION):
320 if repo.ui.has_section(_NARROWACL_SECTION):
317 kwargs = exchange.applynarrowacl(repo, kwargs)
321 kwargs = exchange.applynarrowacl(repo, kwargs)
318
322
319 if (kwargs.get(r'narrow', False) and
323 if (kwargs.get(r'narrow', False) and
320 repo.ui.configbool('experimental', 'narrowservebrokenellipses')):
324 repo.ui.configbool('experimental', 'narrowservebrokenellipses')):
321 getbundlechangegrouppart_narrow(*args, **kwargs)
325 getbundlechangegrouppart_narrow(*args, **kwargs)
322 elif kwargs.get(r'widen', False) and kwargs.get(r'narrow', False):
326 elif kwargs.get(r'widen', False) and kwargs.get(r'narrow', False):
323 getbundlechangegrouppart_widen(*args, **kwargs)
327 getbundlechangegrouppart_widen(*args, **kwargs)
324 else:
328 else:
325 origcgfn(*args, **kwargs)
329 origcgfn(*args, **kwargs)
326 exchange.getbundle2partsmapping['changegroup'] = wrappedcgfn
330 exchange.getbundle2partsmapping['changegroup'] = wrappedcgfn
327
331
328 # Extend changegroup receiver so client can fixup after widen requests.
332 # Extend changegroup receiver so client can fixup after widen requests.
329 origcghandler = bundle2.parthandlermapping['changegroup']
333 origcghandler = bundle2.parthandlermapping['changegroup']
330 def wrappedcghandler(op, inpart):
334 def wrappedcghandler(op, inpart):
331 origcghandler(op, inpart)
335 origcghandler(op, inpart)
332 if util.safehasattr(op, '_widen_bundle'):
336 if util.safehasattr(op, '_widen_bundle'):
333 handlechangegroup_widen(op, inpart)
337 handlechangegroup_widen(op, inpart)
334 wrappedcghandler.params = origcghandler.params
338 wrappedcghandler.params = origcghandler.params
335 bundle2.parthandlermapping['changegroup'] = wrappedcghandler
339 bundle2.parthandlermapping['changegroup'] = wrappedcghandler
@@ -1,389 +1,389 b''
1 #testcases tree flat
1 #testcases tree flat
2 $ . "$TESTDIR/narrow-library.sh"
2 $ . "$TESTDIR/narrow-library.sh"
3
3
4 #if tree
4 #if tree
5 $ cat << EOF >> $HGRCPATH
5 $ cat << EOF >> $HGRCPATH
6 > [experimental]
6 > [experimental]
7 > treemanifest = 1
7 > treemanifest = 1
8 > EOF
8 > EOF
9 #endif
9 #endif
10
10
11 $ hg init master
11 $ hg init master
12 $ cd master
12 $ cd master
13
13
14 $ mkdir inside
14 $ mkdir inside
15 $ echo 'inside' > inside/f
15 $ echo 'inside' > inside/f
16 $ hg add inside/f
16 $ hg add inside/f
17 $ hg commit -m 'add inside'
17 $ hg commit -m 'add inside'
18
18
19 $ mkdir widest
19 $ mkdir widest
20 $ echo 'widest' > widest/f
20 $ echo 'widest' > widest/f
21 $ hg add widest/f
21 $ hg add widest/f
22 $ hg commit -m 'add widest'
22 $ hg commit -m 'add widest'
23
23
24 $ mkdir outside
24 $ mkdir outside
25 $ echo 'outside' > outside/f
25 $ echo 'outside' > outside/f
26 $ hg add outside/f
26 $ hg add outside/f
27 $ hg commit -m 'add outside'
27 $ hg commit -m 'add outside'
28
28
29 $ cd ..
29 $ cd ..
30
30
31 narrow clone the inside file
31 narrow clone the inside file
32
32
33 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
33 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
34 requesting all changes
34 requesting all changes
35 adding changesets
35 adding changesets
36 adding manifests
36 adding manifests
37 adding file changes
37 adding file changes
38 added 3 changesets with 1 changes to 1 files
38 added 3 changesets with 1 changes to 1 files
39 new changesets *:* (glob)
39 new changesets *:* (glob)
40 updating to branch default
40 updating to branch default
41 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
41 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 $ cd narrow
42 $ cd narrow
43 $ hg tracked
43 $ hg tracked
44 I path:inside
44 I path:inside
45 $ ls
45 $ ls
46 inside
46 inside
47 $ cat inside/f
47 $ cat inside/f
48 inside
48 inside
49 $ cd ..
49 $ cd ..
50
50
51 add more upstream files which we will include in a wider narrow spec
51 add more upstream files which we will include in a wider narrow spec
52
52
53 $ cd master
53 $ cd master
54
54
55 $ mkdir wider
55 $ mkdir wider
56 $ echo 'wider' > wider/f
56 $ echo 'wider' > wider/f
57 $ hg add wider/f
57 $ hg add wider/f
58 $ echo 'widest v2' > widest/f
58 $ echo 'widest v2' > widest/f
59 $ hg commit -m 'add wider, update widest'
59 $ hg commit -m 'add wider, update widest'
60
60
61 $ echo 'widest v3' > widest/f
61 $ echo 'widest v3' > widest/f
62 $ hg commit -m 'update widest v3'
62 $ hg commit -m 'update widest v3'
63
63
64 $ echo 'inside v2' > inside/f
64 $ echo 'inside v2' > inside/f
65 $ hg commit -m 'update inside'
65 $ hg commit -m 'update inside'
66
66
67 $ mkdir outside2
67 $ mkdir outside2
68 $ echo 'outside2' > outside2/f
68 $ echo 'outside2' > outside2/f
69 $ hg add outside2/f
69 $ hg add outside2/f
70 $ hg commit -m 'add outside2'
70 $ hg commit -m 'add outside2'
71
71
72 $ echo 'widest v4' > widest/f
72 $ echo 'widest v4' > widest/f
73 $ hg commit -m 'update widest v4'
73 $ hg commit -m 'update widest v4'
74
74
75 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
75 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
76 7: update widest v4
76 7: update widest v4
77 6: add outside2
77 6: add outside2
78 5: update inside
78 5: update inside
79 4: update widest v3
79 4: update widest v3
80 3: add wider, update widest
80 3: add wider, update widest
81 2: add outside
81 2: add outside
82 1: add widest
82 1: add widest
83 0: add inside
83 0: add inside
84
84
85 $ cd ..
85 $ cd ..
86
86
87 Widen the narrow spec to see the widest file. This should not get the newly
87 Widen the narrow spec to see the widest file. This should not get the newly
88 added upstream revisions.
88 added upstream revisions.
89
89
90 $ cd narrow
90 $ cd narrow
91 $ hg tracked --addinclude widest/f
91 $ hg tracked --addinclude widest/f
92 comparing with ssh://user@dummy/master
92 comparing with ssh://user@dummy/master
93 searching for changes
93 searching for changes
94 no changes found
94 no changes found
95 adding changesets
95 adding changesets
96 adding manifests
96 adding manifests
97 adding file changes
97 adding file changes
98 added 0 changesets with 1 changes to 2 files
98 added 0 changesets with 1 changes to 1 files
99 3 local changesets published
99 3 local changesets published
100 $ hg tracked
100 $ hg tracked
101 I path:inside
101 I path:inside
102 I path:widest/f
102 I path:widest/f
103
103
104 $ cat widest/f
104 $ cat widest/f
105 widest
105 widest
106
106
107 Pull down the newly added upstream revision.
107 Pull down the newly added upstream revision.
108
108
109 $ hg pull
109 $ hg pull
110 pulling from ssh://user@dummy/master
110 pulling from ssh://user@dummy/master
111 searching for changes
111 searching for changes
112 adding changesets
112 adding changesets
113 adding manifests
113 adding manifests
114 adding file changes
114 adding file changes
115 added 5 changesets with 4 changes to 2 files
115 added 5 changesets with 4 changes to 2 files
116 new changesets *:* (glob)
116 new changesets *:* (glob)
117 (run 'hg update' to get a working copy)
117 (run 'hg update' to get a working copy)
118 $ hg update -r 'desc("add wider")'
118 $ hg update -r 'desc("add wider")'
119 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
119 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
120
120
121 $ cat widest/f
121 $ cat widest/f
122 widest v2
122 widest v2
123
123
124 $ hg update -r 'desc("update inside")'
124 $ hg update -r 'desc("update inside")'
125 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
125 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
126 $ cat widest/f
126 $ cat widest/f
127 widest v3
127 widest v3
128 $ cat inside/f
128 $ cat inside/f
129 inside v2
129 inside v2
130
130
131 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
131 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
132 7: update widest v4
132 7: update widest v4
133 6: add outside2
133 6: add outside2
134 5: update inside
134 5: update inside
135 4: update widest v3
135 4: update widest v3
136 3: add wider, update widest
136 3: add wider, update widest
137 2: add outside
137 2: add outside
138 1: add widest
138 1: add widest
139 0: add inside
139 0: add inside
140
140
141 Check that widening with a newline fails
141 Check that widening with a newline fails
142
142
143 $ hg tracked --addinclude 'widest
143 $ hg tracked --addinclude 'widest
144 > '
144 > '
145 abort: newlines are not allowed in narrowspec paths
145 abort: newlines are not allowed in narrowspec paths
146 [255]
146 [255]
147
147
148 widen the narrow spec to include the wider file
148 widen the narrow spec to include the wider file
149
149
150 $ hg tracked --addinclude wider
150 $ hg tracked --addinclude wider
151 comparing with ssh://user@dummy/master
151 comparing with ssh://user@dummy/master
152 searching for changes
152 searching for changes
153 no changes found
153 no changes found
154 adding changesets
154 adding changesets
155 adding manifests
155 adding manifests
156 adding file changes
156 adding file changes
157 added 0 changesets with 1 changes to 3 files
157 added 0 changesets with 1 changes to 1 files
158 5 local changesets published
158 5 local changesets published
159 $ hg tracked
159 $ hg tracked
160 I path:inside
160 I path:inside
161 I path:wider
161 I path:wider
162 I path:widest/f
162 I path:widest/f
163 $ hg update 'desc("add widest")'
163 $ hg update 'desc("add widest")'
164 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
164 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
165 $ cat widest/f
165 $ cat widest/f
166 widest
166 widest
167 $ hg update 'desc("add wider, update widest")'
167 $ hg update 'desc("add wider, update widest")'
168 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
168 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
169 $ cat wider/f
169 $ cat wider/f
170 wider
170 wider
171 $ cat widest/f
171 $ cat widest/f
172 widest v2
172 widest v2
173 $ hg update 'desc("update widest v3")'
173 $ hg update 'desc("update widest v3")'
174 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
175 $ cat widest/f
175 $ cat widest/f
176 widest v3
176 widest v3
177 $ hg update 'desc("update widest v4")'
177 $ hg update 'desc("update widest v4")'
178 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
178 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
179 $ cat widest/f
179 $ cat widest/f
180 widest v4
180 widest v4
181
181
182 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
182 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
183 7: update widest v4
183 7: update widest v4
184 6: add outside2
184 6: add outside2
185 5: update inside
185 5: update inside
186 4: update widest v3
186 4: update widest v3
187 3: add wider, update widest
187 3: add wider, update widest
188 2: add outside
188 2: add outside
189 1: add widest
189 1: add widest
190 0: add inside
190 0: add inside
191
191
192 separate suite of tests: files from 0-10 modified in changes 0-10. This allows
192 separate suite of tests: files from 0-10 modified in changes 0-10. This allows
193 more obvious precise tests tickling particular corner cases.
193 more obvious precise tests tickling particular corner cases.
194
194
195 $ cd ..
195 $ cd ..
196 $ hg init upstream
196 $ hg init upstream
197 $ cd upstream
197 $ cd upstream
198 $ for x in `$TESTDIR/seq.py 0 10`
198 $ for x in `$TESTDIR/seq.py 0 10`
199 > do
199 > do
200 > mkdir d$x
200 > mkdir d$x
201 > echo $x > d$x/f
201 > echo $x > d$x/f
202 > hg add d$x/f
202 > hg add d$x/f
203 > hg commit -m "add d$x/f"
203 > hg commit -m "add d$x/f"
204 > done
204 > done
205 $ hg log -T "{rev}: {desc}\n"
205 $ hg log -T "{rev}: {desc}\n"
206 10: add d10/f
206 10: add d10/f
207 9: add d9/f
207 9: add d9/f
208 8: add d8/f
208 8: add d8/f
209 7: add d7/f
209 7: add d7/f
210 6: add d6/f
210 6: add d6/f
211 5: add d5/f
211 5: add d5/f
212 4: add d4/f
212 4: add d4/f
213 3: add d3/f
213 3: add d3/f
214 2: add d2/f
214 2: add d2/f
215 1: add d1/f
215 1: add d1/f
216 0: add d0/f
216 0: add d0/f
217
217
218 make narrow clone with every third node.
218 make narrow clone with every third node.
219
219
220 $ cd ..
220 $ cd ..
221 $ hg clone --narrow ssh://user@dummy/upstream narrow2 --include d0 --include d3 --include d6 --include d9
221 $ hg clone --narrow ssh://user@dummy/upstream narrow2 --include d0 --include d3 --include d6 --include d9
222 requesting all changes
222 requesting all changes
223 adding changesets
223 adding changesets
224 adding manifests
224 adding manifests
225 adding file changes
225 adding file changes
226 added 11 changesets with 4 changes to 4 files
226 added 11 changesets with 4 changes to 4 files
227 new changesets *:* (glob)
227 new changesets *:* (glob)
228 updating to branch default
228 updating to branch default
229 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
229 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
230 $ cd narrow2
230 $ cd narrow2
231 $ hg tracked
231 $ hg tracked
232 I path:d0
232 I path:d0
233 I path:d3
233 I path:d3
234 I path:d6
234 I path:d6
235 I path:d9
235 I path:d9
236 $ hg verify
236 $ hg verify
237 checking changesets
237 checking changesets
238 checking manifests
238 checking manifests
239 checking directory manifests (tree !)
239 checking directory manifests (tree !)
240 crosschecking files in changesets and manifests
240 crosschecking files in changesets and manifests
241 checking files
241 checking files
242 checked 11 changesets with 4 changes to 4 files
242 checked 11 changesets with 4 changes to 4 files
243 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
243 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
244 10: add d10/f
244 10: add d10/f
245 9: add d9/f
245 9: add d9/f
246 8: add d8/f
246 8: add d8/f
247 7: add d7/f
247 7: add d7/f
248 6: add d6/f
248 6: add d6/f
249 5: add d5/f
249 5: add d5/f
250 4: add d4/f
250 4: add d4/f
251 3: add d3/f
251 3: add d3/f
252 2: add d2/f
252 2: add d2/f
253 1: add d1/f
253 1: add d1/f
254 0: add d0/f
254 0: add d0/f
255 $ hg tracked --addinclude d1
255 $ hg tracked --addinclude d1
256 comparing with ssh://user@dummy/upstream
256 comparing with ssh://user@dummy/upstream
257 searching for changes
257 searching for changes
258 no changes found
258 no changes found
259 adding changesets
259 adding changesets
260 adding manifests
260 adding manifests
261 adding file changes
261 adding file changes
262 added 0 changesets with 1 changes to 5 files
262 added 0 changesets with 1 changes to 1 files
263 11 local changesets published
263 11 local changesets published
264 $ hg tracked
264 $ hg tracked
265 I path:d0
265 I path:d0
266 I path:d1
266 I path:d1
267 I path:d3
267 I path:d3
268 I path:d6
268 I path:d6
269 I path:d9
269 I path:d9
270 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
270 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
271 10: add d10/f
271 10: add d10/f
272 9: add d9/f
272 9: add d9/f
273 8: add d8/f
273 8: add d8/f
274 7: add d7/f
274 7: add d7/f
275 6: add d6/f
275 6: add d6/f
276 5: add d5/f
276 5: add d5/f
277 4: add d4/f
277 4: add d4/f
278 3: add d3/f
278 3: add d3/f
279 2: add d2/f
279 2: add d2/f
280 1: add d1/f
280 1: add d1/f
281 0: add d0/f
281 0: add d0/f
282
282
283 Verify shouldn't claim the repo is corrupt after a widen.
283 Verify shouldn't claim the repo is corrupt after a widen.
284
284
285 $ hg verify
285 $ hg verify
286 checking changesets
286 checking changesets
287 checking manifests
287 checking manifests
288 checking directory manifests (tree !)
288 checking directory manifests (tree !)
289 crosschecking files in changesets and manifests
289 crosschecking files in changesets and manifests
290 checking files
290 checking files
291 checked 11 changesets with 5 changes to 5 files
291 checked 11 changesets with 5 changes to 5 files
292
292
293 Widening preserves parent of local commit
293 Widening preserves parent of local commit
294
294
295 $ cd ..
295 $ cd ..
296 $ hg clone -q --narrow ssh://user@dummy/upstream narrow3 --include d2 -r 2
296 $ hg clone -q --narrow ssh://user@dummy/upstream narrow3 --include d2 -r 2
297 $ cd narrow3
297 $ cd narrow3
298 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
298 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
299 2: add d2/f
299 2: add d2/f
300 1: add d1/f
300 1: add d1/f
301 0: add d0/f
301 0: add d0/f
302 $ hg pull -q -r 3
302 $ hg pull -q -r 3
303 $ hg co -q tip
303 $ hg co -q tip
304 $ hg pull -q -r 4
304 $ hg pull -q -r 4
305 $ echo local > d2/f
305 $ echo local > d2/f
306 $ hg ci -m local
306 $ hg ci -m local
307 created new head
307 created new head
308 $ hg tracked -q --addinclude d0 --addinclude d9
308 $ hg tracked -q --addinclude d0 --addinclude d9
309
309
310 Widening preserves bookmarks
310 Widening preserves bookmarks
311
311
312 $ cd ..
312 $ cd ..
313 $ hg clone -q --narrow ssh://user@dummy/upstream narrow-bookmarks --include d4
313 $ hg clone -q --narrow ssh://user@dummy/upstream narrow-bookmarks --include d4
314 $ cd narrow-bookmarks
314 $ cd narrow-bookmarks
315 $ echo local > d4/f
315 $ echo local > d4/f
316 $ hg ci -m local
316 $ hg ci -m local
317 $ hg bookmarks bookmark
317 $ hg bookmarks bookmark
318 $ hg bookmarks
318 $ hg bookmarks
319 * bookmark 11:* (glob)
319 * bookmark 11:* (glob)
320 $ hg -q tracked --addinclude d2
320 $ hg -q tracked --addinclude d2
321 $ hg bookmarks
321 $ hg bookmarks
322 * bookmark 11:* (glob)
322 * bookmark 11:* (glob)
323 $ hg log -r bookmark -T '{desc}\n'
323 $ hg log -r bookmark -T '{desc}\n'
324 local
324 local
325
325
326 Widening that fails can be recovered from
326 Widening that fails can be recovered from
327
327
328 $ cd ..
328 $ cd ..
329 $ hg clone -q --narrow ssh://user@dummy/upstream interrupted --include d0
329 $ hg clone -q --narrow ssh://user@dummy/upstream interrupted --include d0
330 $ cd interrupted
330 $ cd interrupted
331 $ echo local > d0/f
331 $ echo local > d0/f
332 $ hg ci -m local
332 $ hg ci -m local
333 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
333 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
334 11: local
334 11: local
335 10: add d10/f
335 10: add d10/f
336 9: add d9/f
336 9: add d9/f
337 8: add d8/f
337 8: add d8/f
338 7: add d7/f
338 7: add d7/f
339 6: add d6/f
339 6: add d6/f
340 5: add d5/f
340 5: add d5/f
341 4: add d4/f
341 4: add d4/f
342 3: add d3/f
342 3: add d3/f
343 2: add d2/f
343 2: add d2/f
344 1: add d1/f
344 1: add d1/f
345 0: add d0/f
345 0: add d0/f
346 $ hg bookmarks bookmark
346 $ hg bookmarks bookmark
347 $ hg --config hooks.pretxnchangegroup.bad=false tracked --addinclude d1
347 $ hg --config hooks.pretxnchangegroup.bad=false tracked --addinclude d1
348 comparing with ssh://user@dummy/upstream
348 comparing with ssh://user@dummy/upstream
349 searching for changes
349 searching for changes
350 no changes found
350 no changes found
351 adding changesets
351 adding changesets
352 adding manifests
352 adding manifests
353 adding file changes
353 adding file changes
354 added 0 changesets with 1 changes to 2 files
354 added 0 changesets with 1 changes to 1 files
355 11 local changesets published
355 11 local changesets published
356 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
356 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
357 11: local
357 11: local
358 10: add d10/f
358 10: add d10/f
359 9: add d9/f
359 9: add d9/f
360 8: add d8/f
360 8: add d8/f
361 7: add d7/f
361 7: add d7/f
362 6: add d6/f
362 6: add d6/f
363 5: add d5/f
363 5: add d5/f
364 4: add d4/f
364 4: add d4/f
365 3: add d3/f
365 3: add d3/f
366 2: add d2/f
366 2: add d2/f
367 1: add d1/f
367 1: add d1/f
368 0: add d0/f
368 0: add d0/f
369 $ hg bookmarks
369 $ hg bookmarks
370 * bookmark 11:* (glob)
370 * bookmark 11:* (glob)
371 $ hg unbundle .hg/strip-backup/*-widen.hg
371 $ hg unbundle .hg/strip-backup/*-widen.hg
372 abort: .hg/strip-backup/*-widen.hg: $ENOTDIR$ (windows !)
372 abort: .hg/strip-backup/*-widen.hg: $ENOTDIR$ (windows !)
373 abort: $ENOENT$: .hg/strip-backup/*-widen.hg (no-windows !)
373 abort: $ENOENT$: .hg/strip-backup/*-widen.hg (no-windows !)
374 [255]
374 [255]
375 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
375 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
376 11: local
376 11: local
377 10: add d10/f
377 10: add d10/f
378 9: add d9/f
378 9: add d9/f
379 8: add d8/f
379 8: add d8/f
380 7: add d7/f
380 7: add d7/f
381 6: add d6/f
381 6: add d6/f
382 5: add d5/f
382 5: add d5/f
383 4: add d4/f
383 4: add d4/f
384 3: add d3/f
384 3: add d3/f
385 2: add d2/f
385 2: add d2/f
386 1: add d1/f
386 1: add d1/f
387 0: add d0/f
387 0: add d0/f
388 $ hg bookmarks
388 $ hg bookmarks
389 * bookmark 11:* (glob)
389 * bookmark 11:* (glob)
General Comments 0
You need to be logged in to leave comments. Login now