##// END OF EJS Templates
narrow: use narrow_widen wireproto command to widen in case of ellipses...
Pulkit Goyal -
r42605:c767e655 default
parent child Browse files
Show More
@@ -1,463 +1,493 b''
1 1 # narrowcommands.py - command modifications for narrowhg extension
2 2 #
3 3 # Copyright 2017 Google, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7 from __future__ import absolute_import
8 8
9 9 import itertools
10 10 import os
11 11
12 12 from mercurial.i18n import _
13 13 from mercurial import (
14 14 bundle2,
15 15 cmdutil,
16 16 commands,
17 17 discovery,
18 18 encoding,
19 19 error,
20 20 exchange,
21 21 extensions,
22 22 hg,
23 23 narrowspec,
24 24 node,
25 25 pycompat,
26 26 registrar,
27 27 repair,
28 28 repository,
29 29 repoview,
30 30 sparse,
31 31 util,
32 32 wireprototypes,
33 33 )
34 34
35 35 table = {}
36 36 command = registrar.command(table)
37 37
38 38 def setup():
39 39 """Wraps user-facing mercurial commands with narrow-aware versions."""
40 40
41 41 entry = extensions.wrapcommand(commands.table, 'clone', clonenarrowcmd)
42 42 entry[1].append(('', 'narrow', None,
43 43 _("create a narrow clone of select files")))
44 44 entry[1].append(('', 'depth', '',
45 45 _("limit the history fetched by distance from heads")))
46 46 entry[1].append(('', 'narrowspec', '',
47 47 _("read narrowspecs from file")))
48 48 # TODO(durin42): unify sparse/narrow --include/--exclude logic a bit
49 49 if 'sparse' not in extensions.enabled():
50 50 entry[1].append(('', 'include', [],
51 51 _("specifically fetch this file/directory")))
52 52 entry[1].append(
53 53 ('', 'exclude', [],
54 54 _("do not fetch this file/directory, even if included")))
55 55
56 56 entry = extensions.wrapcommand(commands.table, 'pull', pullnarrowcmd)
57 57 entry[1].append(('', 'depth', '',
58 58 _("limit the history fetched by distance from heads")))
59 59
60 60 extensions.wrapcommand(commands.table, 'archive', archivenarrowcmd)
61 61
62 62 def clonenarrowcmd(orig, ui, repo, *args, **opts):
63 63 """Wraps clone command, so 'hg clone' first wraps localrepo.clone()."""
64 64 opts = pycompat.byteskwargs(opts)
65 65 wrappedextraprepare = util.nullcontextmanager()
66 66 narrowspecfile = opts['narrowspec']
67 67
68 68 if narrowspecfile:
69 69 filepath = os.path.join(encoding.getcwd(), narrowspecfile)
70 70 ui.status(_("reading narrowspec from '%s'\n") % filepath)
71 71 try:
72 72 fdata = util.readfile(filepath)
73 73 except IOError as inst:
74 74 raise error.Abort(_("cannot read narrowspecs from '%s': %s") %
75 75 (filepath, encoding.strtolocal(inst.strerror)))
76 76
77 77 includes, excludes, profiles = sparse.parseconfig(ui, fdata, 'narrow')
78 78 if profiles:
79 79 raise error.Abort(_("cannot specify other files using '%include' in"
80 80 " narrowspec"))
81 81
82 82 narrowspec.validatepatterns(includes)
83 83 narrowspec.validatepatterns(excludes)
84 84
85 85 # narrowspec is passed so we should assume that user wants narrow clone
86 86 opts['narrow'] = True
87 87 opts['include'].extend(includes)
88 88 opts['exclude'].extend(excludes)
89 89
90 90 if opts['narrow']:
91 91 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
92 92 orig(pullop, kwargs)
93 93
94 94 if opts.get('depth'):
95 95 kwargs['depth'] = opts['depth']
96 96 wrappedextraprepare = extensions.wrappedfunction(exchange,
97 97 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
98 98
99 99 with wrappedextraprepare:
100 100 return orig(ui, repo, *args, **pycompat.strkwargs(opts))
101 101
102 102 def pullnarrowcmd(orig, ui, repo, *args, **opts):
103 103 """Wraps pull command to allow modifying narrow spec."""
104 104 wrappedextraprepare = util.nullcontextmanager()
105 105 if repository.NARROW_REQUIREMENT in repo.requirements:
106 106
107 107 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
108 108 orig(pullop, kwargs)
109 109 if opts.get(r'depth'):
110 110 kwargs['depth'] = opts[r'depth']
111 111 wrappedextraprepare = extensions.wrappedfunction(exchange,
112 112 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
113 113
114 114 with wrappedextraprepare:
115 115 return orig(ui, repo, *args, **opts)
116 116
117 117 def archivenarrowcmd(orig, ui, repo, *args, **opts):
118 118 """Wraps archive command to narrow the default includes."""
119 119 if repository.NARROW_REQUIREMENT in repo.requirements:
120 120 repo_includes, repo_excludes = repo.narrowpats
121 121 includes = set(opts.get(r'include', []))
122 122 excludes = set(opts.get(r'exclude', []))
123 123 includes, excludes, unused_invalid = narrowspec.restrictpatterns(
124 124 includes, excludes, repo_includes, repo_excludes)
125 125 if includes:
126 126 opts[r'include'] = includes
127 127 if excludes:
128 128 opts[r'exclude'] = excludes
129 129 return orig(ui, repo, *args, **opts)
130 130
131 131 def pullbundle2extraprepare(orig, pullop, kwargs):
132 132 repo = pullop.repo
133 133 if repository.NARROW_REQUIREMENT not in repo.requirements:
134 134 return orig(pullop, kwargs)
135 135
136 136 if wireprototypes.NARROWCAP not in pullop.remote.capabilities():
137 137 raise error.Abort(_("server does not support narrow clones"))
138 138 orig(pullop, kwargs)
139 139 kwargs['narrow'] = True
140 140 include, exclude = repo.narrowpats
141 141 kwargs['oldincludepats'] = include
142 142 kwargs['oldexcludepats'] = exclude
143 143 if include:
144 144 kwargs['includepats'] = include
145 145 if exclude:
146 146 kwargs['excludepats'] = exclude
147 147 # calculate known nodes only in ellipses cases because in non-ellipses cases
148 148 # we have all the nodes
149 if wireprototypes.ELLIPSESCAP in pullop.remote.capabilities():
149 if wireprototypes.ELLIPSESCAP1 in pullop.remote.capabilities():
150 150 kwargs['known'] = [node.hex(ctx.node()) for ctx in
151 151 repo.set('::%ln', pullop.common)
152 152 if ctx.node() != node.nullid]
153 153 if not kwargs['known']:
154 154 # Mercurial serializes an empty list as '' and deserializes it as
155 155 # [''], so delete it instead to avoid handling the empty string on
156 156 # the server.
157 157 del kwargs['known']
158 158
159 159 extensions.wrapfunction(exchange,'_pullbundle2extraprepare',
160 160 pullbundle2extraprepare)
161 161
162 162 def _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
163 163 newincludes, newexcludes, force):
164 164 oldmatch = narrowspec.match(repo.root, oldincludes, oldexcludes)
165 165 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
166 166
167 167 # This is essentially doing "hg outgoing" to find all local-only
168 168 # commits. We will then check that the local-only commits don't
169 169 # have any changes to files that will be untracked.
170 170 unfi = repo.unfiltered()
171 171 outgoing = discovery.findcommonoutgoing(unfi, remote,
172 172 commoninc=commoninc)
173 173 ui.status(_('looking for local changes to affected paths\n'))
174 174 localnodes = []
175 175 for n in itertools.chain(outgoing.missing, outgoing.excluded):
176 176 if any(oldmatch(f) and not newmatch(f) for f in unfi[n].files()):
177 177 localnodes.append(n)
178 178 revstostrip = unfi.revs('descendants(%ln)', localnodes)
179 179 hiddenrevs = repoview.filterrevs(repo, 'visible')
180 180 visibletostrip = list(repo.changelog.node(r)
181 181 for r in (revstostrip - hiddenrevs))
182 182 if visibletostrip:
183 183 ui.status(_('The following changeset(s) or their ancestors have '
184 184 'local changes not on the remote:\n'))
185 185 maxnodes = 10
186 186 if ui.verbose or len(visibletostrip) <= maxnodes:
187 187 for n in visibletostrip:
188 188 ui.status('%s\n' % node.short(n))
189 189 else:
190 190 for n in visibletostrip[:maxnodes]:
191 191 ui.status('%s\n' % node.short(n))
192 192 ui.status(_('...and %d more, use --verbose to list all\n') %
193 193 (len(visibletostrip) - maxnodes))
194 194 if not force:
195 195 raise error.Abort(_('local changes found'),
196 196 hint=_('use --force-delete-local-changes to '
197 197 'ignore'))
198 198
199 199 with ui.uninterruptible():
200 200 if revstostrip:
201 201 tostrip = [unfi.changelog.node(r) for r in revstostrip]
202 202 if repo['.'].node() in tostrip:
203 203 # stripping working copy, so move to a different commit first
204 204 urev = max(repo.revs('(::%n) - %ln + null',
205 205 repo['.'].node(), visibletostrip))
206 206 hg.clean(repo, urev)
207 207 overrides = {('devel', 'strip-obsmarkers'): False}
208 208 with ui.configoverride(overrides, 'narrow'):
209 209 repair.strip(ui, unfi, tostrip, topic='narrow')
210 210
211 211 todelete = []
212 212 for f, f2, size in repo.store.datafiles():
213 213 if f.startswith('data/'):
214 214 file = f[5:-2]
215 215 if not newmatch(file):
216 216 todelete.append(f)
217 217 elif f.startswith('meta/'):
218 218 dir = f[5:-13]
219 219 dirs = sorted(util.dirs({dir})) + [dir]
220 220 include = True
221 221 for d in dirs:
222 222 visit = newmatch.visitdir(d)
223 223 if not visit:
224 224 include = False
225 225 break
226 226 if visit == 'all':
227 227 break
228 228 if not include:
229 229 todelete.append(f)
230 230
231 231 repo.destroying()
232 232
233 233 with repo.transaction('narrowing'):
234 234 # Update narrowspec before removing revlogs, so repo won't be
235 235 # corrupt in case of crash
236 236 repo.setnarrowpats(newincludes, newexcludes)
237 237
238 238 for f in todelete:
239 239 ui.status(_('deleting %s\n') % f)
240 240 util.unlinkpath(repo.svfs.join(f))
241 241 repo.store.markremoved(f)
242 242
243 243 narrowspec.updateworkingcopy(repo, assumeclean=True)
244 244 narrowspec.copytoworkingcopy(repo)
245 245
246 246 repo.destroyed()
247 247
248 248 def _widen(ui, repo, remote, commoninc, oldincludes, oldexcludes,
249 249 newincludes, newexcludes):
250 250 # for now we assume that if a server has ellipses enabled, we will be
251 251 # exchanging ellipses nodes. In future we should add ellipses as a client
252 252 # side requirement (maybe) to distinguish a client is shallow or not and
253 253 # then send that information to server whether we want ellipses or not.
254 254 # Theoretically a non-ellipses repo should be able to use narrow
255 255 # functionality from an ellipses enabled server
256 ellipsesremote = wireprototypes.ELLIPSESCAP in remote.capabilities()
256 remotecap = remote.capabilities()
257 ellipsesremote = any(cap in remotecap
258 for cap in wireprototypes.SUPPORTED_ELLIPSESCAP)
259
260 # check whether we are talking to a server which supports old version of
261 # ellipses capabilities
262 isoldellipses = (ellipsesremote and wireprototypes.ELLIPSESCAP1 in
263 remotecap and wireprototypes.ELLIPSESCAP not in remotecap)
257 264
258 265 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
259 266 orig(pullop, kwargs)
260 267 # The old{in,ex}cludepats have already been set by orig()
261 268 kwargs['includepats'] = newincludes
262 269 kwargs['excludepats'] = newexcludes
263 270 wrappedextraprepare = extensions.wrappedfunction(exchange,
264 271 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
265 272
266 273 # define a function that narrowbundle2 can call after creating the
267 274 # backup bundle, but before applying the bundle from the server
268 275 def setnewnarrowpats():
269 276 repo.setnarrowpats(newincludes, newexcludes)
270 277 repo.setnewnarrowpats = setnewnarrowpats
271 278 # silence the devel-warning of applying an empty changegroup
272 279 overrides = {('devel', 'all-warnings'): False}
273 280
274 281 with ui.uninterruptible():
275 282 common = commoninc[0]
276 283 if ellipsesremote:
277 284 ds = repo.dirstate
278 285 p1, p2 = ds.p1(), ds.p2()
279 286 with ds.parentchange():
280 287 ds.setparents(node.nullid, node.nullid)
281 288 with wrappedextraprepare:
282 with repo.ui.configoverride(overrides, 'widen'):
289 if isoldellipses:
283 290 exchange.pull(repo, remote, heads=common)
291 else:
292 known = [node.hex(ctx.node()) for ctx in
293 repo.set('::%ln', common)
294 if ctx.node() != node.nullid]
295
296 with remote.commandexecutor() as e:
297 bundle = e.callcommand('narrow_widen', {
298 'oldincludes': oldincludes,
299 'oldexcludes': oldexcludes,
300 'newincludes': newincludes,
301 'newexcludes': newexcludes,
302 'cgversion': '03',
303 'commonheads': common,
304 'known': known,
305 'ellipses': True,
306 }).result()
307 trmanager = exchange.transactionmanager(repo, 'widen',
308 remote.url())
309 with trmanager:
310 op = bundle2.bundleoperation(repo,
311 trmanager.transaction, source='widen')
312 bundle2.processbundle(repo, bundle, op=op)
313
284 314 with ds.parentchange():
285 315 ds.setparents(p1, p2)
286 316 else:
287 317 with remote.commandexecutor() as e:
288 318 bundle = e.callcommand('narrow_widen', {
289 319 'oldincludes': oldincludes,
290 320 'oldexcludes': oldexcludes,
291 321 'newincludes': newincludes,
292 322 'newexcludes': newexcludes,
293 323 'cgversion': '03',
294 324 'commonheads': common,
295 325 'known': [],
296 326 'ellipses': False,
297 327 }).result()
298 328
299 329 with repo.transaction('widening') as tr:
300 330 with repo.ui.configoverride(overrides, 'widen'):
301 331 tgetter = lambda: tr
302 332 bundle2.processbundle(repo, bundle,
303 333 transactiongetter=tgetter)
304 334
305 335 with repo.transaction('widening'):
306 336 repo.setnewnarrowpats()
307 337 narrowspec.updateworkingcopy(repo)
308 338 narrowspec.copytoworkingcopy(repo)
309 339
310 340 # TODO(rdamazio): Make new matcher format and update description
311 341 @command('tracked',
312 342 [('', 'addinclude', [], _('new paths to include')),
313 343 ('', 'removeinclude', [], _('old paths to no longer include')),
314 344 ('', 'addexclude', [], _('new paths to exclude')),
315 345 ('', 'import-rules', '', _('import narrowspecs from a file')),
316 346 ('', 'removeexclude', [], _('old paths to no longer exclude')),
317 347 ('', 'clear', False, _('whether to replace the existing narrowspec')),
318 348 ('', 'force-delete-local-changes', False,
319 349 _('forces deletion of local changes when narrowing')),
320 350 ('', 'update-working-copy', False,
321 351 _('update working copy when the store has changed')),
322 352 ] + commands.remoteopts,
323 353 _('[OPTIONS]... [REMOTE]'),
324 354 inferrepo=True)
325 355 def trackedcmd(ui, repo, remotepath=None, *pats, **opts):
326 356 """show or change the current narrowspec
327 357
328 358 With no argument, shows the current narrowspec entries, one per line. Each
329 359 line will be prefixed with 'I' or 'X' for included or excluded patterns,
330 360 respectively.
331 361
332 362 The narrowspec is comprised of expressions to match remote files and/or
333 363 directories that should be pulled into your client.
334 364 The narrowspec has *include* and *exclude* expressions, with excludes always
335 365 trumping includes: that is, if a file matches an exclude expression, it will
336 366 be excluded even if it also matches an include expression.
337 367 Excluding files that were never included has no effect.
338 368
339 369 Each included or excluded entry is in the format described by
340 370 'hg help patterns'.
341 371
342 372 The options allow you to add or remove included and excluded expressions.
343 373
344 374 If --clear is specified, then all previous includes and excludes are DROPPED
345 375 and replaced by the new ones specified to --addinclude and --addexclude.
346 376 If --clear is specified without any further options, the narrowspec will be
347 377 empty and will not match any files.
348 378
349 379 --import-rules accepts a path to a file containing rules, allowing you to
350 380 add --addinclude, --addexclude rules in bulk. Like the other include and
351 381 exclude switches, the changes are applied immediately.
352 382 """
353 383 opts = pycompat.byteskwargs(opts)
354 384 if repository.NARROW_REQUIREMENT not in repo.requirements:
355 385 raise error.Abort(_('the tracked command is only supported on '
356 386 'respositories cloned with --narrow'))
357 387
358 388 # Before supporting, decide whether it "hg tracked --clear" should mean
359 389 # tracking no paths or all paths.
360 390 if opts['clear']:
361 391 raise error.Abort(_('the --clear option is not yet supported'))
362 392
363 393 # import rules from a file
364 394 newrules = opts.get('import_rules')
365 395 if newrules:
366 396 try:
367 397 filepath = os.path.join(encoding.getcwd(), newrules)
368 398 fdata = util.readfile(filepath)
369 399 except IOError as inst:
370 400 raise error.Abort(_("cannot read narrowspecs from '%s': %s") %
371 401 (filepath, encoding.strtolocal(inst.strerror)))
372 402 includepats, excludepats, profiles = sparse.parseconfig(ui, fdata,
373 403 'narrow')
374 404 if profiles:
375 405 raise error.Abort(_("including other spec files using '%include' "
376 406 "is not supported in narrowspec"))
377 407 opts['addinclude'].extend(includepats)
378 408 opts['addexclude'].extend(excludepats)
379 409
380 410 addedincludes = narrowspec.parsepatterns(opts['addinclude'])
381 411 removedincludes = narrowspec.parsepatterns(opts['removeinclude'])
382 412 addedexcludes = narrowspec.parsepatterns(opts['addexclude'])
383 413 removedexcludes = narrowspec.parsepatterns(opts['removeexclude'])
384 414
385 415 update_working_copy = opts['update_working_copy']
386 416 only_show = not (addedincludes or removedincludes or addedexcludes or
387 417 removedexcludes or newrules or update_working_copy)
388 418
389 419 oldincludes, oldexcludes = repo.narrowpats
390 420
391 421 # filter the user passed additions and deletions into actual additions and
392 422 # deletions of excludes and includes
393 423 addedincludes -= oldincludes
394 424 removedincludes &= oldincludes
395 425 addedexcludes -= oldexcludes
396 426 removedexcludes &= oldexcludes
397 427
398 428 widening = addedincludes or removedexcludes
399 429 narrowing = removedincludes or addedexcludes
400 430
401 431 # Only print the current narrowspec.
402 432 if only_show:
403 433 ui.pager('tracked')
404 434 fm = ui.formatter('narrow', opts)
405 435 for i in sorted(oldincludes):
406 436 fm.startitem()
407 437 fm.write('status', '%s ', 'I', label='narrow.included')
408 438 fm.write('pat', '%s\n', i, label='narrow.included')
409 439 for i in sorted(oldexcludes):
410 440 fm.startitem()
411 441 fm.write('status', '%s ', 'X', label='narrow.excluded')
412 442 fm.write('pat', '%s\n', i, label='narrow.excluded')
413 443 fm.end()
414 444 return 0
415 445
416 446 if update_working_copy:
417 447 with repo.wlock(), repo.lock(), repo.transaction('narrow-wc'):
418 448 narrowspec.updateworkingcopy(repo)
419 449 narrowspec.copytoworkingcopy(repo)
420 450 return 0
421 451
422 452 if not widening and not narrowing:
423 453 ui.status(_("nothing to widen or narrow\n"))
424 454 return 0
425 455
426 456 with repo.wlock(), repo.lock():
427 457 cmdutil.bailifchanged(repo)
428 458
429 459 # Find the revisions we have in common with the remote. These will
430 460 # be used for finding local-only changes for narrowing. They will
431 461 # also define the set of revisions to update for widening.
432 462 remotepath = ui.expandpath(remotepath or 'default')
433 463 url, branches = hg.parseurl(remotepath)
434 464 ui.status(_('comparing with %s\n') % util.hidepassword(url))
435 465 remote = hg.peer(repo, opts, url)
436 466
437 467 # check narrow support before doing anything if widening needs to be
438 468 # performed. In future we should also abort if client is ellipses and
439 469 # server does not support ellipses
440 470 if widening and wireprototypes.NARROWCAP not in remote.capabilities():
441 471 raise error.Abort(_("server does not support narrow clones"))
442 472
443 473 commoninc = discovery.findcommonincoming(repo, remote)
444 474
445 475 if narrowing:
446 476 newincludes = oldincludes - removedincludes
447 477 newexcludes = oldexcludes | addedexcludes
448 478 _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
449 479 newincludes, newexcludes,
450 480 opts['force_delete_local_changes'])
451 481 # _narrow() updated the narrowspec and _widen() below needs to
452 482 # use the updated values as its base (otherwise removed includes
453 483 # and addedexcludes will be lost in the resulting narrowspec)
454 484 oldincludes = newincludes
455 485 oldexcludes = newexcludes
456 486
457 487 if widening:
458 488 newincludes = oldincludes | addedincludes
459 489 newexcludes = oldexcludes - removedexcludes
460 490 _widen(ui, repo, remote, commoninc, oldincludes, oldexcludes,
461 491 newincludes, newexcludes)
462 492
463 493 return 0
@@ -1,107 +1,115 b''
1 1 # narrowwirepeer.py - passes narrow spec with unbundle command
2 2 #
3 3 # Copyright 2017 Google, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 from mercurial import (
11 11 bundle2,
12 12 error,
13 13 extensions,
14 14 hg,
15 15 narrowspec,
16 node as nodemod,
16 17 pycompat,
17 18 wireprototypes,
18 19 wireprotov1peer,
19 20 wireprotov1server,
20 21 )
21 22
23 from . import narrowbundle2
24
22 25 def uisetup():
23 26 wireprotov1peer.wirepeer.narrow_widen = peernarrowwiden
24 27
25 28 def reposetup(repo):
26 29 def wirereposetup(ui, peer):
27 30 def wrapped(orig, cmd, *args, **kwargs):
28 31 if cmd == 'unbundle':
29 32 # TODO: don't blindly add include/exclude wireproto
30 33 # arguments to unbundle.
31 34 include, exclude = repo.narrowpats
32 35 kwargs[r"includepats"] = ','.join(include)
33 36 kwargs[r"excludepats"] = ','.join(exclude)
34 37 return orig(cmd, *args, **kwargs)
35 38 extensions.wrapfunction(peer, '_calltwowaystream', wrapped)
36 39 hg.wirepeersetupfuncs.append(wirereposetup)
37 40
38 41 @wireprotov1server.wireprotocommand('narrow_widen', 'oldincludes oldexcludes'
39 42 ' newincludes newexcludes'
40 43 ' commonheads cgversion'
41 44 ' known ellipses',
42 45 permission='pull')
43 46 def narrow_widen(repo, proto, oldincludes, oldexcludes, newincludes,
44 47 newexcludes, commonheads, cgversion, known, ellipses):
45 48 """wireprotocol command to send data when a narrow clone is widen. We will
46 49 be sending a changegroup here.
47 50
48 51 The current set of arguments which are required:
49 52 oldincludes: the old includes of the narrow copy
50 53 oldexcludes: the old excludes of the narrow copy
51 54 newincludes: the new includes of the narrow copy
52 55 newexcludes: the new excludes of the narrow copy
53 56 commonheads: list of heads which are common between the server and client
54 57 cgversion(maybe): the changegroup version to produce
55 58 known: list of nodes which are known on the client (used in ellipses cases)
56 59 ellipses: whether to send ellipses data or not
57 60 """
58 61
59 62 preferuncompressed = False
60 63 try:
61 64 oldincludes = wireprototypes.decodelist(oldincludes)
62 65 newincludes = wireprototypes.decodelist(newincludes)
63 66 oldexcludes = wireprototypes.decodelist(oldexcludes)
64 67 newexcludes = wireprototypes.decodelist(newexcludes)
65 68 # validate the patterns
66 69 narrowspec.validatepatterns(set(oldincludes))
67 70 narrowspec.validatepatterns(set(newincludes))
68 71 narrowspec.validatepatterns(set(oldexcludes))
69 72 narrowspec.validatepatterns(set(newexcludes))
70 73
71 74 common = wireprototypes.decodelist(commonheads)
72 known = None
73 if known:
74 known = wireprototypes.decodelist(known)
75 known = wireprototypes.decodelist(known)
76 known = {nodemod.bin(n) for n in known}
75 77 if ellipses == '0':
76 78 ellipses = False
77 79 else:
78 80 ellipses = bool(ellipses)
79 81 cgversion = cgversion
80 newmatch = narrowspec.match(repo.root, include=newincludes,
81 exclude=newexcludes)
82 oldmatch = narrowspec.match(repo.root, include=oldincludes,
83 exclude=oldexcludes)
84 82
85 bundler = bundle2.widen_bundle(repo, oldmatch, newmatch, common, known,
86 cgversion, ellipses)
83 if not ellipses:
84 newmatch = narrowspec.match(repo.root, include=newincludes,
85 exclude=newexcludes)
86 oldmatch = narrowspec.match(repo.root, include=oldincludes,
87 exclude=oldexcludes)
88 bundler = bundle2.widen_bundle(repo, oldmatch, newmatch, common,
89 known, cgversion, ellipses)
90 else:
91 bundler = bundle2.bundle20(repo.ui)
92 narrowbundle2.generateellipsesbundle2(bundler, repo, oldincludes,
93 oldexcludes, newincludes, newexcludes, cgversion, common,
94 list(common), known, None)
87 95 except error.Abort as exc:
88 96 bundler = bundle2.bundle20(repo.ui)
89 97 manargs = [('message', pycompat.bytestr(exc))]
90 98 advargs = []
91 99 if exc.hint is not None:
92 100 advargs.append(('hint', exc.hint))
93 101 bundler.addpart(bundle2.bundlepart('error:abort', manargs, advargs))
94 102 preferuncompressed = True
95 103
96 104 chunks = bundler.getchunks()
97 105 return wireprototypes.streamres(gen=chunks,
98 106 prefer_uncompressed=preferuncompressed)
99 107
100 108 def peernarrowwiden(remote, **kwargs):
101 109 for ch in (r'oldincludes', r'newincludes', r'oldexcludes', r'newexcludes',
102 110 r'commonheads', r'known'):
103 111 kwargs[ch] = wireprototypes.encodelist(kwargs[ch])
104 112
105 113 kwargs[r'ellipses'] = '%i' % bool(kwargs[r'ellipses'])
106 114 f = remote._callcompressable('narrow_widen', **kwargs)
107 115 return bundle2.getunbundler(remote.ui, f)
@@ -1,403 +1,405 b''
1 1 # Copyright 2018 Gregory Szorc <gregory.szorc@gmail.com>
2 2 #
3 3 # This software may be used and distributed according to the terms of the
4 4 # GNU General Public License version 2 or any later version.
5 5
6 6 from __future__ import absolute_import
7 7
8 8 from .node import (
9 9 bin,
10 10 hex,
11 11 )
12 12 from .i18n import _
13 13 from .thirdparty import (
14 14 attr,
15 15 )
16 16 from . import (
17 17 error,
18 18 util,
19 19 )
20 20 from .utils import (
21 21 compression,
22 22 interfaceutil,
23 23 )
24 24
25 25 # Names of the SSH protocol implementations.
26 26 SSHV1 = 'ssh-v1'
27 27 # These are advertised over the wire. Increment the counters at the end
28 28 # to reflect BC breakages.
29 29 SSHV2 = 'exp-ssh-v2-0003'
30 30 HTTP_WIREPROTO_V2 = 'exp-http-v2-0003'
31 31
32 32 NARROWCAP = 'exp-narrow-1'
33 ELLIPSESCAP = 'exp-ellipses-1'
33 ELLIPSESCAP1 = 'exp-ellipses-1'
34 ELLIPSESCAP = 'exp-ellipses-2'
35 SUPPORTED_ELLIPSESCAP = (ELLIPSESCAP1, ELLIPSESCAP)
34 36
35 37 # All available wire protocol transports.
36 38 TRANSPORTS = {
37 39 SSHV1: {
38 40 'transport': 'ssh',
39 41 'version': 1,
40 42 },
41 43 SSHV2: {
42 44 'transport': 'ssh',
43 45 # TODO mark as version 2 once all commands are implemented.
44 46 'version': 1,
45 47 },
46 48 'http-v1': {
47 49 'transport': 'http',
48 50 'version': 1,
49 51 },
50 52 HTTP_WIREPROTO_V2: {
51 53 'transport': 'http',
52 54 'version': 2,
53 55 }
54 56 }
55 57
56 58 class bytesresponse(object):
57 59 """A wire protocol response consisting of raw bytes."""
58 60 def __init__(self, data):
59 61 self.data = data
60 62
61 63 class ooberror(object):
62 64 """wireproto reply: failure of a batch of operation
63 65
64 66 Something failed during a batch call. The error message is stored in
65 67 `self.message`.
66 68 """
67 69 def __init__(self, message):
68 70 self.message = message
69 71
70 72 class pushres(object):
71 73 """wireproto reply: success with simple integer return
72 74
73 75 The call was successful and returned an integer contained in `self.res`.
74 76 """
75 77 def __init__(self, res, output):
76 78 self.res = res
77 79 self.output = output
78 80
79 81 class pusherr(object):
80 82 """wireproto reply: failure
81 83
82 84 The call failed. The `self.res` attribute contains the error message.
83 85 """
84 86 def __init__(self, res, output):
85 87 self.res = res
86 88 self.output = output
87 89
88 90 class streamres(object):
89 91 """wireproto reply: binary stream
90 92
91 93 The call was successful and the result is a stream.
92 94
93 95 Accepts a generator containing chunks of data to be sent to the client.
94 96
95 97 ``prefer_uncompressed`` indicates that the data is expected to be
96 98 uncompressable and that the stream should therefore use the ``none``
97 99 engine.
98 100 """
99 101 def __init__(self, gen=None, prefer_uncompressed=False):
100 102 self.gen = gen
101 103 self.prefer_uncompressed = prefer_uncompressed
102 104
103 105 class streamreslegacy(object):
104 106 """wireproto reply: uncompressed binary stream
105 107
106 108 The call was successful and the result is a stream.
107 109
108 110 Accepts a generator containing chunks of data to be sent to the client.
109 111
110 112 Like ``streamres``, but sends an uncompressed data for "version 1" clients
111 113 using the application/mercurial-0.1 media type.
112 114 """
113 115 def __init__(self, gen=None):
114 116 self.gen = gen
115 117
116 118 # list of nodes encoding / decoding
117 119 def decodelist(l, sep=' '):
118 120 if l:
119 121 return [bin(v) for v in l.split(sep)]
120 122 return []
121 123
122 124 def encodelist(l, sep=' '):
123 125 try:
124 126 return sep.join(map(hex, l))
125 127 except TypeError:
126 128 raise
127 129
128 130 # batched call argument encoding
129 131
130 132 def escapebatcharg(plain):
131 133 return (plain
132 134 .replace(':', ':c')
133 135 .replace(',', ':o')
134 136 .replace(';', ':s')
135 137 .replace('=', ':e'))
136 138
137 139 def unescapebatcharg(escaped):
138 140 return (escaped
139 141 .replace(':e', '=')
140 142 .replace(':s', ';')
141 143 .replace(':o', ',')
142 144 .replace(':c', ':'))
143 145
144 146 # mapping of options accepted by getbundle and their types
145 147 #
146 148 # Meant to be extended by extensions. It is extensions responsibility to ensure
147 149 # such options are properly processed in exchange.getbundle.
148 150 #
149 151 # supported types are:
150 152 #
151 153 # :nodes: list of binary nodes
152 154 # :csv: list of comma-separated values
153 155 # :scsv: list of comma-separated values return as set
154 156 # :plain: string with no transformation needed.
155 157 GETBUNDLE_ARGUMENTS = {
156 158 'heads': 'nodes',
157 159 'bookmarks': 'boolean',
158 160 'common': 'nodes',
159 161 'obsmarkers': 'boolean',
160 162 'phases': 'boolean',
161 163 'bundlecaps': 'scsv',
162 164 'listkeys': 'csv',
163 165 'cg': 'boolean',
164 166 'cbattempted': 'boolean',
165 167 'stream': 'boolean',
166 168 'includepats': 'csv',
167 169 'excludepats': 'csv',
168 170 }
169 171
170 172 class baseprotocolhandler(interfaceutil.Interface):
171 173 """Abstract base class for wire protocol handlers.
172 174
173 175 A wire protocol handler serves as an interface between protocol command
174 176 handlers and the wire protocol transport layer. Protocol handlers provide
175 177 methods to read command arguments, redirect stdio for the duration of
176 178 the request, handle response types, etc.
177 179 """
178 180
179 181 name = interfaceutil.Attribute(
180 182 """The name of the protocol implementation.
181 183
182 184 Used for uniquely identifying the transport type.
183 185 """)
184 186
185 187 def getargs(args):
186 188 """return the value for arguments in <args>
187 189
188 190 For version 1 transports, returns a list of values in the same
189 191 order they appear in ``args``. For version 2 transports, returns
190 192 a dict mapping argument name to value.
191 193 """
192 194
193 195 def getprotocaps():
194 196 """Returns the list of protocol-level capabilities of client
195 197
196 198 Returns a list of capabilities as declared by the client for
197 199 the current request (or connection for stateful protocol handlers)."""
198 200
199 201 def getpayload():
200 202 """Provide a generator for the raw payload.
201 203
202 204 The caller is responsible for ensuring that the full payload is
203 205 processed.
204 206 """
205 207
206 208 def mayberedirectstdio():
207 209 """Context manager to possibly redirect stdio.
208 210
209 211 The context manager yields a file-object like object that receives
210 212 stdout and stderr output when the context manager is active. Or it
211 213 yields ``None`` if no I/O redirection occurs.
212 214
213 215 The intent of this context manager is to capture stdio output
214 216 so it may be sent in the response. Some transports support streaming
215 217 stdio to the client in real time. For these transports, stdio output
216 218 won't be captured.
217 219 """
218 220
219 221 def client():
220 222 """Returns a string representation of this client (as bytes)."""
221 223
222 224 def addcapabilities(repo, caps):
223 225 """Adds advertised capabilities specific to this protocol.
224 226
225 227 Receives the list of capabilities collected so far.
226 228
227 229 Returns a list of capabilities. The passed in argument can be returned.
228 230 """
229 231
230 232 def checkperm(perm):
231 233 """Validate that the client has permissions to perform a request.
232 234
233 235 The argument is the permission required to proceed. If the client
234 236 doesn't have that permission, the exception should raise or abort
235 237 in a protocol specific manner.
236 238 """
237 239
238 240 class commandentry(object):
239 241 """Represents a declared wire protocol command."""
240 242 def __init__(self, func, args='', transports=None,
241 243 permission='push', cachekeyfn=None, extracapabilitiesfn=None):
242 244 self.func = func
243 245 self.args = args
244 246 self.transports = transports or set()
245 247 self.permission = permission
246 248 self.cachekeyfn = cachekeyfn
247 249 self.extracapabilitiesfn = extracapabilitiesfn
248 250
249 251 def _merge(self, func, args):
250 252 """Merge this instance with an incoming 2-tuple.
251 253
252 254 This is called when a caller using the old 2-tuple API attempts
253 255 to replace an instance. The incoming values are merged with
254 256 data not captured by the 2-tuple and a new instance containing
255 257 the union of the two objects is returned.
256 258 """
257 259 return commandentry(func, args=args, transports=set(self.transports),
258 260 permission=self.permission)
259 261
260 262 # Old code treats instances as 2-tuples. So expose that interface.
261 263 def __iter__(self):
262 264 yield self.func
263 265 yield self.args
264 266
265 267 def __getitem__(self, i):
266 268 if i == 0:
267 269 return self.func
268 270 elif i == 1:
269 271 return self.args
270 272 else:
271 273 raise IndexError('can only access elements 0 and 1')
272 274
273 275 class commanddict(dict):
274 276 """Container for registered wire protocol commands.
275 277
276 278 It behaves like a dict. But __setitem__ is overwritten to allow silent
277 279 coercion of values from 2-tuples for API compatibility.
278 280 """
279 281 def __setitem__(self, k, v):
280 282 if isinstance(v, commandentry):
281 283 pass
282 284 # Cast 2-tuples to commandentry instances.
283 285 elif isinstance(v, tuple):
284 286 if len(v) != 2:
285 287 raise ValueError('command tuples must have exactly 2 elements')
286 288
287 289 # It is common for extensions to wrap wire protocol commands via
288 290 # e.g. ``wireproto.commands[x] = (newfn, args)``. Because callers
289 291 # doing this aren't aware of the new API that uses objects to store
290 292 # command entries, we automatically merge old state with new.
291 293 if k in self:
292 294 v = self[k]._merge(v[0], v[1])
293 295 else:
294 296 # Use default values from @wireprotocommand.
295 297 v = commandentry(v[0], args=v[1],
296 298 transports=set(TRANSPORTS),
297 299 permission='push')
298 300 else:
299 301 raise ValueError('command entries must be commandentry instances '
300 302 'or 2-tuples')
301 303
302 304 return super(commanddict, self).__setitem__(k, v)
303 305
304 306 def commandavailable(self, command, proto):
305 307 """Determine if a command is available for the requested protocol."""
306 308 assert proto.name in TRANSPORTS
307 309
308 310 entry = self.get(command)
309 311
310 312 if not entry:
311 313 return False
312 314
313 315 if proto.name not in entry.transports:
314 316 return False
315 317
316 318 return True
317 319
318 320 def supportedcompengines(ui, role):
319 321 """Obtain the list of supported compression engines for a request."""
320 322 assert role in (compression.CLIENTROLE, compression.SERVERROLE)
321 323
322 324 compengines = compression.compengines.supportedwireengines(role)
323 325
324 326 # Allow config to override default list and ordering.
325 327 if role == compression.SERVERROLE:
326 328 configengines = ui.configlist('server', 'compressionengines')
327 329 config = 'server.compressionengines'
328 330 else:
329 331 # This is currently implemented mainly to facilitate testing. In most
330 332 # cases, the server should be in charge of choosing a compression engine
331 333 # because a server has the most to lose from a sub-optimal choice. (e.g.
332 334 # CPU DoS due to an expensive engine or a network DoS due to poor
333 335 # compression ratio).
334 336 configengines = ui.configlist('experimental',
335 337 'clientcompressionengines')
336 338 config = 'experimental.clientcompressionengines'
337 339
338 340 # No explicit config. Filter out the ones that aren't supposed to be
339 341 # advertised and return default ordering.
340 342 if not configengines:
341 343 attr = 'serverpriority' if role == util.SERVERROLE else 'clientpriority'
342 344 return [e for e in compengines
343 345 if getattr(e.wireprotosupport(), attr) > 0]
344 346
345 347 # If compression engines are listed in the config, assume there is a good
346 348 # reason for it (like server operators wanting to achieve specific
347 349 # performance characteristics). So fail fast if the config references
348 350 # unusable compression engines.
349 351 validnames = set(e.name() for e in compengines)
350 352 invalidnames = set(e for e in configengines if e not in validnames)
351 353 if invalidnames:
352 354 raise error.Abort(_('invalid compression engine defined in %s: %s') %
353 355 (config, ', '.join(sorted(invalidnames))))
354 356
355 357 compengines = [e for e in compengines if e.name() in configengines]
356 358 compengines = sorted(compengines,
357 359 key=lambda e: configengines.index(e.name()))
358 360
359 361 if not compengines:
360 362 raise error.Abort(_('%s config option does not specify any known '
361 363 'compression engines') % config,
362 364 hint=_('usable compression engines: %s') %
363 365 ', '.sorted(validnames))
364 366
365 367 return compengines
366 368
367 369 @attr.s
368 370 class encodedresponse(object):
369 371 """Represents response data that is already content encoded.
370 372
371 373 Wire protocol version 2 only.
372 374
373 375 Commands typically emit Python objects that are encoded and sent over the
374 376 wire. If commands emit an object of this type, the encoding step is bypassed
375 377 and the content from this object is used instead.
376 378 """
377 379 data = attr.ib()
378 380
379 381 @attr.s
380 382 class alternatelocationresponse(object):
381 383 """Represents a response available at an alternate location.
382 384
383 385 Instances are sent in place of actual response objects when the server
384 386 is sending a "content redirect" response.
385 387
386 388 Only compatible with wire protocol version 2.
387 389 """
388 390 url = attr.ib()
389 391 mediatype = attr.ib()
390 392 size = attr.ib(default=None)
391 393 fullhashes = attr.ib(default=None)
392 394 fullhashseed = attr.ib(default=None)
393 395 serverdercerts = attr.ib(default=None)
394 396 servercadercerts = attr.ib(default=None)
395 397
396 398 @attr.s
397 399 class indefinitebytestringresponse(object):
398 400 """Represents an object to be encoded to an indefinite length bytestring.
399 401
400 402 Instances are initialized from an iterable of chunks, with each chunk being
401 403 a bytes instance.
402 404 """
403 405 chunks = attr.ib()
@@ -1,469 +1,459 b''
1 1 $ . "$TESTDIR/narrow-library.sh"
2 2
3 3 initialize nested directories to validate complex include/exclude patterns
4 4
5 5 $ hg init master
6 6 $ cd master
7 7 $ cat >> .hg/hgrc <<EOF
8 8 > [narrow]
9 9 > serveellipses=True
10 10 > EOF
11 11
12 12 $ echo root > root
13 13 $ hg add root
14 14 $ hg commit -m 'add root'
15 15
16 16 $ for d in dir1 dir2 dir1/dirA dir1/dirB dir2/dirA dir2/dirB
17 17 > do
18 18 > mkdir -p $d
19 19 > echo $d/foo > $d/foo
20 20 > hg add $d/foo
21 21 > hg commit -m "add $d/foo"
22 22 > echo $d/bar > $d/bar
23 23 > hg add $d/bar
24 24 > hg commit -m "add $d/bar"
25 25 > done
26 26 #if execbit
27 27 $ chmod +x dir1/dirA/foo
28 28 $ hg commit -m "make dir1/dirA/foo executable"
29 29 #else
30 30 $ hg import --bypass - <<EOF
31 31 > # HG changeset patch
32 32 > make dir1/dirA/foo executable
33 33 >
34 34 > diff --git a/dir1/dirA/foo b/dir1/dirA/foo
35 35 > old mode 100644
36 36 > new mode 100755
37 37 > EOF
38 38 applying patch from stdin
39 39 $ hg update -qr tip
40 40 #endif
41 41 $ hg log -G -T '{rev} {node|short} {files}\n'
42 42 @ 13 c87ca422d521 dir1/dirA/foo
43 43 |
44 44 o 12 951b8a83924e dir2/dirB/bar
45 45 |
46 46 o 11 01ae5a51b563 dir2/dirB/foo
47 47 |
48 48 o 10 5eababdf0ac5 dir2/dirA/bar
49 49 |
50 50 o 9 99d690663739 dir2/dirA/foo
51 51 |
52 52 o 8 8e80155d5445 dir1/dirB/bar
53 53 |
54 54 o 7 406760310428 dir1/dirB/foo
55 55 |
56 56 o 6 623466a5f475 dir1/dirA/bar
57 57 |
58 58 o 5 06ff3a5be997 dir1/dirA/foo
59 59 |
60 60 o 4 33227af02764 dir2/bar
61 61 |
62 62 o 3 5e1f9d8d7c69 dir2/foo
63 63 |
64 64 o 2 594bc4b13d4a dir1/bar
65 65 |
66 66 o 1 47f480a08324 dir1/foo
67 67 |
68 68 o 0 2a4f0c3b67da root
69 69
70 70 $ cd ..
71 71
72 72 clone a narrow portion of the master, such that we can widen it later
73 73
74 74 $ hg clone --narrow ssh://user@dummy/master narrow \
75 75 > --include dir1 \
76 76 > --include dir2 \
77 77 > --exclude dir1/dirA \
78 78 > --exclude dir1/dirB \
79 79 > --exclude dir2/dirA \
80 80 > --exclude dir2/dirB
81 81 requesting all changes
82 82 adding changesets
83 83 adding manifests
84 84 adding file changes
85 85 added 6 changesets with 4 changes to 4 files
86 86 new changesets *:* (glob)
87 87 updating to branch default
88 88 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 89
90 90 $ cd narrow
91 91 $ hg tracked
92 92 I path:dir1
93 93 I path:dir2
94 94 X path:dir1/dirA
95 95 X path:dir1/dirB
96 96 X path:dir2/dirA
97 97 X path:dir2/dirB
98 98 $ hg manifest -r tip
99 99 dir1/bar
100 100 dir1/dirA/bar
101 101 dir1/dirA/foo
102 102 dir1/dirB/bar
103 103 dir1/dirB/foo
104 104 dir1/foo
105 105 dir2/bar
106 106 dir2/dirA/bar
107 107 dir2/dirA/foo
108 108 dir2/dirB/bar
109 109 dir2/dirB/foo
110 110 dir2/foo
111 111 root
112 112 $ find * | sort
113 113 dir1
114 114 dir1/bar
115 115 dir1/foo
116 116 dir2
117 117 dir2/bar
118 118 dir2/foo
119 119 $ hg log -G -T '{rev} {node|short}{if(ellipsis, "...")} {files}\n'
120 120 @ 5 c87ca422d521... dir1/dirA/foo
121 121 |
122 122 o 4 33227af02764 dir2/bar
123 123 |
124 124 o 3 5e1f9d8d7c69 dir2/foo
125 125 |
126 126 o 2 594bc4b13d4a dir1/bar
127 127 |
128 128 o 1 47f480a08324 dir1/foo
129 129 |
130 130 o 0 2a4f0c3b67da... root
131 131
132 132
133 133 widen the narrow checkout
134 134
135 135 $ hg tracked --removeexclude dir1/dirA
136 136 comparing with ssh://user@dummy/master
137 137 searching for changes
138 no changes found
139 138 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
140 139 adding changesets
141 140 adding manifests
142 141 adding file changes
143 142 added 9 changesets with 6 changes to 6 files
144 new changesets *:* (glob)
145 143 $ hg tracked
146 144 I path:dir1
147 145 I path:dir2
148 146 X path:dir1/dirB
149 147 X path:dir2/dirA
150 148 X path:dir2/dirB
151 149 $ find * | sort
152 150 dir1
153 151 dir1/bar
154 152 dir1/dirA
155 153 dir1/dirA/bar
156 154 dir1/dirA/foo
157 155 dir1/foo
158 156 dir2
159 157 dir2/bar
160 158 dir2/foo
161 159
162 160 #if execbit
163 161 $ test -x dir1/dirA/foo && echo executable
164 162 executable
165 163 $ test -x dir1/dirA/bar || echo not executable
166 164 not executable
167 165 #endif
168 166
169 167 $ hg log -G -T '{rev} {node|short}{if(ellipsis, "...")} {files}\n'
170 168 @ 8 c87ca422d521 dir1/dirA/foo
171 169 |
172 170 o 7 951b8a83924e... dir2/dirB/bar
173 171 |
174 172 o 6 623466a5f475 dir1/dirA/bar
175 173 |
176 174 o 5 06ff3a5be997 dir1/dirA/foo
177 175 |
178 176 o 4 33227af02764 dir2/bar
179 177 |
180 178 o 3 5e1f9d8d7c69 dir2/foo
181 179 |
182 180 o 2 594bc4b13d4a dir1/bar
183 181 |
184 182 o 1 47f480a08324 dir1/foo
185 183 |
186 184 o 0 2a4f0c3b67da... root
187 185
188 186
189 187 widen narrow spec again, but exclude a file in previously included spec
190 188
191 189 $ hg tracked --removeexclude dir2/dirB --addexclude dir1/dirA/bar
192 190 comparing with ssh://user@dummy/master
193 191 searching for changes
194 192 looking for local changes to affected paths
195 193 deleting data/dir1/dirA/bar.i (reporevlogstore !)
196 194 deleting data/dir1/dirA/bar/0eca1d0cbdaea4651d1d04d71976a6d2d9bfaae5 (reposimplestore !)
197 195 deleting data/dir1/dirA/bar/index (reposimplestore !)
198 no changes found
199 196 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
200 197 adding changesets
201 198 adding manifests
202 199 adding file changes
203 200 added 11 changesets with 7 changes to 7 files
204 new changesets *:* (glob)
205 201 $ hg tracked
206 202 I path:dir1
207 203 I path:dir2
208 204 X path:dir1/dirA/bar
209 205 X path:dir1/dirB
210 206 X path:dir2/dirA
211 207 $ find * | sort
212 208 dir1
213 209 dir1/bar
214 210 dir1/dirA
215 211 dir1/dirA/foo
216 212 dir1/foo
217 213 dir2
218 214 dir2/bar
219 215 dir2/dirB
220 216 dir2/dirB/bar
221 217 dir2/dirB/foo
222 218 dir2/foo
223 219 $ hg log -G -T '{rev} {node|short}{if(ellipsis, "...")} {files}\n'
224 220 @ 10 c87ca422d521 dir1/dirA/foo
225 221 |
226 222 o 9 951b8a83924e dir2/dirB/bar
227 223 |
228 224 o 8 01ae5a51b563 dir2/dirB/foo
229 225 |
230 226 o 7 5eababdf0ac5... dir2/dirA/bar
231 227 |
232 228 o 6 623466a5f475... dir1/dirA/bar
233 229 |
234 230 o 5 06ff3a5be997 dir1/dirA/foo
235 231 |
236 232 o 4 33227af02764 dir2/bar
237 233 |
238 234 o 3 5e1f9d8d7c69 dir2/foo
239 235 |
240 236 o 2 594bc4b13d4a dir1/bar
241 237 |
242 238 o 1 47f480a08324 dir1/foo
243 239 |
244 240 o 0 2a4f0c3b67da... root
245 241
246 242
247 243 widen narrow spec yet again, excluding a directory in previous spec
248 244
249 245 $ hg tracked --removeexclude dir2/dirA --addexclude dir1/dirA
250 246 comparing with ssh://user@dummy/master
251 247 searching for changes
252 248 looking for local changes to affected paths
253 249 deleting data/dir1/dirA/foo.i (reporevlogstore !)
254 250 deleting data/dir1/dirA/foo/162caeb3d55dceb1fee793aa631ac8c73fcb8b5e (reposimplestore !)
255 251 deleting data/dir1/dirA/foo/index (reposimplestore !)
256 no changes found
257 252 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
258 253 adding changesets
259 254 adding manifests
260 255 adding file changes
261 256 added 13 changesets with 8 changes to 8 files
262 new changesets *:* (glob)
263 257 $ hg tracked
264 258 I path:dir1
265 259 I path:dir2
266 260 X path:dir1/dirA
267 261 X path:dir1/dirA/bar
268 262 X path:dir1/dirB
269 263 $ find * | sort
270 264 dir1
271 265 dir1/bar
272 266 dir1/foo
273 267 dir2
274 268 dir2/bar
275 269 dir2/dirA
276 270 dir2/dirA/bar
277 271 dir2/dirA/foo
278 272 dir2/dirB
279 273 dir2/dirB/bar
280 274 dir2/dirB/foo
281 275 dir2/foo
282 276 $ hg log -G -T '{rev} {node|short}{if(ellipsis, "...")} {files}\n'
283 277 @ 12 c87ca422d521... dir1/dirA/foo
284 278 |
285 279 o 11 951b8a83924e dir2/dirB/bar
286 280 |
287 281 o 10 01ae5a51b563 dir2/dirB/foo
288 282 |
289 283 o 9 5eababdf0ac5 dir2/dirA/bar
290 284 |
291 285 o 8 99d690663739 dir2/dirA/foo
292 286 |
293 287 o 7 8e80155d5445... dir1/dirB/bar
294 288 |
295 289 o 6 623466a5f475... dir1/dirA/bar
296 290 |
297 291 o 5 06ff3a5be997... dir1/dirA/foo
298 292 |
299 293 o 4 33227af02764 dir2/bar
300 294 |
301 295 o 3 5e1f9d8d7c69 dir2/foo
302 296 |
303 297 o 2 594bc4b13d4a dir1/bar
304 298 |
305 299 o 1 47f480a08324 dir1/foo
306 300 |
307 301 o 0 2a4f0c3b67da... root
308 302
309 303
310 304 include a directory that was previously explicitly excluded
311 305
312 306 $ hg tracked --removeexclude dir1/dirA
313 307 comparing with ssh://user@dummy/master
314 308 searching for changes
315 no changes found
316 309 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
317 310 adding changesets
318 311 adding manifests
319 312 adding file changes
320 313 added 13 changesets with 9 changes to 9 files
321 new changesets *:* (glob)
322 314 $ hg tracked
323 315 I path:dir1
324 316 I path:dir2
325 317 X path:dir1/dirA/bar
326 318 X path:dir1/dirB
327 319 $ find * | sort
328 320 dir1
329 321 dir1/bar
330 322 dir1/dirA
331 323 dir1/dirA/foo
332 324 dir1/foo
333 325 dir2
334 326 dir2/bar
335 327 dir2/dirA
336 328 dir2/dirA/bar
337 329 dir2/dirA/foo
338 330 dir2/dirB
339 331 dir2/dirB/bar
340 332 dir2/dirB/foo
341 333 dir2/foo
342 334 $ hg log -G -T '{rev} {node|short}{if(ellipsis, "...")} {files}\n'
343 335 @ 12 c87ca422d521 dir1/dirA/foo
344 336 |
345 337 o 11 951b8a83924e dir2/dirB/bar
346 338 |
347 339 o 10 01ae5a51b563 dir2/dirB/foo
348 340 |
349 341 o 9 5eababdf0ac5 dir2/dirA/bar
350 342 |
351 343 o 8 99d690663739 dir2/dirA/foo
352 344 |
353 345 o 7 8e80155d5445... dir1/dirB/bar
354 346 |
355 347 o 6 623466a5f475... dir1/dirA/bar
356 348 |
357 349 o 5 06ff3a5be997 dir1/dirA/foo
358 350 |
359 351 o 4 33227af02764 dir2/bar
360 352 |
361 353 o 3 5e1f9d8d7c69 dir2/foo
362 354 |
363 355 o 2 594bc4b13d4a dir1/bar
364 356 |
365 357 o 1 47f480a08324 dir1/foo
366 358 |
367 359 o 0 2a4f0c3b67da... root
368 360
369 361
370 362 $ cd ..
371 363
372 364 clone a narrow portion of the master, such that we can widen it later
373 365
374 366 $ hg clone --narrow ssh://user@dummy/master narrow2 --include dir1/dirA
375 367 requesting all changes
376 368 adding changesets
377 369 adding manifests
378 370 adding file changes
379 371 added 5 changesets with 2 changes to 2 files
380 372 new changesets *:* (glob)
381 373 updating to branch default
382 374 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
383 375 $ cd narrow2
384 376 $ find * | sort
385 377 dir1
386 378 dir1/dirA
387 379 dir1/dirA/bar
388 380 dir1/dirA/foo
389 381 $ hg tracked --addinclude dir1
390 382 comparing with ssh://user@dummy/master
391 383 searching for changes
392 no changes found
393 384 saved backup bundle to $TESTTMP/narrow2/.hg/strip-backup/*-widen.hg (glob)
394 385 adding changesets
395 386 adding manifests
396 387 adding file changes
397 388 added 10 changesets with 6 changes to 6 files
398 new changesets *:* (glob)
399 389 $ find * | sort
400 390 dir1
401 391 dir1/bar
402 392 dir1/dirA
403 393 dir1/dirA/bar
404 394 dir1/dirA/foo
405 395 dir1/dirB
406 396 dir1/dirB/bar
407 397 dir1/dirB/foo
408 398 dir1/foo
409 399 $ hg log -G -T '{rev} {node|short}{if(ellipsis, "...")} {files}\n'
410 400 @ 9 c87ca422d521 dir1/dirA/foo
411 401 |
412 402 o 8 951b8a83924e... dir2/dirB/bar
413 403 |
414 404 o 7 8e80155d5445 dir1/dirB/bar
415 405 |
416 406 o 6 406760310428 dir1/dirB/foo
417 407 |
418 408 o 5 623466a5f475 dir1/dirA/bar
419 409 |
420 410 o 4 06ff3a5be997 dir1/dirA/foo
421 411 |
422 412 o 3 33227af02764... dir2/bar
423 413 |
424 414 o 2 594bc4b13d4a dir1/bar
425 415 |
426 416 o 1 47f480a08324 dir1/foo
427 417 |
428 418 o 0 2a4f0c3b67da... root
429 419
430 420
431 421 Illegal patterns are rejected
432 422
433 423 $ hg tracked --addinclude glob:**
434 424 abort: invalid prefix on narrow pattern: glob:**
435 425 (narrow patterns must begin with one of the following: path:, rootfilesin:)
436 426 [255]
437 427
438 428 $ hg tracked --addexclude set:ignored
439 429 abort: invalid prefix on narrow pattern: set:ignored
440 430 (narrow patterns must begin with one of the following: path:, rootfilesin:)
441 431 [255]
442 432
443 433 $ cat .hg/store/narrowspec
444 434 [include]
445 435 path:dir1
446 436 path:dir1/dirA
447 437 [exclude]
448 438
449 439 $ cat > .hg/store/narrowspec << EOF
450 440 > [include]
451 441 > glob:**
452 442 > EOF
453 443
454 444 $ hg tracked
455 445 abort: invalid prefix on narrow pattern: glob:**
456 446 (narrow patterns must begin with one of the following: path:, rootfilesin:)
457 447 [255]
458 448
459 449 $ cat > .hg/store/narrowspec << EOF
460 450 > [include]
461 451 > path:.
462 452 > [exclude]
463 453 > set:ignored
464 454 > EOF
465 455
466 456 $ hg tracked
467 457 abort: invalid prefix on narrow pattern: set:ignored
468 458 (narrow patterns must begin with one of the following: path:, rootfilesin:)
469 459 [255]
@@ -1,230 +1,224 b''
1 1 #testcases flat tree
2 2 $ . "$TESTDIR/narrow-library.sh"
3 3
4 4 #if tree
5 5 $ cat << EOF >> $HGRCPATH
6 6 > [experimental]
7 7 > treemanifest = 1
8 8 > EOF
9 9 #endif
10 10
11 11 $ hg init master
12 12 $ cd master
13 13 $ cat >> .hg/hgrc <<EOF
14 14 > [narrow]
15 15 > serveellipses=True
16 16 > EOF
17 17
18 18 $ mkdir inside
19 19 $ echo 'inside' > inside/f
20 20 $ hg add inside/f
21 21 $ hg commit -m 'add inside'
22 22
23 23 $ mkdir widest
24 24 $ echo 'widest' > widest/f
25 25 $ hg add widest/f
26 26 $ hg commit -m 'add widest'
27 27
28 28 $ mkdir outside
29 29 $ echo 'outside' > outside/f
30 30 $ hg add outside/f
31 31 $ hg commit -m 'add outside'
32 32
33 33 $ cd ..
34 34
35 35 narrow clone the inside file
36 36
37 37 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
38 38 requesting all changes
39 39 adding changesets
40 40 adding manifests
41 41 adding file changes
42 42 added 2 changesets with 1 changes to 1 files
43 43 new changesets *:* (glob)
44 44 updating to branch default
45 45 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 46 $ cd narrow
47 47 $ hg tracked
48 48 I path:inside
49 49 $ ls
50 50 inside
51 51 $ cat inside/f
52 52 inside
53 53 $ cd ..
54 54
55 55 add more upstream files which we will include in a wider narrow spec
56 56
57 57 $ cd master
58 58
59 59 $ mkdir wider
60 60 $ echo 'wider' > wider/f
61 61 $ hg add wider/f
62 62 $ echo 'widest v2' > widest/f
63 63 $ hg commit -m 'add wider, update widest'
64 64
65 65 $ echo 'widest v3' > widest/f
66 66 $ hg commit -m 'update widest v3'
67 67
68 68 $ echo 'inside v2' > inside/f
69 69 $ hg commit -m 'update inside'
70 70
71 71 $ mkdir outside2
72 72 $ echo 'outside2' > outside2/f
73 73 $ hg add outside2/f
74 74 $ hg commit -m 'add outside2'
75 75
76 76 $ echo 'widest v4' > widest/f
77 77 $ hg commit -m 'update widest v4'
78 78
79 79 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
80 80 7: update widest v4
81 81 6: add outside2
82 82 5: update inside
83 83 4: update widest v3
84 84 3: add wider, update widest
85 85 2: add outside
86 86 1: add widest
87 87 0: add inside
88 88
89 89 $ cd ..
90 90
91 91 Testing the --import-rules flag of `hg tracked` command
92 92
93 93 $ cd narrow
94 94 $ hg tracked --import-rules
95 95 hg tracked: option --import-rules requires argument
96 96 hg tracked [OPTIONS]... [REMOTE]
97 97
98 98 show or change the current narrowspec
99 99
100 100 options ([+] can be repeated):
101 101
102 102 --addinclude VALUE [+] new paths to include
103 103 --removeinclude VALUE [+] old paths to no longer include
104 104 --addexclude VALUE [+] new paths to exclude
105 105 --import-rules VALUE import narrowspecs from a file
106 106 --removeexclude VALUE [+] old paths to no longer exclude
107 107 --clear whether to replace the existing narrowspec
108 108 --force-delete-local-changes forces deletion of local changes when
109 109 narrowing
110 110 --update-working-copy update working copy when the store has
111 111 changed
112 112 -e --ssh CMD specify ssh command to use
113 113 --remotecmd CMD specify hg command to run on the remote side
114 114 --insecure do not verify server certificate (ignoring
115 115 web.cacerts config)
116 116
117 117 (use 'hg tracked -h' to show more help)
118 118 [255]
119 119 $ hg tracked --import-rules doesnotexist
120 120 abort: cannot read narrowspecs from '$TESTTMP/narrow/doesnotexist': $ENOENT$
121 121 [255]
122 122
123 123 $ cat > specs <<EOF
124 124 > %include foo
125 125 > [include]
126 126 > path:widest/
127 127 > [exclude]
128 128 > path:inside/
129 129 > EOF
130 130
131 131 $ hg tracked --import-rules specs
132 132 abort: including other spec files using '%include' is not supported in narrowspec
133 133 [255]
134 134
135 135 $ cat > specs <<EOF
136 136 > [include]
137 137 > outisde
138 138 > [exclude]
139 139 > inside
140 140 > EOF
141 141
142 142 $ hg tracked --import-rules specs
143 143 comparing with ssh://user@dummy/master
144 144 searching for changes
145 145 looking for local changes to affected paths
146 146 deleting data/inside/f.i
147 147 deleting meta/inside/00manifest.i (tree !)
148 no changes found
149 148 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
150 149 adding changesets
151 150 adding manifests
152 151 adding file changes
153 152 added 2 changesets with 0 changes to 0 files
154 new changesets *:* (glob)
155 153 $ hg tracked
156 154 I path:outisde
157 155 X path:inside
158 156
159 157 Testing the --import-rules flag with --addinclude and --addexclude
160 158
161 159 $ cat > specs <<EOF
162 160 > [include]
163 161 > widest
164 162 > EOF
165 163
166 164 $ hg tracked --import-rules specs --addinclude 'wider/'
167 165 comparing with ssh://user@dummy/master
168 166 searching for changes
169 no changes found
170 167 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
171 168 adding changesets
172 169 adding manifests
173 170 adding file changes
174 171 added 3 changesets with 1 changes to 1 files
175 new changesets *:* (glob)
176 172 $ hg tracked
177 173 I path:outisde
178 174 I path:wider
179 175 I path:widest
180 176 X path:inside
181 177
182 178 $ cat > specs <<EOF
183 179 > [exclude]
184 180 > outside2
185 181 > EOF
186 182
187 183 $ hg tracked --import-rules specs --addexclude 'widest'
188 184 comparing with ssh://user@dummy/master
189 185 searching for changes
190 186 looking for local changes to affected paths
191 187 deleting data/widest/f.i
192 188 deleting meta/widest/00manifest.i (tree !)
193 189 $ hg tracked
194 190 I path:outisde
195 191 I path:wider
196 192 X path:inside
197 193 X path:outside2
198 194 X path:widest
199 195
200 196 $ hg tracked --import-rules specs --clear
201 197 abort: the --clear option is not yet supported
202 198 [255]
203 199
204 200 Testing with passing a out of wdir file
205 201
206 202 $ cat > ../nspecs <<EOF
207 203 > [include]
208 204 > widest
209 205 > EOF
210 206
211 207 $ hg tracked --import-rules ../nspecs
212 208 comparing with ssh://user@dummy/master
213 209 searching for changes
214 no changes found
215 210 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
216 211 adding changesets
217 212 adding manifests
218 213 adding file changes
219 214 added 3 changesets with 0 changes to 0 files
220 new changesets *:* (glob)
221 215
222 216 $ cd ..
223 217
224 218 Testing tracked command on a non-narrow repo
225 219
226 220 $ hg init non-narrow
227 221 $ cd non-narrow
228 222 $ hg tracked --addinclude foobar
229 223 abort: the tracked command is only supported on respositories cloned with --narrow
230 224 [255]
@@ -1,370 +1,363 b''
1 1 #testcases flat tree
2 2 $ . "$TESTDIR/narrow-library.sh"
3 3
4 4 #if tree
5 5 $ cat << EOF >> $HGRCPATH
6 6 > [experimental]
7 7 > treemanifest = 1
8 8 > EOF
9 9 #endif
10 10
11 11 $ hg init master
12 12 $ cd master
13 13 $ cat >> .hg/hgrc <<EOF
14 14 > [narrow]
15 15 > serveellipses=True
16 16 > EOF
17 17
18 18 $ mkdir inside
19 19 $ echo 'inside' > inside/f
20 20 $ hg add inside/f
21 21 $ hg commit -m 'add inside'
22 22
23 23 $ mkdir widest
24 24 $ echo 'widest' > widest/f
25 25 $ hg add widest/f
26 26 $ hg commit -m 'add widest'
27 27
28 28 $ mkdir outside
29 29 $ echo 'outside' > outside/f
30 30 $ hg add outside/f
31 31 $ hg commit -m 'add outside'
32 32
33 33 $ cd ..
34 34
35 35 narrow clone the inside file
36 36
37 37 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
38 38 requesting all changes
39 39 adding changesets
40 40 adding manifests
41 41 adding file changes
42 42 added 2 changesets with 1 changes to 1 files
43 43 new changesets *:* (glob)
44 44 updating to branch default
45 45 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 46 $ cd narrow
47 47 $ hg tracked
48 48 I path:inside
49 49 $ ls
50 50 inside
51 51 $ cat inside/f
52 52 inside
53 53 $ cd ..
54 54
55 55 add more upstream files which we will include in a wider narrow spec
56 56
57 57 $ cd master
58 58
59 59 $ mkdir wider
60 60 $ echo 'wider' > wider/f
61 61 $ hg add wider/f
62 62 $ echo 'widest v2' > widest/f
63 63 $ hg commit -m 'add wider, update widest'
64 64
65 65 $ echo 'widest v3' > widest/f
66 66 $ hg commit -m 'update widest v3'
67 67
68 68 $ echo 'inside v2' > inside/f
69 69 $ hg commit -m 'update inside'
70 70
71 71 $ mkdir outside2
72 72 $ echo 'outside2' > outside2/f
73 73 $ hg add outside2/f
74 74 $ hg commit -m 'add outside2'
75 75
76 76 $ echo 'widest v4' > widest/f
77 77 $ hg commit -m 'update widest v4'
78 78
79 79 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
80 80 7: update widest v4
81 81 6: add outside2
82 82 5: update inside
83 83 4: update widest v3
84 84 3: add wider, update widest
85 85 2: add outside
86 86 1: add widest
87 87 0: add inside
88 88
89 89 $ cd ..
90 90
91 91 Widen the narrow spec to see the widest file. This should not get the newly
92 92 added upstream revisions.
93 93
94 94 $ cd narrow
95 95 $ hg tracked --addinclude widest/f
96 96 comparing with ssh://user@dummy/master
97 97 searching for changes
98 no changes found
99 98 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
100 99 adding changesets
101 100 adding manifests
102 101 adding file changes
103 102 added 3 changesets with 2 changes to 2 files
104 new changesets *:* (glob)
105 103 $ hg tracked
106 104 I path:inside
107 105 I path:widest/f
108 106
109 107 $ cat widest/f
110 108 widest
111 109
112 110 Pull down the newly added upstream revision.
113 111
114 112 $ hg pull
115 113 pulling from ssh://user@dummy/master
116 114 searching for changes
117 115 adding changesets
118 116 adding manifests
119 117 adding file changes
120 118 added 5 changesets with 4 changes to 2 files
121 119 new changesets *:* (glob)
122 120 (run 'hg update' to get a working copy)
123 121 $ hg update -r 'desc("add wider")'
124 122 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
125 123 $ cat widest/f
126 124 widest v2
127 125
128 126 $ hg update -r 'desc("update inside")'
129 127 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
130 128 $ cat widest/f
131 129 widest v3
132 130 $ cat inside/f
133 131 inside v2
134 132
135 133 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
136 134 7: update widest v4
137 135 ...6: add outside2
138 136 5: update inside
139 137 4: update widest v3
140 138 3: add wider, update widest
141 139 ...2: add outside
142 140 1: add widest
143 141 0: add inside
144 142
145 143 Check that widening with a newline fails
146 144
147 145 $ hg tracked --addinclude 'widest
148 146 > '
149 147 abort: newlines are not allowed in narrowspec paths
150 148 [255]
151 149
152 150 widen the narrow spec to include the wider file
153 151
154 152 $ hg tracked --addinclude wider
155 153 comparing with ssh://user@dummy/master
156 154 searching for changes
157 no changes found
158 155 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
159 156 adding changesets
160 157 adding manifests
161 158 adding file changes
162 159 added 8 changesets with 7 changes to 3 files
163 new changesets *:* (glob)
164 160 $ hg tracked
165 161 I path:inside
166 162 I path:wider
167 163 I path:widest/f
168 164 $ hg update 'desc("add widest")'
169 165 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
170 166 $ cat widest/f
171 167 widest
172 168 $ hg update 'desc("add wider, update widest")'
173 169 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 170 $ cat wider/f
175 171 wider
176 172 $ cat widest/f
177 173 widest v2
178 174 $ hg update 'desc("update widest v3")'
179 175 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
180 176 $ cat widest/f
181 177 widest v3
182 178 $ hg update 'desc("update widest v4")'
183 179 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
184 180 $ cat widest/f
185 181 widest v4
186 182
187 183 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
188 184 7: update widest v4
189 185 ...6: add outside2
190 186 5: update inside
191 187 4: update widest v3
192 188 3: add wider, update widest
193 189 ...2: add outside
194 190 1: add widest
195 191 0: add inside
196 192
197 193 separate suite of tests: files from 0-10 modified in changes 0-10. This allows
198 194 more obvious precise tests tickling particular corner cases.
199 195
200 196 $ cd ..
201 197 $ hg init upstream
202 198 $ cd upstream
203 199 $ cat >> .hg/hgrc <<EOF
204 200 > [narrow]
205 201 > serveellipses=True
206 202 > EOF
207 203 $ for x in `$TESTDIR/seq.py 0 10`
208 204 > do
209 205 > mkdir d$x
210 206 > echo $x > d$x/f
211 207 > hg add d$x/f
212 208 > hg commit -m "add d$x/f"
213 209 > done
214 210 $ hg log -T "{rev}: {desc}\n"
215 211 10: add d10/f
216 212 9: add d9/f
217 213 8: add d8/f
218 214 7: add d7/f
219 215 6: add d6/f
220 216 5: add d5/f
221 217 4: add d4/f
222 218 3: add d3/f
223 219 2: add d2/f
224 220 1: add d1/f
225 221 0: add d0/f
226 222
227 223 make narrow clone with every third node.
228 224
229 225 $ cd ..
230 226 $ hg clone --narrow ssh://user@dummy/upstream narrow2 --include d0 --include d3 --include d6 --include d9
231 227 requesting all changes
232 228 adding changesets
233 229 adding manifests
234 230 adding file changes
235 231 added 8 changesets with 4 changes to 4 files
236 232 new changesets *:* (glob)
237 233 updating to branch default
238 234 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
239 235 $ cd narrow2
240 236 $ hg tracked
241 237 I path:d0
242 238 I path:d3
243 239 I path:d6
244 240 I path:d9
245 241 $ hg verify
246 242 checking changesets
247 243 checking manifests
248 244 checking directory manifests (tree !)
249 245 crosschecking files in changesets and manifests
250 246 checking files
251 247 checked 8 changesets with 4 changes to 4 files
252 248 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
253 249 ...7: add d10/f
254 250 6: add d9/f
255 251 ...5: add d8/f
256 252 4: add d6/f
257 253 ...3: add d5/f
258 254 2: add d3/f
259 255 ...1: add d2/f
260 256 0: add d0/f
261 257 $ hg tracked --addinclude d1
262 258 comparing with ssh://user@dummy/upstream
263 259 searching for changes
264 no changes found
265 260 saved backup bundle to $TESTTMP/narrow2/.hg/strip-backup/*-widen.hg (glob)
266 261 adding changesets
267 262 adding manifests
268 263 adding file changes
269 264 added 9 changesets with 5 changes to 5 files
270 new changesets *:* (glob)
271 265 $ hg tracked
272 266 I path:d0
273 267 I path:d1
274 268 I path:d3
275 269 I path:d6
276 270 I path:d9
277 271 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
278 272 ...8: add d10/f
279 273 7: add d9/f
280 274 ...6: add d8/f
281 275 5: add d6/f
282 276 ...4: add d5/f
283 277 3: add d3/f
284 278 ...2: add d2/f
285 279 1: add d1/f
286 280 0: add d0/f
287 281
288 282 Verify shouldn't claim the repo is corrupt after a widen.
289 283
290 284 $ hg verify
291 285 checking changesets
292 286 checking manifests
293 287 checking directory manifests (tree !)
294 288 crosschecking files in changesets and manifests
295 289 checking files
296 290 checked 9 changesets with 5 changes to 5 files
297 291
298 292 Widening preserves parent of local commit
299 293
300 294 $ cd ..
301 295 $ hg clone -q --narrow ssh://user@dummy/upstream narrow3 --include d2 -r 2
302 296 $ cd narrow3
303 297 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
304 298 1: add d2/f
305 299 ...0: add d1/f
306 300 $ hg pull -q -r 3
307 301 $ hg co -q tip
308 302 $ hg pull -q -r 4
309 303 $ echo local > d2/f
310 304 $ hg ci -m local
311 305 created new head
312 306 $ hg tracked -q --addinclude d0 --addinclude d9
313 307
314 308 Widening preserves bookmarks
315 309
316 310 $ cd ..
317 311 $ hg clone -q --narrow ssh://user@dummy/upstream narrow-bookmarks --include d4
318 312 $ cd narrow-bookmarks
319 313 $ echo local > d4/f
320 314 $ hg ci -m local
321 315 $ hg bookmarks bookmark
322 316 $ hg bookmarks
323 317 * bookmark 3:* (glob)
324 318 $ hg -q tracked --addinclude d2
325 319 $ hg bookmarks
326 320 * bookmark 5:* (glob)
327 321 $ hg log -r bookmark -T '{desc}\n'
328 322 local
329 323
330 324 Widening that fails can be recovered from
331 325
332 326 $ cd ..
333 327 $ hg clone -q --narrow ssh://user@dummy/upstream interrupted --include d0
334 328 $ cd interrupted
335 329 $ echo local > d0/f
336 330 $ hg ci -m local
337 331 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
338 332 2: local
339 333 ...1: add d10/f
340 334 0: add d0/f
341 335 $ hg bookmarks bookmark
342 336 $ hg --config hooks.pretxnchangegroup.bad=false tracked --addinclude d1
343 337 comparing with ssh://user@dummy/upstream
344 338 searching for changes
345 no changes found
346 339 saved backup bundle to $TESTTMP/interrupted/.hg/strip-backup/*-widen.hg (glob)
347 340 adding changesets
348 341 adding manifests
349 342 adding file changes
350 343 added 3 changesets with 2 changes to 2 files
351 344 transaction abort!
352 345 rollback completed
353 346 abort: pretxnchangegroup.bad hook exited with status 1
354 347 [255]
355 348 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
356 349 $ hg bookmarks
357 350 no bookmarks set
358 351 $ hg unbundle .hg/strip-backup/*-widen.hg
359 352 adding changesets
360 353 adding manifests
361 354 adding file changes
362 355 added 3 changesets with 2 changes to 1 files
363 356 new changesets *:* (glob)
364 357 (run 'hg update' to get a working copy)
365 358 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
366 359 2: local
367 360 ...1: add d10/f
368 361 0: add d0/f
369 362 $ hg bookmarks
370 363 * bookmark 2:* (glob)
@@ -1,449 +1,447 b''
1 1 #testcases flat tree
2 2 #testcases lfs-on lfs-off
3 3
4 4 #if lfs-on
5 5 $ cat >> $HGRCPATH <<EOF
6 6 > [extensions]
7 7 > lfs =
8 8 > EOF
9 9 #endif
10 10
11 11 $ . "$TESTDIR/narrow-library.sh"
12 12
13 13 #if tree
14 14 $ cat << EOF >> $HGRCPATH
15 15 > [experimental]
16 16 > treemanifest = 1
17 17 > EOF
18 18 #endif
19 19
20 20 $ hg init master
21 21 $ cd master
22 22 $ cat >> .hg/hgrc <<EOF
23 23 > [narrow]
24 24 > serveellipses=True
25 25 > EOF
26 26 $ for x in `$TESTDIR/seq.py 0 10`
27 27 > do
28 28 > mkdir d$x
29 29 > echo $x > d$x/f
30 30 > hg add d$x/f
31 31 > hg commit -m "add d$x/f"
32 32 > done
33 33 $ hg log -T "{rev}: {desc}\n"
34 34 10: add d10/f
35 35 9: add d9/f
36 36 8: add d8/f
37 37 7: add d7/f
38 38 6: add d6/f
39 39 5: add d5/f
40 40 4: add d4/f
41 41 3: add d3/f
42 42 2: add d2/f
43 43 1: add d1/f
44 44 0: add d0/f
45 45 $ cd ..
46 46
47 47 Error if '.' or '..' are in the directory to track.
48 48 $ hg clone --narrow ssh://user@dummy/master foo --include ./asdf
49 49 abort: "." and ".." are not allowed in narrowspec paths
50 50 [255]
51 51 $ hg clone --narrow ssh://user@dummy/master foo --include asdf/..
52 52 abort: "." and ".." are not allowed in narrowspec paths
53 53 [255]
54 54 $ hg clone --narrow ssh://user@dummy/master foo --include a/./c
55 55 abort: "." and ".." are not allowed in narrowspec paths
56 56 [255]
57 57
58 58 Names with '.' in them are OK.
59 59 $ hg clone --narrow ssh://user@dummy/master should-work --include a/.b/c
60 60 requesting all changes
61 61 adding changesets
62 62 adding manifests
63 63 adding file changes
64 64 added 1 changesets with 0 changes to 0 files
65 65 new changesets * (glob)
66 66 updating to branch default
67 67 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
68 68
69 69 Test repo with local changes
70 70 $ hg clone --narrow ssh://user@dummy/master narrow-local-changes --include d0 --include d3 --include d6
71 71 requesting all changes
72 72 adding changesets
73 73 adding manifests
74 74 adding file changes
75 75 added 6 changesets with 3 changes to 3 files
76 76 new changesets *:* (glob)
77 77 updating to branch default
78 78 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 79 $ cd narrow-local-changes
80 80 $ cat >> $HGRCPATH << EOF
81 81 > [experimental]
82 82 > evolution=createmarkers
83 83 > EOF
84 84 $ echo local change >> d0/f
85 85 $ hg ci -m 'local change to d0'
86 86 $ hg co '.^'
87 87 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 88 $ echo local change >> d3/f
89 89 $ hg ci -m 'local hidden change to d3'
90 90 created new head
91 91 $ hg ci --amend -m 'local change to d3'
92 92 $ hg tracked --removeinclude d0
93 93 comparing with ssh://user@dummy/master
94 94 searching for changes
95 95 looking for local changes to affected paths
96 96 The following changeset(s) or their ancestors have local changes not on the remote:
97 97 * (glob)
98 98 abort: local changes found
99 99 (use --force-delete-local-changes to ignore)
100 100 [255]
101 101 Check that nothing was removed by the failed attempts
102 102 $ hg tracked
103 103 I path:d0
104 104 I path:d3
105 105 I path:d6
106 106 $ hg files
107 107 d0/f
108 108 d3/f
109 109 d6/f
110 110 $ find *
111 111 d0
112 112 d0/f
113 113 d3
114 114 d3/f
115 115 d6
116 116 d6/f
117 117 $ hg verify -q
118 118 Force deletion of local changes
119 119 $ hg log -T "{rev}: {desc} {outsidenarrow}\n"
120 120 8: local change to d3
121 121 6: local change to d0
122 122 5: add d10/f outsidenarrow
123 123 4: add d6/f
124 124 3: add d5/f outsidenarrow
125 125 2: add d3/f
126 126 1: add d2/f outsidenarrow
127 127 0: add d0/f
128 128 $ hg tracked --removeinclude d0 --force-delete-local-changes
129 129 comparing with ssh://user@dummy/master
130 130 searching for changes
131 131 looking for local changes to affected paths
132 132 The following changeset(s) or their ancestors have local changes not on the remote:
133 133 * (glob)
134 134 saved backup bundle to $TESTTMP/narrow-local-changes/.hg/strip-backup/*-narrow.hg (glob)
135 135 deleting data/d0/f.i (reporevlogstore !)
136 136 deleting meta/d0/00manifest.i (tree !)
137 137 deleting data/d0/f/362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (reposimplestore !)
138 138 deleting data/d0/f/4374b5650fc5ae54ac857c0f0381971fdde376f7 (reposimplestore !)
139 139 deleting data/d0/f/index (reposimplestore !)
140 140
141 141 $ hg log -T "{rev}: {desc} {outsidenarrow}\n"
142 142 7: local change to d3
143 143 5: add d10/f outsidenarrow
144 144 4: add d6/f
145 145 3: add d5/f outsidenarrow
146 146 2: add d3/f
147 147 1: add d2/f outsidenarrow
148 148 0: add d0/f outsidenarrow
149 149 Can restore stripped local changes after widening
150 150 $ hg tracked --addinclude d0 -q
151 151 $ hg unbundle .hg/strip-backup/*-narrow.hg -q
152 152 $ hg --hidden co -r 'desc("local change to d0")' -q
153 153 $ cat d0/f
154 154 0
155 155 local change
156 156 Pruned commits affecting removed paths should not prevent narrowing
157 157 $ hg co '.^'
158 158 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
159 159 $ hg debugobsolete `hg log -T '{node}' -r 'desc("local change to d0")'`
160 160 obsoleted 1 changesets
161 161 $ hg tracked --removeinclude d0
162 162 comparing with ssh://user@dummy/master
163 163 searching for changes
164 164 looking for local changes to affected paths
165 165 saved backup bundle to $TESTTMP/narrow-local-changes/.hg/strip-backup/*-narrow.hg (glob)
166 166 deleting data/d0/f.i (reporevlogstore !)
167 167 deleting meta/d0/00manifest.i (tree !)
168 168 deleting data/d0/f/362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (reposimplestore !)
169 169 deleting data/d0/f/4374b5650fc5ae54ac857c0f0381971fdde376f7 (reposimplestore !)
170 170 deleting data/d0/f/index (reposimplestore !)
171 171
172 172 Updates off of stripped commit if necessary
173 173 $ hg co -r 'desc("local change to d3")' -q
174 174 $ echo local change >> d6/f
175 175 $ hg ci -m 'local change to d6'
176 176 $ hg tracked --removeinclude d3 --force-delete-local-changes
177 177 comparing with ssh://user@dummy/master
178 178 searching for changes
179 179 looking for local changes to affected paths
180 180 The following changeset(s) or their ancestors have local changes not on the remote:
181 181 * (glob)
182 182 * (glob)
183 183 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
184 184 saved backup bundle to $TESTTMP/narrow-local-changes/.hg/strip-backup/*-narrow.hg (glob)
185 185 deleting data/d3/f.i (reporevlogstore !)
186 186 deleting meta/d3/00manifest.i (tree !)
187 187 deleting data/d3/f/2661d26c649684b482d10f91960cc3db683c38b4 (reposimplestore !)
188 188 deleting data/d3/f/99fa7136105a15e2045ce3d9152e4837c5349e4d (reposimplestore !)
189 189 deleting data/d3/f/index (reposimplestore !)
190 190 $ hg log -T '{desc}\n' -r .
191 191 add d10/f
192 192 Updates to nullid if necessary
193 193 $ hg tracked --addinclude d3 -q
194 194 $ hg co null -q
195 195 $ mkdir d3
196 196 $ echo local change > d3/f
197 197 $ hg add d3/f
198 198 $ hg ci -m 'local change to d3'
199 199 created new head
200 200 $ hg tracked --removeinclude d3 --force-delete-local-changes
201 201 comparing with ssh://user@dummy/master
202 202 searching for changes
203 203 looking for local changes to affected paths
204 204 The following changeset(s) or their ancestors have local changes not on the remote:
205 205 * (glob)
206 206 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
207 207 saved backup bundle to $TESTTMP/narrow-local-changes/.hg/strip-backup/*-narrow.hg (glob)
208 208 deleting data/d3/f.i (reporevlogstore !)
209 209 deleting meta/d3/00manifest.i (tree !)
210 210 deleting data/d3/f/2661d26c649684b482d10f91960cc3db683c38b4 (reposimplestore !)
211 211 deleting data/d3/f/5ce0767945cbdbca3b924bb9fbf5143f72ab40ac (reposimplestore !)
212 212 deleting data/d3/f/index (reposimplestore !)
213 213 $ hg id
214 214 000000000000
215 215 $ cd ..
216 216
217 217 Narrowing doesn't resurrect old commits (unlike what regular `hg strip` does)
218 218 $ hg clone --narrow ssh://user@dummy/master narrow-obsmarkers --include d0 --include d3 -q
219 219 $ cd narrow-obsmarkers
220 220 $ echo a >> d0/f2
221 221 $ hg add d0/f2
222 222 $ hg ci -m 'modify d0/'
223 223 $ echo a >> d3/f2
224 224 $ hg add d3/f2
225 225 $ hg commit --amend -m 'modify d0/ and d3/'
226 226 $ hg log -T "{rev}: {desc}\n"
227 227 5: modify d0/ and d3/
228 228 3: add d10/f
229 229 2: add d3/f
230 230 1: add d2/f
231 231 0: add d0/f
232 232 $ hg tracked --removeinclude d3 --force-delete-local-changes -q
233 233 $ hg log -T "{rev}: {desc}\n"
234 234 3: add d10/f
235 235 2: add d3/f
236 236 1: add d2/f
237 237 0: add d0/f
238 238 $ cd ..
239 239
240 240 Widening doesn't lose bookmarks
241 241 $ hg clone --narrow ssh://user@dummy/master widen-bookmarks --include d0 -q
242 242 $ cd widen-bookmarks
243 243 $ hg bookmark my-bookmark
244 244 $ hg log -T "{rev}: {desc} {bookmarks}\n"
245 245 1: add d10/f my-bookmark
246 246 0: add d0/f
247 247 $ hg tracked --addinclude d3 -q
248 248 $ hg log -T "{rev}: {desc} {bookmarks}\n"
249 249 3: add d10/f my-bookmark
250 250 2: add d3/f
251 251 1: add d2/f
252 252 0: add d0/f
253 253 $ cd ..
254 254
255 255 Can remove last include, making repo empty
256 256 $ hg clone --narrow ssh://user@dummy/master narrow-empty --include d0 -r 5
257 257 adding changesets
258 258 adding manifests
259 259 adding file changes
260 260 added 2 changesets with 1 changes to 1 files
261 261 new changesets *:* (glob)
262 262 updating to branch default
263 263 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
264 264 $ cd narrow-empty
265 265 $ hg tracked --removeinclude d0
266 266 comparing with ssh://user@dummy/master
267 267 searching for changes
268 268 looking for local changes to affected paths
269 269 deleting data/d0/f.i (reporevlogstore !)
270 270 deleting meta/d0/00manifest.i (tree !)
271 271 deleting data/d0/f/362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (reposimplestore !)
272 272 deleting data/d0/f/index (reposimplestore !)
273 273 $ hg tracked
274 274 $ hg files
275 275 [1]
276 276 $ test -d d0
277 277 [1]
278 278 Do some work in the empty clone
279 279 $ hg diff --change .
280 280 $ hg branch foo
281 281 marked working directory as branch foo
282 282 (branches are permanent and global, did you want a bookmark?)
283 283 $ hg ci -m empty
284 284 $ hg log -T "{rev}: {desc} {outsidenarrow}\n"
285 285 2: empty
286 286 1: add d5/f outsidenarrow
287 287 0: add d0/f outsidenarrow
288 288 $ hg pull -q
289 289 Can widen the empty clone
290 290 $ hg tracked --addinclude d0
291 291 comparing with ssh://user@dummy/master
292 292 searching for changes
293 no changes found
294 293 saved backup bundle to $TESTTMP/narrow-empty/.hg/strip-backup/*-widen.hg (glob)
295 294 adding changesets
296 295 adding manifests
297 296 adding file changes
298 297 added 3 changesets with 1 changes to 1 files
299 new changesets *:* (glob)
300 298 $ hg tracked
301 299 I path:d0
302 300 $ hg files
303 301 d0/f
304 302 $ find *
305 303 d0
306 304 d0/f
307 305 $ cd ..
308 306
309 307 TODO(martinvonz): test including e.g. d3/g and then removing it once
310 308 https://bitbucket.org/Google/narrowhg/issues/6 is fixed
311 309
312 310 $ hg clone --narrow ssh://user@dummy/master narrow --include d0 --include d3 --include d6 --include d9
313 311 requesting all changes
314 312 adding changesets
315 313 adding manifests
316 314 adding file changes
317 315 added 8 changesets with 4 changes to 4 files
318 316 new changesets *:* (glob)
319 317 updating to branch default
320 318 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
321 319 $ cd narrow
322 320 $ hg tracked
323 321 I path:d0
324 322 I path:d3
325 323 I path:d6
326 324 I path:d9
327 325 $ hg tracked --removeinclude d6
328 326 comparing with ssh://user@dummy/master
329 327 searching for changes
330 328 looking for local changes to affected paths
331 329 deleting data/d6/f.i (reporevlogstore !)
332 330 deleting meta/d6/00manifest.i (tree !)
333 331 deleting data/d6/f/7339d30678f451ac8c3f38753beeb4cf2e1655c7 (reposimplestore !)
334 332 deleting data/d6/f/index (reposimplestore !)
335 333 $ hg tracked
336 334 I path:d0
337 335 I path:d3
338 336 I path:d9
339 337 #if repofncache
340 338 $ hg debugrebuildfncache
341 339 fncache already up to date
342 340 #endif
343 341 $ find *
344 342 d0
345 343 d0/f
346 344 d3
347 345 d3/f
348 346 d9
349 347 d9/f
350 348 $ hg verify -q
351 349 $ hg tracked --addexclude d3/f
352 350 comparing with ssh://user@dummy/master
353 351 searching for changes
354 352 looking for local changes to affected paths
355 353 deleting data/d3/f.i (reporevlogstore !)
356 354 deleting data/d3/f/2661d26c649684b482d10f91960cc3db683c38b4 (reposimplestore !)
357 355 deleting data/d3/f/index (reposimplestore !)
358 356 $ hg tracked
359 357 I path:d0
360 358 I path:d3
361 359 I path:d9
362 360 X path:d3/f
363 361 #if repofncache
364 362 $ hg debugrebuildfncache
365 363 fncache already up to date
366 364 #endif
367 365 $ find *
368 366 d0
369 367 d0/f
370 368 d9
371 369 d9/f
372 370 $ hg verify -q
373 371 $ hg tracked --addexclude d0
374 372 comparing with ssh://user@dummy/master
375 373 searching for changes
376 374 looking for local changes to affected paths
377 375 deleting data/d0/f.i (reporevlogstore !)
378 376 deleting meta/d0/00manifest.i (tree !)
379 377 deleting data/d0/f/362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (reposimplestore !)
380 378 deleting data/d0/f/index (reposimplestore !)
381 379 $ hg tracked
382 380 I path:d3
383 381 I path:d9
384 382 X path:d0
385 383 X path:d3/f
386 384 #if repofncache
387 385 $ hg debugrebuildfncache
388 386 fncache already up to date
389 387 #endif
390 388 $ find *
391 389 d9
392 390 d9/f
393 391
394 392 Make a 15 of changes to d9 to test the path without --verbose
395 393 (Note: using regexes instead of "* (glob)" because if the test fails, it
396 394 produces more sensible diffs)
397 395 $ hg tracked
398 396 I path:d3
399 397 I path:d9
400 398 X path:d0
401 399 X path:d3/f
402 400 $ for x in `$TESTDIR/seq.py 1 15`
403 401 > do
404 402 > echo local change >> d9/f
405 403 > hg commit -m "change $x to d9/f"
406 404 > done
407 405 $ hg tracked --removeinclude d9
408 406 comparing with ssh://user@dummy/master
409 407 searching for changes
410 408 looking for local changes to affected paths
411 409 The following changeset(s) or their ancestors have local changes not on the remote:
412 410 ^[0-9a-f]{12}$ (re)
413 411 ^[0-9a-f]{12}$ (re)
414 412 ^[0-9a-f]{12}$ (re)
415 413 ^[0-9a-f]{12}$ (re)
416 414 ^[0-9a-f]{12}$ (re)
417 415 ^[0-9a-f]{12}$ (re)
418 416 ^[0-9a-f]{12}$ (re)
419 417 ^[0-9a-f]{12}$ (re)
420 418 ^[0-9a-f]{12}$ (re)
421 419 ^[0-9a-f]{12}$ (re)
422 420 ...and 5 more, use --verbose to list all
423 421 abort: local changes found
424 422 (use --force-delete-local-changes to ignore)
425 423 [255]
426 424 Now test it *with* verbose.
427 425 $ hg tracked --removeinclude d9 --verbose
428 426 comparing with ssh://user@dummy/master
429 427 searching for changes
430 428 looking for local changes to affected paths
431 429 The following changeset(s) or their ancestors have local changes not on the remote:
432 430 ^[0-9a-f]{12}$ (re)
433 431 ^[0-9a-f]{12}$ (re)
434 432 ^[0-9a-f]{12}$ (re)
435 433 ^[0-9a-f]{12}$ (re)
436 434 ^[0-9a-f]{12}$ (re)
437 435 ^[0-9a-f]{12}$ (re)
438 436 ^[0-9a-f]{12}$ (re)
439 437 ^[0-9a-f]{12}$ (re)
440 438 ^[0-9a-f]{12}$ (re)
441 439 ^[0-9a-f]{12}$ (re)
442 440 ^[0-9a-f]{12}$ (re)
443 441 ^[0-9a-f]{12}$ (re)
444 442 ^[0-9a-f]{12}$ (re)
445 443 ^[0-9a-f]{12}$ (re)
446 444 ^[0-9a-f]{12}$ (re)
447 445 abort: local changes found
448 446 (use --force-delete-local-changes to ignore)
449 447 [255]
General Comments 0
You need to be logged in to leave comments. Login now