##// END OF EJS Templates
narrow: import experimental extension from narrowhg revision cb51d673e9c5...
Augie Fackler -
r36096:a2a6e724 default
parent child Browse files
Show More
@@ -0,0 +1,111 b''
1 # __init__.py - narrowhg extension
2 #
3 # Copyright 2017 Google, Inc.
4 #
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.
7 '''create clones which fetch history data for subset of files (EXPERIMENTAL)'''
8
9 from __future__ import absolute_import
10
11 from mercurial import __version__
12 if __version__.version < '3.7':
13 raise ImportError(
14 'narrowhg requires mercurial 3.7 or newer')
15
16 try:
17 from .__versionnum__ import version
18 __version__ = version
19 except ImportError:
20 pass
21
22 from mercurial import (
23 extensions,
24 hg,
25 localrepo,
26 registrar,
27 util,
28 verify as verifymod,
29 )
30
31 from . import (
32 narrowbundle2,
33 narrowchangegroup,
34 narrowcommands,
35 narrowcopies,
36 narrowdirstate,
37 narrowmerge,
38 narrowpatch,
39 narrowrepo,
40 narrowrevlog,
41 narrowtemplates,
42 narrowwirepeer,
43 )
44
45 configtable = {}
46 configitem = registrar.configitem(configtable)
47 # Narrowhg *has* support for serving ellipsis nodes (which are used at
48 # least by Google's internal server), but that support is pretty
49 # fragile and has a lot of problems on real-world repositories that
50 # have complex graph topologies. This could probably be corrected, but
51 # absent someone needing the full support for ellipsis nodes in
52 # repositories with merges, it's unlikely this work will get done. As
53 # of this writining in late 2017, all repositories large enough for
54 # ellipsis nodes to be a hard requirement also enforce strictly linear
55 # history for other scaling reasons.
56 configitem('experimental', 'narrowservebrokenellipses',
57 default=False,
58 alias=[('narrow', 'serveellipses')],
59 )
60
61 # Export the commands table for Mercurial to see.
62 cmdtable = narrowcommands.table
63
64 localrepo.localrepository._basesupported.add(narrowrepo.requirement)
65
66 def uisetup(ui):
67 """Wraps user-facing mercurial commands with narrow-aware versions."""
68 narrowrevlog.setup()
69 narrowbundle2.setup()
70 narrowmerge.setup()
71 narrowtemplates.setup()
72 narrowcommands.setup()
73 narrowchangegroup.setup()
74 narrowwirepeer.uisetup()
75
76 def reposetup(ui, repo):
77 """Wraps local repositories with narrow repo support."""
78 if not isinstance(repo, localrepo.localrepository):
79 return
80
81 if narrowrepo.requirement in repo.requirements:
82 narrowrepo.wraprepo(repo, True)
83 narrowcopies.setup(repo)
84 narrowdirstate.setup(repo)
85 narrowpatch.setup(repo)
86 narrowwirepeer.reposetup(repo)
87
88 def _narrowvalidpath(orig, repo, path):
89 matcher = getattr(repo, 'narrowmatch', None)
90 if matcher is None:
91 return orig(repo, path)
92 matcher = matcher()
93 if matcher.visitdir(path) or matcher(path):
94 return orig(repo, path)
95 return False
96
97 def _verifierinit(orig, self, repo, matcher=None):
98 # The verifier's matcher argument was desgined for narrowhg, so it should
99 # be None from core. If another extension passes a matcher (unlikely),
100 # we'll have to fail until matchers can be composed more easily.
101 assert matcher is None
102 matcher = getattr(repo, 'narrowmatch', lambda: None)()
103 orig(self, repo, matcher)
104
105 def extsetup(ui):
106 if util.safehasattr(verifymod, '_validpath'):
107 extensions.wrapfunction(verifymod, '_validpath', _narrowvalidpath)
108 else:
109 extensions.wrapfunction(verifymod.verifier, '__init__', _verifierinit)
110 extensions.wrapfunction(hg, 'postshare', narrowrepo.wrappostshare)
111 extensions.wrapfunction(hg, 'copystore', narrowrepo.unsharenarrowspec)
This diff has been collapsed as it changes many lines, (503 lines changed) Show them Hide them
@@ -0,0 +1,503 b''
1 # narrowbundle2.py - bundle2 extensions for narrow repository support
2 #
3 # Copyright 2017 Google, Inc.
4 #
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.
7
8 from __future__ import absolute_import
9
10 import collections
11 import errno
12 import struct
13
14 from mercurial.i18n import _
15 from mercurial.node import (
16 bin,
17 nullid,
18 nullrev,
19 )
20 from mercurial import (
21 bundle2,
22 changegroup,
23 dagutil,
24 error,
25 exchange,
26 extensions,
27 repair,
28 util,
29 wireproto,
30 )
31
32 from . import (
33 narrowrepo,
34 narrowspec,
35 )
36
37 narrowcap = 'narrow'
38 narrowacl_section = 'narrowhgacl'
39 changespecpart = narrowcap + ':changespec'
40 specpart = narrowcap + ':spec'
41 specpart_include = 'include'
42 specpart_exclude = 'exclude'
43 killnodesignal = 'KILL'
44 donesignal = 'DONE'
45 elidedcsheader = '>20s20s20sl' # cset id, p1, p2, len(text)
46 elidedmfheader = '>20s20s20s20sl' # manifest id, p1, p2, link id, len(text)
47 csheadersize = struct.calcsize(elidedcsheader)
48 mfheadersize = struct.calcsize(elidedmfheader)
49
50 # When advertising capabilities, always include narrow clone support.
51 def getrepocaps_narrow(orig, repo, **kwargs):
52 caps = orig(repo, **kwargs)
53 caps[narrowcap] = ['v0']
54 return caps
55
56 def _computeellipsis(repo, common, heads, known, match, depth=None):
57 """Compute the shape of a narrowed DAG.
58
59 Args:
60 repo: The repository we're transferring.
61 common: The roots of the DAG range we're transferring.
62 May be just [nullid], which means all ancestors of heads.
63 heads: The heads of the DAG range we're transferring.
64 match: The narrowmatcher that allows us to identify relevant changes.
65 depth: If not None, only consider nodes to be full nodes if they are at
66 most depth changesets away from one of heads.
67
68 Returns:
69 A tuple of (visitnodes, relevant_nodes, ellipsisroots) where:
70
71 visitnodes: The list of nodes (either full or ellipsis) which
72 need to be sent to the client.
73 relevant_nodes: The set of changelog nodes which change a file inside
74 the narrowspec. The client needs these as non-ellipsis nodes.
75 ellipsisroots: A dict of {rev: parents} that is used in
76 narrowchangegroup to produce ellipsis nodes with the
77 correct parents.
78 """
79 cl = repo.changelog
80 mfl = repo.manifestlog
81
82 cldag = dagutil.revlogdag(cl)
83 # dagutil does not like nullid/nullrev
84 commonrevs = cldag.internalizeall(common - set([nullid])) | set([nullrev])
85 headsrevs = cldag.internalizeall(heads)
86 if depth:
87 revdepth = {h: 0 for h in headsrevs}
88
89 ellipsisheads = collections.defaultdict(set)
90 ellipsisroots = collections.defaultdict(set)
91
92 def addroot(head, curchange):
93 """Add a root to an ellipsis head, splitting heads with 3 roots."""
94 ellipsisroots[head].add(curchange)
95 # Recursively split ellipsis heads with 3 roots by finding the
96 # roots' youngest common descendant which is an elided merge commit.
97 # That descendant takes 2 of the 3 roots as its own, and becomes a
98 # root of the head.
99 while len(ellipsisroots[head]) > 2:
100 child, roots = splithead(head)
101 splitroots(head, child, roots)
102 head = child # Recurse in case we just added a 3rd root
103
104 def splitroots(head, child, roots):
105 ellipsisroots[head].difference_update(roots)
106 ellipsisroots[head].add(child)
107 ellipsisroots[child].update(roots)
108 ellipsisroots[child].discard(child)
109
110 def splithead(head):
111 r1, r2, r3 = sorted(ellipsisroots[head])
112 for nr1, nr2 in ((r2, r3), (r1, r3), (r1, r2)):
113 mid = repo.revs('sort(merge() & %d::%d & %d::%d, -rev)',
114 nr1, head, nr2, head)
115 for j in mid:
116 if j == nr2:
117 return nr2, (nr1, nr2)
118 if j not in ellipsisroots or len(ellipsisroots[j]) < 2:
119 return j, (nr1, nr2)
120 raise error.Abort('Failed to split up ellipsis node! head: %d, '
121 'roots: %d %d %d' % (head, r1, r2, r3))
122
123 missing = list(cl.findmissingrevs(common=commonrevs, heads=headsrevs))
124 visit = reversed(missing)
125 relevant_nodes = set()
126 visitnodes = map(cl.node, missing)
127 required = set(headsrevs) | known
128 for rev in visit:
129 clrev = cl.changelogrevision(rev)
130 ps = cldag.parents(rev)
131 if depth is not None:
132 curdepth = revdepth[rev]
133 for p in ps:
134 revdepth[p] = min(curdepth + 1, revdepth.get(p, depth + 1))
135 needed = False
136 shallow_enough = depth is None or revdepth[rev] <= depth
137 if shallow_enough:
138 curmf = mfl[clrev.manifest].read()
139 if ps:
140 # We choose to not trust the changed files list in
141 # changesets because it's not always correct. TODO: could
142 # we trust it for the non-merge case?
143 p1mf = mfl[cl.changelogrevision(ps[0]).manifest].read()
144 needed = any(match(f) for f in curmf.diff(p1mf).iterkeys())
145 if not needed and len(ps) > 1:
146 # For merge changes, the list of changed files is not
147 # helpful, since we need to emit the merge if a file
148 # in the narrow spec has changed on either side of the
149 # merge. As a result, we do a manifest diff to check.
150 p2mf = mfl[cl.changelogrevision(ps[1]).manifest].read()
151 needed = any(match(f) for f in curmf.diff(p2mf).iterkeys())
152 else:
153 # For a root node, we need to include the node if any
154 # files in the node match the narrowspec.
155 needed = any(match(f) for f in curmf)
156
157 if needed:
158 for head in ellipsisheads[rev]:
159 addroot(head, rev)
160 for p in ps:
161 required.add(p)
162 relevant_nodes.add(cl.node(rev))
163 else:
164 if not ps:
165 ps = [nullrev]
166 if rev in required:
167 for head in ellipsisheads[rev]:
168 addroot(head, rev)
169 for p in ps:
170 ellipsisheads[p].add(rev)
171 else:
172 for p in ps:
173 ellipsisheads[p] |= ellipsisheads[rev]
174
175 # add common changesets as roots of their reachable ellipsis heads
176 for c in commonrevs:
177 for head in ellipsisheads[c]:
178 addroot(head, c)
179 return visitnodes, relevant_nodes, ellipsisroots
180
181 def _packellipsischangegroup(repo, common, match, relevant_nodes,
182 ellipsisroots, visitnodes, depth, source, version):
183 if version in ('01', '02'):
184 raise error.Abort(
185 'ellipsis nodes require at least cg3 on client and server, '
186 'but negotiated version %s' % version)
187 # We wrap cg1packer.revchunk, using a side channel to pass
188 # relevant_nodes into that area. Then if linknode isn't in the
189 # set, we know we have an ellipsis node and we should defer
190 # sending that node's data. We override close() to detect
191 # pending ellipsis nodes and flush them.
192 packer = changegroup.getbundler(version, repo)
193 # Let the packer have access to the narrow matcher so it can
194 # omit filelogs and dirlogs as needed
195 packer._narrow_matcher = lambda : match
196 # Give the packer the list of nodes which should not be
197 # ellipsis nodes. We store this rather than the set of nodes
198 # that should be an ellipsis because for very large histories
199 # we expect this to be significantly smaller.
200 packer.full_nodes = relevant_nodes
201 # Maps ellipsis revs to their roots at the changelog level.
202 packer.precomputed_ellipsis = ellipsisroots
203 # Maps CL revs to per-revlog revisions. Cleared in close() at
204 # the end of each group.
205 packer.clrev_to_localrev = {}
206 packer.next_clrev_to_localrev = {}
207 # Maps changelog nodes to changelog revs. Filled in once
208 # during changelog stage and then left unmodified.
209 packer.clnode_to_rev = {}
210 packer.changelog_done = False
211 # If true, informs the packer that it is serving shallow content and might
212 # need to pack file contents not introduced by the changes being packed.
213 packer.is_shallow = depth is not None
214
215 return packer.generate(common, visitnodes, False, source)
216
217 # Serve a changegroup for a client with a narrow clone.
218 def getbundlechangegrouppart_narrow(bundler, repo, source,
219 bundlecaps=None, b2caps=None, heads=None,
220 common=None, **kwargs):
221 cgversions = b2caps.get('changegroup')
222 getcgkwargs = {}
223 if cgversions: # 3.1 and 3.2 ship with an empty value
224 cgversions = [v for v in cgversions
225 if v in changegroup.supportedoutgoingversions(repo)]
226 if not cgversions:
227 raise ValueError(_('no common changegroup version'))
228 version = getcgkwargs['version'] = max(cgversions)
229 else:
230 raise ValueError(_("server does not advertise changegroup version,"
231 " can't negotiate support for ellipsis nodes"))
232
233 include = sorted(filter(bool, kwargs.get('includepats', [])))
234 exclude = sorted(filter(bool, kwargs.get('excludepats', [])))
235 newmatch = narrowspec.match(repo.root, include=include, exclude=exclude)
236 if not repo.ui.configbool("experimental", "narrowservebrokenellipses"):
237 outgoing = exchange._computeoutgoing(repo, heads, common)
238 if not outgoing.missing:
239 return
240 if util.safehasattr(changegroup, 'getsubsetraw'):
241 # getsubsetraw was replaced with makestream in hg in 92f1e2be8ab6
242 # (2017/09/10).
243 packer = changegroup.getbundler(version, repo)
244 packer._narrow_matcher = lambda : newmatch
245 cg = changegroup.getsubsetraw(repo, outgoing, packer, source)
246 else:
247 def wrappedgetbundler(orig, *args, **kwargs):
248 bundler = orig(*args, **kwargs)
249 bundler._narrow_matcher = lambda : newmatch
250 return bundler
251 with extensions.wrappedfunction(changegroup, 'getbundler',
252 wrappedgetbundler):
253 cg = changegroup.makestream(repo, outgoing, version, source)
254 part = bundler.newpart('changegroup', data=cg)
255 part.addparam('version', version)
256 if 'treemanifest' in repo.requirements:
257 part.addparam('treemanifest', '1')
258
259 if include or exclude:
260 narrowspecpart = bundler.newpart(specpart)
261 if include:
262 narrowspecpart.addparam(
263 specpart_include, '\n'.join(include), mandatory=True)
264 if exclude:
265 narrowspecpart.addparam(
266 specpart_exclude, '\n'.join(exclude), mandatory=True)
267
268 return
269
270 depth = kwargs.get('depth', None)
271 if depth is not None:
272 depth = int(depth)
273 if depth < 1:
274 raise error.Abort(_('depth must be positive, got %d') % depth)
275
276 heads = set(heads or repo.heads())
277 common = set(common or [nullid])
278 oldinclude = sorted(filter(bool, kwargs.get('oldincludepats', [])))
279 oldexclude = sorted(filter(bool, kwargs.get('oldexcludepats', [])))
280 known = {bin(n) for n in kwargs.get('known', [])}
281 if known and (oldinclude != include or oldexclude != exclude):
282 # Steps:
283 # 1. Send kill for "$known & ::common"
284 #
285 # 2. Send changegroup for ::common
286 #
287 # 3. Proceed.
288 #
289 # In the future, we can send kills for only the specific
290 # nodes we know should go away or change shape, and then
291 # send a data stream that tells the client something like this:
292 #
293 # a) apply this changegroup
294 # b) apply nodes XXX, YYY, ZZZ that you already have
295 # c) goto a
296 #
297 # until they've built up the full new state.
298 # Convert to revnums and intersect with "common". The client should
299 # have made it a subset of "common" already, but let's be safe.
300 known = set(repo.revs("%ln & ::%ln", known, common))
301 # TODO: we could send only roots() of this set, and the
302 # list of nodes in common, and the client could work out
303 # what to strip, instead of us explicitly sending every
304 # single node.
305 deadrevs = known
306 def genkills():
307 for r in deadrevs:
308 yield killnodesignal
309 yield repo.changelog.node(r)
310 yield donesignal
311 bundler.newpart(changespecpart, data=genkills())
312 newvisit, newfull, newellipsis = _computeellipsis(
313 repo, set(), common, known, newmatch)
314 if newvisit:
315 cg = _packellipsischangegroup(
316 repo, common, newmatch, newfull, newellipsis,
317 newvisit, depth, source, version)
318 part = bundler.newpart('changegroup', data=cg)
319 part.addparam('version', version)
320 if 'treemanifest' in repo.requirements:
321 part.addparam('treemanifest', '1')
322
323 visitnodes, relevant_nodes, ellipsisroots = _computeellipsis(
324 repo, common, heads, set(), newmatch, depth=depth)
325
326 repo.ui.debug('Found %d relevant revs\n' % len(relevant_nodes))
327 if visitnodes:
328 cg = _packellipsischangegroup(
329 repo, common, newmatch, relevant_nodes, ellipsisroots,
330 visitnodes, depth, source, version)
331 part = bundler.newpart('changegroup', data=cg)
332 part.addparam('version', version)
333 if 'treemanifest' in repo.requirements:
334 part.addparam('treemanifest', '1')
335
336 def applyacl_narrow(repo, kwargs):
337 username = repo.ui.shortuser(repo.ui.username())
338 user_includes = repo.ui.configlist(
339 narrowacl_section, username + '.includes',
340 repo.ui.configlist(narrowacl_section, 'default.includes'))
341 user_excludes = repo.ui.configlist(
342 narrowacl_section, username + '.excludes',
343 repo.ui.configlist(narrowacl_section, 'default.excludes'))
344 if not user_includes:
345 raise error.Abort(_("{} configuration for user {} is empty")
346 .format(narrowacl_section, username))
347
348 user_includes = [
349 'path:.' if p == '*' else 'path:' + p for p in user_includes]
350 user_excludes = [
351 'path:.' if p == '*' else 'path:' + p for p in user_excludes]
352
353 req_includes = set(kwargs.get('includepats', []))
354 req_excludes = set(kwargs.get('excludepats', []))
355
356 invalid_includes = []
357 req_includes, req_excludes = narrowspec.restrictpatterns(
358 req_includes, req_excludes,
359 user_includes, user_excludes, invalid_includes)
360
361 if invalid_includes:
362 raise error.Abort(
363 _("The following includes are not accessible for {}: {}")
364 .format(username, invalid_includes))
365
366 new_args = {}
367 new_args.update(kwargs)
368 new_args['includepats'] = req_includes
369 if req_excludes:
370 new_args['excludepats'] = req_excludes
371 return new_args
372
373 @bundle2.parthandler(specpart, (specpart_include, specpart_exclude))
374 def _handlechangespec_2(op, inpart):
375 includepats = set(inpart.params.get(specpart_include, '').splitlines())
376 excludepats = set(inpart.params.get(specpart_exclude, '').splitlines())
377 narrowspec.save(op.repo, includepats, excludepats)
378 if not narrowrepo.requirement in op.repo.requirements:
379 op.repo.requirements.add(narrowrepo.requirement)
380 op.repo._writerequirements()
381 op.repo.invalidate(clearfilecache=True)
382
383 @bundle2.parthandler(changespecpart)
384 def _handlechangespec(op, inpart):
385 repo = op.repo
386 cl = repo.changelog
387
388 # changesets which need to be stripped entirely. either they're no longer
389 # needed in the new narrow spec, or the server is sending a replacement
390 # in the changegroup part.
391 clkills = set()
392
393 # A changespec part contains all the updates to ellipsis nodes
394 # that will happen as a result of widening or narrowing a
395 # repo. All the changes that this block encounters are ellipsis
396 # nodes or flags to kill an existing ellipsis.
397 chunksignal = changegroup.readexactly(inpart, 4)
398 while chunksignal != donesignal:
399 if chunksignal == killnodesignal:
400 # a node used to be an ellipsis but isn't anymore
401 ck = changegroup.readexactly(inpart, 20)
402 if cl.hasnode(ck):
403 clkills.add(ck)
404 else:
405 raise error.Abort(
406 _('unexpected changespec node chunk type: %s') % chunksignal)
407 chunksignal = changegroup.readexactly(inpart, 4)
408
409 if clkills:
410 # preserve bookmarks that repair.strip() would otherwise strip
411 bmstore = repo._bookmarks
412 class dummybmstore(dict):
413 def applychanges(self, repo, tr, changes):
414 pass
415 def recordchange(self, tr): # legacy version
416 pass
417 repo._bookmarks = dummybmstore()
418 chgrpfile = repair.strip(op.ui, repo, list(clkills), backup=True,
419 topic='widen')
420 repo._bookmarks = bmstore
421 if chgrpfile:
422 # presence of _widen_bundle attribute activates widen handler later
423 op._widen_bundle = chgrpfile
424 # Set the new narrowspec if we're widening. The setnewnarrowpats() method
425 # will currently always be there when using the core+narrowhg server, but
426 # other servers may include a changespec part even when not widening (e.g.
427 # because we're deepening a shallow repo).
428 if util.safehasattr(repo, 'setnewnarrowpats'):
429 repo.setnewnarrowpats()
430
431 def handlechangegroup_widen(op, inpart):
432 """Changegroup exchange handler which restores temporarily-stripped nodes"""
433 # We saved a bundle with stripped node data we must now restore.
434 # This approach is based on mercurial/repair.py@6ee26a53c111.
435 repo = op.repo
436 ui = op.ui
437
438 chgrpfile = op._widen_bundle
439 del op._widen_bundle
440 vfs = repo.vfs
441
442 ui.note(_("adding branch\n"))
443 f = vfs.open(chgrpfile, "rb")
444 try:
445 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
446 if not ui.verbose:
447 # silence internal shuffling chatter
448 ui.pushbuffer()
449 if isinstance(gen, bundle2.unbundle20):
450 with repo.transaction('strip') as tr:
451 bundle2.processbundle(repo, gen, lambda: tr)
452 else:
453 gen.apply(repo, 'strip', 'bundle:' + vfs.join(chgrpfile), True)
454 if not ui.verbose:
455 ui.popbuffer()
456 finally:
457 f.close()
458
459 # remove undo files
460 for undovfs, undofile in repo.undofiles():
461 try:
462 undovfs.unlink(undofile)
463 except OSError as e:
464 if e.errno != errno.ENOENT:
465 ui.warn(_('error removing %s: %s\n') %
466 (undovfs.join(undofile), str(e)))
467
468 # Remove partial backup only if there were no exceptions
469 vfs.unlink(chgrpfile)
470
471 def setup():
472 """Enable narrow repo support in bundle2-related extension points."""
473 extensions.wrapfunction(bundle2, 'getrepocaps', getrepocaps_narrow)
474
475 wireproto.gboptsmap['narrow'] = 'boolean'
476 wireproto.gboptsmap['depth'] = 'plain'
477 wireproto.gboptsmap['oldincludepats'] = 'csv'
478 wireproto.gboptsmap['oldexcludepats'] = 'csv'
479 wireproto.gboptsmap['includepats'] = 'csv'
480 wireproto.gboptsmap['excludepats'] = 'csv'
481 wireproto.gboptsmap['known'] = 'csv'
482
483 # Extend changegroup serving to handle requests from narrow clients.
484 origcgfn = exchange.getbundle2partsmapping['changegroup']
485 def wrappedcgfn(*args, **kwargs):
486 repo = args[1]
487 if repo.ui.has_section(narrowacl_section):
488 getbundlechangegrouppart_narrow(
489 *args, **applyacl_narrow(repo, kwargs))
490 elif kwargs.get('narrow', False):
491 getbundlechangegrouppart_narrow(*args, **kwargs)
492 else:
493 origcgfn(*args, **kwargs)
494 exchange.getbundle2partsmapping['changegroup'] = wrappedcgfn
495
496 # Extend changegroup receiver so client can fixup after widen requests.
497 origcghandler = bundle2.parthandlermapping['changegroup']
498 def wrappedcghandler(op, inpart):
499 origcghandler(op, inpart)
500 if util.safehasattr(op, '_widen_bundle'):
501 handlechangegroup_widen(op, inpart)
502 wrappedcghandler.params = origcghandler.params
503 bundle2.parthandlermapping['changegroup'] = wrappedcghandler
@@ -0,0 +1,385 b''
1 # narrowchangegroup.py - narrow clone changegroup creation and consumption
2 #
3 # Copyright 2017 Google, Inc.
4 #
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.
7
8 from __future__ import absolute_import
9
10 from mercurial.i18n import _
11 from mercurial import (
12 changegroup,
13 error,
14 extensions,
15 manifest,
16 mdiff,
17 node,
18 util,
19 )
20
21 from . import (
22 narrowrepo,
23 narrowrevlog,
24 )
25
26 def setup():
27
28 def supportedoutgoingversions(orig, repo):
29 versions = orig(repo)
30 if narrowrepo.requirement in repo.requirements:
31 versions.discard('01')
32 versions.discard('02')
33 return versions
34
35 extensions.wrapfunction(changegroup, 'supportedoutgoingversions',
36 supportedoutgoingversions)
37
38 def prune(orig, self, revlog, missing, commonrevs):
39 if isinstance(revlog, manifest.manifestrevlog):
40 matcher = getattr(self._repo, 'narrowmatch',
41 getattr(self, '_narrow_matcher', None))
42 if (matcher is not None and
43 not matcher().visitdir(revlog._dir[:-1] or '.')):
44 return []
45 return orig(self, revlog, missing, commonrevs)
46
47 extensions.wrapfunction(changegroup.cg1packer, 'prune', prune)
48
49 def generatefiles(orig, self, changedfiles, linknodes, commonrevs,
50 source):
51 matcher = getattr(self._repo, 'narrowmatch',
52 getattr(self, '_narrow_matcher', None))
53 if matcher is not None:
54 narrowmatch = matcher()
55 changedfiles = filter(narrowmatch, changedfiles)
56 if getattr(self, 'is_shallow', False):
57 # See comment in generate() for why this sadness is a thing.
58 mfdicts = self._mfdicts
59 del self._mfdicts
60 # In a shallow clone, the linknodes callback needs to also include
61 # those file nodes that are in the manifests we sent but weren't
62 # introduced by those manifests.
63 commonctxs = [self._repo[c] for c in commonrevs]
64 oldlinknodes = linknodes
65 clrev = self._repo.changelog.rev
66 def linknodes(flog, fname):
67 for c in commonctxs:
68 try:
69 fnode = c.filenode(fname)
70 self.clrev_to_localrev[c.rev()] = flog.rev(fnode)
71 except error.ManifestLookupError:
72 pass
73 links = oldlinknodes(flog, fname)
74 if len(links) != len(mfdicts):
75 for mf, lr in mfdicts:
76 fnode = mf.get(fname, None)
77 if fnode in links:
78 links[fnode] = min(links[fnode], lr, key=clrev)
79 elif fnode:
80 links[fnode] = lr
81 return links
82 return orig(self, changedfiles, linknodes, commonrevs, source)
83 extensions.wrapfunction(
84 changegroup.cg1packer, 'generatefiles', generatefiles)
85
86 def ellipsisdata(packer, rev, revlog, p1, p2, data, linknode):
87 n = revlog.node(rev)
88 p1n, p2n = revlog.node(p1), revlog.node(p2)
89 flags = revlog.flags(rev)
90 flags |= narrowrevlog.ELLIPSIS_NODE_FLAG
91 meta = packer.builddeltaheader(
92 n, p1n, p2n, node.nullid, linknode, flags)
93 # TODO: try and actually send deltas for ellipsis data blocks
94 diffheader = mdiff.trivialdiffheader(len(data))
95 l = len(meta) + len(diffheader) + len(data)
96 return ''.join((changegroup.chunkheader(l),
97 meta,
98 diffheader,
99 data))
100
101 def close(orig, self):
102 getattr(self, 'clrev_to_localrev', {}).clear()
103 if getattr(self, 'next_clrev_to_localrev', {}):
104 self.clrev_to_localrev = self.next_clrev_to_localrev
105 del self.next_clrev_to_localrev
106 self.changelog_done = True
107 return orig(self)
108 extensions.wrapfunction(changegroup.cg1packer, 'close', close)
109
110 # In a perfect world, we'd generate better ellipsis-ified graphs
111 # for non-changelog revlogs. In practice, we haven't started doing
112 # that yet, so the resulting DAGs for the manifestlog and filelogs
113 # are actually full of bogus parentage on all the ellipsis
114 # nodes. This has the side effect that, while the contents are
115 # correct, the individual DAGs might be completely out of whack in
116 # a case like 882681bc3166 and its ancestors (back about 10
117 # revisions or so) in the main hg repo.
118 #
119 # The one invariant we *know* holds is that the new (potentially
120 # bogus) DAG shape will be valid if we order the nodes in the
121 # order that they're introduced in dramatis personae by the
122 # changelog, so what we do is we sort the non-changelog histories
123 # by the order in which they are used by the changelog.
124 def _sortgroup(orig, self, revlog, nodelist, lookup):
125 if not util.safehasattr(self, 'full_nodes') or not self.clnode_to_rev:
126 return orig(self, revlog, nodelist, lookup)
127 key = lambda n: self.clnode_to_rev[lookup(n)]
128 return [revlog.rev(n) for n in sorted(nodelist, key=key)]
129
130 extensions.wrapfunction(changegroup.cg1packer, '_sortgroup', _sortgroup)
131
132 def generate(orig, self, commonrevs, clnodes, fastpathlinkrev, source):
133 '''yield a sequence of changegroup chunks (strings)'''
134 # Note: other than delegating to orig, the only deviation in
135 # logic from normal hg's generate is marked with BEGIN/END
136 # NARROW HACK.
137 if not util.safehasattr(self, 'full_nodes'):
138 # not sending a narrow bundle
139 for x in orig(self, commonrevs, clnodes, fastpathlinkrev, source):
140 yield x
141 return
142
143 repo = self._repo
144 cl = repo.changelog
145 mfl = repo.manifestlog
146 mfrevlog = mfl._revlog
147
148 clrevorder = {}
149 mfs = {} # needed manifests
150 fnodes = {} # needed file nodes
151 changedfiles = set()
152
153 # Callback for the changelog, used to collect changed files and manifest
154 # nodes.
155 # Returns the linkrev node (identity in the changelog case).
156 def lookupcl(x):
157 c = cl.read(x)
158 clrevorder[x] = len(clrevorder)
159 # BEGIN NARROW HACK
160 #
161 # Only update mfs if x is going to be sent. Otherwise we
162 # end up with bogus linkrevs specified for manifests and
163 # we skip some manifest nodes that we should otherwise
164 # have sent.
165 if x in self.full_nodes or cl.rev(x) in self.precomputed_ellipsis:
166 n = c[0]
167 # record the first changeset introducing this manifest version
168 mfs.setdefault(n, x)
169 # Set this narrow-specific dict so we have the lowest manifest
170 # revnum to look up for this cl revnum. (Part of mapping
171 # changelog ellipsis parents to manifest ellipsis parents)
172 self.next_clrev_to_localrev.setdefault(cl.rev(x),
173 mfrevlog.rev(n))
174 # We can't trust the changed files list in the changeset if the
175 # client requested a shallow clone.
176 if self.is_shallow:
177 changedfiles.update(mfl[c[0]].read().keys())
178 else:
179 changedfiles.update(c[3])
180 # END NARROW HACK
181 # Record a complete list of potentially-changed files in
182 # this manifest.
183 return x
184
185 self._verbosenote(_('uncompressed size of bundle content:\n'))
186 size = 0
187 for chunk in self.group(clnodes, cl, lookupcl, units=_('changesets')):
188 size += len(chunk)
189 yield chunk
190 self._verbosenote(_('%8.i (changelog)\n') % size)
191
192 # We need to make sure that the linkrev in the changegroup refers to
193 # the first changeset that introduced the manifest or file revision.
194 # The fastpath is usually safer than the slowpath, because the filelogs
195 # are walked in revlog order.
196 #
197 # When taking the slowpath with reorder=None and the manifest revlog
198 # uses generaldelta, the manifest may be walked in the "wrong" order.
199 # Without 'clrevorder', we would get an incorrect linkrev (see fix in
200 # cc0ff93d0c0c).
201 #
202 # When taking the fastpath, we are only vulnerable to reordering
203 # of the changelog itself. The changelog never uses generaldelta, so
204 # it is only reordered when reorder=True. To handle this case, we
205 # simply take the slowpath, which already has the 'clrevorder' logic.
206 # This was also fixed in cc0ff93d0c0c.
207 fastpathlinkrev = fastpathlinkrev and not self._reorder
208 # Treemanifests don't work correctly with fastpathlinkrev
209 # either, because we don't discover which directory nodes to
210 # send along with files. This could probably be fixed.
211 fastpathlinkrev = fastpathlinkrev and (
212 'treemanifest' not in repo.requirements)
213 # Shallow clones also don't work correctly with fastpathlinkrev
214 # because file nodes may need to be sent for a manifest even if they
215 # weren't introduced by that manifest.
216 fastpathlinkrev = fastpathlinkrev and not self.is_shallow
217
218 moreargs = []
219 if self.generatemanifests.func_code.co_argcount == 7:
220 # The source argument was added to generatemanifests in hg in
221 # 75cc1f1e11f2 (2017/09/11).
222 moreargs.append(source)
223 for chunk in self.generatemanifests(commonrevs, clrevorder,
224 fastpathlinkrev, mfs, fnodes, *moreargs):
225 yield chunk
226 # BEGIN NARROW HACK
227 mfdicts = None
228 if self.is_shallow:
229 mfdicts = [(self._repo.manifestlog[n].read(), lr)
230 for (n, lr) in mfs.iteritems()]
231 # END NARROW HACK
232 mfs.clear()
233 clrevs = set(cl.rev(x) for x in clnodes)
234
235 if not fastpathlinkrev:
236 def linknodes(unused, fname):
237 return fnodes.get(fname, {})
238 else:
239 cln = cl.node
240 def linknodes(filerevlog, fname):
241 llr = filerevlog.linkrev
242 fln = filerevlog.node
243 revs = ((r, llr(r)) for r in filerevlog)
244 return dict((fln(r), cln(lr)) for r, lr in revs if lr in clrevs)
245
246 # BEGIN NARROW HACK
247 #
248 # We need to pass the mfdicts variable down into
249 # generatefiles(), but more than one command might have
250 # wrapped generatefiles so we can't modify the function
251 # signature. Instead, we pass the data to ourselves using an
252 # instance attribute. I'm sorry.
253 self._mfdicts = mfdicts
254 # END NARROW HACK
255 for chunk in self.generatefiles(changedfiles, linknodes, commonrevs,
256 source):
257 yield chunk
258
259 yield self.close()
260
261 if clnodes:
262 repo.hook('outgoing', node=node.hex(clnodes[0]), source=source)
263 extensions.wrapfunction(changegroup.cg1packer, 'generate', generate)
264
265 def revchunk(orig, self, revlog, rev, prev, linknode):
266 if not util.safehasattr(self, 'full_nodes'):
267 # not sending a narrow changegroup
268 for x in orig(self, revlog, rev, prev, linknode):
269 yield x
270 return
271 # build up some mapping information that's useful later. See
272 # the local() nested function below.
273 if not self.changelog_done:
274 self.clnode_to_rev[linknode] = rev
275 linkrev = rev
276 self.clrev_to_localrev[linkrev] = rev
277 else:
278 linkrev = self.clnode_to_rev[linknode]
279 self.clrev_to_localrev[linkrev] = rev
280 # This is a node to send in full, because the changeset it
281 # corresponds to was a full changeset.
282 if linknode in self.full_nodes:
283 for x in orig(self, revlog, rev, prev, linknode):
284 yield x
285 return
286 # At this point, a node can either be one we should skip or an
287 # ellipsis. If it's not an ellipsis, bail immediately.
288 if linkrev not in self.precomputed_ellipsis:
289 return
290 linkparents = self.precomputed_ellipsis[linkrev]
291 def local(clrev):
292 """Turn a changelog revnum into a local revnum.
293
294 The ellipsis dag is stored as revnums on the changelog,
295 but when we're producing ellipsis entries for
296 non-changelog revlogs, we need to turn those numbers into
297 something local. This does that for us, and during the
298 changelog sending phase will also expand the stored
299 mappings as needed.
300 """
301 if clrev == node.nullrev:
302 return node.nullrev
303 if not self.changelog_done:
304 # If we're doing the changelog, it's possible that we
305 # have a parent that is already on the client, and we
306 # need to store some extra mapping information so that
307 # our contained ellipsis nodes will be able to resolve
308 # their parents.
309 if clrev not in self.clrev_to_localrev:
310 clnode = revlog.node(clrev)
311 self.clnode_to_rev[clnode] = clrev
312 return clrev
313 # Walk the ellipsis-ized changelog breadth-first looking for a
314 # change that has been linked from the current revlog.
315 #
316 # For a flat manifest revlog only a single step should be necessary
317 # as all relevant changelog entries are relevant to the flat
318 # manifest.
319 #
320 # For a filelog or tree manifest dirlog however not every changelog
321 # entry will have been relevant, so we need to skip some changelog
322 # nodes even after ellipsis-izing.
323 walk = [clrev]
324 while walk:
325 p = walk[0]
326 walk = walk[1:]
327 if p in self.clrev_to_localrev:
328 return self.clrev_to_localrev[p]
329 elif p in self.full_nodes:
330 walk.extend([pp for pp in self._repo.changelog.parentrevs(p)
331 if pp != node.nullrev])
332 elif p in self.precomputed_ellipsis:
333 walk.extend([pp for pp in self.precomputed_ellipsis[p]
334 if pp != node.nullrev])
335 else:
336 # In this case, we've got an ellipsis with parents
337 # outside the current bundle (likely an
338 # incremental pull). We "know" that we can use the
339 # value of this same revlog at whatever revision
340 # is pointed to by linknode. "Know" is in scare
341 # quotes because I haven't done enough examination
342 # of edge cases to convince myself this is really
343 # a fact - it works for all the (admittedly
344 # thorough) cases in our testsuite, but I would be
345 # somewhat unsurprised to find a case in the wild
346 # where this breaks down a bit. That said, I don't
347 # know if it would hurt anything.
348 for i in xrange(rev, 0, -1):
349 if revlog.linkrev(i) == clrev:
350 return i
351 # We failed to resolve a parent for this node, so
352 # we crash the changegroup construction.
353 raise error.Abort(
354 'unable to resolve parent while packing %r %r'
355 ' for changeset %r' % (revlog.indexfile, rev, clrev))
356 return node.nullrev
357
358 if not linkparents or (
359 revlog.parentrevs(rev) == (node.nullrev, node.nullrev)):
360 p1, p2 = node.nullrev, node.nullrev
361 elif len(linkparents) == 1:
362 p1, = sorted(local(p) for p in linkparents)
363 p2 = node.nullrev
364 else:
365 p1, p2 = sorted(local(p) for p in linkparents)
366 yield ellipsisdata(
367 self, rev, revlog, p1, p2, revlog.revision(rev), linknode)
368 extensions.wrapfunction(changegroup.cg1packer, 'revchunk', revchunk)
369
370 def deltaparent(orig, self, revlog, rev, p1, p2, prev):
371 if util.safehasattr(self, 'full_nodes'):
372 # TODO: send better deltas when in narrow mode.
373 #
374 # changegroup.group() loops over revisions to send,
375 # including revisions we'll skip. What this means is that
376 # `prev` will be a potentially useless delta base for all
377 # ellipsis nodes, as the client likely won't have it. In
378 # the future we should do bookkeeping about which nodes
379 # have been sent to the client, and try to be
380 # significantly smarter about delta bases. This is
381 # slightly tricky because this same code has to work for
382 # all revlogs, and we don't have the linkrev/linknode here.
383 return p1
384 return orig(self, revlog, rev, p1, p2, prev)
385 extensions.wrapfunction(changegroup.cg2packer, 'deltaparent', deltaparent)
@@ -0,0 +1,402 b''
1 # narrowcommands.py - command modifications for narrowhg extension
2 #
3 # Copyright 2017 Google, Inc.
4 #
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.
7 from __future__ import absolute_import
8
9 import itertools
10
11 from mercurial.i18n import _
12 from mercurial import (
13 cmdutil,
14 commands,
15 discovery,
16 error,
17 exchange,
18 extensions,
19 hg,
20 merge,
21 node,
22 registrar,
23 repair,
24 repoview,
25 util,
26 )
27
28 from . import (
29 narrowbundle2,
30 narrowrepo,
31 narrowspec,
32 )
33
34 table = {}
35 command = registrar.command(table)
36
37 def setup():
38 """Wraps user-facing mercurial commands with narrow-aware versions."""
39
40 entry = extensions.wrapcommand(commands.table, 'clone', clonenarrowcmd)
41 entry[1].append(('', 'narrow', None,
42 _("create a narrow clone of select files")))
43 entry[1].append(('', 'depth', '',
44 _("limit the history fetched by distance from heads")))
45 # TODO(durin42): unify sparse/narrow --include/--exclude logic a bit
46 if 'sparse' not in extensions.enabled():
47 entry[1].append(('', 'include', [],
48 _("specifically fetch this file/directory")))
49 entry[1].append(
50 ('', 'exclude', [],
51 _("do not fetch this file/directory, even if included")))
52
53 entry = extensions.wrapcommand(commands.table, 'pull', pullnarrowcmd)
54 entry[1].append(('', 'depth', '',
55 _("limit the history fetched by distance from heads")))
56
57 extensions.wrapcommand(commands.table, 'archive', archivenarrowcmd)
58
59 def expandpull(pullop, includepats, excludepats):
60 if not narrowspec.needsexpansion(includepats):
61 return includepats, excludepats
62
63 heads = pullop.heads or pullop.rheads
64 includepats, excludepats = pullop.remote.expandnarrow(
65 includepats, excludepats, heads)
66 pullop.repo.ui.debug('Expanded narrowspec to inc=%s, exc=%s\n' % (
67 includepats, excludepats))
68 return set(includepats), set(excludepats)
69
70 def clonenarrowcmd(orig, ui, repo, *args, **opts):
71 """Wraps clone command, so 'hg clone' first wraps localrepo.clone()."""
72 wrappedextraprepare = util.nullcontextmanager()
73 opts_narrow = opts['narrow']
74 if opts_narrow:
75 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
76 # Create narrow spec patterns from clone flags
77 includepats = narrowspec.parsepatterns(opts['include'])
78 excludepats = narrowspec.parsepatterns(opts['exclude'])
79
80 # If necessary, ask the server to expand the narrowspec.
81 includepats, excludepats = expandpull(
82 pullop, includepats, excludepats)
83
84 if not includepats and excludepats:
85 # If nothing was included, we assume the user meant to include
86 # everything, except what they asked to exclude.
87 includepats = {'path:.'}
88
89 narrowspec.save(pullop.repo, includepats, excludepats)
90
91 # This will populate 'includepats' etc with the values from the
92 # narrowspec we just saved.
93 orig(pullop, kwargs)
94
95 if opts.get('depth'):
96 kwargs['depth'] = opts['depth']
97 wrappedextraprepare = extensions.wrappedfunction(exchange,
98 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
99
100 def pullnarrow(orig, repo, *args, **kwargs):
101 narrowrepo.wraprepo(repo.unfiltered(), opts_narrow)
102 if isinstance(repo, repoview.repoview):
103 repo.__class__.__bases__ = (repo.__class__.__bases__[0],
104 repo.unfiltered().__class__)
105 if opts_narrow:
106 repo.requirements.add(narrowrepo.requirement)
107 repo._writerequirements()
108
109 return orig(repo, *args, **kwargs)
110
111 wrappedpull = extensions.wrappedfunction(exchange, 'pull', pullnarrow)
112
113 with wrappedextraprepare, wrappedpull:
114 return orig(ui, repo, *args, **opts)
115
116 def pullnarrowcmd(orig, ui, repo, *args, **opts):
117 """Wraps pull command to allow modifying narrow spec."""
118 wrappedextraprepare = util.nullcontextmanager()
119 if narrowrepo.requirement in repo.requirements:
120
121 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
122 orig(pullop, kwargs)
123 if opts.get('depth'):
124 kwargs['depth'] = opts['depth']
125 wrappedextraprepare = extensions.wrappedfunction(exchange,
126 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
127
128 with wrappedextraprepare:
129 return orig(ui, repo, *args, **opts)
130
131 def archivenarrowcmd(orig, ui, repo, *args, **opts):
132 """Wraps archive command to narrow the default includes."""
133 if narrowrepo.requirement in repo.requirements:
134 repo_includes, repo_excludes = repo.narrowpats
135 includes = set(opts.get('include', []))
136 excludes = set(opts.get('exclude', []))
137 includes, excludes = narrowspec.restrictpatterns(
138 includes, excludes, repo_includes, repo_excludes)
139 if includes:
140 opts['include'] = includes
141 if excludes:
142 opts['exclude'] = excludes
143 return orig(ui, repo, *args, **opts)
144
145 def pullbundle2extraprepare(orig, pullop, kwargs):
146 repo = pullop.repo
147 if narrowrepo.requirement not in repo.requirements:
148 return orig(pullop, kwargs)
149
150 if narrowbundle2.narrowcap not in pullop.remotebundle2caps:
151 raise error.Abort(_("server doesn't support narrow clones"))
152 orig(pullop, kwargs)
153 kwargs['narrow'] = True
154 include, exclude = repo.narrowpats
155 kwargs['oldincludepats'] = include
156 kwargs['oldexcludepats'] = exclude
157 kwargs['includepats'] = include
158 kwargs['excludepats'] = exclude
159 kwargs['known'] = [node.hex(ctx.node()) for ctx in
160 repo.set('::%ln', pullop.common)
161 if ctx.node() != node.nullid]
162 if not kwargs['known']:
163 # Mercurial serialized an empty list as '' and deserializes it as
164 # [''], so delete it instead to avoid handling the empty string on the
165 # server.
166 del kwargs['known']
167
168 extensions.wrapfunction(exchange,'_pullbundle2extraprepare',
169 pullbundle2extraprepare)
170
171 def _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
172 newincludes, newexcludes, force):
173 oldmatch = narrowspec.match(repo.root, oldincludes, oldexcludes)
174 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
175
176 # This is essentially doing "hg outgoing" to find all local-only
177 # commits. We will then check that the local-only commits don't
178 # have any changes to files that will be untracked.
179 unfi = repo.unfiltered()
180 outgoing = discovery.findcommonoutgoing(unfi, remote,
181 commoninc=commoninc)
182 ui.status(_('looking for local changes to affected paths\n'))
183 localnodes = []
184 for n in itertools.chain(outgoing.missing, outgoing.excluded):
185 if any(oldmatch(f) and not newmatch(f) for f in unfi[n].files()):
186 localnodes.append(n)
187 revstostrip = unfi.revs('descendants(%ln)', localnodes)
188 hiddenrevs = repoview.filterrevs(repo, 'visible')
189 visibletostrip = list(repo.changelog.node(r)
190 for r in (revstostrip - hiddenrevs))
191 if visibletostrip:
192 ui.status(_('The following changeset(s) or their ancestors have '
193 'local changes not on the remote:\n'))
194 maxnodes = 10
195 if ui.verbose or len(visibletostrip) <= maxnodes:
196 for n in visibletostrip:
197 ui.status('%s\n' % node.short(n))
198 else:
199 for n in visibletostrip[:maxnodes]:
200 ui.status('%s\n' % node.short(n))
201 ui.status(_('...and %d more, use --verbose to list all\n') %
202 (len(visibletostrip) - maxnodes))
203 if not force:
204 raise error.Abort(_('local changes found'),
205 hint=_('use --force-delete-local-changes to '
206 'ignore'))
207
208 if revstostrip:
209 tostrip = [unfi.changelog.node(r) for r in revstostrip]
210 if repo['.'].node() in tostrip:
211 # stripping working copy, so move to a different commit first
212 urev = max(repo.revs('(::%n) - %ln + null',
213 repo['.'].node(), visibletostrip))
214 hg.clean(repo, urev)
215 repair.strip(ui, unfi, tostrip, topic='narrow')
216
217 todelete = []
218 for f, f2, size in repo.store.datafiles():
219 if f.startswith('data/'):
220 file = f[5:-2]
221 if not newmatch(file):
222 todelete.append(f)
223 elif f.startswith('meta/'):
224 dir = f[5:-13]
225 dirs = ['.'] + sorted(util.dirs({dir})) + [dir]
226 include = True
227 for d in dirs:
228 visit = newmatch.visitdir(d)
229 if not visit:
230 include = False
231 break
232 if visit == 'all':
233 break
234 if not include:
235 todelete.append(f)
236
237 repo.destroying()
238
239 with repo.transaction("narrowing"):
240 for f in todelete:
241 ui.status(_('deleting %s\n') % f)
242 util.unlinkpath(repo.svfs.join(f))
243 repo.store.markremoved(f)
244
245 for f in repo.dirstate:
246 if not newmatch(f):
247 repo.dirstate.drop(f)
248 repo.wvfs.unlinkpath(f)
249 repo.setnarrowpats(newincludes, newexcludes)
250
251 repo.destroyed()
252
253 def _widen(ui, repo, remote, commoninc, newincludes, newexcludes):
254 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
255
256 # TODO(martinvonz): Get expansion working with widening/narrowing.
257 if narrowspec.needsexpansion(newincludes):
258 raise error.Abort('Expansion not yet supported on pull')
259
260 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
261 orig(pullop, kwargs)
262 # The old{in,ex}cludepats have already been set by orig()
263 kwargs['includepats'] = newincludes
264 kwargs['excludepats'] = newexcludes
265 wrappedextraprepare = extensions.wrappedfunction(exchange,
266 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
267
268 # define a function that narrowbundle2 can call after creating the
269 # backup bundle, but before applying the bundle from the server
270 def setnewnarrowpats():
271 repo.setnarrowpats(newincludes, newexcludes)
272 repo.setnewnarrowpats = setnewnarrowpats
273
274 ds = repo.dirstate
275 p1, p2 = ds.p1(), ds.p2()
276 with ds.parentchange():
277 ds.setparents(node.nullid, node.nullid)
278 common = commoninc[0]
279 with wrappedextraprepare:
280 exchange.pull(repo, remote, heads=common)
281 with ds.parentchange():
282 ds.setparents(p1, p2)
283
284 actions = {k: [] for k in 'a am f g cd dc r dm dg m e k p pr'.split()}
285 addgaction = actions['g'].append
286
287 mf = repo['.'].manifest().matches(newmatch)
288 for f, fn in mf.iteritems():
289 if f not in repo.dirstate:
290 addgaction((f, (mf.flags(f), False),
291 "add from widened narrow clone"))
292
293 merge.applyupdates(repo, actions, wctx=repo[None],
294 mctx=repo['.'], overwrite=False)
295 merge.recordupdates(repo, actions, branchmerge=False)
296
297 # TODO(rdamazio): Make new matcher format and update description
298 @command('tracked',
299 [('', 'addinclude', [], _('new paths to include')),
300 ('', 'removeinclude', [], _('old paths to no longer include')),
301 ('', 'addexclude', [], _('new paths to exclude')),
302 ('', 'removeexclude', [], _('old paths to no longer exclude')),
303 ('', 'clear', False, _('whether to replace the existing narrowspec')),
304 ('', 'force-delete-local-changes', False,
305 _('forces deletion of local changes when narrowing')),
306 ] + commands.remoteopts,
307 _('[OPTIONS]... [REMOTE]'),
308 inferrepo=True)
309 def trackedcmd(ui, repo, remotepath=None, *pats, **opts):
310 """show or change the current narrowspec
311
312 With no argument, shows the current narrowspec entries, one per line. Each
313 line will be prefixed with 'I' or 'X' for included or excluded patterns,
314 respectively.
315
316 The narrowspec is comprised of expressions to match remote files and/or
317 directories that should be pulled into your client.
318 The narrowspec has *include* and *exclude* expressions, with excludes always
319 trumping includes: that is, if a file matches an exclude expression, it will
320 be excluded even if it also matches an include expression.
321 Excluding files that were never included has no effect.
322
323 Each included or excluded entry is in the format described by
324 'hg help patterns'.
325
326 The options allow you to add or remove included and excluded expressions.
327
328 If --clear is specified, then all previous includes and excludes are DROPPED
329 and replaced by the new ones specified to --addinclude and --addexclude.
330 If --clear is specified without any further options, the narrowspec will be
331 empty and will not match any files.
332 """
333 if narrowrepo.requirement not in repo.requirements:
334 ui.warn(_('The narrow command is only supported on respositories cloned'
335 ' with --narrow.\n'))
336 return 1
337
338 # Before supporting, decide whether it "hg tracked --clear" should mean
339 # tracking no paths or all paths.
340 if opts['clear']:
341 ui.warn(_('The --clear option is not yet supported.\n'))
342 return 1
343
344 if narrowspec.needsexpansion(opts['addinclude'] + opts['addexclude']):
345 raise error.Abort('Expansion not yet supported on widen/narrow')
346
347 addedincludes = narrowspec.parsepatterns(opts['addinclude'])
348 removedincludes = narrowspec.parsepatterns(opts['removeinclude'])
349 addedexcludes = narrowspec.parsepatterns(opts['addexclude'])
350 removedexcludes = narrowspec.parsepatterns(opts['removeexclude'])
351 widening = addedincludes or removedexcludes
352 narrowing = removedincludes or addedexcludes
353 only_show = not widening and not narrowing
354
355 # Only print the current narrowspec.
356 if only_show:
357 include, exclude = repo.narrowpats
358
359 ui.pager('tracked')
360 fm = ui.formatter('narrow', opts)
361 for i in sorted(include):
362 fm.startitem()
363 fm.write('status', '%s ', 'I', label='narrow.included')
364 fm.write('pat', '%s\n', i, label='narrow.included')
365 for i in sorted(exclude):
366 fm.startitem()
367 fm.write('status', '%s ', 'X', label='narrow.excluded')
368 fm.write('pat', '%s\n', i, label='narrow.excluded')
369 fm.end()
370 return 0
371
372 with repo.wlock(), repo.lock():
373 cmdutil.bailifchanged(repo)
374
375 # Find the revisions we have in common with the remote. These will
376 # be used for finding local-only changes for narrowing. They will
377 # also define the set of revisions to update for widening.
378 remotepath = ui.expandpath(remotepath or 'default')
379 url, branches = hg.parseurl(remotepath)
380 ui.status(_('comparing with %s\n') % util.hidepassword(url))
381 remote = hg.peer(repo, opts, url)
382 commoninc = discovery.findcommonincoming(repo, remote)
383
384 oldincludes, oldexcludes = repo.narrowpats
385 if narrowing:
386 newincludes = oldincludes - removedincludes
387 newexcludes = oldexcludes | addedexcludes
388 _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
389 newincludes, newexcludes,
390 opts['force_delete_local_changes'])
391 # _narrow() updated the narrowspec and _widen() below needs to
392 # use the updated values as its base (otherwise removed includes
393 # and addedexcludes will be lost in the resulting narrowspec)
394 oldincludes = newincludes
395 oldexcludes = newexcludes
396
397 if widening:
398 newincludes = oldincludes | addedincludes
399 newexcludes = oldexcludes - removedexcludes
400 _widen(ui, repo, remote, commoninc, newincludes, newexcludes)
401
402 return 0
@@ -0,0 +1,35 b''
1 # narrowcopies.py - extensions to mercurial copies module to support narrow
2 # clones
3 #
4 # Copyright 2017 Google, Inc.
5 #
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
8
9 from __future__ import absolute_import
10
11 from mercurial import (
12 copies,
13 extensions,
14 util,
15 )
16
17 def setup(repo):
18 def _computeforwardmissing(orig, a, b, match=None):
19 missing = orig(a, b, match)
20 if util.safehasattr(repo, 'narrowmatch'):
21 narrowmatch = repo.narrowmatch()
22 missing = filter(narrowmatch, missing)
23 return missing
24
25 def _checkcopies(orig, srcctx, dstctx, f, base, tca, remotebase, limit,
26 data):
27 if util.safehasattr(repo, 'narrowmatch'):
28 narrowmatch = repo.narrowmatch()
29 if not narrowmatch(f):
30 return
31 orig(srcctx, dstctx, f, base, tca, remotebase, limit, data)
32
33 extensions.wrapfunction(copies, '_computeforwardmissing',
34 _computeforwardmissing)
35 extensions.wrapfunction(copies, '_checkcopies', _checkcopies)
@@ -0,0 +1,80 b''
1 # narrowdirstate.py - extensions to mercurial dirstate to support narrow clones
2 #
3 # Copyright 2017 Google, Inc.
4 #
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.
7
8 from __future__ import absolute_import
9
10 from mercurial.i18n import _
11 from mercurial import (
12 dirstate,
13 error,
14 extensions,
15 match as matchmod,
16 util as hgutil,
17 )
18
19 from . import narrowspec
20
21 def setup(repo):
22 """Add narrow spec dirstate ignore, block changes outside narrow spec."""
23
24 def walk(orig, self, match, subrepos, unknown, ignored, full=True,
25 narrowonly=True):
26 if narrowonly:
27 narrowmatch = repo.narrowmatch()
28 match = matchmod.intersectmatchers(match, narrowmatch)
29 return orig(self, match, subrepos, unknown, ignored, full)
30
31 extensions.wrapfunction(dirstate.dirstate, 'walk', walk)
32
33 # Prevent adding files that are outside the sparse checkout
34 editfuncs = ['normal', 'add', 'normallookup', 'copy', 'remove', 'merge']
35 for func in editfuncs:
36 def _wrapper(orig, self, *args):
37 dirstate = repo.dirstate
38 narrowmatch = repo.narrowmatch()
39 for f in args:
40 if f is not None and not narrowmatch(f) and f not in dirstate:
41 raise error.Abort(_("cannot track '%s' - it is outside " +
42 "the narrow clone") % f)
43 return orig(self, *args)
44 extensions.wrapfunction(dirstate.dirstate, func, _wrapper)
45
46 def filterrebuild(orig, self, parent, allfiles, changedfiles=None):
47 if changedfiles is None:
48 # Rebuilding entire dirstate, let's filter allfiles to match the
49 # narrowspec.
50 allfiles = [f for f in allfiles if repo.narrowmatch()(f)]
51 orig(self, parent, allfiles, changedfiles)
52
53 extensions.wrapfunction(dirstate.dirstate, 'rebuild', filterrebuild)
54
55 def _narrowbackupname(backupname):
56 assert 'dirstate' in backupname
57 return backupname.replace('dirstate', narrowspec.FILENAME)
58
59 def restorebackup(orig, self, tr, backupname):
60 self._opener.rename(_narrowbackupname(backupname), narrowspec.FILENAME,
61 checkambig=True)
62 orig(self, tr, backupname)
63
64 extensions.wrapfunction(dirstate.dirstate, 'restorebackup', restorebackup)
65
66 def savebackup(orig, self, tr, backupname):
67 orig(self, tr, backupname)
68
69 narrowbackupname = _narrowbackupname(backupname)
70 self._opener.tryunlink(narrowbackupname)
71 hgutil.copyfile(self._opener.join(narrowspec.FILENAME),
72 self._opener.join(narrowbackupname), hardlink=True)
73
74 extensions.wrapfunction(dirstate.dirstate, 'savebackup', savebackup)
75
76 def clearbackup(orig, self, tr, backupname):
77 orig(self, tr, backupname)
78 self._opener.unlink(_narrowbackupname(backupname))
79
80 extensions.wrapfunction(dirstate.dirstate, 'clearbackup', clearbackup)
@@ -0,0 +1,76 b''
1 # narrowmerge.py - extensions to mercurial merge module to support narrow clones
2 #
3 # Copyright 2017 Google, Inc.
4 #
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.
7
8 from __future__ import absolute_import
9
10 from mercurial.i18n import _
11 from mercurial import (
12 copies,
13 error,
14 extensions,
15 merge,
16 util,
17 )
18
19 def setup():
20 def _manifestmerge(orig, repo, wctx, p2, pa, branchmerge, *args, **kwargs):
21 """Filter updates to only lay out files that match the narrow spec."""
22 actions, diverge, renamedelete = orig(
23 repo, wctx, p2, pa, branchmerge, *args, **kwargs)
24
25 if not util.safehasattr(repo, 'narrowmatch'):
26 return actions, diverge, renamedelete
27
28 nooptypes = set(['k']) # TODO: handle with nonconflicttypes
29 nonconflicttypes = set('a am c cm f g r e'.split())
30 narrowmatch = repo.narrowmatch()
31 for f, action in actions.items():
32 if narrowmatch(f):
33 pass
34 elif not branchmerge:
35 del actions[f] # just updating, ignore changes outside clone
36 elif action[0] in nooptypes:
37 del actions[f] # merge does not affect file
38 elif action[0] in nonconflicttypes:
39 raise error.Abort(_('merge affects file \'%s\' outside narrow, '
40 'which is not yet supported') % f,
41 hint=_('merging in the other direction '
42 'may work'))
43 else:
44 raise error.Abort(_('conflict in file \'%s\' is outside '
45 'narrow clone') % f)
46
47 return actions, diverge, renamedelete
48
49 extensions.wrapfunction(merge, 'manifestmerge', _manifestmerge)
50
51 def _checkcollision(orig, repo, wmf, actions):
52 if util.safehasattr(repo, 'narrowmatch'):
53 narrowmatch = repo.narrowmatch()
54 wmf = wmf.matches(narrowmatch)
55 if actions:
56 narrowactions = {}
57 for m, actionsfortype in actions.iteritems():
58 narrowactions[m] = []
59 for (f, args, msg) in actionsfortype:
60 if narrowmatch(f):
61 narrowactions[m].append((f, args, msg))
62 actions = narrowactions
63 return orig(repo, wmf, actions)
64
65 extensions.wrapfunction(merge, '_checkcollision', _checkcollision)
66
67 def _computenonoverlap(orig, repo, *args, **kwargs):
68 u1, u2 = orig(repo, *args, **kwargs)
69 if not util.safehasattr(repo, 'narrowmatch'):
70 return u1, u2
71
72 narrowmatch = repo.narrowmatch()
73 u1 = [f for f in u1 if narrowmatch(f)]
74 u2 = [f for f in u2 if narrowmatch(f)]
75 return u1, u2
76 extensions.wrapfunction(copies, '_computenonoverlap', _computenonoverlap)
@@ -0,0 +1,42 b''
1 # narrowpatch.py - extensions to mercurial patch module to support narrow clones
2 #
3 # Copyright 2017 Google, Inc.
4 #
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.
7
8 from __future__ import absolute_import
9
10 from mercurial import (
11 extensions,
12 patch,
13 util,
14 )
15
16 def setup(repo):
17 def _filepairs(orig, *args):
18 """Only includes files within the narrow spec in the diff."""
19 if util.safehasattr(repo, 'narrowmatch'):
20 narrowmatch = repo.narrowmatch()
21 for x in orig(*args):
22 f1, f2, copyop = x
23 if ((not f1 or narrowmatch(f1)) and
24 (not f2 or narrowmatch(f2))):
25 yield x
26 else:
27 for x in orig(*args):
28 yield x
29
30 def trydiff(orig, repo, revs, ctx1, ctx2, modified, added, removed,
31 copy, getfilectx, *args, **kwargs):
32 if util.safehasattr(repo, 'narrowmatch'):
33 narrowmatch = repo.narrowmatch()
34 modified = filter(narrowmatch, modified)
35 added = filter(narrowmatch, added)
36 removed = filter(narrowmatch, removed)
37 copy = {k: v for k, v in copy.iteritems() if narrowmatch(k)}
38 return orig(repo, revs, ctx1, ctx2, modified, added, removed, copy,
39 getfilectx, *args, **kwargs)
40
41 extensions.wrapfunction(patch, '_filepairs', _filepairs)
42 extensions.wrapfunction(patch, 'trydiff', trydiff)
@@ -0,0 +1,110 b''
1 # narrowrepo.py - repository which supports narrow revlogs, lazy loading
2 #
3 # Copyright 2017 Google, Inc.
4 #
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.
7
8 from __future__ import absolute_import
9
10 from mercurial import (
11 bundlerepo,
12 localrepo,
13 match as matchmod,
14 scmutil,
15 )
16
17 from .. import (
18 share,
19 )
20
21 from . import (
22 narrowrevlog,
23 narrowspec,
24 )
25
26 requirement = 'narrowhg'
27
28 def wrappostshare(orig, sourcerepo, destrepo, **kwargs):
29 orig(sourcerepo, destrepo, **kwargs)
30 if requirement in sourcerepo.requirements:
31 with destrepo.wlock():
32 with destrepo.vfs('shared', 'a') as fp:
33 fp.write(narrowspec.FILENAME + '\n')
34
35 def unsharenarrowspec(orig, ui, repo, repopath):
36 if (requirement in repo.requirements
37 and repo.path == repopath and repo.shared()):
38 srcrepo = share._getsrcrepo(repo)
39 with srcrepo.vfs(narrowspec.FILENAME) as f:
40 spec = f.read()
41 with repo.vfs(narrowspec.FILENAME, 'w') as f:
42 f.write(spec)
43 return orig(ui, repo, repopath)
44
45 def wraprepo(repo, opts_narrow):
46 """Enables narrow clone functionality on a single local repository."""
47
48 cacheprop = localrepo.storecache
49 if isinstance(repo, bundlerepo.bundlerepository):
50 # We have to use a different caching property decorator for
51 # bundlerepo because storecache blows up in strange ways on a
52 # bundlerepo. Fortunately, there's no risk of data changing in
53 # a bundlerepo.
54 cacheprop = lambda name: localrepo.unfilteredpropertycache
55
56 class narrowrepository(repo.__class__):
57
58 def _constructmanifest(self):
59 manifest = super(narrowrepository, self)._constructmanifest()
60 narrowrevlog.makenarrowmanifestrevlog(manifest, repo)
61 return manifest
62
63 @cacheprop('00manifest.i')
64 def manifestlog(self):
65 mfl = super(narrowrepository, self).manifestlog
66 narrowrevlog.makenarrowmanifestlog(mfl, self)
67 return mfl
68
69 def file(self, f):
70 fl = super(narrowrepository, self).file(f)
71 narrowrevlog.makenarrowfilelog(fl, self.narrowmatch())
72 return fl
73
74 @localrepo.repofilecache(narrowspec.FILENAME)
75 def narrowpats(self):
76 return narrowspec.load(self)
77
78 @localrepo.repofilecache(narrowspec.FILENAME)
79 def _narrowmatch(self):
80 include, exclude = self.narrowpats
81 if not opts_narrow and not include and not exclude:
82 return matchmod.always(self.root, '')
83 return narrowspec.match(self.root, include=include, exclude=exclude)
84
85 # TODO(martinvonz): make this property-like instead?
86 def narrowmatch(self):
87 return self._narrowmatch
88
89 def setnarrowpats(self, newincludes, newexcludes):
90 narrowspec.save(self, newincludes, newexcludes)
91 self.invalidate(clearfilecache=True)
92
93 # I'm not sure this is the right place to do this filter.
94 # context._manifestmatches() would probably be better, or perhaps
95 # move it to a later place, in case some of the callers do want to know
96 # which directories changed. This seems to work for now, though.
97 def status(self, *args, **kwargs):
98 s = super(narrowrepository, self).status(*args, **kwargs)
99 narrowmatch = self.narrowmatch()
100 modified = filter(narrowmatch, s.modified)
101 added = filter(narrowmatch, s.added)
102 removed = filter(narrowmatch, s.removed)
103 deleted = filter(narrowmatch, s.deleted)
104 unknown = filter(narrowmatch, s.unknown)
105 ignored = filter(narrowmatch, s.ignored)
106 clean = filter(narrowmatch, s.clean)
107 return scmutil.status(modified, added, removed, deleted, unknown,
108 ignored, clean)
109
110 repo.__class__ = narrowrepository
@@ -0,0 +1,163 b''
1 # narrowrevlog.py - revlog storing irrelevant nodes as "ellipsis" nodes
2 #
3 # Copyright 2017 Google, Inc.
4 #
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.
7
8 from __future__ import absolute_import
9
10 from mercurial import (
11 manifest,
12 revlog,
13 util,
14 )
15
16 ELLIPSIS_NODE_FLAG = 1 << 14
17 revlog.REVIDX_KNOWN_FLAGS |= ELLIPSIS_NODE_FLAG
18 if (util.safehasattr(revlog, 'REVIDX_FLAGS_ORDER') and
19 ELLIPSIS_NODE_FLAG not in revlog.REVIDX_FLAGS_ORDER):
20 revlog.REVIDX_FLAGS_ORDER.append(ELLIPSIS_NODE_FLAG)
21
22 def readtransform(self, text):
23 return text, False
24
25 def writetransform(self, text):
26 return text, False
27
28 def rawtransform(self, text):
29 return False
30
31 if util.safehasattr(revlog, 'addflagprocessor'):
32 revlog.addflagprocessor(ELLIPSIS_NODE_FLAG,
33 (readtransform, writetransform, rawtransform))
34
35 def setup():
36 # We just wanted to add the flag processor, which is done at module
37 # load time.
38 pass
39
40 class excludeddir(manifest.treemanifest):
41 def __init__(self, dir, node):
42 super(excludeddir, self).__init__(dir)
43 self._node = node
44 # Add an empty file, which will be included by iterators and such,
45 # appearing as the directory itself (i.e. something like "dir/")
46 self._files[''] = node
47 self._flags[''] = 't'
48
49 # Manifests outside the narrowspec should never be modified, so avoid
50 # copying. This makes a noticeable difference when there are very many
51 # directories outside the narrowspec. Also, it makes sense for the copy to
52 # be of the same type as the original, which would not happen with the
53 # super type's copy().
54 def copy(self):
55 return self
56
57 class excludeddirmanifestctx(manifest.treemanifestctx):
58 def __init__(self, dir, node):
59 self._dir = dir
60 self._node = node
61
62 def read(self):
63 return excludeddir(self._dir, self._node)
64
65 def write(self, *args):
66 raise AssertionError('Attempt to write manifest from excluded dir %s' %
67 self._dir)
68
69 class excludedmanifestrevlog(manifest.manifestrevlog):
70 def __init__(self, dir):
71 self._dir = dir
72
73 def __len__(self):
74 raise AssertionError('Attempt to get length of excluded dir %s' %
75 self._dir)
76
77 def rev(self, node):
78 raise AssertionError('Attempt to get rev from excluded dir %s' %
79 self._dir)
80
81 def linkrev(self, node):
82 raise AssertionError('Attempt to get linkrev from excluded dir %s' %
83 self._dir)
84
85 def node(self, rev):
86 raise AssertionError('Attempt to get node from excluded dir %s' %
87 self._dir)
88
89 def add(self, *args, **kwargs):
90 # We should never write entries in dirlogs outside the narrow clone.
91 # However, the method still gets called from writesubtree() in
92 # _addtree(), so we need to handle it. We should possibly make that
93 # avoid calling add() with a clean manifest (_dirty is always False
94 # in excludeddir instances).
95 pass
96
97 def makenarrowmanifestrevlog(mfrevlog, repo):
98 if util.safehasattr(mfrevlog, '_narrowed'):
99 return
100
101 class narrowmanifestrevlog(mfrevlog.__class__):
102 # This function is called via debug{revlog,index,data}, but also during
103 # at least some push operations. This will be used to wrap/exclude the
104 # child directories when using treemanifests.
105 def dirlog(self, dir):
106 if dir and not dir.endswith('/'):
107 dir = dir + '/'
108 if not repo.narrowmatch().visitdir(dir[:-1] or '.'):
109 return excludedmanifestrevlog(dir)
110 result = super(narrowmanifestrevlog, self).dirlog(dir)
111 makenarrowmanifestrevlog(result, repo)
112 return result
113
114 mfrevlog.__class__ = narrowmanifestrevlog
115 mfrevlog._narrowed = True
116
117 def makenarrowmanifestlog(mfl, repo):
118 class narrowmanifestlog(mfl.__class__):
119 def get(self, dir, node, verify=True):
120 if not repo.narrowmatch().visitdir(dir[:-1] or '.'):
121 return excludeddirmanifestctx(dir, node)
122 return super(narrowmanifestlog, self).get(dir, node, verify=verify)
123 mfl.__class__ = narrowmanifestlog
124
125 def makenarrowfilelog(fl, narrowmatch):
126 class narrowfilelog(fl.__class__):
127 def renamed(self, node):
128 m = super(narrowfilelog, self).renamed(node)
129 if m and not narrowmatch(m[0]):
130 return None
131 return m
132
133 def size(self, rev):
134 # We take advantage of the fact that remotefilelog
135 # lacks a node() method to just skip the
136 # rename-checking logic when on remotefilelog. This
137 # might be incorrect on other non-revlog-based storage
138 # engines, but for now this seems to be fine.
139 if util.safehasattr(self, 'node'):
140 node = self.node(rev)
141 # Because renamed() is overridden above to
142 # sometimes return None even if there is metadata
143 # in the revlog, size can be incorrect for
144 # copies/renames, so we need to make sure we call
145 # the super class's implementation of renamed()
146 # for the purpose of size calculation.
147 if super(narrowfilelog, self).renamed(node):
148 return len(self.read(node))
149 return super(narrowfilelog, self).size(rev)
150
151 def cmp(self, node, text):
152 different = super(narrowfilelog, self).cmp(node, text)
153 if different:
154 # Similar to size() above, if the file was copied from
155 # a file outside the narrowspec, the super class's
156 # would have returned True because we tricked it into
157 # thinking that the file was not renamed.
158 if super(narrowfilelog, self).renamed(node):
159 t2 = self.read(node)
160 return t2 != text
161 return different
162
163 fl.__class__ = narrowfilelog
@@ -0,0 +1,204 b''
1 # narrowspec.py - methods for working with a narrow view of a repository
2 #
3 # Copyright 2017 Google, Inc.
4 #
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.
7
8 from __future__ import absolute_import
9
10 import errno
11
12 from mercurial.i18n import _
13 from mercurial import (
14 error,
15 match as matchmod,
16 util,
17 )
18
19 from .. import (
20 share,
21 )
22
23 FILENAME = 'narrowspec'
24
25 def _parsestoredpatterns(text):
26 """Parses the narrowspec format that's stored on disk."""
27 patlist = None
28 includepats = []
29 excludepats = []
30 for l in text.splitlines():
31 if l == '[includes]':
32 if patlist is None:
33 patlist = includepats
34 else:
35 raise error.Abort(_('narrowspec includes section must appear '
36 'at most once, before excludes'))
37 elif l == '[excludes]':
38 if patlist is not excludepats:
39 patlist = excludepats
40 else:
41 raise error.Abort(_('narrowspec excludes section must appear '
42 'at most once'))
43 else:
44 patlist.append(l)
45
46 return set(includepats), set(excludepats)
47
48 def parseserverpatterns(text):
49 """Parses the narrowspec format that's returned by the server."""
50 includepats = set()
51 excludepats = set()
52
53 # We get one entry per line, in the format "<key> <value>".
54 # It's OK for value to contain other spaces.
55 for kp in (l.split(' ', 1) for l in text.splitlines()):
56 if len(kp) != 2:
57 raise error.Abort(_('Invalid narrowspec pattern line: "%s"') % kp)
58 key = kp[0]
59 pat = kp[1]
60 if key == 'include':
61 includepats.add(pat)
62 elif key == 'exclude':
63 excludepats.add(pat)
64 else:
65 raise error.Abort(_('Invalid key "%s" in server response') % key)
66
67 return includepats, excludepats
68
69 def normalizesplitpattern(kind, pat):
70 """Returns the normalized version of a pattern and kind.
71
72 Returns a tuple with the normalized kind and normalized pattern.
73 """
74 pat = pat.rstrip('/')
75 _validatepattern(pat)
76 return kind, pat
77
78 def _numlines(s):
79 """Returns the number of lines in s, including ending empty lines."""
80 # We use splitlines because it is Unicode-friendly and thus Python 3
81 # compatible. However, it does not count empty lines at the end, so trick
82 # it by adding a character at the end.
83 return len((s + 'x').splitlines())
84
85 def _validatepattern(pat):
86 """Validates the pattern and aborts if it is invalid."""
87
88 # We use newlines as separators in the narrowspec file, so don't allow them
89 # in patterns.
90 if _numlines(pat) > 1:
91 raise error.Abort('newlines are not allowed in narrowspec paths')
92
93 components = pat.split('/')
94 if '.' in components or '..' in components:
95 raise error.Abort(_('"." and ".." are not allowed in narrowspec paths'))
96
97 def normalizepattern(pattern, defaultkind='path'):
98 """Returns the normalized version of a text-format pattern.
99
100 If the pattern has no kind, the default will be added.
101 """
102 kind, pat = matchmod._patsplit(pattern, defaultkind)
103 return '%s:%s' % normalizesplitpattern(kind, pat)
104
105 def parsepatterns(pats):
106 """Parses a list of patterns into a typed pattern set."""
107 return set(normalizepattern(p) for p in pats)
108
109 def format(includes, excludes):
110 output = '[includes]\n'
111 for i in sorted(includes - excludes):
112 output += i + '\n'
113 output += '[excludes]\n'
114 for e in sorted(excludes):
115 output += e + '\n'
116 return output
117
118 def match(root, include=None, exclude=None):
119 if not include:
120 # Passing empty include and empty exclude to matchmod.match()
121 # gives a matcher that matches everything, so explicitly use
122 # the nevermatcher.
123 return matchmod.never(root, '')
124 return matchmod.match(root, '', [], include=include or [],
125 exclude=exclude or [])
126
127 def needsexpansion(includes):
128 return [i for i in includes if i.startswith('include:')]
129
130 def load(repo):
131 if repo.shared():
132 repo = share._getsrcrepo(repo)
133 try:
134 spec = repo.vfs.read(FILENAME)
135 except IOError as e:
136 # Treat "narrowspec does not exist" the same as "narrowspec file exists
137 # and is empty".
138 if e.errno == errno.ENOENT:
139 # Without this the next call to load will use the cached
140 # non-existence of the file, which can cause some odd issues.
141 repo.invalidate(clearfilecache=True)
142 return set(), set()
143 raise
144 return _parsestoredpatterns(spec)
145
146 def save(repo, includepats, excludepats):
147 spec = format(includepats, excludepats)
148 if repo.shared():
149 repo = share._getsrcrepo(repo)
150 repo.vfs.write(FILENAME, spec)
151
152 def restrictpatterns(req_includes, req_excludes, repo_includes, repo_excludes,
153 invalid_includes=None):
154 r""" Restricts the patterns according to repo settings,
155 results in a logical AND operation
156
157 :param req_includes: requested includes
158 :param req_excludes: requested excludes
159 :param repo_includes: repo includes
160 :param repo_excludes: repo excludes
161 :param invalid_includes: an array to collect invalid includes
162 :return: include and exclude patterns
163
164 >>> restrictpatterns({'f1','f2'}, {}, ['f1'], [])
165 (set(['f1']), {})
166 >>> restrictpatterns({'f1'}, {}, ['f1','f2'], [])
167 (set(['f1']), {})
168 >>> restrictpatterns({'f1/fc1', 'f3/fc3'}, {}, ['f1','f2'], [])
169 (set(['f1/fc1']), {})
170 >>> restrictpatterns({'f1_fc1'}, {}, ['f1','f2'], [])
171 ([], set(['path:.']))
172 >>> restrictpatterns({'f1/../f2/fc2'}, {}, ['f1','f2'], [])
173 (set(['f2/fc2']), {})
174 >>> restrictpatterns({'f1/../f3/fc3'}, {}, ['f1','f2'], [])
175 ([], set(['path:.']))
176 >>> restrictpatterns({'f1/$non_exitent_var'}, {}, ['f1','f2'], [])
177 (set(['f1/$non_exitent_var']), {})
178 """
179 res_excludes = req_excludes.copy()
180 res_excludes.update(repo_excludes)
181 if not req_includes:
182 res_includes = set(repo_includes)
183 elif 'path:.' not in repo_includes:
184 res_includes = []
185 for req_include in req_includes:
186 req_include = util.expandpath(util.normpath(req_include))
187 if req_include in repo_includes:
188 res_includes.append(req_include)
189 continue
190 valid = False
191 for repo_include in repo_includes:
192 if req_include.startswith(repo_include + '/'):
193 valid = True
194 res_includes.append(req_include)
195 break
196 if not valid and invalid_includes is not None:
197 invalid_includes.append(req_include)
198 if len(res_includes) == 0:
199 res_excludes = {'path:.'}
200 else:
201 res_includes = set(res_includes)
202 else:
203 res_includes = set(req_includes)
204 return res_includes, res_excludes
@@ -0,0 +1,50 b''
1 # narrowtemplates.py - added template keywords for narrow clones
2 #
3 # Copyright 2017 Google, Inc.
4 #
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.
7
8 from __future__ import absolute_import
9
10 from mercurial import (
11 revset,
12 templatekw,
13 util,
14 )
15
16 from . import narrowrevlog
17
18 def _isellipsis(repo, rev):
19 if repo.changelog.flags(rev) & narrowrevlog.ELLIPSIS_NODE_FLAG:
20 return True
21 return False
22
23 def ellipsis(repo, ctx, templ, **args):
24 """:ellipsis: String. 'ellipsis' if the change is an ellipsis node,
25 else ''."""
26 if _isellipsis(repo, ctx.rev()):
27 return 'ellipsis'
28 return ''
29
30 def outsidenarrow(repo, ctx, templ, **args):
31 """:outsidenarrow: String. 'outsidenarrow' if the change affects no
32 tracked files, else ''."""
33 if util.safehasattr(repo, 'narrowmatch'):
34 m = repo.narrowmatch()
35 if not any(m(f) for f in ctx.files()):
36 return 'outsidenarrow'
37 return ''
38
39 def ellipsisrevset(repo, subset, x):
40 """``ellipsis()``
41 Changesets that are ellipsis nodes.
42 """
43 return subset.filter(lambda r: _isellipsis(repo, r))
44
45 def setup():
46 templatekw.keywords['ellipsis'] = ellipsis
47 templatekw.keywords['outsidenarrow'] = outsidenarrow
48
49 revset.symbols['ellipsis'] = ellipsisrevset
50 revset.safesymbols.add('ellipsis')
@@ -0,0 +1,51 b''
1 # narrowwirepeer.py - passes narrow spec with unbundle command
2 #
3 # Copyright 2017 Google, Inc.
4 #
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.
7
8 from __future__ import absolute_import
9
10 from mercurial.i18n import _
11 from mercurial import (
12 error,
13 extensions,
14 hg,
15 node,
16 )
17
18 from . import narrowspec
19
20 def uisetup():
21 def peersetup(ui, peer):
22 # We must set up the expansion before reposetup below, since it's used
23 # at clone time before we have a repo.
24 class expandingpeer(peer.__class__):
25 def expandnarrow(self, narrow_include, narrow_exclude, nodes):
26 ui.status(_("expanding narrowspec\n"))
27 if not self.capable('expandnarrow'):
28 raise error.Abort(
29 'peer does not support expanding narrowspecs')
30
31 hex_nodes = (node.hex(n) for n in nodes)
32 new_narrowspec = self._call(
33 'expandnarrow',
34 includepats=','.join(narrow_include),
35 excludepats=','.join(narrow_exclude),
36 nodes=','.join(hex_nodes))
37
38 return narrowspec.parseserverpatterns(new_narrowspec)
39 peer.__class__ = expandingpeer
40 hg.wirepeersetupfuncs.append(peersetup)
41
42 def reposetup(repo):
43 def wirereposetup(ui, peer):
44 def wrapped(orig, cmd, *args, **kwargs):
45 if cmd == 'unbundle':
46 include, exclude = repo.narrowpats
47 kwargs["includepats"] = ','.join(include)
48 kwargs["excludepats"] = ','.join(exclude)
49 return orig(cmd, *args, **kwargs)
50 extensions.wrapfunction(peer, '_calltwowaystream', wrapped)
51 hg.wirepeersetupfuncs.append(wirereposetup)
@@ -0,0 +1,9 b''
1 cat >> $HGRCPATH <<EOF
2 [extensions]
3 narrow=
4 [ui]
5 ssh=python "$TESTDIR/dummyssh"
6 [experimental]
7 bundle2-exp = True
8 changegroup3 = True
9 EOF
@@ -0,0 +1,42 b''
1 Make a narrow clone then archive it
2 $ . "$TESTDIR/narrow-library.sh"
3
4 $ hg init master
5 $ cd master
6
7 $ for x in `$TESTDIR/seq.py 3`; do
8 > echo $x > "f$x"
9 > hg add "f$x"
10 > hg commit -m "Add $x"
11 > done
12 $ cat >> .hg/hgrc << EOF
13 > [narrowhgacl]
14 > default.includes=f1 f2
15 > EOF
16 $ hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid
17 $ cat hg.pid >> "$DAEMON_PIDS"
18
19 $ cd ..
20 $ hg clone http://localhost:$HGPORT1 narrowclone1
21 requesting all changes
22 adding changesets
23 adding manifests
24 adding file changes
25 added 3 changesets with 2 changes to 2 files
26 new changesets * (glob)
27 updating to branch default
28 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
29
30 The clone directory should only contain f1 and f2
31 $ ls -1 narrowclone1 | sort
32 f1
33 f2
34
35 Requirements should contain narrowhg
36 $ cat narrowclone1/.hg/requires | grep narrowhg
37 narrowhg
38
39 NarrowHG should track f1 and f2
40 $ hg -R narrowclone1 tracked
41 I path:f1
42 I path:f2
@@ -0,0 +1,32 b''
1 Make a narrow clone then archive it
2 $ . "$TESTDIR/narrow-library.sh"
3
4 $ hg init master
5 $ cd master
6
7 $ for x in `$TESTDIR/seq.py 3`; do
8 > echo $x > "f$x"
9 > hg add "f$x"
10 > hg commit -m "Add $x"
11 > done
12
13 $ hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid
14 $ cat hg.pid >> "$DAEMON_PIDS"
15
16 $ cd ..
17 $ hg clone --narrow --include f1 --include f2 http://localhost:$HGPORT1/ narrowclone1
18 requesting all changes
19 adding changesets
20 adding manifests
21 adding file changes
22 added 3 changesets with 2 changes to 2 files
23 new changesets * (glob)
24 updating to branch default
25 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
26
27 The tar should only contain f1 and f2
28 $ cd narrowclone1
29 $ hg archive -t tgz repo.tgz
30 $ tar tfz repo.tgz
31 repo/f1
32 repo/f2
@@ -0,0 +1,130 b''
1 $ . "$TESTDIR/narrow-library.sh"
2
3 $ hg init master
4 $ cd master
5 $ mkdir dir
6 $ mkdir dir/src
7 $ cd dir/src
8 $ for x in `$TESTDIR/seq.py 20`; do echo $x > "f$x"; hg add "f$x"; hg commit -m "Commit src $x"; done
9 $ cd ..
10 $ mkdir tests
11 $ cd tests
12 $ for x in `$TESTDIR/seq.py 20`; do echo $x > "t$x"; hg add "t$x"; hg commit -m "Commit test $x"; done
13 $ cd ../../..
14
15 narrow clone a file, f10
16
17 $ hg clone --narrow ssh://user@dummy/master narrow --noupdate --include "dir/src/f10"
18 requesting all changes
19 adding changesets
20 adding manifests
21 adding file changes
22 added 40 changesets with 1 changes to 1 files
23 new changesets *:* (glob)
24 $ cd narrow
25 $ cat .hg/requires | grep -v generaldelta
26 dotencode
27 fncache
28 narrowhg
29 revlogv1
30 store
31
32 $ cat .hg/narrowspec
33 [includes]
34 path:dir/src/f10
35 [excludes]
36 $ hg update
37 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
38 $ find * | sort
39 dir
40 dir/src
41 dir/src/f10
42 $ cat dir/src/f10
43 10
44
45 $ cd ..
46
47 narrow clone a directory, tests/, except tests/t19
48
49 $ hg clone --narrow ssh://user@dummy/master narrowdir --noupdate --include "dir/tests/" --exclude "dir/tests/t19"
50 requesting all changes
51 adding changesets
52 adding manifests
53 adding file changes
54 added 40 changesets with 19 changes to 19 files
55 new changesets *:* (glob)
56 $ cd narrowdir
57 $ cat .hg/narrowspec
58 [includes]
59 path:dir/tests
60 [excludes]
61 path:dir/tests/t19
62 $ hg update
63 19 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 $ find * | sort
65 dir
66 dir/tests
67 dir/tests/t1
68 dir/tests/t10
69 dir/tests/t11
70 dir/tests/t12
71 dir/tests/t13
72 dir/tests/t14
73 dir/tests/t15
74 dir/tests/t16
75 dir/tests/t17
76 dir/tests/t18
77 dir/tests/t2
78 dir/tests/t20
79 dir/tests/t3
80 dir/tests/t4
81 dir/tests/t5
82 dir/tests/t6
83 dir/tests/t7
84 dir/tests/t8
85 dir/tests/t9
86
87 $ cd ..
88
89 narrow clone everything but a directory (tests/)
90
91 $ hg clone --narrow ssh://user@dummy/master narrowroot --noupdate --exclude "dir/tests"
92 requesting all changes
93 adding changesets
94 adding manifests
95 adding file changes
96 added 40 changesets with 20 changes to 20 files
97 new changesets *:* (glob)
98 $ cd narrowroot
99 $ cat .hg/narrowspec
100 [includes]
101 path:.
102 [excludes]
103 path:dir/tests
104 $ hg update
105 20 files updated, 0 files merged, 0 files removed, 0 files unresolved
106 $ find * | sort
107 dir
108 dir/src
109 dir/src/f1
110 dir/src/f10
111 dir/src/f11
112 dir/src/f12
113 dir/src/f13
114 dir/src/f14
115 dir/src/f15
116 dir/src/f16
117 dir/src/f17
118 dir/src/f18
119 dir/src/f19
120 dir/src/f2
121 dir/src/f20
122 dir/src/f3
123 dir/src/f4
124 dir/src/f5
125 dir/src/f6
126 dir/src/f7
127 dir/src/f8
128 dir/src/f9
129
130 $ cd ..
@@ -0,0 +1,53 b''
1 Test attempting a narrow clone against a server that doesn't support narrowhg.
2
3 $ . "$TESTDIR/narrow-library.sh"
4
5 $ hg init master
6 $ cd master
7
8 $ for x in `$TESTDIR/seq.py 10`; do
9 > echo $x > "f$x"
10 > hg add "f$x"
11 > hg commit -m "Add $x"
12 > done
13
14 $ hg serve -a localhost -p $HGPORT1 --config extensions.narrow=! -d \
15 > --pid-file=hg.pid
16 $ cat hg.pid >> "$DAEMON_PIDS"
17 $ hg serve -a localhost -p $HGPORT2 -d --pid-file=hg.pid
18 $ cat hg.pid >> "$DAEMON_PIDS"
19
20 Verify that narrow is advertised in the bundle2 capabilities:
21 $ echo capabilities | hg -R . serve --stdio | \
22 > python -c "import sys, urllib; print urllib.unquote_plus(list(sys.stdin)[1])" | grep narrow
23 narrow=v0
24
25 $ cd ..
26
27 $ hg clone --narrow --include f1 http://localhost:$HGPORT1/ narrowclone
28 requesting all changes
29 abort: server doesn't support narrow clones
30 [255]
31
32 Make a narrow clone (via HGPORT2), then try to narrow and widen
33 into it (from HGPORT1) to prove that narrowing is fine and widening fails
34 gracefully:
35 $ hg clone -r 0 --narrow --include f1 http://localhost:$HGPORT2/ narrowclone
36 adding changesets
37 adding manifests
38 adding file changes
39 added 1 changesets with 1 changes to 1 files
40 new changesets * (glob)
41 updating to branch default
42 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 $ cd narrowclone
44 $ hg tracked --addexclude f2 http://localhost:$HGPORT1/
45 comparing with http://localhost:$HGPORT1/
46 searching for changes
47 looking for local changes to affected paths
48 $ hg tracked --addinclude f1 http://localhost:$HGPORT1/
49 comparing with http://localhost:$HGPORT1/
50 searching for changes
51 no changes found
52 abort: server doesn't support narrow clones
53 [255]
@@ -0,0 +1,148 b''
1 Testing narrow clones when changesets modifying a matching file exist on
2 multiple branches
3
4 $ . "$TESTDIR/narrow-library.sh"
5
6 $ hg init master
7 $ cd master
8 $ cat >> .hg/hgrc <<EOF
9 > [narrow]
10 > serveellipses=True
11 > EOF
12
13 $ hg branch default
14 marked working directory as branch default
15 (branches are permanent and global, did you want a bookmark?)
16 $ for x in `$TESTDIR/seq.py 10`; do
17 > echo $x > "f$x"
18 > hg add "f$x"
19 > hg commit -m "Add $x"
20 > done
21
22 $ hg branch release-v1
23 marked working directory as branch release-v1
24 (branches are permanent and global, did you want a bookmark?)
25 $ hg commit -m "Start release for v1"
26
27 $ hg update default
28 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 $ for x in `$TESTDIR/seq.py 10`; do
30 > echo "$x v2" > "f$x"
31 > hg commit -m "Update $x to v2"
32 > done
33
34 $ hg update release-v1
35 10 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 $ hg branch release-v1
37 marked working directory as branch release-v1
38 $ for x in `$TESTDIR/seq.py 1 5`; do
39 > echo "$x v1 hotfix" > "f$x"
40 > hg commit -m "Hotfix $x in v1"
41 > done
42
43 $ hg update default
44 10 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 $ hg branch release-v2
46 marked working directory as branch release-v2
47 $ hg commit -m "Start release for v2"
48
49 $ hg update default
50 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
51 $ hg branch default
52 marked working directory as branch default
53 $ for x in `$TESTDIR/seq.py 10`; do
54 > echo "$x v3" > "f$x"
55 > hg commit -m "Update $x to v3"
56 > done
57
58 $ hg update release-v2
59 10 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 $ hg branch release-v2
61 marked working directory as branch release-v2
62 $ for x in `$TESTDIR/seq.py 4 9`; do
63 > echo "$x v2 hotfix" > "f$x"
64 > hg commit -m "Hotfix $x in v2"
65 > done
66
67 $ hg heads -T '{rev} <- {p1rev} ({branch}): {desc}\n'
68 42 <- 41 (release-v2): Hotfix 9 in v2
69 36 <- 35 (default): Update 10 to v3
70 25 <- 24 (release-v1): Hotfix 5 in v1
71
72 $ cd ..
73
74 We now have 3 branches: default, which has v3 of all files, release-v1 which
75 has v1 of all files, and release-v2 with v2 of all files.
76
77 Narrow clone which should get all branches
78
79 $ hg clone --narrow ssh://user@dummy/master narrow --include "f5"
80 requesting all changes
81 adding changesets
82 adding manifests
83 adding file changes
84 added 12 changesets with 5 changes to 1 files (+2 heads)
85 new changesets *:* (glob)
86 updating to branch default
87 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 $ cd narrow
89 $ hg log -G -T "{if(ellipsis, '...')}{node|short} ({branch}): {desc}\n"
90 o ...031f516143fe (release-v2): Hotfix 9 in v2
91 |
92 o 9cd7f7bb9ca1 (release-v2): Hotfix 5 in v2
93 |
94 o ...37bbc88f3ef0 (release-v2): Hotfix 4 in v2
95 |
96 | @ ...dae2f368ca07 (default): Update 10 to v3
97 | |
98 | o 9c224e89cb31 (default): Update 5 to v3
99 | |
100 | o ...04fb59c7c9dc (default): Update 4 to v3
101 |/
102 | o b2253e82401f (release-v1): Hotfix 5 in v1
103 | |
104 | o ...960ac37d74fd (release-v1): Hotfix 4 in v1
105 | |
106 o | 986298e3f347 (default): Update 5 to v2
107 | |
108 o | ...75d539c667ec (default): Update 4 to v2
109 |/
110 o 04c71bd5707f (default): Add 5
111 |
112 o ...881b3891d041 (default): Add 4
113
114
115 Narrow clone the first file, hitting edge condition where unaligned
116 changeset and manifest revnums cross branches.
117
118 $ hg clone --narrow ssh://user@dummy/master narrow --include "f1"
119 requesting all changes
120 adding changesets
121 adding manifests
122 adding file changes
123 added 10 changesets with 4 changes to 1 files (+2 heads)
124 new changesets *:* (glob)
125 updating to branch default
126 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
127 $ cd narrow
128 $ hg log -G -T "{if(ellipsis, '...')}{node|short} ({branch}): {desc}\n"
129 o ...031f516143fe (release-v2): Hotfix 9 in v2
130 |
131 | @ ...dae2f368ca07 (default): Update 10 to v3
132 | |
133 | o 1f5d184b8e96 (default): Update 1 to v3
134 |/
135 | o ...b2253e82401f (release-v1): Hotfix 5 in v1
136 | |
137 | o 133502f6b7e5 (release-v1): Hotfix 1 in v1
138 | |
139 o | ...79165c83d644 (default): Update 10 to v2
140 | |
141 o | c7b7a5f2f088 (default): Update 1 to v2
142 | |
143 | o ...f0531a3db7a9 (release-v1): Start release for v1
144 |/
145 o ...6a3f0f0abef3 (default): Add 10
146 |
147 o e012ac15eaaa (default): Add 1
148
@@ -0,0 +1,225 b''
1 $ . "$TESTDIR/narrow-library.sh"
2
3 $ hg init master
4 $ cd master
5 $ cat >> .hg/hgrc <<EOF
6 > [narrow]
7 > serveellipses=True
8 > EOF
9 $ mkdir dir
10 $ mkdir dir/src
11 $ cd dir/src
12 $ for x in `$TESTDIR/seq.py 20`; do echo $x > "f$x"; hg add "f$x"; hg commit -m "Commit src $x"; done
13 $ cd ..
14 $ mkdir tests
15 $ cd tests
16 $ for x in `$TESTDIR/seq.py 20`; do echo $x > "t$x"; hg add "t$x"; hg commit -m "Commit test $x"; done
17 $ cd ../../..
18
19 narrow clone a file, f10
20
21 $ hg clone --narrow ssh://user@dummy/master narrow --noupdate --include "dir/src/f10"
22 requesting all changes
23 adding changesets
24 adding manifests
25 adding file changes
26 added 3 changesets with 1 changes to 1 files
27 new changesets *:* (glob)
28 $ cd narrow
29 $ cat .hg/requires | grep -v generaldelta
30 dotencode
31 fncache
32 narrowhg
33 revlogv1
34 store
35
36 $ cat .hg/narrowspec
37 [includes]
38 path:dir/src/f10
39 [excludes]
40 $ hg tracked
41 I path:dir/src/f10
42 $ hg update
43 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
44 $ find * | sort
45 dir
46 dir/src
47 dir/src/f10
48 $ cat dir/src/f10
49 10
50
51 $ cd ..
52
53 narrow clone with a newline should fail
54
55 $ hg clone --narrow ssh://user@dummy/master narrow_fail --noupdate --include 'dir/src/f10
56 > '
57 requesting all changes
58 abort: newlines are not allowed in narrowspec paths
59 [255]
60
61 narrow clone a directory, tests/, except tests/t19
62
63 $ hg clone --narrow ssh://user@dummy/master narrowdir --noupdate --include "dir/tests/" --exclude "dir/tests/t19"
64 requesting all changes
65 adding changesets
66 adding manifests
67 adding file changes
68 added 21 changesets with 19 changes to 19 files
69 new changesets *:* (glob)
70 $ cd narrowdir
71 $ cat .hg/narrowspec
72 [includes]
73 path:dir/tests
74 [excludes]
75 path:dir/tests/t19
76 $ hg tracked
77 I path:dir/tests
78 X path:dir/tests/t19
79 $ hg update
80 19 files updated, 0 files merged, 0 files removed, 0 files unresolved
81 $ find * | sort
82 dir
83 dir/tests
84 dir/tests/t1
85 dir/tests/t10
86 dir/tests/t11
87 dir/tests/t12
88 dir/tests/t13
89 dir/tests/t14
90 dir/tests/t15
91 dir/tests/t16
92 dir/tests/t17
93 dir/tests/t18
94 dir/tests/t2
95 dir/tests/t20
96 dir/tests/t3
97 dir/tests/t4
98 dir/tests/t5
99 dir/tests/t6
100 dir/tests/t7
101 dir/tests/t8
102 dir/tests/t9
103
104 $ cd ..
105
106 narrow clone everything but a directory (tests/)
107
108 $ hg clone --narrow ssh://user@dummy/master narrowroot --noupdate --exclude "dir/tests"
109 requesting all changes
110 adding changesets
111 adding manifests
112 adding file changes
113 added 21 changesets with 20 changes to 20 files
114 new changesets *:* (glob)
115 $ cd narrowroot
116 $ cat .hg/narrowspec
117 [includes]
118 path:.
119 [excludes]
120 path:dir/tests
121 $ hg tracked
122 I path:.
123 X path:dir/tests
124 $ hg update
125 20 files updated, 0 files merged, 0 files removed, 0 files unresolved
126 $ find * | sort
127 dir
128 dir/src
129 dir/src/f1
130 dir/src/f10
131 dir/src/f11
132 dir/src/f12
133 dir/src/f13
134 dir/src/f14
135 dir/src/f15
136 dir/src/f16
137 dir/src/f17
138 dir/src/f18
139 dir/src/f19
140 dir/src/f2
141 dir/src/f20
142 dir/src/f3
143 dir/src/f4
144 dir/src/f5
145 dir/src/f6
146 dir/src/f7
147 dir/src/f8
148 dir/src/f9
149
150 $ cd ..
151
152 narrow clone no paths at all
153
154 $ hg clone --narrow ssh://user@dummy/master narrowempty --noupdate
155 requesting all changes
156 adding changesets
157 adding manifests
158 adding file changes
159 added 1 changesets with 0 changes to 0 files
160 new changesets * (glob)
161 $ cd narrowempty
162 $ hg tracked
163 $ hg update
164 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
165 $ ls
166
167 $ cd ..
168
169 simple clone
170 $ hg clone ssh://user@dummy/master simpleclone
171 requesting all changes
172 adding changesets
173 adding manifests
174 adding file changes
175 added 40 changesets with 40 changes to 40 files
176 new changesets * (glob)
177 updating to branch default
178 40 files updated, 0 files merged, 0 files removed, 0 files unresolved
179 $ cd simpleclone
180 $ find * | sort
181 dir
182 dir/src
183 dir/src/f1
184 dir/src/f10
185 dir/src/f11
186 dir/src/f12
187 dir/src/f13
188 dir/src/f14
189 dir/src/f15
190 dir/src/f16
191 dir/src/f17
192 dir/src/f18
193 dir/src/f19
194 dir/src/f2
195 dir/src/f20
196 dir/src/f3
197 dir/src/f4
198 dir/src/f5
199 dir/src/f6
200 dir/src/f7
201 dir/src/f8
202 dir/src/f9
203 dir/tests
204 dir/tests/t1
205 dir/tests/t10
206 dir/tests/t11
207 dir/tests/t12
208 dir/tests/t13
209 dir/tests/t14
210 dir/tests/t15
211 dir/tests/t16
212 dir/tests/t17
213 dir/tests/t18
214 dir/tests/t19
215 dir/tests/t2
216 dir/tests/t20
217 dir/tests/t3
218 dir/tests/t4
219 dir/tests/t5
220 dir/tests/t6
221 dir/tests/t7
222 dir/tests/t8
223 dir/tests/t9
224
225 $ cd ..
@@ -0,0 +1,21 b''
1 $ cd $TESTDIR && python $RUNTESTDIR/run-tests.py \
2 > --extra-config-opt experimental.treemanifest=1 test-narrow-commit.t 2>&1 | \
3 > grep -v 'unexpected mercurial lib' | egrep -v '\(expected'
4
5 --- */tests/test-narrow-commit.t (glob)
6 +++ */tests/test-narrow-commit.t.err (glob)
7 @@ -\d+,\d+ \+\d+,\d+ @@ (re)
8 created new head
9 $ hg files -r .
10 inside/f1
11 - outside/f1
12 + outside/
13 Some filesystems (notably FAT/exFAT only store timestamps with 2
14 seconds of precision, so by sleeping for 3 seconds, we can ensure that
15 the timestamps of files stored by dirstate will appear older than the
16
17 ERROR: test-narrow-commit.t output changed
18 !
19 Failed test-narrow-commit.t: output changed
20 # Ran 1 tests, 0 skipped, 1 failed.
21 python hash seed: * (glob)
@@ -0,0 +1,79 b''
1 $ . "$TESTDIR/narrow-library.sh"
2
3 create full repo
4
5 $ hg init master
6 $ cd master
7
8 $ mkdir inside
9 $ echo inside > inside/f1
10 $ mkdir outside
11 $ echo outside > outside/f1
12 $ hg ci -Aqm 'initial'
13
14 $ echo modified > inside/f1
15 $ hg ci -qm 'modify inside'
16
17 $ echo modified > outside/f1
18 $ hg ci -qm 'modify outside'
19
20 $ cd ..
21
22 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
23 requesting all changes
24 adding changesets
25 adding manifests
26 adding file changes
27 added 3 changesets with 2 changes to 1 files
28 new changesets *:* (glob)
29 updating to branch default
30 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
31 $ cd narrow
32
33 $ hg update -q 0
34
35 Can not modify dirstate outside
36
37 $ mkdir outside
38 $ touch outside/f1
39 $ hg debugwalk -I 'relglob:f1'
40 matcher: <includematcher includes='(?:(?:|.*/)f1(?:/|$))'>
41 f inside/f1 inside/f1
42 $ hg add outside/f1
43 abort: cannot track 'outside/f1' - it is outside the narrow clone
44 [255]
45 $ touch outside/f3
46 $ hg add outside/f3
47 abort: cannot track 'outside/f3' - it is outside the narrow clone
48 [255]
49 $ rm -r outside
50
51 Can modify dirstate inside
52
53 $ echo modified > inside/f1
54 $ touch inside/f3
55 $ hg add inside/f3
56 $ hg status
57 M inside/f1
58 A inside/f3
59 $ hg revert -qC .
60 $ rm inside/f3
61
62 Can commit changes inside. Leaves outside unchanged.
63
64 $ hg update -q 'desc("initial")'
65 $ echo modified2 > inside/f1
66 $ hg commit -m 'modify inside/f1'
67 created new head
68 $ hg files -r .
69 inside/f1
70 outside/f1
71 Some filesystems (notably FAT/exFAT only store timestamps with 2
72 seconds of precision, so by sleeping for 3 seconds, we can ensure that
73 the timestamps of files stored by dirstate will appear older than the
74 dirstate file, and therefore we'll be able to get stable output from
75 debugdirstate. If we don't do this, the test can be slightly flaky.
76 $ sleep 3
77 $ hg status
78 $ hg debugdirstate --nodates
79 n 644 10 set inside/f1
@@ -0,0 +1,57 b''
1
2 $ . "$TESTDIR/narrow-library.sh"
3
4 create full repo
5
6 $ hg init master
7 $ cd master
8
9 $ mkdir inside
10 $ echo inside > inside/f1
11 $ mkdir outside
12 $ echo outside > outside/f2
13 $ hg ci -Aqm 'initial'
14
15 $ hg mv outside/f2 inside/f2
16 $ hg ci -qm 'move f2 from outside'
17
18 $ echo modified > inside/f2
19 $ hg ci -qm 'modify inside/f2'
20
21 $ cd ..
22
23 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
24 requesting all changes
25 adding changesets
26 adding manifests
27 adding file changes
28 added 3 changesets with 3 changes to 2 files
29 new changesets *:* (glob)
30 updating to branch default
31 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
32 $ cd narrow
33
34 $ hg co 'desc("move f2")'
35 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 $ hg status
37 $ hg diff
38 $ hg diff --change . --git
39 diff --git a/inside/f2 b/inside/f2
40 new file mode 100644
41 --- /dev/null
42 +++ b/inside/f2
43 @@ -0,0 +1,1 @@
44 +outside
45
46 $ hg log --follow inside/f2 -r tip
47 changeset: 2:bcfb756e0ca9
48 tag: tip
49 user: test
50 date: Thu Jan 01 00:00:00 1970 +0000
51 summary: modify inside/f2
52
53 changeset: 1:5a016133b2bb
54 user: test
55 date: Thu Jan 01 00:00:00 1970 +0000
56 summary: move f2 from outside
57
@@ -0,0 +1,43 b''
1 $ . "$TESTDIR/narrow-library.sh"
2 $ hg init repo
3 $ cd repo
4 $ cat << EOF > .hg/narrowspec
5 > [includes]
6 > path:foo
7 > [excludes]
8 > EOF
9 $ echo treemanifest >> .hg/requires
10 $ echo narrowhg >> .hg/requires
11 $ mkdir -p foo/bar
12 $ echo b > foo/f
13 $ echo c > foo/bar/f
14 $ hg commit -Am hi
15 adding foo/bar/f
16 adding foo/f
17 $ hg debugindex -m
18 rev offset length delta linkrev nodeid p1 p2
19 0 0 47 -1 0 14a5d056d75a 000000000000 000000000000
20 $ hg debugindex --dir foo
21 rev offset length delta linkrev nodeid p1 p2
22 0 0 77 -1 0 e635c7857aef 000000000000 000000000000
23 $ hg debugindex --dir foo/
24 rev offset length delta linkrev nodeid p1 p2
25 0 0 77 -1 0 e635c7857aef 000000000000 000000000000
26 $ hg debugindex --dir foo/bar
27 rev offset length delta linkrev nodeid p1 p2
28 0 0 44 -1 0 e091d4224761 000000000000 000000000000
29 $ hg debugindex --dir foo/bar/
30 rev offset length delta linkrev nodeid p1 p2
31 0 0 44 -1 0 e091d4224761 000000000000 000000000000
32 $ hg debugdata -m 0
33 foo\x00e635c7857aef92ac761ce5741a99da159abbbb24t (esc)
34 $ hg debugdata --dir foo 0
35 bar\x00e091d42247613adff5d41b67f15fe7189ee97b39t (esc)
36 f\x001e88685f5ddec574a34c70af492f95b6debc8741 (esc)
37 $ hg debugdata --dir foo/ 0
38 bar\x00e091d42247613adff5d41b67f15fe7189ee97b39t (esc)
39 f\x001e88685f5ddec574a34c70af492f95b6debc8741 (esc)
40 $ hg debugdata --dir foo/bar 0
41 f\x00149da44f2a4e14f488b7bd4157945a9837408c00 (esc)
42 $ hg debugdata --dir foo/bar/ 0
43 f\x00149da44f2a4e14f488b7bd4157945a9837408c00 (esc)
@@ -0,0 +1,31 b''
1 $ . "$TESTDIR/narrow-library.sh"
2 $ hg init master
3 $ cd master
4 $ echo treemanifest >> .hg/requires
5 $ echo 'contents of file' > file
6 $ mkdir foo
7 $ echo 'contents of foo/bar' > foo/bar
8 $ hg ci -Am 'some change'
9 adding file
10 adding foo/bar
11
12 $ cd ..
13 $ hg clone --narrow ssh://user@dummy/master copy --include=foo
14 requesting all changes
15 adding changesets
16 adding manifests
17 adding file changes
18 added 1 changesets with 1 changes to 1 files
19 new changesets * (glob)
20 updating to branch default
21 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
22 $ cd copy
23
24 $ hg debugdirstate
25 n * 20 unset foo/bar (glob)
26 $ mv .hg/dirstate .hg/old_dirstate
27 $ dd bs=40 count=1 if=.hg/old_dirstate of=.hg/dirstate 2>/dev/null
28 $ hg debugdirstate
29 $ hg debugrebuilddirstate
30 $ hg debugdirstate
31 n * * unset foo/bar (glob)
@@ -0,0 +1,207 b''
1
2 $ . "$TESTDIR/narrow-library.sh"
3
4 create full repo
5
6 $ hg init master
7 $ cd master
8 $ cat >> .hg/hgrc <<EOF
9 > [narrow]
10 > serveellipses=True
11 > EOF
12
13 $ mkdir inside
14 $ echo 1 > inside/f
15 $ hg commit -Aqm 'initial inside'
16
17 $ mkdir outside
18 $ echo 1 > outside/f
19 $ hg commit -Aqm 'initial outside'
20
21 $ echo 2a > outside/f
22 $ hg commit -Aqm 'outside 2a'
23 $ echo 3 > inside/f
24 $ hg commit -Aqm 'inside 3'
25 $ echo 4a > outside/f
26 $ hg commit -Aqm 'outside 4a'
27 $ hg update '.~3'
28 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
29
30 $ echo 2b > outside/f
31 $ hg commit -Aqm 'outside 2b'
32 $ echo 3 > inside/f
33 $ hg commit -Aqm 'inside 3'
34 $ echo 4b > outside/f
35 $ hg commit -Aqm 'outside 4b'
36 $ hg update '.~3'
37 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
38
39 $ echo 2c > outside/f
40 $ hg commit -Aqm 'outside 2c'
41 $ echo 3 > inside/f
42 $ hg commit -Aqm 'inside 3'
43 $ echo 4c > outside/f
44 $ hg commit -Aqm 'outside 4c'
45 $ hg update '.~3'
46 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
47
48 $ echo 2d > outside/f
49 $ hg commit -Aqm 'outside 2d'
50 $ echo 3 > inside/f
51 $ hg commit -Aqm 'inside 3'
52 $ echo 4d > outside/f
53 $ hg commit -Aqm 'outside 4d'
54
55 $ hg update -r 'desc("outside 4a")'
56 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 $ hg merge -r 'desc("outside 4b")' 2>&1 | egrep -v '(warning:|incomplete!)'
58 merging outside/f
59 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
60 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
61 $ echo 5 > outside/f
62 $ rm outside/f.orig
63 $ hg resolve --mark outside/f
64 (no more unresolved files)
65 $ hg commit -m 'merge a/b 5'
66 $ echo 6 > outside/f
67 $ hg commit -Aqm 'outside 6'
68
69 $ hg merge -r 'desc("outside 4c")' 2>&1 | egrep -v '(warning:|incomplete!)'
70 merging outside/f
71 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
72 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
73 $ echo 7 > outside/f
74 $ rm outside/f.orig
75 $ hg resolve --mark outside/f
76 (no more unresolved files)
77 $ hg commit -Aqm 'merge a/b/c 7'
78 $ echo 8 > outside/f
79 $ hg commit -Aqm 'outside 8'
80
81 $ hg merge -r 'desc("outside 4d")' 2>&1 | egrep -v '(warning:|incomplete!)'
82 merging outside/f
83 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
84 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
85 $ echo 9 > outside/f
86 $ rm outside/f.orig
87 $ hg resolve --mark outside/f
88 (no more unresolved files)
89 $ hg commit -Aqm 'merge a/b/c/d 9'
90 $ echo 10 > outside/f
91 $ hg commit -Aqm 'outside 10'
92
93 $ echo 11 > inside/f
94 $ hg commit -Aqm 'inside 11'
95 $ echo 12 > outside/f
96 $ hg commit -Aqm 'outside 12'
97
98 $ hg log -G -T '{rev} {node|short} {desc}\n'
99 @ 21 8d874d57adea outside 12
100 |
101 o 20 7ef88b4dd4fa inside 11
102 |
103 o 19 2a20009de83e outside 10
104 |
105 o 18 3ac1f5779de3 merge a/b/c/d 9
106 |\
107 | o 17 38a9c2f7e546 outside 8
108 | |
109 | o 16 094aa62fc898 merge a/b/c 7
110 | |\
111 | | o 15 f29d083d32e4 outside 6
112 | | |
113 | | o 14 2dc11382541d merge a/b 5
114 | | |\
115 o | | | 13 27d07ef97221 outside 4d
116 | | | |
117 o | | | 12 465567bdfb2d inside 3
118 | | | |
119 o | | | 11 d1c61993ec83 outside 2d
120 | | | |
121 | o | | 10 56859a8e33b9 outside 4c
122 | | | |
123 | o | | 9 bb96a08b062a inside 3
124 | | | |
125 | o | | 8 b844052e7b3b outside 2c
126 |/ / /
127 | | o 7 9db2d8fcc2a6 outside 4b
128 | | |
129 | | o 6 6418167787a6 inside 3
130 | | |
131 +---o 5 77344f344d83 outside 2b
132 | |
133 | o 4 9cadde08dc9f outside 4a
134 | |
135 | o 3 019ef06f125b inside 3
136 | |
137 | o 2 75e40c075a19 outside 2a
138 |/
139 o 1 906d6c682641 initial outside
140 |
141 o 0 9f8e82b51004 initial inside
142
143
144 Now narrow clone this and get a hopefully correct graph
145
146 $ cd ..
147 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
148 requesting all changes
149 adding changesets
150 adding manifests
151 adding file changes
152 added 14 changesets with 3 changes to 1 files
153 new changesets *:* (glob)
154 updating to branch default
155 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
156 $ cd narrow
157
158 To make updating the tests easier, we print the emitted nodes
159 sorted. This makes it easier to identify when the same node structure
160 has been emitted, just in a different order.
161
162 $ hg log -T '{if(ellipsis,"...")}{node|short} {p1node|short} {p2node|short} {desc}\n' | sort
163 ...094aa62fc898 6418167787a6 bb96a08b062a merge a/b/c 7
164 ...2a20009de83e 019ef06f125b 3ac1f5779de3 outside 10
165 ...3ac1f5779de3 465567bdfb2d 094aa62fc898 merge a/b/c/d 9
166 ...75e40c075a19 9f8e82b51004 000000000000 outside 2a
167 ...77344f344d83 9f8e82b51004 000000000000 outside 2b
168 ...8d874d57adea 7ef88b4dd4fa 000000000000 outside 12
169 ...b844052e7b3b 9f8e82b51004 000000000000 outside 2c
170 ...d1c61993ec83 9f8e82b51004 000000000000 outside 2d
171 019ef06f125b 75e40c075a19 000000000000 inside 3
172 465567bdfb2d d1c61993ec83 000000000000 inside 3
173 6418167787a6 77344f344d83 000000000000 inside 3
174 7ef88b4dd4fa 2a20009de83e 000000000000 inside 11
175 9f8e82b51004 000000000000 000000000000 initial inside
176 bb96a08b062a b844052e7b3b 000000000000 inside 3
177
178 But seeing the graph is also nice:
179 $ hg log -G -T '{if(ellipsis,"...")}{node|short} {desc}\n'
180 @ ...8d874d57adea outside 12
181 |
182 o 7ef88b4dd4fa inside 11
183 |
184 o ...2a20009de83e outside 10
185 |\
186 | o ...3ac1f5779de3 merge a/b/c/d 9
187 | |\
188 | | o ...094aa62fc898 merge a/b/c 7
189 | | |\
190 | o | | 465567bdfb2d inside 3
191 | | | |
192 | o | | ...d1c61993ec83 outside 2d
193 | | | |
194 | | | o bb96a08b062a inside 3
195 | | | |
196 | +---o ...b844052e7b3b outside 2c
197 | | |
198 | | o 6418167787a6 inside 3
199 | | |
200 | | o ...77344f344d83 outside 2b
201 | |/
202 o | 019ef06f125b inside 3
203 | |
204 o | ...75e40c075a19 outside 2a
205 |/
206 o 9f8e82b51004 initial inside
207
@@ -0,0 +1,210 b''
1
2 $ . "$TESTDIR/narrow-library.sh"
3
4 create full repo
5
6 $ hg init master
7 $ cd master
8 $ cat >> .hg/hgrc <<EOF
9 > [narrow]
10 > serveellipses=True
11 > EOF
12
13 $ mkdir inside
14 $ echo 1 > inside/f
15 $ mkdir inside2
16 $ echo 1 > inside2/f
17 $ mkdir outside
18 $ echo 1 > outside/f
19 $ hg ci -Aqm 'initial'
20
21 $ echo 2 > inside/f
22 $ hg ci -qm 'inside 2'
23
24 $ echo 2 > inside2/f
25 $ hg ci -qm 'inside2 2'
26
27 $ echo 2 > outside/f
28 $ hg ci -qm 'outside 2'
29
30 $ cd ..
31
32 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
33 requesting all changes
34 adding changesets
35 adding manifests
36 adding file changes
37 added 3 changesets with 2 changes to 1 files
38 new changesets *:* (glob)
39 updating to branch default
40 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
41
42 $ hg clone --narrow ssh://user@dummy/master narrow2 --include inside --include inside2
43 requesting all changes
44 adding changesets
45 adding manifests
46 adding file changes
47 added 4 changesets with 4 changes to 2 files
48 new changesets *:* (glob)
49 updating to branch default
50 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
51
52 Can push to wider repo if change does not affect paths in wider repo that are
53 not also in narrower repo
54
55 $ cd narrow
56 $ echo 3 > inside/f
57 $ hg ci -m 'inside 3'
58 $ hg push ssh://user@dummy/narrow2
59 pushing to ssh://user@dummy/narrow2
60 searching for changes
61 remote: adding changesets
62 remote: adding manifests
63 remote: adding file changes
64 remote: added 1 changesets with 1 changes to 1 files
65
66 Can push to narrower repo if change affects only paths within remote's
67 narrow spec
68
69 $ cd ../narrow2
70 $ cat >> .hg/hgrc <<EOF
71 > [narrow]
72 > serveellipses=True
73 > EOF
74 $ hg co -r 'desc("inside 3")'
75 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 $ echo 4 > inside/f
77 $ hg ci -m 'inside 4'
78 $ hg push ssh://user@dummy/narrow
79 pushing to ssh://user@dummy/narrow
80 searching for changes
81 remote: adding changesets
82 remote: adding manifests
83 remote: adding file changes
84 remote: added 1 changesets with 1 changes to 1 files
85
86 Can push to narrow repo if change affects only paths outside remote's
87 narrow spec
88
89 $ echo 3 > inside2/f
90 $ hg ci -m 'inside2 3'
91 TODO: this should be successful
92 $ hg push ssh://user@dummy/narrow
93 pushing to ssh://user@dummy/narrow
94 searching for changes
95 remote: adding changesets
96 remote: adding manifests
97 remote: adding file changes
98 remote: transaction abort!
99 remote: rollback completed
100 remote: abort: data/inside2/f.i@4a1aa07735e6: unknown parent!
101 abort: stream ended unexpectedly (got 0 bytes, expected 4)
102 [255]
103
104 Can pull from wider repo if change affects only paths outside remote's
105 narrow spec
106 $ echo 4 > inside2/f
107 $ hg ci -m 'inside2 4'
108 $ hg log -G -T '{rev} {node|short} {files}\n'
109 @ 7 d78a96df731d inside2/f
110 |
111 o 6 8c26f5218962 inside2/f
112 |
113 o 5 ba3480e2f9de inside/f
114 |
115 o 4 4e5edd526618 inside/f
116 |
117 o 3 81e7e07b7ab0 outside/f
118 |
119 o 2 f3993b8c0c2b inside2/f
120 |
121 o 1 8cd66ca966b4 inside/f
122 |
123 o 0 c8057d6f53ab inside/f inside2/f outside/f
124
125 $ cd ../narrow
126 $ hg log -G -T '{rev} {node|short} {files}\n'
127 o 4 ba3480e2f9de inside/f
128 |
129 @ 3 4e5edd526618 inside/f
130 |
131 o 2 81e7e07b7ab0 outside/f
132 |
133 o 1 8cd66ca966b4 inside/f
134 |
135 o 0 c8057d6f53ab inside/f inside2/f outside/f
136
137 $ hg pull ssh://user@dummy/narrow2
138 pulling from ssh://user@dummy/narrow2
139 searching for changes
140 remote: abort: unable to resolve parent while packing 'data/inside2/f.i' 3 for changeset 5 (?)
141 adding changesets
142 remote: abort: unexpected error: unable to resolve parent while packing 'data/inside2/f.i' 3 for changeset 5
143 transaction abort!
144 rollback completed
145 abort: pull failed on remote
146 [255]
147
148 Check that the resulting history is valid in the full repo
149
150 $ cd ../narrow2
151 $ hg push ssh://user@dummy/master
152 pushing to ssh://user@dummy/master
153 searching for changes
154 remote: adding changesets
155 remote: adding manifests
156 remote: adding file changes
157 remote: added 4 changesets with 4 changes to 2 files
158 $ cd ../master
159 $ hg verify
160 checking changesets
161 checking manifests
162 crosschecking files in changesets and manifests
163 checking files
164 3 files, 8 changesets, 10 total revisions
165
166 Can not push to wider repo if change affects paths in wider repo that are
167 not also in narrower repo
168 $ cd ../master
169 $ hg co -r 'desc("inside2 4")'
170 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
171 $ echo 5 > inside2/f
172 $ hg ci -m 'inside2 5'
173 $ hg log -G -T '{rev} {node|short} {files}\n'
174 @ 8 5970befb64ba inside2/f
175 |
176 o 7 d78a96df731d inside2/f
177 |
178 o 6 8c26f5218962 inside2/f
179 |
180 o 5 ba3480e2f9de inside/f
181 |
182 o 4 4e5edd526618 inside/f
183 |
184 o 3 81e7e07b7ab0 outside/f
185 |
186 o 2 f3993b8c0c2b inside2/f
187 |
188 o 1 8cd66ca966b4 inside/f
189 |
190 o 0 c8057d6f53ab inside/f inside2/f outside/f
191
192 $ cd ../narrow
193 $ hg pull
194 pulling from ssh://user@dummy/master
195 searching for changes
196 adding changesets
197 adding manifests
198 adding file changes
199 added 1 changesets with 0 changes to 0 files
200 new changesets * (glob)
201 (run 'hg update' to get a working copy)
202 TODO: this should tell the user that their narrow clone does not have the
203 necessary content to be able to push to the target
204 $ hg push ssh://user@dummy/narrow2
205 pushing to ssh://user@dummy/narrow2
206 searching for changes
207 remote has heads on branch 'default' that are not known locally: d78a96df731d
208 abort: push creates new remote head 5970befb64ba!
209 (pull and merge or see 'hg help push' for details about pushing new heads)
210 [255]
@@ -0,0 +1,170 b''
1 $ . "$TESTDIR/narrow-library.sh"
2
3 $ hg init master
4 $ cd master
5
6 $ mkdir inside
7 $ echo inside > inside/f1
8 $ mkdir outside
9 $ echo outside > outside/f2
10 $ mkdir patchdir
11 $ echo patch_this > patchdir/f3
12 $ hg ci -Aqm 'initial'
13
14 $ cd ..
15
16 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
17 requesting all changes
18 adding changesets
19 adding manifests
20 adding file changes
21 added 1 changesets with 1 changes to 1 files
22 new changesets dff6a2a6d433
23 updating to branch default
24 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
25
26 $ cd narrow
27
28 $ mkdir outside
29 $ echo other_contents > outside/f2
30 $ grep outside .hg/narrowspec
31 [1]
32 $ grep outside .hg/dirstate
33 [1]
34 $ hg status
35
36 `hg status` did not add outside.
37 $ grep outside .hg/narrowspec
38 [1]
39 $ grep outside .hg/dirstate
40 [1]
41
42 Unfortunately this is not really a candidate for adding to narrowhg proper,
43 since it depends on some other source for providing the manifests (when using
44 treemanifests) and file contents. Something like a virtual filesystem and/or
45 remotefilelog. We want to be useful when not using those systems, so we do not
46 have this method available in narrowhg proper at the moment.
47 $ cat > "$TESTTMP/expand_extension.py" <<EOF
48 > import os
49 > import sys
50 >
51 > from mercurial import extensions
52 > from mercurial import localrepo
53 > from mercurial import match as matchmod
54 > from mercurial import patch
55 > from mercurial import util as hgutil
56 >
57 > def expandnarrowspec(ui, repo, newincludes=None):
58 > if not newincludes:
59 > return
60 > import sys
61 > newincludes = set([newincludes])
62 > narrowhg = extensions.find('narrow')
63 > includes, excludes = repo.narrowpats
64 > currentmatcher = narrowhg.narrowspec.match(repo.root, includes, excludes)
65 > includes = includes | newincludes
66 > if not repo.currenttransaction():
67 > ui.develwarn('expandnarrowspec called outside of transaction!')
68 > repo.setnarrowpats(includes, excludes)
69 > newmatcher = narrowhg.narrowspec.match(repo.root, includes, excludes)
70 > added = matchmod.differencematcher(newmatcher, currentmatcher)
71 > for f in repo['.'].manifest().walk(added):
72 > repo.dirstate.normallookup(f)
73 >
74 > def makeds(ui, repo):
75 > def wrapds(orig, self):
76 > ds = orig(self)
77 > class expandingdirstate(ds.__class__):
78 > # Mercurial 4.4 uses this version.
79 > @hgutil.propertycache
80 > def _map(self):
81 > ret = super(expandingdirstate, self)._map
82 > with repo.wlock(), repo.lock(), repo.transaction(
83 > 'expandnarrowspec'):
84 > expandnarrowspec(ui, repo, os.environ.get('DIRSTATEINCLUDES'))
85 > return ret
86 > # Mercurial 4.3.3 and earlier uses this version. It seems that
87 > # narrowhg does not currently support this version, but we include
88 > # it just in case backwards compatibility is restored.
89 > def _read(self):
90 > ret = super(expandingdirstate, self)._read()
91 > with repo.wlock(), repo.lock(), repo.transaction(
92 > 'expandnarrowspec'):
93 > expandnarrowspec(ui, repo, os.environ.get('DIRSTATEINCLUDES'))
94 > return ret
95 > ds.__class__ = expandingdirstate
96 > return ds
97 > return wrapds
98 >
99 > def reposetup(ui, repo):
100 > extensions.wrapfilecache(localrepo.localrepository, 'dirstate',
101 > makeds(ui, repo))
102 > def overridepatch(orig, *args, **kwargs):
103 > with repo.wlock():
104 > expandnarrowspec(ui, repo, os.environ.get('PATCHINCLUDES'))
105 > return orig(*args, **kwargs)
106 >
107 > extensions.wrapfunction(patch, 'patch', overridepatch)
108 > EOF
109 $ cat >> ".hg/hgrc" <<EOF
110 > [extensions]
111 > expand_extension = $TESTTMP/expand_extension.py
112 > EOF
113
114 Since we do not have the ability to rely on a virtual filesystem or
115 remotefilelog in the test, we just fake it by copying the data from the 'master'
116 repo.
117 $ cp -a ../master/.hg/store/data/* .hg/store/data
118 Do that for patchdir as well.
119 $ cp -a ../master/patchdir .
120
121 `hg status` will now add outside, but not patchdir.
122 $ DIRSTATEINCLUDES=path:outside hg status
123 M outside/f2
124 $ grep outside .hg/narrowspec
125 path:outside
126 $ grep outside .hg/dirstate > /dev/null
127 $ grep patchdir .hg/narrowspec
128 [1]
129 $ grep patchdir .hg/dirstate
130 [1]
131
132 Get rid of the modification to outside/f2.
133 $ hg update -C .
134 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
135
136 This patch will not apply cleanly at the moment, so `hg import` will break
137 $ cat > "$TESTTMP/foo.patch" <<EOF
138 > --- patchdir/f3
139 > +++ patchdir/f3
140 > @@ -1,1 +1,1 @@
141 > -this should be "patch_this", but its not, so patch fails
142 > +this text is irrelevant
143 > EOF
144 $ PATCHINCLUDES=path:patchdir hg import -p0 -e "$TESTTMP/foo.patch" -m ignored
145 applying $TESTTMP/foo.patch
146 patching file patchdir/f3
147 Hunk #1 FAILED at 0
148 1 out of 1 hunks FAILED -- saving rejects to file patchdir/f3.rej
149 abort: patch failed to apply
150 [255]
151 $ grep patchdir .hg/narrowspec
152 [1]
153 $ grep patchdir .hg/dirstate > /dev/null
154 [1]
155
156 Let's make it apply cleanly and see that it *did* expand properly
157 $ cat > "$TESTTMP/foo.patch" <<EOF
158 > --- patchdir/f3
159 > +++ patchdir/f3
160 > @@ -1,1 +1,1 @@
161 > -patch_this
162 > +patched_this
163 > EOF
164 $ PATCHINCLUDES=path:patchdir hg import -p0 -e "$TESTTMP/foo.patch" -m message
165 applying $TESTTMP/foo.patch
166 $ cat patchdir/f3
167 patched_this
168 $ grep patchdir .hg/narrowspec
169 path:patchdir
170 $ grep patchdir .hg/dirstate > /dev/null
@@ -0,0 +1,28 b''
1 $ cd $TESTDIR && python $RUNTESTDIR/run-tests.py \
2 > --extra-config-opt experimental.treemanifest=1 test-narrow-merge.t 2>&1 | \
3 > grep -v 'unexpected mercurial lib' | egrep -v '\(expected'
4
5 --- */tests/test-narrow-merge.t (glob)
6 +++ */tests/test-narrow-merge.t.err (glob)
7 @@ -\d+,\d+ \+\d+,\d+ @@ (re)
8
9 $ hg update -q 'desc("modify inside/f1")'
10 $ hg merge 'desc("modify outside/f1")'
11 - abort: merge affects file 'outside/f1' outside narrow, which is not yet supported
12 + abort: merge affects file 'outside/' outside narrow, which is not yet supported
13 (merging in the other direction may work)
14 [255]
15
16 @@ -\d+,\d+ \+\d+,\d+ @@ (re)
17
18 $ hg update -q 'desc("modify outside/f1")'
19 $ hg merge 'desc("conflicting outside/f1")'
20 - abort: conflict in file 'outside/f1' is outside narrow clone
21 + abort: conflict in file 'outside/' is outside narrow clone
22 [255]
23
24 ERROR: test-narrow-merge.t output changed
25 !
26 Failed test-narrow-merge.t: output changed
27 # Ran 1 tests, 0 skipped, 1 failed.
28 python hash seed: * (glob)
@@ -0,0 +1,94 b''
1
2 $ . "$TESTDIR/narrow-library.sh"
3
4 create full repo
5
6 $ hg init master
7 $ cd master
8 $ cat >> .hg/hgrc <<EOF
9 > [narrow]
10 > serveellipses=True
11 > EOF
12
13 $ mkdir inside
14 $ echo inside1 > inside/f1
15 $ echo inside2 > inside/f2
16 $ mkdir outside
17 $ echo outside1 > outside/f1
18 $ echo outside2 > outside/f2
19 $ hg ci -Aqm 'initial'
20
21 $ echo modified > inside/f1
22 $ hg ci -qm 'modify inside/f1'
23
24 $ hg update -q 0
25 $ echo modified > inside/f2
26 $ hg ci -qm 'modify inside/f2'
27
28 $ hg update -q 0
29 $ echo modified2 > inside/f1
30 $ hg ci -qm 'conflicting inside/f1'
31
32 $ hg update -q 0
33 $ echo modified > outside/f1
34 $ hg ci -qm 'modify outside/f1'
35
36 $ hg update -q 0
37 $ echo modified2 > outside/f1
38 $ hg ci -qm 'conflicting outside/f1'
39
40 $ cd ..
41
42 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
43 requesting all changes
44 adding changesets
45 adding manifests
46 adding file changes
47 added 6 changesets with 5 changes to 2 files (+4 heads)
48 new changesets *:* (glob)
49 updating to branch default
50 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
51 $ cd narrow
52
53 $ hg update -q 0
54
55 Can merge in when no files outside narrow spec are involved
56
57 $ hg update -q 'desc("modify inside/f1")'
58 $ hg merge 'desc("modify inside/f2")'
59 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 (branch merge, don't forget to commit)
61 $ hg commit -m 'merge inside changes'
62
63 Can merge conflicting changes inside narrow spec
64
65 $ hg update -q 'desc("modify inside/f1")'
66 $ hg merge 'desc("conflicting inside/f1")' 2>&1 | egrep -v '(warning:|incomplete!)'
67 merging inside/f1
68 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
69 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
70 $ echo modified3 > inside/f1
71 $ hg resolve -m
72 (no more unresolved files)
73 $ hg commit -m 'merge inside/f1'
74
75 TODO: Can merge non-conflicting changes outside narrow spec
76
77 $ hg update -q 'desc("modify inside/f1")'
78 $ hg merge 'desc("modify outside/f1")'
79 abort: merge affects file 'outside/f1' outside narrow, which is not yet supported
80 (merging in the other direction may work)
81 [255]
82
83 $ hg update -q 'desc("modify outside/f1")'
84 $ hg merge 'desc("modify inside/f1")'
85 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
86 (branch merge, don't forget to commit)
87 $ hg ci -m 'merge from inside to outside'
88
89 Refuses merge of conflicting outside changes
90
91 $ hg update -q 'desc("modify outside/f1")'
92 $ hg merge 'desc("conflicting outside/f1")'
93 abort: conflict in file 'outside/f1' is outside narrow clone
94 [255]
@@ -0,0 +1,5 b''
1 $ cd $TESTDIR && python $RUNTESTDIR/run-tests.py \
2 > --extra-config-opt experimental.treemanifest=1 test-patch.t 2>&1 | \
3 > grep -v 'unexpected mercurial lib' | egrep -v '\(expected'
4 .
5 # Ran 1 tests, 0 skipped, 0 failed.
@@ -0,0 +1,76 b''
1
2 $ . "$TESTDIR/narrow-library.sh"
3
4 create full repo
5
6 $ hg init master
7 $ cd master
8
9 $ mkdir inside
10 $ echo inside > inside/f1
11 $ mkdir outside
12 $ echo outside > outside/f1
13 $ hg ci -Aqm 'initial'
14
15 $ echo modified > inside/f1
16 $ hg ci -qm 'modify inside'
17
18 $ echo modified > outside/f1
19 $ hg ci -qm 'modify outside'
20
21 $ cd ..
22
23 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
24 requesting all changes
25 adding changesets
26 adding manifests
27 adding file changes
28 added 3 changesets with 2 changes to 1 files
29 new changesets *:* (glob)
30 updating to branch default
31 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
32 $ cd narrow
33
34 Can show patch touching paths outside
35
36 $ hg log -p
37 changeset: 2:* (glob)
38 tag: tip
39 user: test
40 date: Thu Jan 01 00:00:00 1970 +0000
41 summary: modify outside
42
43
44 changeset: 1:* (glob)
45 user: test
46 date: Thu Jan 01 00:00:00 1970 +0000
47 summary: modify inside
48
49 diff -r * -r * inside/f1 (glob)
50 --- a/inside/f1 Thu Jan 01 00:00:00 1970 +0000
51 +++ b/inside/f1 Thu Jan 01 00:00:00 1970 +0000
52 @@ -1,1 +1,1 @@
53 -inside
54 +modified
55
56 changeset: 0:* (glob)
57 user: test
58 date: Thu Jan 01 00:00:00 1970 +0000
59 summary: initial
60
61 diff -r 000000000000 -r * inside/f1 (glob)
62 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
63 +++ b/inside/f1 Thu Jan 01 00:00:00 1970 +0000
64 @@ -0,0 +1,1 @@
65 +inside
66
67
68 $ hg status --rev 1 --rev 2
69
70 Can show copies inside the narrow clone
71
72 $ hg cp inside/f1 inside/f2
73 $ hg diff --git
74 diff --git a/inside/f1 b/inside/f2
75 copy from inside/f1
76 copy to inside/f2
@@ -0,0 +1,418 b''
1 $ . "$TESTDIR/narrow-library.sh"
2
3 initialize nested directories to validate complex include/exclude patterns
4
5 $ hg init master
6 $ cd master
7 $ cat >> .hg/hgrc <<EOF
8 > [narrow]
9 > serveellipses=True
10 > EOF
11
12 $ echo root > root
13 $ hg add root
14 $ hg commit -m 'add root'
15
16 $ for d in dir1 dir2 dir1/dirA dir1/dirB dir2/dirA dir2/dirB
17 > do
18 > mkdir -p $d
19 > echo $d/foo > $d/foo
20 > hg add $d/foo
21 > hg commit -m "add $d/foo"
22 > echo $d/bar > $d/bar
23 > hg add $d/bar
24 > hg commit -m "add $d/bar"
25 > done
26 $ chmod +x dir1/dirA/foo
27 $ hg commit -m "make dir1/dirA/foo executable"
28 $ hg log -G -T '{rev} {node|short} {files}\n'
29 @ 13 c87ca422d521 dir1/dirA/foo
30 |
31 o 12 951b8a83924e dir2/dirB/bar
32 |
33 o 11 01ae5a51b563 dir2/dirB/foo
34 |
35 o 10 5eababdf0ac5 dir2/dirA/bar
36 |
37 o 9 99d690663739 dir2/dirA/foo
38 |
39 o 8 8e80155d5445 dir1/dirB/bar
40 |
41 o 7 406760310428 dir1/dirB/foo
42 |
43 o 6 623466a5f475 dir1/dirA/bar
44 |
45 o 5 06ff3a5be997 dir1/dirA/foo
46 |
47 o 4 33227af02764 dir2/bar
48 |
49 o 3 5e1f9d8d7c69 dir2/foo
50 |
51 o 2 594bc4b13d4a dir1/bar
52 |
53 o 1 47f480a08324 dir1/foo
54 |
55 o 0 2a4f0c3b67da root
56
57 $ cd ..
58
59 clone a narrow portion of the master, such that we can widen it later
60
61 $ hg clone --narrow ssh://user@dummy/master narrow \
62 > --include dir1 \
63 > --include dir2 \
64 > --exclude dir1/dirA \
65 > --exclude dir1/dirB \
66 > --exclude dir2/dirA \
67 > --exclude dir2/dirB
68 requesting all changes
69 adding changesets
70 adding manifests
71 adding file changes
72 added 6 changesets with 4 changes to 4 files
73 new changesets *:* (glob)
74 updating to branch default
75 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
76
77 $ cd narrow
78 $ cat .hg/narrowspec
79 [includes]
80 path:dir1
81 path:dir2
82 [excludes]
83 path:dir1/dirA
84 path:dir1/dirB
85 path:dir2/dirA
86 path:dir2/dirB
87 $ hg manifest -r tip
88 dir1/bar
89 dir1/dirA/bar
90 dir1/dirA/foo
91 dir1/dirB/bar
92 dir1/dirB/foo
93 dir1/foo
94 dir2/bar
95 dir2/dirA/bar
96 dir2/dirA/foo
97 dir2/dirB/bar
98 dir2/dirB/foo
99 dir2/foo
100 root
101 $ find * | sort
102 dir1
103 dir1/bar
104 dir1/foo
105 dir2
106 dir2/bar
107 dir2/foo
108 $ hg log -G -T '{rev} {node|short}{if(ellipsis, "...")} {files}\n'
109 @ 5 c87ca422d521... dir1/dirA/foo
110 |
111 o 4 33227af02764 dir2/bar
112 |
113 o 3 5e1f9d8d7c69 dir2/foo
114 |
115 o 2 594bc4b13d4a dir1/bar
116 |
117 o 1 47f480a08324 dir1/foo
118 |
119 o 0 2a4f0c3b67da... root
120
121
122 widen the narrow checkout
123
124 $ hg tracked --removeexclude dir1/dirA
125 comparing with ssh://user@dummy/master
126 searching for changes
127 no changes found
128 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
129 adding changesets
130 adding manifests
131 adding file changes
132 added 9 changesets with 6 changes to 6 files
133 new changesets *:* (glob)
134 $ cat .hg/narrowspec
135 [includes]
136 path:dir1
137 path:dir2
138 [excludes]
139 path:dir1/dirB
140 path:dir2/dirA
141 path:dir2/dirB
142 $ find * | sort
143 dir1
144 dir1/bar
145 dir1/dirA
146 dir1/dirA/bar
147 dir1/dirA/foo
148 dir1/foo
149 dir2
150 dir2/bar
151 dir2/foo
152 $ test -x dir1/dirA/foo && echo executable
153 executable
154 $ test -x dir1/dirA/bar || echo not executable
155 not executable
156 $ hg log -G -T '{rev} {node|short}{if(ellipsis, "...")} {files}\n'
157 @ 8 c87ca422d521 dir1/dirA/foo
158 |
159 o 7 951b8a83924e... dir2/dirB/bar
160 |
161 o 6 623466a5f475 dir1/dirA/bar
162 |
163 o 5 06ff3a5be997 dir1/dirA/foo
164 |
165 o 4 33227af02764 dir2/bar
166 |
167 o 3 5e1f9d8d7c69 dir2/foo
168 |
169 o 2 594bc4b13d4a dir1/bar
170 |
171 o 1 47f480a08324 dir1/foo
172 |
173 o 0 2a4f0c3b67da... root
174
175
176 widen narrow spec again, but exclude a file in previously included spec
177
178 $ hg tracked --removeexclude dir2/dirB --addexclude dir1/dirA/bar
179 comparing with ssh://user@dummy/master
180 searching for changes
181 looking for local changes to affected paths
182 deleting data/dir1/dirA/bar.i
183 no changes found
184 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
185 adding changesets
186 adding manifests
187 adding file changes
188 added 11 changesets with 7 changes to 7 files
189 new changesets *:* (glob)
190 $ cat .hg/narrowspec
191 [includes]
192 path:dir1
193 path:dir2
194 [excludes]
195 path:dir1/dirA/bar
196 path:dir1/dirB
197 path:dir2/dirA
198 $ find * | sort
199 dir1
200 dir1/bar
201 dir1/dirA
202 dir1/dirA/foo
203 dir1/foo
204 dir2
205 dir2/bar
206 dir2/dirB
207 dir2/dirB/bar
208 dir2/dirB/foo
209 dir2/foo
210 $ hg log -G -T '{rev} {node|short}{if(ellipsis, "...")} {files}\n'
211 @ 10 c87ca422d521 dir1/dirA/foo
212 |
213 o 9 951b8a83924e dir2/dirB/bar
214 |
215 o 8 01ae5a51b563 dir2/dirB/foo
216 |
217 o 7 5eababdf0ac5... dir2/dirA/bar
218 |
219 o 6 623466a5f475... dir1/dirA/bar
220 |
221 o 5 06ff3a5be997 dir1/dirA/foo
222 |
223 o 4 33227af02764 dir2/bar
224 |
225 o 3 5e1f9d8d7c69 dir2/foo
226 |
227 o 2 594bc4b13d4a dir1/bar
228 |
229 o 1 47f480a08324 dir1/foo
230 |
231 o 0 2a4f0c3b67da... root
232
233
234 widen narrow spec yet again, excluding a directory in previous spec
235
236 $ hg tracked --removeexclude dir2/dirA --addexclude dir1/dirA
237 comparing with ssh://user@dummy/master
238 searching for changes
239 looking for local changes to affected paths
240 deleting data/dir1/dirA/foo.i
241 no changes found
242 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
243 adding changesets
244 adding manifests
245 adding file changes
246 added 13 changesets with 8 changes to 8 files
247 new changesets *:* (glob)
248 $ cat .hg/narrowspec
249 [includes]
250 path:dir1
251 path:dir2
252 [excludes]
253 path:dir1/dirA
254 path:dir1/dirA/bar
255 path:dir1/dirB
256 $ find * | sort
257 dir1
258 dir1/bar
259 dir1/foo
260 dir2
261 dir2/bar
262 dir2/dirA
263 dir2/dirA/bar
264 dir2/dirA/foo
265 dir2/dirB
266 dir2/dirB/bar
267 dir2/dirB/foo
268 dir2/foo
269 $ hg log -G -T '{rev} {node|short}{if(ellipsis, "...")} {files}\n'
270 @ 12 c87ca422d521... dir1/dirA/foo
271 |
272 o 11 951b8a83924e dir2/dirB/bar
273 |
274 o 10 01ae5a51b563 dir2/dirB/foo
275 |
276 o 9 5eababdf0ac5 dir2/dirA/bar
277 |
278 o 8 99d690663739 dir2/dirA/foo
279 |
280 o 7 8e80155d5445... dir1/dirB/bar
281 |
282 o 6 623466a5f475... dir1/dirA/bar
283 |
284 o 5 06ff3a5be997... dir1/dirA/foo
285 |
286 o 4 33227af02764 dir2/bar
287 |
288 o 3 5e1f9d8d7c69 dir2/foo
289 |
290 o 2 594bc4b13d4a dir1/bar
291 |
292 o 1 47f480a08324 dir1/foo
293 |
294 o 0 2a4f0c3b67da... root
295
296
297 include a directory that was previously explicitly excluded
298
299 $ hg tracked --removeexclude dir1/dirA
300 comparing with ssh://user@dummy/master
301 searching for changes
302 no changes found
303 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
304 adding changesets
305 adding manifests
306 adding file changes
307 added 13 changesets with 9 changes to 9 files
308 new changesets *:* (glob)
309 $ cat .hg/narrowspec
310 [includes]
311 path:dir1
312 path:dir2
313 [excludes]
314 path:dir1/dirA/bar
315 path:dir1/dirB
316 $ find * | sort
317 dir1
318 dir1/bar
319 dir1/dirA
320 dir1/dirA/foo
321 dir1/foo
322 dir2
323 dir2/bar
324 dir2/dirA
325 dir2/dirA/bar
326 dir2/dirA/foo
327 dir2/dirB
328 dir2/dirB/bar
329 dir2/dirB/foo
330 dir2/foo
331 $ hg log -G -T '{rev} {node|short}{if(ellipsis, "...")} {files}\n'
332 @ 12 c87ca422d521 dir1/dirA/foo
333 |
334 o 11 951b8a83924e dir2/dirB/bar
335 |
336 o 10 01ae5a51b563 dir2/dirB/foo
337 |
338 o 9 5eababdf0ac5 dir2/dirA/bar
339 |
340 o 8 99d690663739 dir2/dirA/foo
341 |
342 o 7 8e80155d5445... dir1/dirB/bar
343 |
344 o 6 623466a5f475... dir1/dirA/bar
345 |
346 o 5 06ff3a5be997 dir1/dirA/foo
347 |
348 o 4 33227af02764 dir2/bar
349 |
350 o 3 5e1f9d8d7c69 dir2/foo
351 |
352 o 2 594bc4b13d4a dir1/bar
353 |
354 o 1 47f480a08324 dir1/foo
355 |
356 o 0 2a4f0c3b67da... root
357
358
359 $ cd ..
360
361 clone a narrow portion of the master, such that we can widen it later
362
363 $ hg clone --narrow ssh://user@dummy/master narrow2 --include dir1/dirA
364 requesting all changes
365 adding changesets
366 adding manifests
367 adding file changes
368 added 5 changesets with 2 changes to 2 files
369 new changesets *:* (glob)
370 updating to branch default
371 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
372 $ cd narrow2
373 $ find * | sort
374 dir1
375 dir1/dirA
376 dir1/dirA/bar
377 dir1/dirA/foo
378 $ hg tracked --addinclude dir1
379 comparing with ssh://user@dummy/master
380 searching for changes
381 no changes found
382 saved backup bundle to $TESTTMP/narrow2/.hg/strip-backup/*-widen.hg (glob)
383 adding changesets
384 adding manifests
385 adding file changes
386 added 10 changesets with 6 changes to 6 files
387 new changesets *:* (glob)
388 $ find * | sort
389 dir1
390 dir1/bar
391 dir1/dirA
392 dir1/dirA/bar
393 dir1/dirA/foo
394 dir1/dirB
395 dir1/dirB/bar
396 dir1/dirB/foo
397 dir1/foo
398 $ hg log -G -T '{rev} {node|short}{if(ellipsis, "...")} {files}\n'
399 @ 9 c87ca422d521 dir1/dirA/foo
400 |
401 o 8 951b8a83924e... dir2/dirB/bar
402 |
403 o 7 8e80155d5445 dir1/dirB/bar
404 |
405 o 6 406760310428 dir1/dirB/foo
406 |
407 o 5 623466a5f475 dir1/dirA/bar
408 |
409 o 4 06ff3a5be997 dir1/dirA/foo
410 |
411 o 3 33227af02764... dir2/bar
412 |
413 o 2 594bc4b13d4a dir1/bar
414 |
415 o 1 47f480a08324 dir1/foo
416 |
417 o 0 2a4f0c3b67da... root
418
@@ -0,0 +1,175 b''
1 $ . "$TESTDIR/narrow-library.sh"
2
3 $ hg init master
4 $ cd master
5 $ cat >> .hg/hgrc <<EOF
6 > [narrow]
7 > serveellipses=True
8 > EOF
9 $ for x in `$TESTDIR/seq.py 10`
10 > do
11 > echo $x > "f$x"
12 > hg add "f$x"
13 > hg commit -m "Commit f$x"
14 > done
15 $ cd ..
16
17 narrow clone a couple files, f2 and f8
18
19 $ hg clone --narrow ssh://user@dummy/master narrow --include "f2" --include "f8"
20 requesting all changes
21 adding changesets
22 adding manifests
23 adding file changes
24 added 5 changesets with 2 changes to 2 files
25 new changesets *:* (glob)
26 updating to branch default
27 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
28 $ cd narrow
29 $ ls
30 f2
31 f8
32 $ cat f2 f8
33 2
34 8
35
36 $ cd ..
37
38 change every upstream file twice
39
40 $ cd master
41 $ for x in `$TESTDIR/seq.py 10`
42 > do
43 > echo "update#1 $x" >> "f$x"
44 > hg commit -m "Update#1 to f$x" "f$x"
45 > done
46 $ for x in `$TESTDIR/seq.py 10`
47 > do
48 > echo "update#2 $x" >> "f$x"
49 > hg commit -m "Update#2 to f$x" "f$x"
50 > done
51 $ cd ..
52
53 look for incoming changes
54
55 $ cd narrow
56 $ hg incoming --limit 3
57 comparing with ssh://user@dummy/master
58 searching for changes
59 changeset: 5:ddc055582556
60 user: test
61 date: Thu Jan 01 00:00:00 1970 +0000
62 summary: Update#1 to f1
63
64 changeset: 6:f66eb5ad621d
65 user: test
66 date: Thu Jan 01 00:00:00 1970 +0000
67 summary: Update#1 to f2
68
69 changeset: 7:c42ecff04e99
70 user: test
71 date: Thu Jan 01 00:00:00 1970 +0000
72 summary: Update#1 to f3
73
74
75 Interrupting the pull is safe
76 $ hg --config hooks.pretxnchangegroup.bad=false pull -q
77 transaction abort!
78 rollback completed
79 abort: pretxnchangegroup.bad hook exited with status 1
80 [255]
81 $ hg id
82 223311e70a6f tip
83
84 pull new changes down to the narrow clone. Should get 8 new changesets: 4
85 relevant to the narrow spec, and 4 ellipsis nodes gluing them all together.
86
87 $ hg pull
88 pulling from ssh://user@dummy/master
89 searching for changes
90 adding changesets
91 adding manifests
92 adding file changes
93 added 9 changesets with 4 changes to 2 files
94 new changesets *:* (glob)
95 (run 'hg update' to get a working copy)
96 $ hg log -T '{rev}: {desc}\n'
97 13: Update#2 to f10
98 12: Update#2 to f8
99 11: Update#2 to f7
100 10: Update#2 to f2
101 9: Update#2 to f1
102 8: Update#1 to f8
103 7: Update#1 to f7
104 6: Update#1 to f2
105 5: Update#1 to f1
106 4: Commit f10
107 3: Commit f8
108 2: Commit f7
109 1: Commit f2
110 0: Commit f1
111 $ hg update tip
112 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
113
114 add a change and push it
115
116 $ echo "update#3 2" >> f2
117 $ hg commit -m "Update#3 to f2" f2
118 $ hg log f2 -T '{rev}: {desc}\n'
119 14: Update#3 to f2
120 10: Update#2 to f2
121 6: Update#1 to f2
122 1: Commit f2
123 $ hg push
124 pushing to ssh://user@dummy/master
125 searching for changes
126 remote: adding changesets
127 remote: adding manifests
128 remote: adding file changes
129 remote: added 1 changesets with 1 changes to 1 files
130 $ cd ..
131
132 $ cd master
133 $ hg log f2 -T '{rev}: {desc}\n'
134 30: Update#3 to f2
135 21: Update#2 to f2
136 11: Update#1 to f2
137 1: Commit f2
138 $ hg log -l 3 -T '{rev}: {desc}\n'
139 30: Update#3 to f2
140 29: Update#2 to f10
141 28: Update#2 to f9
142
143 Can pull into repo with a single commit
144
145 $ cd ..
146 $ hg clone -q --narrow ssh://user@dummy/master narrow2 --include "f1" -r 0
147 $ cd narrow2
148 $ hg pull -q -r 1
149 transaction abort!
150 rollback completed
151 abort: pull failed on remote
152 [255]
153
154 Can use 'hg share':
155 $ cat >> $HGRCPATH <<EOF
156 > [extensions]
157 > share=
158 > EOF
159
160 $ cd ..
161 $ hg share narrow2 narrow2-share
162 updating working directory
163 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
164 $ cd narrow2-share
165 $ hg status
166
167 We should also be able to unshare without breaking everything:
168 $ hg unshare
169 devel-warn: write with no wlock: "narrowspec" at: */hgext/narrow/narrowrepo.py:41 (unsharenarrowspec) (glob)
170 $ hg verify
171 checking changesets
172 checking manifests
173 crosschecking files in changesets and manifests
174 checking files
175 1 files, 1 changesets, 1 total revisions
@@ -0,0 +1,93 b''
1
2 $ . "$TESTDIR/narrow-library.sh"
3
4 create full repo
5
6 $ hg init master
7 $ cd master
8
9 $ mkdir inside
10 $ echo inside1 > inside/f1
11 $ echo inside2 > inside/f2
12 $ mkdir outside
13 $ echo outside1 > outside/f1
14 $ echo outside2 > outside/f2
15 $ hg ci -Aqm 'initial'
16
17 $ echo modified > inside/f1
18 $ hg ci -qm 'modify inside/f1'
19
20 $ hg update -q 0
21 $ echo modified2 > inside/f2
22 $ hg ci -qm 'modify inside/f2'
23
24 $ hg update -q 0
25 $ echo modified > outside/f1
26 $ hg ci -qm 'modify outside/f1'
27
28 $ hg update -q 0
29 $ echo modified2 > outside/f1
30 $ hg ci -qm 'conflicting outside/f1'
31
32 $ cd ..
33
34 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
35 requesting all changes
36 adding changesets
37 adding manifests
38 adding file changes
39 added 5 changesets with 4 changes to 2 files (+3 heads)
40 new changesets *:* (glob)
41 updating to branch default
42 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 $ cd narrow
44 $ cat >> $HGRCPATH <<EOF
45 > [extensions]
46 > rebase=
47 > EOF
48
49 $ hg update -q 0
50
51 Can rebase onto commit where no files outside narrow spec are involved
52
53 $ hg update -q 0
54 $ echo modified > inside/f2
55 $ hg ci -qm 'modify inside/f2'
56 $ hg rebase -d 'desc("modify inside/f1")'
57 rebasing 5:c2f36d04e05d "modify inside/f2" (tip)
58 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-rebase.hg (glob)
59
60 Can rebase onto conflicting changes inside narrow spec
61
62 $ hg update -q 0
63 $ echo conflicting > inside/f1
64 $ hg ci -qm 'conflicting inside/f1'
65 $ hg rebase -d 'desc("modify inside/f1")' 2>&1 | egrep -v '(warning:|incomplete!)'
66 rebasing 6:cdce97fbf653 "conflicting inside/f1" (tip)
67 merging inside/f1
68 unresolved conflicts (see hg resolve, then hg rebase --continue)
69 $ echo modified3 > inside/f1
70 $ hg resolve -m 2>&1 | grep -v continue:
71 (no more unresolved files)
72 $ hg rebase --continue
73 rebasing 6:cdce97fbf653 "conflicting inside/f1" (tip)
74 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-rebase.hg (glob)
75
76 Can rebase onto non-conflicting changes outside narrow spec
77
78 $ hg update -q 0
79 $ echo modified > inside/f2
80 $ hg ci -qm 'modify inside/f2'
81 $ hg rebase -d 'desc("modify outside/f1")'
82 rebasing 7:c2f36d04e05d "modify inside/f2" (tip)
83 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-rebase.hg (glob)
84
85 Rebase interrupts on conflicting changes outside narrow spec
86
87 $ hg update -q 'desc("conflicting outside/f1")'
88 $ hg phase -f -d .
89 no phases changed
90 $ hg rebase -d 'desc("modify outside/f1")'
91 rebasing 4:707c035aadb6 "conflicting outside/f1"
92 abort: conflict in file 'outside/f1' is outside narrow clone
93 [255]
@@ -0,0 +1,345 b''
1 $ . "$TESTDIR/narrow-library.sh"
2
3 create full repo
4
5 $ hg init master
6 $ cd master
7 $ cat >> .hg/hgrc <<EOF
8 > [narrow]
9 > serveellipses=True
10 > EOF
11
12 $ mkdir inside
13 $ echo 1 > inside/f
14 $ hg commit -Aqm 'initial inside'
15
16 $ mkdir outside
17 $ echo 1 > outside/f
18 $ hg commit -Aqm 'initial outside'
19
20 $ echo 2a > outside/f
21 $ hg commit -Aqm 'outside 2a'
22 $ echo 3 > inside/f
23 $ hg commit -Aqm 'inside 3'
24 $ echo 4a > outside/f
25 $ hg commit -Aqm 'outside 4a'
26 $ hg update '.~3'
27 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
28
29 $ echo 2b > outside/f
30 $ hg commit -Aqm 'outside 2b'
31 $ echo 3 > inside/f
32 $ hg commit -Aqm 'inside 3'
33 $ echo 4b > outside/f
34 $ hg commit -Aqm 'outside 4b'
35 $ hg update '.~3'
36 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
37
38 $ echo 2c > outside/f
39 $ hg commit -Aqm 'outside 2c'
40 $ echo 3 > inside/f
41 $ hg commit -Aqm 'inside 3'
42 $ echo 4c > outside/f
43 $ hg commit -Aqm 'outside 4c'
44 $ hg update '.~3'
45 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
46
47 $ echo 2d > outside/f
48 $ hg commit -Aqm 'outside 2d'
49 $ echo 3 > inside/f
50 $ hg commit -Aqm 'inside 3'
51 $ echo 4d > outside/f
52 $ hg commit -Aqm 'outside 4d'
53
54 $ hg update -r 'desc("outside 4a")'
55 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
56 $ hg merge -r 'desc("outside 4b")' 2>&1 | egrep -v '(warning:|incomplete!)'
57 merging outside/f
58 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
59 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
60 $ echo 5 > outside/f
61 $ rm outside/f.orig
62 $ hg resolve --mark outside/f
63 (no more unresolved files)
64 $ hg commit -m 'merge a/b 5'
65 $ echo 6 > outside/f
66 $ hg commit -Aqm 'outside 6'
67
68 $ hg merge -r 'desc("outside 4c")' 2>&1 | egrep -v '(warning:|incomplete!)'
69 merging outside/f
70 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
71 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
72 $ echo 7 > outside/f
73 $ rm outside/f.orig
74 $ hg resolve --mark outside/f
75 (no more unresolved files)
76 $ hg commit -Aqm 'merge a/b/c 7'
77 $ echo 8 > outside/f
78 $ hg commit -Aqm 'outside 8'
79
80 $ hg merge -r 'desc("outside 4d")' 2>&1 | egrep -v '(warning:|incomplete!)'
81 merging outside/f
82 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
83 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
84 $ echo 9 > outside/f
85 $ rm outside/f.orig
86 $ hg resolve --mark outside/f
87 (no more unresolved files)
88 $ hg commit -Aqm 'merge a/b/c/d 9'
89 $ echo 10 > outside/f
90 $ hg commit -Aqm 'outside 10'
91
92 $ echo 11 > inside/f
93 $ hg commit -Aqm 'inside 11'
94 $ echo 12 > outside/f
95 $ hg commit -Aqm 'outside 12'
96
97 $ hg log -G -T '{rev} {node|short} {desc}\n'
98 @ 21 8d874d57adea outside 12
99 |
100 o 20 7ef88b4dd4fa inside 11
101 |
102 o 19 2a20009de83e outside 10
103 |
104 o 18 3ac1f5779de3 merge a/b/c/d 9
105 |\
106 | o 17 38a9c2f7e546 outside 8
107 | |
108 | o 16 094aa62fc898 merge a/b/c 7
109 | |\
110 | | o 15 f29d083d32e4 outside 6
111 | | |
112 | | o 14 2dc11382541d merge a/b 5
113 | | |\
114 o | | | 13 27d07ef97221 outside 4d
115 | | | |
116 o | | | 12 465567bdfb2d inside 3
117 | | | |
118 o | | | 11 d1c61993ec83 outside 2d
119 | | | |
120 | o | | 10 56859a8e33b9 outside 4c
121 | | | |
122 | o | | 9 bb96a08b062a inside 3
123 | | | |
124 | o | | 8 b844052e7b3b outside 2c
125 |/ / /
126 | | o 7 9db2d8fcc2a6 outside 4b
127 | | |
128 | | o 6 6418167787a6 inside 3
129 | | |
130 +---o 5 77344f344d83 outside 2b
131 | |
132 | o 4 9cadde08dc9f outside 4a
133 | |
134 | o 3 019ef06f125b inside 3
135 | |
136 | o 2 75e40c075a19 outside 2a
137 |/
138 o 1 906d6c682641 initial outside
139 |
140 o 0 9f8e82b51004 initial inside
141
142
143 Now narrow and shallow clone this and get a hopefully correct graph
144
145 $ cd ..
146 $ hg clone --narrow ssh://user@dummy/master narrow --include inside --depth 7
147 requesting all changes
148 adding changesets
149 adding manifests
150 adding file changes
151 added 8 changesets with 3 changes to 1 files
152 new changesets *:* (glob)
153 updating to branch default
154 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
155 $ cd narrow
156
157 To make updating the tests easier, we print the emitted nodes
158 sorted. This makes it easier to identify when the same node structure
159 has been emitted, just in a different order.
160
161 $ hg log -G -T '{rev} {node|short}{if(ellipsis,"...")} {desc}\n'
162 @ 7 8d874d57adea... outside 12
163 |
164 o 6 7ef88b4dd4fa inside 11
165 |
166 o 5 2a20009de83e... outside 10
167 |
168 o 4 3ac1f5779de3... merge a/b/c/d 9
169 |\
170 | o 3 465567bdfb2d inside 3
171 | |
172 | o 2 d1c61993ec83... outside 2d
173 |
174 o 1 bb96a08b062a inside 3
175 |
176 o 0 b844052e7b3b... outside 2c
177
178
179 $ hg log -T '{if(ellipsis,"...")}{node|short} {p1node|short} {p2node|short} {desc}\n' | sort
180 ...2a20009de83e 000000000000 3ac1f5779de3 outside 10
181 ...3ac1f5779de3 bb96a08b062a 465567bdfb2d merge a/b/c/d 9
182 ...8d874d57adea 7ef88b4dd4fa 000000000000 outside 12
183 ...b844052e7b3b 000000000000 000000000000 outside 2c
184 ...d1c61993ec83 000000000000 000000000000 outside 2d
185 465567bdfb2d d1c61993ec83 000000000000 inside 3
186 7ef88b4dd4fa 2a20009de83e 000000000000 inside 11
187 bb96a08b062a b844052e7b3b 000000000000 inside 3
188
189 $ cd ..
190
191 Incremental test case: show a pull can pull in a conflicted merge even if elided
192
193 $ hg init pullmaster
194 $ cd pullmaster
195 $ cat >> .hg/hgrc <<EOF
196 > [narrow]
197 > serveellipses=True
198 > EOF
199 $ mkdir inside outside
200 $ echo v1 > inside/f
201 $ echo v1 > outside/f
202 $ hg add inside/f outside/f
203 $ hg commit -m init
204
205 $ for line in a b c d
206 > do
207 > hg update -r 0
208 > echo v2$line > outside/f
209 > hg commit -m "outside 2$line"
210 > echo v2$line > inside/f
211 > hg commit -m "inside 2$line"
212 > echo v3$line > outside/f
213 > hg commit -m "outside 3$line"
214 > echo v4$line > outside/f
215 > hg commit -m "outside 4$line"
216 > done
217 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
218 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
219 created new head
220 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
221 created new head
222 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
223 created new head
224
225 $ cd ..
226 $ hg clone --narrow ssh://user@dummy/pullmaster pullshallow \
227 > --include inside --depth 3
228 requesting all changes
229 adding changesets
230 adding manifests
231 adding file changes
232 added 12 changesets with 5 changes to 1 files (+3 heads)
233 new changesets *:* (glob)
234 updating to branch default
235 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
236 $ cd pullshallow
237
238 $ hg log -G -T '{rev} {node|short}{if(ellipsis,"...")} {desc}\n'
239 @ 11 0ebbd712a0c8... outside 4d
240 |
241 o 10 0d4c867aeb23 inside 2d
242 |
243 o 9 e932969c3961... outside 2d
244
245 o 8 33d530345455... outside 4c
246 |
247 o 7 0ce6481bfe07 inside 2c
248 |
249 o 6 caa65c940632... outside 2c
250
251 o 5 3df233defecc... outside 4b
252 |
253 o 4 7162cc6d11a4 inside 2b
254 |
255 o 3 f2a632f0082d... outside 2b
256
257 o 2 b8a3da16ba49... outside 4a
258 |
259 o 1 53f543eb8e45 inside 2a
260 |
261 o 0 1be3e5221c6a... outside 2a
262
263 $ hg log -T '{if(ellipsis,"...")}{node|short} {p1node|short} {p2node|short} {desc}\n' | sort
264 ...0ebbd712a0c8 0d4c867aeb23 000000000000 outside 4d
265 ...1be3e5221c6a 000000000000 000000000000 outside 2a
266 ...33d530345455 0ce6481bfe07 000000000000 outside 4c
267 ...3df233defecc 7162cc6d11a4 000000000000 outside 4b
268 ...b8a3da16ba49 53f543eb8e45 000000000000 outside 4a
269 ...caa65c940632 000000000000 000000000000 outside 2c
270 ...e932969c3961 000000000000 000000000000 outside 2d
271 ...f2a632f0082d 000000000000 000000000000 outside 2b
272 0ce6481bfe07 caa65c940632 000000000000 inside 2c
273 0d4c867aeb23 e932969c3961 000000000000 inside 2d
274 53f543eb8e45 1be3e5221c6a 000000000000 inside 2a
275 7162cc6d11a4 f2a632f0082d 000000000000 inside 2b
276
277 $ cd ../pullmaster
278 $ hg update -r 'desc("outside 4a")'
279 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
280 $ hg merge -r 'desc("outside 4b")' 2>&1 | egrep -v '(warning:|incomplete!)'
281 merging inside/f
282 merging outside/f
283 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
284 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
285 $ echo 3 > inside/f
286 $ echo 5 > outside/f
287 $ rm -f {in,out}side/f.orig
288 $ hg resolve --mark inside/f outside/f
289 (no more unresolved files)
290 $ hg commit -m 'merge a/b 5'
291
292 $ hg update -r 'desc("outside 4c")'
293 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
294 $ hg merge -r 'desc("outside 4d")' 2>&1 | egrep -v '(warning:|incomplete!)'
295 merging inside/f
296 merging outside/f
297 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
298 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
299 $ echo 3 > inside/f
300 $ echo 5 > outside/f
301 $ rm -f {in,out}side/f.orig
302 $ hg resolve --mark inside/f outside/f
303 (no more unresolved files)
304 $ hg commit -m 'merge c/d 5'
305
306 $ hg update -r 'desc("merge a/b 5")'
307 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
308 $ hg merge -r 'desc("merge c/d 5")'
309 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
310 (branch merge, don't forget to commit)
311 $ echo 6 > outside/f
312 $ hg commit -m 'outside 6'
313 $ echo 7 > outside/f
314 $ hg commit -m 'outside 7'
315 $ echo 8 > outside/f
316 $ hg commit -m 'outside 8'
317
318 $ cd ../pullshallow
319 $ hg pull --depth 3
320 pulling from ssh://user@dummy/pullmaster
321 searching for changes
322 adding changesets
323 adding manifests
324 adding file changes
325 added 4 changesets with 3 changes to 1 files (-3 heads)
326 new changesets *:* (glob)
327 (run 'hg update' to get a working copy)
328
329 $ hg log -T '{if(ellipsis,"...")}{node|short} {p1node|short} {p2node|short} {desc}\n' | sort
330 ...0ebbd712a0c8 0d4c867aeb23 000000000000 outside 4d
331 ...1be3e5221c6a 000000000000 000000000000 outside 2a
332 ...33d530345455 0ce6481bfe07 000000000000 outside 4c
333 ...3df233defecc 7162cc6d11a4 000000000000 outside 4b
334 ...b8a3da16ba49 53f543eb8e45 000000000000 outside 4a
335 ...bf545653453e 968003d40c60 000000000000 outside 8
336 ...caa65c940632 000000000000 000000000000 outside 2c
337 ...e932969c3961 000000000000 000000000000 outside 2d
338 ...f2a632f0082d 000000000000 000000000000 outside 2b
339 0ce6481bfe07 caa65c940632 000000000000 inside 2c
340 0d4c867aeb23 e932969c3961 000000000000 inside 2d
341 53f543eb8e45 1be3e5221c6a 000000000000 inside 2a
342 67d49c0bdbda b8a3da16ba49 3df233defecc merge a/b 5
343 7162cc6d11a4 f2a632f0082d 000000000000 inside 2b
344 968003d40c60 67d49c0bdbda e867021d52c2 outside 6
345 e867021d52c2 33d530345455 0ebbd712a0c8 merge c/d 5
@@ -0,0 +1,122 b''
1 $ . "$TESTDIR/narrow-library.sh"
2
3 $ hg init master
4 $ cd master
5 $ cat >> .hg/hgrc <<EOF
6 > [narrow]
7 > serveellipses=True
8 > EOF
9 $ for x in `$TESTDIR/seq.py 10`
10 > do
11 > echo $x > "f$x"
12 > hg add "f$x"
13 > done
14 $ hg commit -m "Add root files"
15 $ mkdir d1 d2
16 $ for x in `$TESTDIR/seq.py 10`
17 > do
18 > echo d1/$x > "d1/f$x"
19 > hg add "d1/f$x"
20 > echo d2/$x > "d2/f$x"
21 > hg add "d2/f$x"
22 > done
23 $ hg commit -m "Add d1 and d2"
24 $ for x in `$TESTDIR/seq.py 10`
25 > do
26 > echo f$x rev2 > "f$x"
27 > echo d1/f$x rev2 > "d1/f$x"
28 > echo d2/f$x rev2 > "d2/f$x"
29 > hg commit -m "Commit rev2 of f$x, d1/f$x, d2/f$x"
30 > done
31 $ cd ..
32
33 narrow and shallow clone the d2 directory
34
35 $ hg clone --narrow ssh://user@dummy/master shallow --include "d2" --depth 2
36 requesting all changes
37 adding changesets
38 adding manifests
39 adding file changes
40 added 4 changesets with 13 changes to 10 files
41 new changesets *:* (glob)
42 updating to branch default
43 10 files updated, 0 files merged, 0 files removed, 0 files unresolved
44 $ cd shallow
45 $ hg log -T '{rev}{if(ellipsis,"...")}: {desc}\n'
46 3: Commit rev2 of f10, d1/f10, d2/f10
47 2: Commit rev2 of f9, d1/f9, d2/f9
48 1: Commit rev2 of f8, d1/f8, d2/f8
49 0...: Commit rev2 of f7, d1/f7, d2/f7
50 $ hg update 0
51 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
52 $ cat d2/f7 d2/f8
53 d2/f7 rev2
54 d2/8
55
56 $ cd ..
57
58 change every upstream file once
59
60 $ cd master
61 $ for x in `$TESTDIR/seq.py 10`
62 > do
63 > echo f$x rev3 > "f$x"
64 > echo d1/f$x rev3 > "d1/f$x"
65 > echo d2/f$x rev3 > "d2/f$x"
66 > hg commit -m "Commit rev3 of f$x, d1/f$x, d2/f$x"
67 > done
68 $ cd ..
69
70 pull new changes with --depth specified. There were 10 changes to the d2
71 directory but the shallow pull should only fetch 3.
72
73 $ cd shallow
74 $ hg pull --depth 2
75 pulling from ssh://user@dummy/master
76 searching for changes
77 adding changesets
78 adding manifests
79 adding file changes
80 added 4 changesets with 10 changes to 10 files
81 new changesets *:* (glob)
82 (run 'hg update' to get a working copy)
83 $ hg log -T '{rev}{if(ellipsis,"...")}: {desc}\n'
84 7: Commit rev3 of f10, d1/f10, d2/f10
85 6: Commit rev3 of f9, d1/f9, d2/f9
86 5: Commit rev3 of f8, d1/f8, d2/f8
87 4...: Commit rev3 of f7, d1/f7, d2/f7
88 3: Commit rev2 of f10, d1/f10, d2/f10
89 2: Commit rev2 of f9, d1/f9, d2/f9
90 1: Commit rev2 of f8, d1/f8, d2/f8
91 0...: Commit rev2 of f7, d1/f7, d2/f7
92 $ hg update 4
93 merging d2/f1
94 merging d2/f2
95 merging d2/f3
96 merging d2/f4
97 merging d2/f5
98 merging d2/f6
99 merging d2/f7
100 3 files updated, 7 files merged, 0 files removed, 0 files unresolved
101 $ cat d2/f7 d2/f8
102 d2/f7 rev3
103 d2/f8 rev2
104 $ hg update 7
105 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
106 $ cat d2/f10
107 d2/f10 rev3
108
109 $ cd ..
110
111 cannot clone with zero or negative depth
112
113 $ hg clone --narrow ssh://user@dummy/master bad --include "d2" --depth 0
114 requesting all changes
115 remote: abort: depth must be positive, got 0
116 abort: pull failed on remote
117 [255]
118 $ hg clone --narrow ssh://user@dummy/master bad --include "d2" --depth -1
119 requesting all changes
120 remote: abort: depth must be positive, got -1
121 abort: pull failed on remote
122 [255]
@@ -0,0 +1,52 b''
1 $ cd $TESTDIR && python $RUNTESTDIR/run-tests.py \
2 > --extra-config-opt experimental.treemanifest=1 test-narrow-strip.t 2>&1 | \
3 > grep -v 'unexpected mercurial lib' | egrep -v '\(expected'
4
5 --- */test-narrow-strip.t (glob)
6 +++ */test-narrow-strip.t.err (glob)
7 @@ -\d+,\d+ \+\d+,\d+ @@ (re)
8 o 0 initial
9
10 $ hg debugdata -m 1
11 - inside/f1\x004d6a634d5ba06331a60c29ee0db8412490a54fcd (esc)
12 - outside/f1\x0084ba604d54dee1f13310ce3d4ac2e8a36636691a (esc)
13 + inside\x006a8bc41df94075d501f9740587a0c0e13c170dc5t (esc)
14 + outside\x00255c2627ebdd3c7dcaa6945246f9b9f02bd45a09t (esc)
15
16 $ rm -f $TESTTMP/narrow/.hg/strip-backup/*-backup.hg
17 $ hg strip .
18 @@ -\d+,\d+ \+\d+,\d+ @@ (re)
19
20 Check that hash of file outside narrow spec got restored
21 $ hg debugdata -m 2
22 - inside/f1\x004d6a634d5ba06331a60c29ee0db8412490a54fcd (esc)
23 - outside/f1\x0084ba604d54dee1f13310ce3d4ac2e8a36636691a (esc)
24 + inside\x006a8bc41df94075d501f9740587a0c0e13c170dc5t (esc)
25 + outside\x00255c2627ebdd3c7dcaa6945246f9b9f02bd45a09t (esc)
26
27 Also verify we can apply the bundle with 'hg pull':
28 $ hg co -r 'desc("modify inside")'
29 @@ -\d+,\d+ \+\d+,\d+ @@ (re)
30 date: Thu Jan 01 00:00:00 1970 +0000
31 summary: initial
32
33 - changeset: 1:9e48d953700d
34 + changeset: 1:3888164bccf0
35 user: test
36 date: Thu Jan 01 00:00:00 1970 +0000
37 summary: modify outside again
38
39 - changeset: 2:f505d5e96aa8
40 + changeset: 2:40b66f95a209
41 tag: tip
42 - parent: 0:a99f4d53924d
43 + parent: 0:c2a5fabcca3c
44 user: test
45 date: Thu Jan 01 00:00:00 1970 +0000
46 summary: modify inside
47
48 ERROR: test-narrow-strip.t output changed
49 !
50 Failed test-narrow-strip.t: output changed
51 # Ran 1 tests, 0 skipped, 1 failed.
52 python hash seed: * (glob)
@@ -0,0 +1,148 b''
1
2 $ . "$TESTDIR/narrow-library.sh"
3
4 create full repo
5
6 $ hg init master
7 $ cd master
8 $ cat >> .hg/hgrc <<EOF
9 > [narrow]
10 > serveellipses=True
11 > EOF
12
13 $ mkdir inside
14 $ echo inside > inside/f1
15 $ mkdir outside
16 $ echo outside > outside/f1
17 $ hg ci -Aqm 'initial'
18
19 $ echo modified > inside/f1
20 $ hg ci -qm 'modify inside'
21
22 $ hg co -q 0
23 $ echo modified > outside/f1
24 $ hg ci -qm 'modify outside'
25
26 $ echo modified again >> outside/f1
27 $ hg ci -qm 'modify outside again'
28
29 $ cd ..
30
31 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
32 requesting all changes
33 adding changesets
34 adding manifests
35 adding file changes
36 added 3 changesets with 2 changes to 1 files (+1 heads)
37 new changesets *:* (glob)
38 updating to branch default
39 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
40 $ cd narrow
41 $ cat >> $HGRCPATH <<EOF
42 > [extensions]
43 > strip=
44 > EOF
45
46 Can strip and recover changesets affecting only files within narrow spec
47
48 $ hg co -r 'desc("modify inside")'
49 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
50 $ rm -f $TESTTMP/narrow/.hg/strip-backup/*-backup.hg
51 $ hg strip .
52 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
53 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-backup.hg (glob)
54 $ hg unbundle .hg/strip-backup/*-backup.hg
55 adding changesets
56 adding manifests
57 adding file changes
58 added 1 changesets with 1 changes to 1 files (+1 heads)
59 new changesets * (glob)
60 (run 'hg heads' to see heads, 'hg merge' to merge)
61
62 Can strip and recover changesets affecting files outside of narrow spec
63
64 $ hg co -r 'desc("modify outside")'
65 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
66 $ hg log -G -T '{rev} {desc}\n'
67 o 2 modify inside
68 |
69 | @ 1 modify outside again
70 |/
71 o 0 initial
72
73 $ hg debugdata -m 1
74 inside/f1\x004d6a634d5ba06331a60c29ee0db8412490a54fcd (esc)
75 outside/f1\x0084ba604d54dee1f13310ce3d4ac2e8a36636691a (esc)
76
77 $ rm -f $TESTTMP/narrow/.hg/strip-backup/*-backup.hg
78 $ hg strip .
79 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
80 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-backup.hg (glob)
81 $ hg unbundle .hg/strip-backup/*-backup.hg
82 adding changesets
83 adding manifests
84 adding file changes
85 added 1 changesets with 0 changes to 0 files (+1 heads)
86 new changesets * (glob)
87 (run 'hg heads' to see heads, 'hg merge' to merge)
88 $ hg log -G -T '{rev} {desc}\n'
89 o 2 modify outside again
90 |
91 | o 1 modify inside
92 |/
93 @ 0 initial
94
95 Check that hash of file outside narrow spec got restored
96 $ hg debugdata -m 2
97 inside/f1\x004d6a634d5ba06331a60c29ee0db8412490a54fcd (esc)
98 outside/f1\x0084ba604d54dee1f13310ce3d4ac2e8a36636691a (esc)
99
100 Also verify we can apply the bundle with 'hg pull':
101 $ hg co -r 'desc("modify inside")'
102 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
103 $ rm .hg/strip-backup/*-backup.hg
104 $ hg strip .
105 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
106 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-backup.hg (glob)
107 $ hg pull .hg/strip-backup/*-backup.hg
108 pulling from .hg/strip-backup/*-backup.hg (glob)
109 searching for changes
110 adding changesets
111 adding manifests
112 adding file changes
113 added 1 changesets with 1 changes to 1 files (+1 heads)
114 new changesets * (glob)
115 (run 'hg heads' to see heads, 'hg merge' to merge)
116
117 $ rm .hg/strip-backup/*-backup.hg
118 $ hg strip 0
119 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
120 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-backup.hg (glob)
121 $ hg incoming .hg/strip-backup/*-backup.hg
122 comparing with .hg/strip-backup/*-backup.hg (glob)
123 changeset: 0:* (glob)
124 user: test
125 date: Thu Jan 01 00:00:00 1970 +0000
126 summary: initial
127
128 changeset: 1:9e48d953700d
129 user: test
130 date: Thu Jan 01 00:00:00 1970 +0000
131 summary: modify outside again
132
133 changeset: 2:f505d5e96aa8
134 tag: tip
135 parent: 0:a99f4d53924d
136 user: test
137 date: Thu Jan 01 00:00:00 1970 +0000
138 summary: modify inside
139
140 $ hg pull .hg/strip-backup/*-backup.hg
141 pulling from .hg/strip-backup/*-backup.hg (glob)
142 requesting all changes
143 adding changesets
144 adding manifests
145 adding file changes
146 added 3 changesets with 2 changes to 1 files (+1 heads)
147 new changesets *:* (glob)
148 (run 'hg heads' to see heads, 'hg merge' to merge)
@@ -0,0 +1,68 b''
1 $ cd $TESTDIR && python $RUNTESTDIR/run-tests.py \
2 > --extra-config-opt experimental.treemanifest=1 test-narrow-narrow.t 2>&1 | \
3 > grep -v 'unexpected mercurial lib' | egrep -v '\(expected'
4
5 --- /*/tests/test-narrow-narrow.t (glob)
6 +++ /*/tests/test-narrow-narrow.t.err (glob)
7 @@ -\d+,\d+ \+\d+,\d+ @@ (re)
8 * (glob)
9 * (glob)
10 deleting data/d0/f.i
11 + deleting meta/d0/00manifest.i
12 $ hg log -T "{node|short}: {desc} {outsidenarrow}\n"
13 *: local change to d3 (glob)
14 *: add d10/f outsidenarrow (glob)
15 @@ -\d+,\d+ \+\d+,\d+ @@ (re)
16 looking for local changes to affected paths
17 saved backup bundle to $TESTTMP/narrow-local-changes/.hg/strip-backup/*-narrow.hg (glob)
18 deleting data/d0/f.i
19 + deleting meta/d0/00manifest.i
20 Updates off of stripped commit if necessary
21 $ hg co -r 'desc("local change to d3")' -q
22 $ echo local change >> d6/f
23 @@ -\d+,\d+ \+\d+,\d+ @@ (re)
24 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
25 saved backup bundle to $TESTTMP/narrow-local-changes/.hg/strip-backup/*-narrow.hg (glob)
26 deleting data/d3/f.i
27 + deleting meta/d3/00manifest.i
28 $ hg log -T '{desc}\n' -r .
29 add d10/f
30 Updates to nullid if necessary
31 @@ -\d+,\d+ \+\d+,\d+ @@ (re)
32 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
33 saved backup bundle to $TESTTMP/narrow-local-changes/.hg/strip-backup/*-narrow.hg (glob)
34 deleting data/d3/f.i
35 + deleting meta/d3/00manifest.i
36 $ hg id
37 000000000000
38 $ cd ..
39 @@ -\d+,\d+ \+\d+,\d+ @@ (re)
40 searching for changes
41 looking for local changes to affected paths
42 deleting data/d0/f.i
43 + deleting meta/d0/00manifest.i
44 $ hg tracked
45 $ hg files
46 [1]
47 @@ -\d+,\d+ \+\d+,\d+ @@ (re)
48 searching for changes
49 looking for local changes to affected paths
50 deleting data/d6/f.i
51 + deleting meta/d6/00manifest.i
52 $ hg tracked
53 I path:d0
54 I path:d3
55 @@ -\d+,\d+ \+\d+,\d+ @@ (re)
56 searching for changes
57 looking for local changes to affected paths
58 deleting data/d0/f.i
59 + deleting meta/d0/00manifest.i
60 $ hg tracked
61 I path:d3
62 I path:d9
63
64 ERROR: test-narrow-narrow.t output changed
65 !
66 Failed test-narrow-narrow.t: output changed
67 # Ran 1 tests, 0 skipped, 1 failed.
68 python hash seed: * (glob)
@@ -0,0 +1,76 b''
1
2 $ . "$TESTDIR/narrow-library.sh"
3
4 create full repo
5
6 $ hg init master
7 $ cd master
8 $ echo init > init
9 $ hg ci -Aqm 'initial'
10
11 $ mkdir inside
12 $ echo inside > inside/f1
13 $ mkdir outside
14 $ echo outside > outside/f1
15 $ hg ci -Aqm 'add inside and outside'
16
17 $ echo modified > inside/f1
18 $ hg ci -qm 'modify inside'
19
20 $ echo modified > outside/f1
21 $ hg ci -qm 'modify outside'
22
23 $ cd ..
24
25 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
26 requesting all changes
27 adding changesets
28 adding manifests
29 adding file changes
30 added 4 changesets with 2 changes to 1 files
31 new changesets *:* (glob)
32 updating to branch default
33 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
34 $ cd narrow
35 $ hg debugindex -c
36 rev offset length base linkrev nodeid p1 p2
37 0 0 64 0 0 9958b1af2add 000000000000 000000000000
38 1 64 81 1 1 2db4ce2a3bfe 9958b1af2add 000000000000
39 2 145 75 2 2 0980ee31a742 2db4ce2a3bfe 000000000000
40 3 220 (76|77) 3 3 4410145019b7 0980ee31a742 000000000000 (re)
41
42 $ hg update -q 0
43
44 Can update to revision with changes inside
45
46 $ hg update -q 'desc("add inside and outside")'
47 $ hg update -q 'desc("modify inside")'
48 $ find *
49 inside
50 inside/f1 (glob)
51 $ cat inside/f1
52 modified
53
54 Can update to revision with changes outside
55
56 $ hg update -q 'desc("modify outside")'
57 $ find *
58 inside
59 inside/f1 (glob)
60 $ cat inside/f1
61 modified
62
63 Can update with a deleted file inside
64
65 $ hg rm inside/f1
66 $ hg update -q 'desc("modify inside")'
67 $ hg update -q 'desc("modify outside")'
68 $ hg update -q 'desc("initial")'
69 $ hg update -q 'desc("modify inside")'
70
71 Can update with a moved file inside
72
73 $ hg mv inside/f1 inside/f2
74 $ hg update -q 'desc("modify outside")'
75 $ hg update -q 'desc("initial")'
76 $ hg update -q 'desc("modify inside")'
@@ -0,0 +1,28 b''
1 $ cd $TESTDIR && python $RUNTESTDIR/run-tests.py \
2 > --extra-config-opt experimental.treemanifest=1 test-narrow-widen.t 2>&1 | \
3 > grep -v 'unexpected mercurial lib' | egrep -v '\(expected'
4
5 --- */test-narrow-widen.t (glob)
6 +++ */test-narrow-widen.t.err (glob)
7 @@ -\d+,\d+ \+\d+,\d+ @@ (re)
8 $ hg verify
9 checking changesets
10 checking manifests
11 + checking directory manifests
12 crosschecking files in changesets and manifests
13 checking files
14 4 files, 8 changesets, 4 total revisions
15 @@ -\d+,\d+ \+\d+,\d+ @@ (re)
16 $ hg verify
17 checking changesets
18 checking manifests
19 + checking directory manifests
20 crosschecking files in changesets and manifests
21 checking files
22 5 files, 9 changesets, 5 total revisions
23
24 ERROR: test-narrow-widen.t output changed
25 !
26 Failed test-narrow-widen.t: output changed
27 # Ran 1 tests, 0 skipped, 1 failed.
28 python hash seed: * (glob)
@@ -0,0 +1,355 b''
1 $ . "$TESTDIR/narrow-library.sh"
2
3 $ hg init master
4 $ cd master
5 $ cat >> .hg/hgrc <<EOF
6 > [narrow]
7 > serveellipses=True
8 > EOF
9
10 $ mkdir inside
11 $ echo 'inside' > inside/f
12 $ hg add inside/f
13 $ hg commit -m 'add inside'
14
15 $ mkdir widest
16 $ echo 'widest' > widest/f
17 $ hg add widest/f
18 $ hg commit -m 'add widest'
19
20 $ mkdir outside
21 $ echo 'outside' > outside/f
22 $ hg add outside/f
23 $ hg commit -m 'add outside'
24
25 $ cd ..
26
27 narrow clone the inside file
28
29 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
30 requesting all changes
31 adding changesets
32 adding manifests
33 adding file changes
34 added 2 changesets with 1 changes to 1 files
35 new changesets *:* (glob)
36 updating to branch default
37 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
38 $ cd narrow
39 $ hg tracked
40 I path:inside
41 $ ls
42 inside
43 $ cat inside/f
44 inside
45 $ cd ..
46
47 add more upstream files which we will include in a wider narrow spec
48
49 $ cd master
50
51 $ mkdir wider
52 $ echo 'wider' > wider/f
53 $ hg add wider/f
54 $ echo 'widest v2' > widest/f
55 $ hg commit -m 'add wider, update widest'
56
57 $ echo 'widest v3' > widest/f
58 $ hg commit -m 'update widest v3'
59
60 $ echo 'inside v2' > inside/f
61 $ hg commit -m 'update inside'
62
63 $ mkdir outside2
64 $ echo 'outside2' > outside2/f
65 $ hg add outside2/f
66 $ hg commit -m 'add outside2'
67
68 $ echo 'widest v4' > widest/f
69 $ hg commit -m 'update widest v4'
70
71 $ hg log -T "{if(ellipsis, '...')}{node|short}: {desc}\n"
72 *: update widest v4 (glob)
73 *: add outside2 (glob)
74 *: update inside (glob)
75 *: update widest v3 (glob)
76 *: add wider, update widest (glob)
77 *: add outside (glob)
78 *: add widest (glob)
79 *: add inside (glob)
80
81 $ cd ..
82
83 Widen the narrow spec to see the wider file. This should not get the newly
84 added upstream revisions.
85
86 $ cd narrow
87 $ hg tracked --addinclude wider/f
88 comparing with ssh://user@dummy/master
89 searching for changes
90 no changes found
91 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
92 adding changesets
93 adding manifests
94 adding file changes
95 added 2 changesets with 1 changes to 1 files
96 new changesets *:* (glob)
97 $ hg tracked
98 I path:inside
99 I path:wider/f
100
101 Pull down the newly added upstream revision.
102
103 $ hg pull
104 pulling from ssh://user@dummy/master
105 searching for changes
106 adding changesets
107 adding manifests
108 adding file changes
109 added 4 changesets with 2 changes to 2 files
110 new changesets *:* (glob)
111 (run 'hg update' to get a working copy)
112 $ hg update -r 'desc("add wider")'
113 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
114 $ cat wider/f
115 wider
116
117 $ hg update -r 'desc("update inside")'
118 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
119 $ cat wider/f
120 wider
121 $ cat inside/f
122 inside v2
123
124 $ hg log -T "{if(ellipsis, '...')}{node|short}: {desc}\n"
125 ...*: update widest v4 (glob)
126 *: update inside (glob)
127 ...*: update widest v3 (glob)
128 *: add wider, update widest (glob)
129 ...*: add outside (glob)
130 *: add inside (glob)
131
132 Check that widening with a newline fails
133
134 $ hg tracked --addinclude 'widest
135 > '
136 abort: newlines are not allowed in narrowspec paths
137 [255]
138
139 widen the narrow spec to include the widest file
140
141 $ hg tracked --addinclude widest
142 comparing with ssh://user@dummy/master
143 searching for changes
144 no changes found
145 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
146 adding changesets
147 adding manifests
148 adding file changes
149 added 8 changesets with 7 changes to 3 files
150 new changesets *:* (glob)
151 $ hg tracked
152 I path:inside
153 I path:wider/f
154 I path:widest
155 $ hg update 'desc("add widest")'
156 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
157 $ cat widest/f
158 widest
159 $ hg update 'desc("add wider, update widest")'
160 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
161 $ cat wider/f
162 wider
163 $ cat widest/f
164 widest v2
165 $ hg update 'desc("update widest v3")'
166 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
167 $ cat widest/f
168 widest v3
169 $ hg update 'desc("update widest v4")'
170 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
171 $ cat widest/f
172 widest v4
173
174 $ hg log -T "{if(ellipsis, '...')}{node|short}: {desc}\n"
175 *: update widest v4 (glob)
176 ...*: add outside2 (glob)
177 *: update inside (glob)
178 *: update widest v3 (glob)
179 *: add wider, update widest (glob)
180 ...*: add outside (glob)
181 *: add widest (glob)
182 *: add inside (glob)
183
184 separate suite of tests: files from 0-10 modified in changes 0-10. This allows
185 more obvious precise tests tickling particular corner cases.
186
187 $ cd ..
188 $ hg init upstream
189 $ cd upstream
190 $ cat >> .hg/hgrc <<EOF
191 > [narrow]
192 > serveellipses=True
193 > EOF
194 $ for x in `$TESTDIR/seq.py 0 10`
195 > do
196 > mkdir d$x
197 > echo $x > d$x/f
198 > hg add d$x/f
199 > hg commit -m "add d$x/f"
200 > done
201 $ hg log -T "{node|short}: {desc}\n"
202 *: add d10/f (glob)
203 *: add d9/f (glob)
204 *: add d8/f (glob)
205 *: add d7/f (glob)
206 *: add d6/f (glob)
207 *: add d5/f (glob)
208 *: add d4/f (glob)
209 *: add d3/f (glob)
210 *: add d2/f (glob)
211 *: add d1/f (glob)
212 *: add d0/f (glob)
213
214 make narrow clone with every third node.
215
216 $ cd ..
217 $ hg clone --narrow ssh://user@dummy/upstream narrow2 --include d0 --include d3 --include d6 --include d9
218 requesting all changes
219 adding changesets
220 adding manifests
221 adding file changes
222 added 8 changesets with 4 changes to 4 files
223 new changesets *:* (glob)
224 updating to branch default
225 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
226 $ cd narrow2
227 $ hg tracked
228 I path:d0
229 I path:d3
230 I path:d6
231 I path:d9
232 $ hg verify
233 checking changesets
234 checking manifests
235 crosschecking files in changesets and manifests
236 checking files
237 4 files, 8 changesets, 4 total revisions
238 $ hg log -T "{if(ellipsis, '...')}{node|short}: {desc}\n"
239 ...*: add d10/f (glob)
240 *: add d9/f (glob)
241 ...*: add d8/f (glob)
242 *: add d6/f (glob)
243 ...*: add d5/f (glob)
244 *: add d3/f (glob)
245 ...*: add d2/f (glob)
246 *: add d0/f (glob)
247 $ hg tracked --addinclude d1
248 comparing with ssh://user@dummy/upstream
249 searching for changes
250 no changes found
251 saved backup bundle to $TESTTMP/narrow2/.hg/strip-backup/*-widen.hg (glob)
252 adding changesets
253 adding manifests
254 adding file changes
255 added 9 changesets with 5 changes to 5 files
256 new changesets *:* (glob)
257 $ hg tracked
258 I path:d0
259 I path:d1
260 I path:d3
261 I path:d6
262 I path:d9
263 $ hg log -T "{if(ellipsis, '...')}{node|short}: {desc}\n"
264 ...*: add d10/f (glob)
265 *: add d9/f (glob)
266 ...*: add d8/f (glob)
267 *: add d6/f (glob)
268 ...*: add d5/f (glob)
269 *: add d3/f (glob)
270 ...*: add d2/f (glob)
271 *: add d1/f (glob)
272 *: add d0/f (glob)
273
274 Verify shouldn't claim the repo is corrupt after a widen.
275
276 $ hg verify
277 checking changesets
278 checking manifests
279 crosschecking files in changesets and manifests
280 checking files
281 5 files, 9 changesets, 5 total revisions
282
283 Widening preserves parent of local commit
284
285 $ cd ..
286 $ hg clone -q --narrow ssh://user@dummy/upstream narrow3 --include d2 -r 2
287 $ cd narrow3
288 $ hg log -T "{if(ellipsis, '...')}{node|short}: {desc}\n"
289 *: add d2/f (glob)
290 ...*: add d1/f (glob)
291 $ hg pull -q -r 3
292 $ hg co -q tip
293 $ hg pull -q -r 4
294 $ echo local > d2/f
295 $ hg ci -m local
296 created new head
297 $ hg tracked -q --addinclude d0 --addinclude d9
298
299 Widening preserves bookmarks
300
301 $ cd ..
302 $ hg clone -q --narrow ssh://user@dummy/upstream narrow-bookmarks --include d4
303 $ cd narrow-bookmarks
304 $ echo local > d4/f
305 $ hg ci -m local
306 $ hg bookmarks bookmark
307 $ hg bookmarks
308 * bookmark 3:* (glob)
309 $ hg -q tracked --addinclude d2
310 $ hg bookmarks
311 * bookmark 5:* (glob)
312 $ hg log -r bookmark -T '{desc}\n'
313 local
314
315 Widening that fails can be recovered from
316
317 $ cd ..
318 $ hg clone -q --narrow ssh://user@dummy/upstream interrupted --include d0
319 $ cd interrupted
320 $ echo local > d0/f
321 $ hg ci -m local
322 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
323 2: local
324 ...1: add d10/f
325 0: add d0/f
326 $ hg bookmarks bookmark
327 $ hg --config hooks.pretxnchangegroup.bad=false tracked --addinclude d1
328 comparing with ssh://user@dummy/upstream
329 searching for changes
330 no changes found
331 saved backup bundle to $TESTTMP/interrupted/.hg/strip-backup/*-widen.hg (glob)
332 adding changesets
333 adding manifests
334 adding file changes
335 added 3 changesets with 2 changes to 2 files
336 transaction abort!
337 rollback completed
338 abort: pretxnchangegroup.bad hook exited with status 1
339 [255]
340 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
341 $ hg bookmarks
342 no bookmarks set
343 $ hg unbundle .hg/strip-backup/*-widen.hg
344 adding changesets
345 adding manifests
346 adding file changes
347 added 3 changesets with 2 changes to 1 files
348 new changesets *:* (glob)
349 (run 'hg update' to get a working copy)
350 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
351 2: local
352 ...1: add d10/f
353 0: add d0/f
354 $ hg bookmarks
355 * bookmark 2:* (glob)
@@ -0,0 +1,358 b''
1 $ . "$TESTDIR/narrow-library.sh"
2
3 $ hg init master
4 $ cd master
5 $ cat >> .hg/hgrc <<EOF
6 > [narrow]
7 > serveellipses=True
8 > EOF
9 $ for x in `$TESTDIR/seq.py 0 10`
10 > do
11 > mkdir d$x
12 > echo $x > d$x/f
13 > hg add d$x/f
14 > hg commit -m "add d$x/f"
15 > done
16 $ hg log -T "{node|short}: {desc}\n"
17 *: add d10/f (glob)
18 *: add d9/f (glob)
19 *: add d8/f (glob)
20 *: add d7/f (glob)
21 *: add d6/f (glob)
22 *: add d5/f (glob)
23 *: add d4/f (glob)
24 *: add d3/f (glob)
25 *: add d2/f (glob)
26 *: add d1/f (glob)
27 *: add d0/f (glob)
28 $ cd ..
29
30 Error if '.' or '..' are in the directory to track.
31 $ hg clone --narrow ssh://user@dummy/master foo --include ./asdf
32 requesting all changes
33 abort: "." and ".." are not allowed in narrowspec paths
34 [255]
35 $ hg clone --narrow ssh://user@dummy/master foo --include asdf/..
36 requesting all changes
37 abort: "." and ".." are not allowed in narrowspec paths
38 [255]
39 $ hg clone --narrow ssh://user@dummy/master foo --include a/./c
40 requesting all changes
41 abort: "." and ".." are not allowed in narrowspec paths
42 [255]
43
44 Names with '.' in them are OK.
45 $ hg clone --narrow ssh://user@dummy/master $RANDOM --include a/.b/c
46 requesting all changes
47 adding changesets
48 adding manifests
49 adding file changes
50 added 1 changesets with 0 changes to 0 files
51 new changesets * (glob)
52 updating to branch default
53 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
54
55 Test repo with local changes
56 $ hg clone --narrow ssh://user@dummy/master narrow-local-changes --include d0 --include d3 --include d6
57 requesting all changes
58 adding changesets
59 adding manifests
60 adding file changes
61 added 6 changesets with 3 changes to 3 files
62 new changesets *:* (glob)
63 updating to branch default
64 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
65 $ cd narrow-local-changes
66 $ cat >> $HGRCPATH << EOF
67 > [experimental]
68 > evolution=createmarkers
69 > EOF
70 $ echo local change >> d0/f
71 $ hg ci -m 'local change to d0'
72 $ hg co '.^'
73 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
74 $ echo local change >> d3/f
75 $ hg ci -m 'local hidden change to d3'
76 created new head
77 $ hg ci --amend -m 'local change to d3'
78 $ hg tracked --removeinclude d0
79 comparing with ssh://user@dummy/master
80 searching for changes
81 looking for local changes to affected paths
82 The following changeset(s) or their ancestors have local changes not on the remote:
83 * (glob)
84 abort: local changes found
85 (use --force-delete-local-changes to ignore)
86 [255]
87 Check that nothing was removed by the failed attempts
88 $ hg tracked
89 I path:d0
90 I path:d3
91 I path:d6
92 $ hg files
93 d0/f
94 d3/f
95 d6/f
96 $ find *
97 d0
98 d0/f
99 d3
100 d3/f
101 d6
102 d6/f
103 $ hg verify -q
104 Force deletion of local changes
105 $ hg log -T "{node|short}: {desc} {outsidenarrow}\n"
106 *: local change to d3 (glob)
107 *: local change to d0 (glob)
108 *: add d10/f outsidenarrow (glob)
109 *: add d6/f (glob)
110 *: add d5/f outsidenarrow (glob)
111 *: add d3/f (glob)
112 *: add d2/f outsidenarrow (glob)
113 *: add d0/f (glob)
114 $ hg tracked --removeinclude d0 --force-delete-local-changes
115 comparing with ssh://user@dummy/master
116 searching for changes
117 looking for local changes to affected paths
118 The following changeset(s) or their ancestors have local changes not on the remote:
119 * (glob)
120 saved backup bundle to $TESTTMP/narrow-local-changes/.hg/strip-backup/*-narrow.hg (glob)
121 deleting data/d0/f.i
122 $ hg log -T "{node|short}: {desc} {outsidenarrow}\n"
123 *: local change to d3 (glob)
124 *: add d10/f outsidenarrow (glob)
125 *: add d6/f (glob)
126 *: add d5/f outsidenarrow (glob)
127 *: add d3/f (glob)
128 *: add d2/f outsidenarrow (glob)
129 *: add d0/f outsidenarrow (glob)
130 Can restore stripped local changes after widening
131 $ hg tracked --addinclude d0 -q
132 $ hg unbundle .hg/strip-backup/*-narrow.hg -q
133 $ hg --hidden co -r 'desc("local change to d0")' -q
134 $ cat d0/f
135 0
136 local change
137 Pruned commits affecting removed paths should not prevent narrowing
138 $ hg co '.^'
139 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
140 $ hg debugobsolete `hg log -T '{node}' -r 'desc("local change to d0")'`
141 obsoleted 1 changesets
142 $ hg tracked --removeinclude d0
143 comparing with ssh://user@dummy/master
144 searching for changes
145 looking for local changes to affected paths
146 saved backup bundle to $TESTTMP/narrow-local-changes/.hg/strip-backup/*-narrow.hg (glob)
147 deleting data/d0/f.i
148 Updates off of stripped commit if necessary
149 $ hg co -r 'desc("local change to d3")' -q
150 $ echo local change >> d6/f
151 $ hg ci -m 'local change to d6'
152 $ hg tracked --removeinclude d3 --force-delete-local-changes
153 comparing with ssh://user@dummy/master
154 searching for changes
155 looking for local changes to affected paths
156 The following changeset(s) or their ancestors have local changes not on the remote:
157 * (glob)
158 * (glob)
159 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
160 saved backup bundle to $TESTTMP/narrow-local-changes/.hg/strip-backup/*-narrow.hg (glob)
161 deleting data/d3/f.i
162 $ hg log -T '{desc}\n' -r .
163 add d10/f
164 Updates to nullid if necessary
165 $ hg tracked --addinclude d3 -q
166 $ hg co null -q
167 $ mkdir d3
168 $ echo local change > d3/f
169 $ hg add d3/f
170 $ hg ci -m 'local change to d3'
171 created new head
172 $ hg tracked --removeinclude d3 --force-delete-local-changes
173 comparing with ssh://user@dummy/master
174 searching for changes
175 looking for local changes to affected paths
176 The following changeset(s) or their ancestors have local changes not on the remote:
177 * (glob)
178 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
179 saved backup bundle to $TESTTMP/narrow-local-changes/.hg/strip-backup/*-narrow.hg (glob)
180 deleting data/d3/f.i
181 $ hg id
182 000000000000
183 $ cd ..
184
185 Can remove last include, making repo empty
186 $ hg clone --narrow ssh://user@dummy/master narrow-empty --include d0 -r 5
187 adding changesets
188 adding manifests
189 adding file changes
190 added 2 changesets with 1 changes to 1 files
191 new changesets *:* (glob)
192 updating to branch default
193 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
194 $ cd narrow-empty
195 $ hg tracked --removeinclude d0
196 comparing with ssh://user@dummy/master
197 searching for changes
198 looking for local changes to affected paths
199 deleting data/d0/f.i
200 $ hg tracked
201 $ hg files
202 [1]
203 $ test -d d0
204 [1]
205 Do some work in the empty clone
206 $ hg diff --change .
207 $ hg branch foo
208 marked working directory as branch foo
209 (branches are permanent and global, did you want a bookmark?)
210 $ hg ci -m empty
211 $ hg pull -q
212 Can widen the empty clone
213 $ hg tracked --addinclude d0
214 comparing with ssh://user@dummy/master
215 searching for changes
216 no changes found
217 saved backup bundle to $TESTTMP/narrow-empty/.hg/strip-backup/*-widen.hg (glob)
218 adding changesets
219 adding manifests
220 adding file changes
221 added 3 changesets with 1 changes to 1 files
222 new changesets *:* (glob)
223 $ hg tracked
224 I path:d0
225 $ hg files
226 d0/f
227 $ find *
228 d0
229 d0/f
230 $ cd ..
231
232 TODO(martinvonz): test including e.g. d3/g and then removing it once
233 https://bitbucket.org/Google/narrowhg/issues/6 is fixed
234
235 $ hg clone --narrow ssh://user@dummy/master narrow --include d0 --include d3 --include d6 --include d9
236 requesting all changes
237 adding changesets
238 adding manifests
239 adding file changes
240 added 8 changesets with 4 changes to 4 files
241 new changesets *:* (glob)
242 updating to branch default
243 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
244 $ cd narrow
245 $ hg tracked
246 I path:d0
247 I path:d3
248 I path:d6
249 I path:d9
250 $ hg tracked --removeinclude d6
251 comparing with ssh://user@dummy/master
252 searching for changes
253 looking for local changes to affected paths
254 deleting data/d6/f.i
255 $ hg tracked
256 I path:d0
257 I path:d3
258 I path:d9
259 $ hg debugrebuildfncache
260 fncache already up to date
261 $ find *
262 d0
263 d0/f
264 d3
265 d3/f
266 d9
267 d9/f
268 $ hg verify -q
269 $ hg tracked --addexclude d3/f
270 comparing with ssh://user@dummy/master
271 searching for changes
272 looking for local changes to affected paths
273 deleting data/d3/f.i
274 $ hg tracked
275 I path:d0
276 I path:d3
277 I path:d9
278 X path:d3/f
279 $ hg debugrebuildfncache
280 fncache already up to date
281 $ find *
282 d0
283 d0/f
284 d9
285 d9/f
286 $ hg verify -q
287 $ hg tracked --addexclude d0
288 comparing with ssh://user@dummy/master
289 searching for changes
290 looking for local changes to affected paths
291 deleting data/d0/f.i
292 $ hg tracked
293 I path:d3
294 I path:d9
295 X path:d0
296 X path:d3/f
297 $ hg debugrebuildfncache
298 fncache already up to date
299 $ find *
300 d9
301 d9/f
302
303 Make a 15 of changes to d9 to test the path without --verbose
304 (Note: using regexes instead of "* (glob)" because if the test fails, it
305 produces more sensible diffs)
306 $ hg tracked
307 I path:d3
308 I path:d9
309 X path:d0
310 X path:d3/f
311 $ for x in `$TESTDIR/seq.py 1 15`
312 > do
313 > echo local change >> d9/f
314 > hg commit -m "change $x to d9/f"
315 > done
316 $ hg tracked --removeinclude d9
317 comparing with ssh://user@dummy/master
318 searching for changes
319 looking for local changes to affected paths
320 The following changeset(s) or their ancestors have local changes not on the remote:
321 ^[0-9a-f]{12}$ (re)
322 ^[0-9a-f]{12}$ (re)
323 ^[0-9a-f]{12}$ (re)
324 ^[0-9a-f]{12}$ (re)
325 ^[0-9a-f]{12}$ (re)
326 ^[0-9a-f]{12}$ (re)
327 ^[0-9a-f]{12}$ (re)
328 ^[0-9a-f]{12}$ (re)
329 ^[0-9a-f]{12}$ (re)
330 ^[0-9a-f]{12}$ (re)
331 ...and 5 more, use --verbose to list all
332 abort: local changes found
333 (use --force-delete-local-changes to ignore)
334 [255]
335 Now test it *with* verbose.
336 $ hg tracked --removeinclude d9 --verbose
337 comparing with ssh://user@dummy/master
338 searching for changes
339 looking for local changes to affected paths
340 The following changeset(s) or their ancestors have local changes not on the remote:
341 ^[0-9a-f]{12}$ (re)
342 ^[0-9a-f]{12}$ (re)
343 ^[0-9a-f]{12}$ (re)
344 ^[0-9a-f]{12}$ (re)
345 ^[0-9a-f]{12}$ (re)
346 ^[0-9a-f]{12}$ (re)
347 ^[0-9a-f]{12}$ (re)
348 ^[0-9a-f]{12}$ (re)
349 ^[0-9a-f]{12}$ (re)
350 ^[0-9a-f]{12}$ (re)
351 ^[0-9a-f]{12}$ (re)
352 ^[0-9a-f]{12}$ (re)
353 ^[0-9a-f]{12}$ (re)
354 ^[0-9a-f]{12}$ (re)
355 ^[0-9a-f]{12}$ (re)
356 abort: local changes found
357 (use --force-delete-local-changes to ignore)
358 [255]
@@ -1,1034 +1,1035 b''
1 1 #
2 2 # This is the mercurial setup script.
3 3 #
4 4 # 'python setup.py install', or
5 5 # 'python setup.py --help' for more options
6 6
7 7 import os
8 8
9 9 supportedpy = '~= 2.7'
10 10 if os.environ.get('HGALLOWPYTHON3', ''):
11 11 # Mercurial will never work on Python 3 before 3.5 due to a lack
12 12 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
13 13 # due to a bug in % formatting in bytestrings.
14 14 #
15 15 # TODO: when we actually work on Python 3, use this string as the
16 16 # actual supportedpy string.
17 17 supportedpy = ','.join([
18 18 '>=2.7',
19 19 '!=3.0.*',
20 20 '!=3.1.*',
21 21 '!=3.2.*',
22 22 '!=3.3.*',
23 23 '!=3.4.*',
24 24 '!=3.6.0',
25 25 '!=3.6.1',
26 26 ])
27 27
28 28 import sys, platform
29 29 if sys.version_info[0] >= 3:
30 30 printf = eval('print')
31 31 libdir_escape = 'unicode_escape'
32 32 def sysstr(s):
33 33 return s.decode('latin-1')
34 34 else:
35 35 libdir_escape = 'string_escape'
36 36 def printf(*args, **kwargs):
37 37 f = kwargs.get('file', sys.stdout)
38 38 end = kwargs.get('end', '\n')
39 39 f.write(b' '.join(args) + end)
40 40 def sysstr(s):
41 41 return s
42 42
43 43 # Attempt to guide users to a modern pip - this means that 2.6 users
44 44 # should have a chance of getting a 4.2 release, and when we ratchet
45 45 # the version requirement forward again hopefully everyone will get
46 46 # something that works for them.
47 47 if sys.version_info < (2, 7, 0, 'final'):
48 48 pip_message = ('This may be due to an out of date pip. '
49 49 'Make sure you have pip >= 9.0.1.')
50 50 try:
51 51 import pip
52 52 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
53 53 if pip_version < (9, 0, 1) :
54 54 pip_message = (
55 55 'Your pip version is out of date, please install '
56 56 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__))
57 57 else:
58 58 # pip is new enough - it must be something else
59 59 pip_message = ''
60 60 except Exception:
61 61 pass
62 62 error = """
63 63 Mercurial does not support Python older than 2.7.
64 64 Python {py} detected.
65 65 {pip}
66 66 """.format(py=sys.version_info, pip=pip_message)
67 67 printf(error, file=sys.stderr)
68 68 sys.exit(1)
69 69
70 70 # Solaris Python packaging brain damage
71 71 try:
72 72 import hashlib
73 73 sha = hashlib.sha1()
74 74 except ImportError:
75 75 try:
76 76 import sha
77 77 sha.sha # silence unused import warning
78 78 except ImportError:
79 79 raise SystemExit(
80 80 "Couldn't import standard hashlib (incomplete Python install).")
81 81
82 82 try:
83 83 import zlib
84 84 zlib.compressobj # silence unused import warning
85 85 except ImportError:
86 86 raise SystemExit(
87 87 "Couldn't import standard zlib (incomplete Python install).")
88 88
89 89 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
90 90 isironpython = False
91 91 try:
92 92 isironpython = (platform.python_implementation()
93 93 .lower().find("ironpython") != -1)
94 94 except AttributeError:
95 95 pass
96 96
97 97 if isironpython:
98 98 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
99 99 else:
100 100 try:
101 101 import bz2
102 102 bz2.BZ2Compressor # silence unused import warning
103 103 except ImportError:
104 104 raise SystemExit(
105 105 "Couldn't import standard bz2 (incomplete Python install).")
106 106
107 107 ispypy = "PyPy" in sys.version
108 108
109 109 import ctypes
110 110 import stat, subprocess, time
111 111 import re
112 112 import shutil
113 113 import tempfile
114 114 from distutils import log
115 115 # We have issues with setuptools on some platforms and builders. Until
116 116 # those are resolved, setuptools is opt-in except for platforms where
117 117 # we don't have issues.
118 118 issetuptools = (os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ)
119 119 if issetuptools:
120 120 from setuptools import setup
121 121 else:
122 122 from distutils.core import setup
123 123 from distutils.ccompiler import new_compiler
124 124 from distutils.core import Command, Extension
125 125 from distutils.dist import Distribution
126 126 from distutils.command.build import build
127 127 from distutils.command.build_ext import build_ext
128 128 from distutils.command.build_py import build_py
129 129 from distutils.command.build_scripts import build_scripts
130 130 from distutils.command.install import install
131 131 from distutils.command.install_lib import install_lib
132 132 from distutils.command.install_scripts import install_scripts
133 133 from distutils.spawn import spawn, find_executable
134 134 from distutils import file_util
135 135 from distutils.errors import (
136 136 CCompilerError,
137 137 DistutilsError,
138 138 DistutilsExecError,
139 139 )
140 140 from distutils.sysconfig import get_python_inc, get_config_var
141 141 from distutils.version import StrictVersion
142 142
143 143 def write_if_changed(path, content):
144 144 """Write content to a file iff the content hasn't changed."""
145 145 if os.path.exists(path):
146 146 with open(path, 'rb') as fh:
147 147 current = fh.read()
148 148 else:
149 149 current = b''
150 150
151 151 if current != content:
152 152 with open(path, 'wb') as fh:
153 153 fh.write(content)
154 154
155 155 scripts = ['hg']
156 156 if os.name == 'nt':
157 157 # We remove hg.bat if we are able to build hg.exe.
158 158 scripts.append('contrib/win32/hg.bat')
159 159
160 160 def cancompile(cc, code):
161 161 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
162 162 devnull = oldstderr = None
163 163 try:
164 164 fname = os.path.join(tmpdir, 'testcomp.c')
165 165 f = open(fname, 'w')
166 166 f.write(code)
167 167 f.close()
168 168 # Redirect stderr to /dev/null to hide any error messages
169 169 # from the compiler.
170 170 # This will have to be changed if we ever have to check
171 171 # for a function on Windows.
172 172 devnull = open('/dev/null', 'w')
173 173 oldstderr = os.dup(sys.stderr.fileno())
174 174 os.dup2(devnull.fileno(), sys.stderr.fileno())
175 175 objects = cc.compile([fname], output_dir=tmpdir)
176 176 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
177 177 return True
178 178 except Exception:
179 179 return False
180 180 finally:
181 181 if oldstderr is not None:
182 182 os.dup2(oldstderr, sys.stderr.fileno())
183 183 if devnull is not None:
184 184 devnull.close()
185 185 shutil.rmtree(tmpdir)
186 186
187 187 # simplified version of distutils.ccompiler.CCompiler.has_function
188 188 # that actually removes its temporary files.
189 189 def hasfunction(cc, funcname):
190 190 code = 'int main(void) { %s(); }\n' % funcname
191 191 return cancompile(cc, code)
192 192
193 193 def hasheader(cc, headername):
194 194 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
195 195 return cancompile(cc, code)
196 196
197 197 # py2exe needs to be installed to work
198 198 try:
199 199 import py2exe
200 200 py2exe.Distribution # silence unused import warning
201 201 py2exeloaded = True
202 202 # import py2exe's patched Distribution class
203 203 from distutils.core import Distribution
204 204 except ImportError:
205 205 py2exeloaded = False
206 206
207 207 def runcmd(cmd, env):
208 208 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
209 209 stderr=subprocess.PIPE, env=env)
210 210 out, err = p.communicate()
211 211 return p.returncode, out, err
212 212
213 213 class hgcommand(object):
214 214 def __init__(self, cmd, env):
215 215 self.cmd = cmd
216 216 self.env = env
217 217
218 218 def run(self, args):
219 219 cmd = self.cmd + args
220 220 returncode, out, err = runcmd(cmd, self.env)
221 221 err = filterhgerr(err)
222 222 if err or returncode != 0:
223 223 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
224 224 printf(err, file=sys.stderr)
225 225 return ''
226 226 return out
227 227
228 228 def filterhgerr(err):
229 229 # If root is executing setup.py, but the repository is owned by
230 230 # another user (as in "sudo python setup.py install") we will get
231 231 # trust warnings since the .hg/hgrc file is untrusted. That is
232 232 # fine, we don't want to load it anyway. Python may warn about
233 233 # a missing __init__.py in mercurial/locale, we also ignore that.
234 234 err = [e for e in err.splitlines()
235 235 if (not e.startswith(b'not trusting file')
236 236 and not e.startswith(b'warning: Not importing')
237 237 and not e.startswith(b'obsolete feature not enabled')
238 238 and not e.startswith(b'devel-warn:'))]
239 239 return b'\n'.join(b' ' + e for e in err)
240 240
241 241 def findhg():
242 242 """Try to figure out how we should invoke hg for examining the local
243 243 repository contents.
244 244
245 245 Returns an hgcommand object."""
246 246 # By default, prefer the "hg" command in the user's path. This was
247 247 # presumably the hg command that the user used to create this repository.
248 248 #
249 249 # This repository may require extensions or other settings that would not
250 250 # be enabled by running the hg script directly from this local repository.
251 251 hgenv = os.environ.copy()
252 252 # Use HGPLAIN to disable hgrc settings that would change output formatting,
253 253 # and disable localization for the same reasons.
254 254 hgenv['HGPLAIN'] = '1'
255 255 hgenv['LANGUAGE'] = 'C'
256 256 hgcmd = ['hg']
257 257 # Run a simple "hg log" command just to see if using hg from the user's
258 258 # path works and can successfully interact with this repository.
259 259 check_cmd = ['log', '-r.', '-Ttest']
260 260 try:
261 261 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
262 262 except EnvironmentError:
263 263 retcode = -1
264 264 if retcode == 0 and not filterhgerr(err):
265 265 return hgcommand(hgcmd, hgenv)
266 266
267 267 # Fall back to trying the local hg installation.
268 268 hgenv = localhgenv()
269 269 hgcmd = [sys.executable, 'hg']
270 270 try:
271 271 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
272 272 except EnvironmentError:
273 273 retcode = -1
274 274 if retcode == 0 and not filterhgerr(err):
275 275 return hgcommand(hgcmd, hgenv)
276 276
277 277 raise SystemExit('Unable to find a working hg binary to extract the '
278 278 'version from the repository tags')
279 279
280 280 def localhgenv():
281 281 """Get an environment dictionary to use for invoking or importing
282 282 mercurial from the local repository."""
283 283 # Execute hg out of this directory with a custom environment which takes
284 284 # care to not use any hgrc files and do no localization.
285 285 env = {'HGMODULEPOLICY': 'py',
286 286 'HGRCPATH': '',
287 287 'LANGUAGE': 'C',
288 288 'PATH': ''} # make pypi modules that use os.environ['PATH'] happy
289 289 if 'LD_LIBRARY_PATH' in os.environ:
290 290 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
291 291 if 'SystemRoot' in os.environ:
292 292 # SystemRoot is required by Windows to load various DLLs. See:
293 293 # https://bugs.python.org/issue13524#msg148850
294 294 env['SystemRoot'] = os.environ['SystemRoot']
295 295 return env
296 296
297 297 version = ''
298 298
299 299 if os.path.isdir('.hg'):
300 300 hg = findhg()
301 301 cmd = ['log', '-r', '.', '--template', '{tags}\n']
302 302 numerictags = [t for t in sysstr(hg.run(cmd)).split() if t[0:1].isdigit()]
303 303 hgid = sysstr(hg.run(['id', '-i'])).strip()
304 304 if not hgid:
305 305 # Bail out if hg is having problems interacting with this repository,
306 306 # rather than falling through and producing a bogus version number.
307 307 # Continuing with an invalid version number will break extensions
308 308 # that define minimumhgversion.
309 309 raise SystemExit('Unable to determine hg version from local repository')
310 310 if numerictags: # tag(s) found
311 311 version = numerictags[-1]
312 312 if hgid.endswith('+'): # propagate the dirty status to the tag
313 313 version += '+'
314 314 else: # no tag found
315 315 ltagcmd = ['parents', '--template', '{latesttag}']
316 316 ltag = sysstr(hg.run(ltagcmd))
317 317 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
318 318 changessince = len(hg.run(changessincecmd).splitlines())
319 319 version = '%s+%s-%s' % (ltag, changessince, hgid)
320 320 if version.endswith('+'):
321 321 version += time.strftime('%Y%m%d')
322 322 elif os.path.exists('.hg_archival.txt'):
323 323 kw = dict([[t.strip() for t in l.split(':', 1)]
324 324 for l in open('.hg_archival.txt')])
325 325 if 'tag' in kw:
326 326 version = kw['tag']
327 327 elif 'latesttag' in kw:
328 328 if 'changessincelatesttag' in kw:
329 329 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
330 330 else:
331 331 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
332 332 else:
333 333 version = kw.get('node', '')[:12]
334 334
335 335 if version:
336 336 versionb = version
337 337 if not isinstance(versionb, bytes):
338 338 versionb = versionb.encode('ascii')
339 339
340 340 write_if_changed('mercurial/__version__.py', b''.join([
341 341 b'# this file is autogenerated by setup.py\n'
342 342 b'version = "%s"\n' % versionb,
343 343 ]))
344 344
345 345 try:
346 346 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
347 347 os.environ['HGMODULEPOLICY'] = 'py'
348 348 from mercurial import __version__
349 349 version = __version__.version
350 350 except ImportError:
351 351 version = 'unknown'
352 352 finally:
353 353 if oldpolicy is None:
354 354 del os.environ['HGMODULEPOLICY']
355 355 else:
356 356 os.environ['HGMODULEPOLICY'] = oldpolicy
357 357
358 358 class hgbuild(build):
359 359 # Insert hgbuildmo first so that files in mercurial/locale/ are found
360 360 # when build_py is run next.
361 361 sub_commands = [('build_mo', None)] + build.sub_commands
362 362
363 363 class hgbuildmo(build):
364 364
365 365 description = "build translations (.mo files)"
366 366
367 367 def run(self):
368 368 if not find_executable('msgfmt'):
369 369 self.warn("could not find msgfmt executable, no translations "
370 370 "will be built")
371 371 return
372 372
373 373 podir = 'i18n'
374 374 if not os.path.isdir(podir):
375 375 self.warn("could not find %s/ directory" % podir)
376 376 return
377 377
378 378 join = os.path.join
379 379 for po in os.listdir(podir):
380 380 if not po.endswith('.po'):
381 381 continue
382 382 pofile = join(podir, po)
383 383 modir = join('locale', po[:-3], 'LC_MESSAGES')
384 384 mofile = join(modir, 'hg.mo')
385 385 mobuildfile = join('mercurial', mofile)
386 386 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
387 387 if sys.platform != 'sunos5':
388 388 # msgfmt on Solaris does not know about -c
389 389 cmd.append('-c')
390 390 self.mkpath(join('mercurial', modir))
391 391 self.make_file([pofile], mobuildfile, spawn, (cmd,))
392 392
393 393
394 394 class hgdist(Distribution):
395 395 pure = False
396 396 cffi = ispypy
397 397
398 398 global_options = Distribution.global_options + \
399 399 [('pure', None, "use pure (slow) Python "
400 400 "code instead of C extensions"),
401 401 ]
402 402
403 403 def has_ext_modules(self):
404 404 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
405 405 # too late for some cases
406 406 return not self.pure and Distribution.has_ext_modules(self)
407 407
408 408 # This is ugly as a one-liner. So use a variable.
409 409 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
410 410 buildextnegops['no-zstd'] = 'zstd'
411 411
412 412 class hgbuildext(build_ext):
413 413 user_options = build_ext.user_options + [
414 414 ('zstd', None, 'compile zstd bindings [default]'),
415 415 ('no-zstd', None, 'do not compile zstd bindings'),
416 416 ]
417 417
418 418 boolean_options = build_ext.boolean_options + ['zstd']
419 419 negative_opt = buildextnegops
420 420
421 421 def initialize_options(self):
422 422 self.zstd = True
423 423 return build_ext.initialize_options(self)
424 424
425 425 def build_extensions(self):
426 426 # Filter out zstd if disabled via argument.
427 427 if not self.zstd:
428 428 self.extensions = [e for e in self.extensions
429 429 if e.name != 'mercurial.zstd']
430 430
431 431 return build_ext.build_extensions(self)
432 432
433 433 def build_extension(self, ext):
434 434 try:
435 435 build_ext.build_extension(self, ext)
436 436 except CCompilerError:
437 437 if not getattr(ext, 'optional', False):
438 438 raise
439 439 log.warn("Failed to build optional extension '%s' (skipping)",
440 440 ext.name)
441 441
442 442 class hgbuildscripts(build_scripts):
443 443 def run(self):
444 444 if os.name != 'nt' or self.distribution.pure:
445 445 return build_scripts.run(self)
446 446
447 447 exebuilt = False
448 448 try:
449 449 self.run_command('build_hgexe')
450 450 exebuilt = True
451 451 except (DistutilsError, CCompilerError):
452 452 log.warn('failed to build optional hg.exe')
453 453
454 454 if exebuilt:
455 455 # Copying hg.exe to the scripts build directory ensures it is
456 456 # installed by the install_scripts command.
457 457 hgexecommand = self.get_finalized_command('build_hgexe')
458 458 dest = os.path.join(self.build_dir, 'hg.exe')
459 459 self.mkpath(self.build_dir)
460 460 self.copy_file(hgexecommand.hgexepath, dest)
461 461
462 462 # Remove hg.bat because it is redundant with hg.exe.
463 463 self.scripts.remove('contrib/win32/hg.bat')
464 464
465 465 return build_scripts.run(self)
466 466
467 467 class hgbuildpy(build_py):
468 468 def finalize_options(self):
469 469 build_py.finalize_options(self)
470 470
471 471 if self.distribution.pure:
472 472 self.distribution.ext_modules = []
473 473 elif self.distribution.cffi:
474 474 from mercurial.cffi import (
475 475 bdiffbuild,
476 476 mpatchbuild,
477 477 )
478 478 exts = [mpatchbuild.ffi.distutils_extension(),
479 479 bdiffbuild.ffi.distutils_extension()]
480 480 # cffi modules go here
481 481 if sys.platform == 'darwin':
482 482 from mercurial.cffi import osutilbuild
483 483 exts.append(osutilbuild.ffi.distutils_extension())
484 484 self.distribution.ext_modules = exts
485 485 else:
486 486 h = os.path.join(get_python_inc(), 'Python.h')
487 487 if not os.path.exists(h):
488 488 raise SystemExit('Python headers are required to build '
489 489 'Mercurial but weren\'t found in %s' % h)
490 490
491 491 def run(self):
492 492 basepath = os.path.join(self.build_lib, 'mercurial')
493 493 self.mkpath(basepath)
494 494
495 495 if self.distribution.pure:
496 496 modulepolicy = 'py'
497 497 elif self.build_lib == '.':
498 498 # in-place build should run without rebuilding C extensions
499 499 modulepolicy = 'allow'
500 500 else:
501 501 modulepolicy = 'c'
502 502
503 503 content = b''.join([
504 504 b'# this file is autogenerated by setup.py\n',
505 505 b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
506 506 ])
507 507 write_if_changed(os.path.join(basepath, '__modulepolicy__.py'),
508 508 content)
509 509
510 510 build_py.run(self)
511 511
512 512 class buildhgextindex(Command):
513 513 description = 'generate prebuilt index of hgext (for frozen package)'
514 514 user_options = []
515 515 _indexfilename = 'hgext/__index__.py'
516 516
517 517 def initialize_options(self):
518 518 pass
519 519
520 520 def finalize_options(self):
521 521 pass
522 522
523 523 def run(self):
524 524 if os.path.exists(self._indexfilename):
525 525 with open(self._indexfilename, 'w') as f:
526 526 f.write('# empty\n')
527 527
528 528 # here no extension enabled, disabled() lists up everything
529 529 code = ('import pprint; from mercurial import extensions; '
530 530 'pprint.pprint(extensions.disabled())')
531 531 returncode, out, err = runcmd([sys.executable, '-c', code],
532 532 localhgenv())
533 533 if err or returncode != 0:
534 534 raise DistutilsExecError(err)
535 535
536 536 with open(self._indexfilename, 'w') as f:
537 537 f.write('# this file is autogenerated by setup.py\n')
538 538 f.write('docs = ')
539 539 f.write(out)
540 540
541 541 class buildhgexe(build_ext):
542 542 description = 'compile hg.exe from mercurial/exewrapper.c'
543 543 user_options = build_ext.user_options + [
544 544 ('long-paths-support', None, 'enable support for long paths on '
545 545 'Windows (off by default and '
546 546 'experimental)'),
547 547 ]
548 548
549 549 LONG_PATHS_MANIFEST = """
550 550 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
551 551 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
552 552 <application>
553 553 <windowsSettings
554 554 xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
555 555 <ws2:longPathAware>true</ws2:longPathAware>
556 556 </windowsSettings>
557 557 </application>
558 558 </assembly>"""
559 559
560 560 def initialize_options(self):
561 561 build_ext.initialize_options(self)
562 562 self.long_paths_support = False
563 563
564 564 def build_extensions(self):
565 565 if os.name != 'nt':
566 566 return
567 567 if isinstance(self.compiler, HackedMingw32CCompiler):
568 568 self.compiler.compiler_so = self.compiler.compiler # no -mdll
569 569 self.compiler.dll_libraries = [] # no -lmsrvc90
570 570
571 571 # Different Python installs can have different Python library
572 572 # names. e.g. the official CPython distribution uses pythonXY.dll
573 573 # and MinGW uses libpythonX.Y.dll.
574 574 _kernel32 = ctypes.windll.kernel32
575 575 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
576 576 ctypes.c_void_p,
577 577 ctypes.c_ulong]
578 578 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
579 579 size = 1000
580 580 buf = ctypes.create_string_buffer(size + 1)
581 581 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
582 582 size)
583 583
584 584 if filelen > 0 and filelen != size:
585 585 dllbasename = os.path.basename(buf.value)
586 586 if not dllbasename.lower().endswith('.dll'):
587 587 raise SystemExit('Python DLL does not end with .dll: %s' %
588 588 dllbasename)
589 589 pythonlib = dllbasename[:-4]
590 590 else:
591 591 log.warn('could not determine Python DLL filename; '
592 592 'assuming pythonXY')
593 593
594 594 hv = sys.hexversion
595 595 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
596 596
597 597 log.info('using %s as Python library name' % pythonlib)
598 598 with open('mercurial/hgpythonlib.h', 'wb') as f:
599 599 f.write('/* this file is autogenerated by setup.py */\n')
600 600 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
601 601 objects = self.compiler.compile(['mercurial/exewrapper.c'],
602 602 output_dir=self.build_temp)
603 603 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
604 604 self.hgtarget = os.path.join(dir, 'hg')
605 605 self.compiler.link_executable(objects, self.hgtarget,
606 606 libraries=[],
607 607 output_dir=self.build_temp)
608 608 if self.long_paths_support:
609 609 self.addlongpathsmanifest()
610 610
611 611 def addlongpathsmanifest(self):
612 612 """Add manifest pieces so that hg.exe understands long paths
613 613
614 614 This is an EXPERIMENTAL feature, use with care.
615 615 To enable long paths support, one needs to do two things:
616 616 - build Mercurial with --long-paths-support option
617 617 - change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\
618 618 LongPathsEnabled to have value 1.
619 619
620 620 Please ignore 'warning 81010002: Unrecognized Element "longPathAware"';
621 621 it happens because Mercurial uses mt.exe circa 2008, which is not
622 622 yet aware of long paths support in the manifest (I think so at least).
623 623 This does not stop mt.exe from embedding/merging the XML properly.
624 624
625 625 Why resource #1 should be used for .exe manifests? I don't know and
626 626 wasn't able to find an explanation for mortals. But it seems to work.
627 627 """
628 628 exefname = self.compiler.executable_filename(self.hgtarget)
629 629 fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
630 630 os.close(fdauto)
631 631 with open(manfname, 'w') as f:
632 632 f.write(self.LONG_PATHS_MANIFEST)
633 633 log.info("long paths manifest is written to '%s'" % manfname)
634 634 inputresource = '-inputresource:%s;#1' % exefname
635 635 outputresource = '-outputresource:%s;#1' % exefname
636 636 log.info("running mt.exe to update hg.exe's manifest in-place")
637 637 # supplying both -manifest and -inputresource to mt.exe makes
638 638 # it merge the embedded and supplied manifests in the -outputresource
639 639 self.spawn(['mt.exe', '-nologo', '-manifest', manfname,
640 640 inputresource, outputresource])
641 641 log.info("done updating hg.exe's manifest")
642 642 os.remove(manfname)
643 643
644 644 @property
645 645 def hgexepath(self):
646 646 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
647 647 return os.path.join(self.build_temp, dir, 'hg.exe')
648 648
649 649 class hginstall(install):
650 650
651 651 user_options = install.user_options + [
652 652 ('old-and-unmanageable', None,
653 653 'noop, present for eggless setuptools compat'),
654 654 ('single-version-externally-managed', None,
655 655 'noop, present for eggless setuptools compat'),
656 656 ]
657 657
658 658 # Also helps setuptools not be sad while we refuse to create eggs.
659 659 single_version_externally_managed = True
660 660
661 661 def get_sub_commands(self):
662 662 # Screen out egg related commands to prevent egg generation. But allow
663 663 # mercurial.egg-info generation, since that is part of modern
664 664 # packaging.
665 665 excl = set(['bdist_egg'])
666 666 return filter(lambda x: x not in excl, install.get_sub_commands(self))
667 667
668 668 class hginstalllib(install_lib):
669 669 '''
670 670 This is a specialization of install_lib that replaces the copy_file used
671 671 there so that it supports setting the mode of files after copying them,
672 672 instead of just preserving the mode that the files originally had. If your
673 673 system has a umask of something like 027, preserving the permissions when
674 674 copying will lead to a broken install.
675 675
676 676 Note that just passing keep_permissions=False to copy_file would be
677 677 insufficient, as it might still be applying a umask.
678 678 '''
679 679
680 680 def run(self):
681 681 realcopyfile = file_util.copy_file
682 682 def copyfileandsetmode(*args, **kwargs):
683 683 src, dst = args[0], args[1]
684 684 dst, copied = realcopyfile(*args, **kwargs)
685 685 if copied:
686 686 st = os.stat(src)
687 687 # Persist executable bit (apply it to group and other if user
688 688 # has it)
689 689 if st[stat.ST_MODE] & stat.S_IXUSR:
690 690 setmode = int('0755', 8)
691 691 else:
692 692 setmode = int('0644', 8)
693 693 m = stat.S_IMODE(st[stat.ST_MODE])
694 694 m = (m & ~int('0777', 8)) | setmode
695 695 os.chmod(dst, m)
696 696 file_util.copy_file = copyfileandsetmode
697 697 try:
698 698 install_lib.run(self)
699 699 finally:
700 700 file_util.copy_file = realcopyfile
701 701
702 702 class hginstallscripts(install_scripts):
703 703 '''
704 704 This is a specialization of install_scripts that replaces the @LIBDIR@ with
705 705 the configured directory for modules. If possible, the path is made relative
706 706 to the directory for scripts.
707 707 '''
708 708
709 709 def initialize_options(self):
710 710 install_scripts.initialize_options(self)
711 711
712 712 self.install_lib = None
713 713
714 714 def finalize_options(self):
715 715 install_scripts.finalize_options(self)
716 716 self.set_undefined_options('install',
717 717 ('install_lib', 'install_lib'))
718 718
719 719 def run(self):
720 720 install_scripts.run(self)
721 721
722 722 # It only makes sense to replace @LIBDIR@ with the install path if
723 723 # the install path is known. For wheels, the logic below calculates
724 724 # the libdir to be "../..". This is because the internal layout of a
725 725 # wheel archive looks like:
726 726 #
727 727 # mercurial-3.6.1.data/scripts/hg
728 728 # mercurial/__init__.py
729 729 #
730 730 # When installing wheels, the subdirectories of the "<pkg>.data"
731 731 # directory are translated to system local paths and files therein
732 732 # are copied in place. The mercurial/* files are installed into the
733 733 # site-packages directory. However, the site-packages directory
734 734 # isn't known until wheel install time. This means we have no clue
735 735 # at wheel generation time what the installed site-packages directory
736 736 # will be. And, wheels don't appear to provide the ability to register
737 737 # custom code to run during wheel installation. This all means that
738 738 # we can't reliably set the libdir in wheels: the default behavior
739 739 # of looking in sys.path must do.
740 740
741 741 if (os.path.splitdrive(self.install_dir)[0] !=
742 742 os.path.splitdrive(self.install_lib)[0]):
743 743 # can't make relative paths from one drive to another, so use an
744 744 # absolute path instead
745 745 libdir = self.install_lib
746 746 else:
747 747 common = os.path.commonprefix((self.install_dir, self.install_lib))
748 748 rest = self.install_dir[len(common):]
749 749 uplevel = len([n for n in os.path.split(rest) if n])
750 750
751 751 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
752 752
753 753 for outfile in self.outfiles:
754 754 with open(outfile, 'rb') as fp:
755 755 data = fp.read()
756 756
757 757 # skip binary files
758 758 if b'\0' in data:
759 759 continue
760 760
761 761 # During local installs, the shebang will be rewritten to the final
762 762 # install path. During wheel packaging, the shebang has a special
763 763 # value.
764 764 if data.startswith(b'#!python'):
765 765 log.info('not rewriting @LIBDIR@ in %s because install path '
766 766 'not known' % outfile)
767 767 continue
768 768
769 769 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
770 770 with open(outfile, 'wb') as fp:
771 771 fp.write(data)
772 772
773 773 cmdclass = {'build': hgbuild,
774 774 'build_mo': hgbuildmo,
775 775 'build_ext': hgbuildext,
776 776 'build_py': hgbuildpy,
777 777 'build_scripts': hgbuildscripts,
778 778 'build_hgextindex': buildhgextindex,
779 779 'install': hginstall,
780 780 'install_lib': hginstalllib,
781 781 'install_scripts': hginstallscripts,
782 782 'build_hgexe': buildhgexe,
783 783 }
784 784
785 785 packages = ['mercurial',
786 786 'mercurial.cext',
787 787 'mercurial.cffi',
788 788 'mercurial.hgweb',
789 789 'mercurial.httpclient',
790 790 'mercurial.pure',
791 791 'mercurial.thirdparty',
792 792 'mercurial.thirdparty.attr',
793 793 'hgext', 'hgext.convert', 'hgext.fsmonitor',
794 794 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
795 'hgext.largefiles', 'hgext.lfs', 'hgext.zeroconf', 'hgext3rd',
795 'hgext.largefiles', 'hgext.lfs', 'hgext.narrow',
796 'hgext.zeroconf', 'hgext3rd',
796 797 'hgdemandimport']
797 798
798 799 common_depends = ['mercurial/bitmanipulation.h',
799 800 'mercurial/compat.h',
800 801 'mercurial/cext/util.h']
801 802 common_include_dirs = ['mercurial']
802 803
803 804 osutil_cflags = []
804 805 osutil_ldflags = []
805 806
806 807 # platform specific macros
807 808 for plat, func in [('bsd', 'setproctitle')]:
808 809 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
809 810 osutil_cflags.append('-DHAVE_%s' % func.upper())
810 811
811 812 for plat, macro, code in [
812 813 ('bsd|darwin', 'BSD_STATFS', '''
813 814 #include <sys/param.h>
814 815 #include <sys/mount.h>
815 816 int main() { struct statfs s; return sizeof(s.f_fstypename); }
816 817 '''),
817 818 ('linux', 'LINUX_STATFS', '''
818 819 #include <linux/magic.h>
819 820 #include <sys/vfs.h>
820 821 int main() { struct statfs s; return sizeof(s.f_type); }
821 822 '''),
822 823 ]:
823 824 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
824 825 osutil_cflags.append('-DHAVE_%s' % macro)
825 826
826 827 if sys.platform == 'darwin':
827 828 osutil_ldflags += ['-framework', 'ApplicationServices']
828 829
829 830 extmodules = [
830 831 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
831 832 include_dirs=common_include_dirs,
832 833 depends=common_depends),
833 834 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
834 835 'mercurial/cext/bdiff.c'],
835 836 include_dirs=common_include_dirs,
836 837 depends=common_depends + ['mercurial/bdiff.h']),
837 838 Extension('mercurial.cext.diffhelpers', ['mercurial/cext/diffhelpers.c'],
838 839 include_dirs=common_include_dirs,
839 840 depends=common_depends),
840 841 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
841 842 'mercurial/cext/mpatch.c'],
842 843 include_dirs=common_include_dirs,
843 844 depends=common_depends),
844 845 Extension('mercurial.cext.parsers', ['mercurial/cext/charencode.c',
845 846 'mercurial/cext/dirs.c',
846 847 'mercurial/cext/manifest.c',
847 848 'mercurial/cext/parsers.c',
848 849 'mercurial/cext/pathencode.c',
849 850 'mercurial/cext/revlog.c'],
850 851 include_dirs=common_include_dirs,
851 852 depends=common_depends + ['mercurial/cext/charencode.h']),
852 853 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
853 854 include_dirs=common_include_dirs,
854 855 extra_compile_args=osutil_cflags,
855 856 extra_link_args=osutil_ldflags,
856 857 depends=common_depends),
857 858 Extension('hgext.fsmonitor.pywatchman.bser',
858 859 ['hgext/fsmonitor/pywatchman/bser.c']),
859 860 ]
860 861
861 862 sys.path.insert(0, 'contrib/python-zstandard')
862 863 import setup_zstd
863 864 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
864 865
865 866 try:
866 867 from distutils import cygwinccompiler
867 868
868 869 # the -mno-cygwin option has been deprecated for years
869 870 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
870 871
871 872 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
872 873 def __init__(self, *args, **kwargs):
873 874 mingw32compilerclass.__init__(self, *args, **kwargs)
874 875 for i in 'compiler compiler_so linker_exe linker_so'.split():
875 876 try:
876 877 getattr(self, i).remove('-mno-cygwin')
877 878 except ValueError:
878 879 pass
879 880
880 881 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
881 882 except ImportError:
882 883 # the cygwinccompiler package is not available on some Python
883 884 # distributions like the ones from the optware project for Synology
884 885 # DiskStation boxes
885 886 class HackedMingw32CCompiler(object):
886 887 pass
887 888
888 889 if os.name == 'nt':
889 890 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
890 891 # extra_link_args to distutils.extensions.Extension() doesn't have any
891 892 # effect.
892 893 from distutils import msvccompiler
893 894
894 895 msvccompilerclass = msvccompiler.MSVCCompiler
895 896
896 897 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
897 898 def initialize(self):
898 899 msvccompilerclass.initialize(self)
899 900 # "warning LNK4197: export 'func' specified multiple times"
900 901 self.ldflags_shared.append('/ignore:4197')
901 902 self.ldflags_shared_debug.append('/ignore:4197')
902 903
903 904 msvccompiler.MSVCCompiler = HackedMSVCCompiler
904 905
905 906 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
906 907 'help/*.txt',
907 908 'help/internals/*.txt',
908 909 'default.d/*.rc',
909 910 'dummycert.pem']}
910 911
911 912 def ordinarypath(p):
912 913 return p and p[0] != '.' and p[-1] != '~'
913 914
914 915 for root in ('templates',):
915 916 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
916 917 curdir = curdir.split(os.sep, 1)[1]
917 918 dirs[:] = filter(ordinarypath, dirs)
918 919 for f in filter(ordinarypath, files):
919 920 f = os.path.join(curdir, f)
920 921 packagedata['mercurial'].append(f)
921 922
922 923 datafiles = []
923 924
924 925 # distutils expects version to be str/unicode. Converting it to
925 926 # unicode on Python 2 still works because it won't contain any
926 927 # non-ascii bytes and will be implicitly converted back to bytes
927 928 # when operated on.
928 929 assert isinstance(version, bytes)
929 930 setupversion = version.decode('ascii')
930 931
931 932 extra = {}
932 933
933 934 if issetuptools:
934 935 extra['python_requires'] = supportedpy
935 936 if py2exeloaded:
936 937 extra['console'] = [
937 938 {'script':'hg',
938 939 'copyright':'Copyright (C) 2005-2018 Matt Mackall and others',
939 940 'product_version':version}]
940 941 # sub command of 'build' because 'py2exe' does not handle sub_commands
941 942 build.sub_commands.insert(0, ('build_hgextindex', None))
942 943 # put dlls in sub directory so that they won't pollute PATH
943 944 extra['zipfile'] = 'lib/library.zip'
944 945
945 946 if os.name == 'nt':
946 947 # Windows binary file versions for exe/dll files must have the
947 948 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
948 949 setupversion = version.split('+', 1)[0]
949 950
950 951 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
951 952 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
952 953 if version:
953 954 version = version[0]
954 955 if sys.version_info[0] == 3:
955 956 version = version.decode('utf-8')
956 957 xcode4 = (version.startswith('Xcode') and
957 958 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
958 959 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
959 960 else:
960 961 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
961 962 # installed, but instead with only command-line tools. Assume
962 963 # that only happens on >= Lion, thus no PPC support.
963 964 xcode4 = True
964 965 xcode51 = False
965 966
966 967 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
967 968 # distutils.sysconfig
968 969 if xcode4:
969 970 os.environ['ARCHFLAGS'] = ''
970 971
971 972 # XCode 5.1 changes clang such that it now fails to compile if the
972 973 # -mno-fused-madd flag is passed, but the version of Python shipped with
973 974 # OS X 10.9 Mavericks includes this flag. This causes problems in all
974 975 # C extension modules, and a bug has been filed upstream at
975 976 # http://bugs.python.org/issue21244. We also need to patch this here
976 977 # so Mercurial can continue to compile in the meantime.
977 978 if xcode51:
978 979 cflags = get_config_var('CFLAGS')
979 980 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
980 981 os.environ['CFLAGS'] = (
981 982 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
982 983
983 984 setup(name='mercurial',
984 985 version=setupversion,
985 986 author='Matt Mackall and many others',
986 987 author_email='mercurial@mercurial-scm.org',
987 988 url='https://mercurial-scm.org/',
988 989 download_url='https://mercurial-scm.org/release/',
989 990 description=('Fast scalable distributed SCM (revision control, version '
990 991 'control) system'),
991 992 long_description=('Mercurial is a distributed SCM tool written in Python.'
992 993 ' It is used by a number of large projects that require'
993 994 ' fast, reliable distributed revision control, such as '
994 995 'Mozilla.'),
995 996 license='GNU GPLv2 or any later version',
996 997 classifiers=[
997 998 'Development Status :: 6 - Mature',
998 999 'Environment :: Console',
999 1000 'Intended Audience :: Developers',
1000 1001 'Intended Audience :: System Administrators',
1001 1002 'License :: OSI Approved :: GNU General Public License (GPL)',
1002 1003 'Natural Language :: Danish',
1003 1004 'Natural Language :: English',
1004 1005 'Natural Language :: German',
1005 1006 'Natural Language :: Italian',
1006 1007 'Natural Language :: Japanese',
1007 1008 'Natural Language :: Portuguese (Brazilian)',
1008 1009 'Operating System :: Microsoft :: Windows',
1009 1010 'Operating System :: OS Independent',
1010 1011 'Operating System :: POSIX',
1011 1012 'Programming Language :: C',
1012 1013 'Programming Language :: Python',
1013 1014 'Topic :: Software Development :: Version Control',
1014 1015 ],
1015 1016 scripts=scripts,
1016 1017 packages=packages,
1017 1018 ext_modules=extmodules,
1018 1019 data_files=datafiles,
1019 1020 package_data=packagedata,
1020 1021 cmdclass=cmdclass,
1021 1022 distclass=hgdist,
1022 1023 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email',
1023 1024 # implicitly imported per module policy
1024 1025 # (cffi wouldn't be used as a frozen exe)
1025 1026 'mercurial.cext',
1026 1027 #'mercurial.cffi',
1027 1028 'mercurial.pure']},
1028 1029 'bdist_mpkg': {'zipdist': False,
1029 1030 'license': 'COPYING',
1030 1031 'readme': 'contrib/macosx/Readme.html',
1031 1032 'welcome': 'contrib/macosx/Welcome.html',
1032 1033 },
1033 1034 },
1034 1035 **extra)
@@ -1,3394 +1,3396 b''
1 1 Short help:
2 2
3 3 $ hg
4 4 Mercurial Distributed SCM
5 5
6 6 basic commands:
7 7
8 8 add add the specified files on the next commit
9 9 annotate show changeset information by line for each file
10 10 clone make a copy of an existing repository
11 11 commit commit the specified files or all outstanding changes
12 12 diff diff repository (or selected files)
13 13 export dump the header and diffs for one or more changesets
14 14 forget forget the specified files on the next commit
15 15 init create a new repository in the given directory
16 16 log show revision history of entire repository or files
17 17 merge merge another revision into working directory
18 18 pull pull changes from the specified source
19 19 push push changes to the specified destination
20 20 remove remove the specified files on the next commit
21 21 serve start stand-alone webserver
22 22 status show changed files in the working directory
23 23 summary summarize working directory state
24 24 update update working directory (or switch revisions)
25 25
26 26 (use 'hg help' for the full list of commands or 'hg -v' for details)
27 27
28 28 $ hg -q
29 29 add add the specified files on the next commit
30 30 annotate show changeset information by line for each file
31 31 clone make a copy of an existing repository
32 32 commit commit the specified files or all outstanding changes
33 33 diff diff repository (or selected files)
34 34 export dump the header and diffs for one or more changesets
35 35 forget forget the specified files on the next commit
36 36 init create a new repository in the given directory
37 37 log show revision history of entire repository or files
38 38 merge merge another revision into working directory
39 39 pull pull changes from the specified source
40 40 push push changes to the specified destination
41 41 remove remove the specified files on the next commit
42 42 serve start stand-alone webserver
43 43 status show changed files in the working directory
44 44 summary summarize working directory state
45 45 update update working directory (or switch revisions)
46 46
47 47 $ hg help
48 48 Mercurial Distributed SCM
49 49
50 50 list of commands:
51 51
52 52 add add the specified files on the next commit
53 53 addremove add all new files, delete all missing files
54 54 annotate show changeset information by line for each file
55 55 archive create an unversioned archive of a repository revision
56 56 backout reverse effect of earlier changeset
57 57 bisect subdivision search of changesets
58 58 bookmarks create a new bookmark or list existing bookmarks
59 59 branch set or show the current branch name
60 60 branches list repository named branches
61 61 bundle create a bundle file
62 62 cat output the current or given revision of files
63 63 clone make a copy of an existing repository
64 64 commit commit the specified files or all outstanding changes
65 65 config show combined config settings from all hgrc files
66 66 copy mark files as copied for the next commit
67 67 diff diff repository (or selected files)
68 68 export dump the header and diffs for one or more changesets
69 69 files list tracked files
70 70 forget forget the specified files on the next commit
71 71 graft copy changes from other branches onto the current branch
72 72 grep search revision history for a pattern in specified files
73 73 heads show branch heads
74 74 help show help for a given topic or a help overview
75 75 identify identify the working directory or specified revision
76 76 import import an ordered set of patches
77 77 incoming show new changesets found in source
78 78 init create a new repository in the given directory
79 79 log show revision history of entire repository or files
80 80 manifest output the current or given revision of the project manifest
81 81 merge merge another revision into working directory
82 82 outgoing show changesets not found in the destination
83 83 paths show aliases for remote repositories
84 84 phase set or show the current phase name
85 85 pull pull changes from the specified source
86 86 push push changes to the specified destination
87 87 recover roll back an interrupted transaction
88 88 remove remove the specified files on the next commit
89 89 rename rename files; equivalent of copy + remove
90 90 resolve redo merges or set/view the merge status of files
91 91 revert restore files to their checkout state
92 92 root print the root (top) of the current working directory
93 93 serve start stand-alone webserver
94 94 status show changed files in the working directory
95 95 summary summarize working directory state
96 96 tag add one or more tags for the current or given revision
97 97 tags list repository tags
98 98 unbundle apply one or more bundle files
99 99 update update working directory (or switch revisions)
100 100 verify verify the integrity of the repository
101 101 version output version and copyright information
102 102
103 103 additional help topics:
104 104
105 105 bundlespec Bundle File Formats
106 106 color Colorizing Outputs
107 107 config Configuration Files
108 108 dates Date Formats
109 109 diffs Diff Formats
110 110 environment Environment Variables
111 111 extensions Using Additional Features
112 112 filesets Specifying File Sets
113 113 flags Command-line flags
114 114 glossary Glossary
115 115 hgignore Syntax for Mercurial Ignore Files
116 116 hgweb Configuring hgweb
117 117 internals Technical implementation topics
118 118 merge-tools Merge Tools
119 119 pager Pager Support
120 120 patterns File Name Patterns
121 121 phases Working with Phases
122 122 revisions Specifying Revisions
123 123 scripting Using Mercurial from scripts and automation
124 124 subrepos Subrepositories
125 125 templating Template Usage
126 126 urls URL Paths
127 127
128 128 (use 'hg help -v' to show built-in aliases and global options)
129 129
130 130 $ hg -q help
131 131 add add the specified files on the next commit
132 132 addremove add all new files, delete all missing files
133 133 annotate show changeset information by line for each file
134 134 archive create an unversioned archive of a repository revision
135 135 backout reverse effect of earlier changeset
136 136 bisect subdivision search of changesets
137 137 bookmarks create a new bookmark or list existing bookmarks
138 138 branch set or show the current branch name
139 139 branches list repository named branches
140 140 bundle create a bundle file
141 141 cat output the current or given revision of files
142 142 clone make a copy of an existing repository
143 143 commit commit the specified files or all outstanding changes
144 144 config show combined config settings from all hgrc files
145 145 copy mark files as copied for the next commit
146 146 diff diff repository (or selected files)
147 147 export dump the header and diffs for one or more changesets
148 148 files list tracked files
149 149 forget forget the specified files on the next commit
150 150 graft copy changes from other branches onto the current branch
151 151 grep search revision history for a pattern in specified files
152 152 heads show branch heads
153 153 help show help for a given topic or a help overview
154 154 identify identify the working directory or specified revision
155 155 import import an ordered set of patches
156 156 incoming show new changesets found in source
157 157 init create a new repository in the given directory
158 158 log show revision history of entire repository or files
159 159 manifest output the current or given revision of the project manifest
160 160 merge merge another revision into working directory
161 161 outgoing show changesets not found in the destination
162 162 paths show aliases for remote repositories
163 163 phase set or show the current phase name
164 164 pull pull changes from the specified source
165 165 push push changes to the specified destination
166 166 recover roll back an interrupted transaction
167 167 remove remove the specified files on the next commit
168 168 rename rename files; equivalent of copy + remove
169 169 resolve redo merges or set/view the merge status of files
170 170 revert restore files to their checkout state
171 171 root print the root (top) of the current working directory
172 172 serve start stand-alone webserver
173 173 status show changed files in the working directory
174 174 summary summarize working directory state
175 175 tag add one or more tags for the current or given revision
176 176 tags list repository tags
177 177 unbundle apply one or more bundle files
178 178 update update working directory (or switch revisions)
179 179 verify verify the integrity of the repository
180 180 version output version and copyright information
181 181
182 182 additional help topics:
183 183
184 184 bundlespec Bundle File Formats
185 185 color Colorizing Outputs
186 186 config Configuration Files
187 187 dates Date Formats
188 188 diffs Diff Formats
189 189 environment Environment Variables
190 190 extensions Using Additional Features
191 191 filesets Specifying File Sets
192 192 flags Command-line flags
193 193 glossary Glossary
194 194 hgignore Syntax for Mercurial Ignore Files
195 195 hgweb Configuring hgweb
196 196 internals Technical implementation topics
197 197 merge-tools Merge Tools
198 198 pager Pager Support
199 199 patterns File Name Patterns
200 200 phases Working with Phases
201 201 revisions Specifying Revisions
202 202 scripting Using Mercurial from scripts and automation
203 203 subrepos Subrepositories
204 204 templating Template Usage
205 205 urls URL Paths
206 206
207 207 Test extension help:
208 208 $ hg help extensions --config extensions.rebase= --config extensions.children=
209 209 Using Additional Features
210 210 """""""""""""""""""""""""
211 211
212 212 Mercurial has the ability to add new features through the use of
213 213 extensions. Extensions may add new commands, add options to existing
214 214 commands, change the default behavior of commands, or implement hooks.
215 215
216 216 To enable the "foo" extension, either shipped with Mercurial or in the
217 217 Python search path, create an entry for it in your configuration file,
218 218 like this:
219 219
220 220 [extensions]
221 221 foo =
222 222
223 223 You may also specify the full path to an extension:
224 224
225 225 [extensions]
226 226 myfeature = ~/.hgext/myfeature.py
227 227
228 228 See 'hg help config' for more information on configuration files.
229 229
230 230 Extensions are not loaded by default for a variety of reasons: they can
231 231 increase startup overhead; they may be meant for advanced usage only; they
232 232 may provide potentially dangerous abilities (such as letting you destroy
233 233 or modify history); they might not be ready for prime time; or they may
234 234 alter some usual behaviors of stock Mercurial. It is thus up to the user
235 235 to activate extensions as needed.
236 236
237 237 To explicitly disable an extension enabled in a configuration file of
238 238 broader scope, prepend its path with !:
239 239
240 240 [extensions]
241 241 # disabling extension bar residing in /path/to/extension/bar.py
242 242 bar = !/path/to/extension/bar.py
243 243 # ditto, but no path was supplied for extension baz
244 244 baz = !
245 245
246 246 enabled extensions:
247 247
248 248 children command to display child changesets (DEPRECATED)
249 249 rebase command to move sets of revisions to a different ancestor
250 250
251 251 disabled extensions:
252 252
253 253 acl hooks for controlling repository access
254 254 blackbox log repository events to a blackbox for debugging
255 255 bugzilla hooks for integrating with the Bugzilla bug tracker
256 256 censor erase file content at a given revision
257 257 churn command to display statistics about repository history
258 258 clonebundles advertise pre-generated bundles to seed clones
259 259 convert import revisions from foreign VCS repositories into
260 260 Mercurial
261 261 eol automatically manage newlines in repository files
262 262 extdiff command to allow external programs to compare revisions
263 263 factotum http authentication with factotum
264 264 githelp try mapping git commands to Mercurial commands
265 265 gpg commands to sign and verify changesets
266 266 hgk browse the repository in a graphical way
267 267 highlight syntax highlighting for hgweb (requires Pygments)
268 268 histedit interactive history editing
269 269 keyword expand keywords in tracked files
270 270 largefiles track large binary files
271 271 mq manage a stack of patches
272 272 notify hooks for sending email push notifications
273 273 patchbomb command to send changesets as (a series of) patch emails
274 274 purge command to delete untracked files from the working
275 275 directory
276 276 relink recreates hardlinks between repository clones
277 277 remotenames showing remotebookmarks and remotebranches in UI
278 278 schemes extend schemes with shortcuts to repository swarms
279 279 share share a common history between several working directories
280 280 shelve save and restore changes to the working directory
281 281 strip strip changesets and their descendants from history
282 282 transplant command to transplant changesets from another branch
283 283 win32mbcs allow the use of MBCS paths with problematic encodings
284 284 zeroconf discover and advertise repositories on the local network
285 285
286 286 Verify that extension keywords appear in help templates
287 287
288 288 $ hg help --config extensions.transplant= templating|grep transplant > /dev/null
289 289
290 290 Test short command list with verbose option
291 291
292 292 $ hg -v help shortlist
293 293 Mercurial Distributed SCM
294 294
295 295 basic commands:
296 296
297 297 add add the specified files on the next commit
298 298 annotate, blame
299 299 show changeset information by line for each file
300 300 clone make a copy of an existing repository
301 301 commit, ci commit the specified files or all outstanding changes
302 302 diff diff repository (or selected files)
303 303 export dump the header and diffs for one or more changesets
304 304 forget forget the specified files on the next commit
305 305 init create a new repository in the given directory
306 306 log, history show revision history of entire repository or files
307 307 merge merge another revision into working directory
308 308 pull pull changes from the specified source
309 309 push push changes to the specified destination
310 310 remove, rm remove the specified files on the next commit
311 311 serve start stand-alone webserver
312 312 status, st show changed files in the working directory
313 313 summary, sum summarize working directory state
314 314 update, up, checkout, co
315 315 update working directory (or switch revisions)
316 316
317 317 global options ([+] can be repeated):
318 318
319 319 -R --repository REPO repository root directory or name of overlay bundle
320 320 file
321 321 --cwd DIR change working directory
322 322 -y --noninteractive do not prompt, automatically pick the first choice for
323 323 all prompts
324 324 -q --quiet suppress output
325 325 -v --verbose enable additional output
326 326 --color TYPE when to colorize (boolean, always, auto, never, or
327 327 debug)
328 328 --config CONFIG [+] set/override config option (use 'section.name=value')
329 329 --debug enable debugging output
330 330 --debugger start debugger
331 331 --encoding ENCODE set the charset encoding (default: ascii)
332 332 --encodingmode MODE set the charset encoding mode (default: strict)
333 333 --traceback always print a traceback on exception
334 334 --time time how long the command takes
335 335 --profile print command execution profile
336 336 --version output version information and exit
337 337 -h --help display help and exit
338 338 --hidden consider hidden changesets
339 339 --pager TYPE when to paginate (boolean, always, auto, or never)
340 340 (default: auto)
341 341
342 342 (use 'hg help' for the full list of commands)
343 343
344 344 $ hg add -h
345 345 hg add [OPTION]... [FILE]...
346 346
347 347 add the specified files on the next commit
348 348
349 349 Schedule files to be version controlled and added to the repository.
350 350
351 351 The files will be added to the repository at the next commit. To undo an
352 352 add before that, see 'hg forget'.
353 353
354 354 If no names are given, add all files to the repository (except files
355 355 matching ".hgignore").
356 356
357 357 Returns 0 if all files are successfully added.
358 358
359 359 options ([+] can be repeated):
360 360
361 361 -I --include PATTERN [+] include names matching the given patterns
362 362 -X --exclude PATTERN [+] exclude names matching the given patterns
363 363 -S --subrepos recurse into subrepositories
364 364 -n --dry-run do not perform actions, just print output
365 365
366 366 (some details hidden, use --verbose to show complete help)
367 367
368 368 Verbose help for add
369 369
370 370 $ hg add -hv
371 371 hg add [OPTION]... [FILE]...
372 372
373 373 add the specified files on the next commit
374 374
375 375 Schedule files to be version controlled and added to the repository.
376 376
377 377 The files will be added to the repository at the next commit. To undo an
378 378 add before that, see 'hg forget'.
379 379
380 380 If no names are given, add all files to the repository (except files
381 381 matching ".hgignore").
382 382
383 383 Examples:
384 384
385 385 - New (unknown) files are added automatically by 'hg add':
386 386
387 387 $ ls
388 388 foo.c
389 389 $ hg status
390 390 ? foo.c
391 391 $ hg add
392 392 adding foo.c
393 393 $ hg status
394 394 A foo.c
395 395
396 396 - Specific files to be added can be specified:
397 397
398 398 $ ls
399 399 bar.c foo.c
400 400 $ hg status
401 401 ? bar.c
402 402 ? foo.c
403 403 $ hg add bar.c
404 404 $ hg status
405 405 A bar.c
406 406 ? foo.c
407 407
408 408 Returns 0 if all files are successfully added.
409 409
410 410 options ([+] can be repeated):
411 411
412 412 -I --include PATTERN [+] include names matching the given patterns
413 413 -X --exclude PATTERN [+] exclude names matching the given patterns
414 414 -S --subrepos recurse into subrepositories
415 415 -n --dry-run do not perform actions, just print output
416 416
417 417 global options ([+] can be repeated):
418 418
419 419 -R --repository REPO repository root directory or name of overlay bundle
420 420 file
421 421 --cwd DIR change working directory
422 422 -y --noninteractive do not prompt, automatically pick the first choice for
423 423 all prompts
424 424 -q --quiet suppress output
425 425 -v --verbose enable additional output
426 426 --color TYPE when to colorize (boolean, always, auto, never, or
427 427 debug)
428 428 --config CONFIG [+] set/override config option (use 'section.name=value')
429 429 --debug enable debugging output
430 430 --debugger start debugger
431 431 --encoding ENCODE set the charset encoding (default: ascii)
432 432 --encodingmode MODE set the charset encoding mode (default: strict)
433 433 --traceback always print a traceback on exception
434 434 --time time how long the command takes
435 435 --profile print command execution profile
436 436 --version output version information and exit
437 437 -h --help display help and exit
438 438 --hidden consider hidden changesets
439 439 --pager TYPE when to paginate (boolean, always, auto, or never)
440 440 (default: auto)
441 441
442 442 Test the textwidth config option
443 443
444 444 $ hg root -h --config ui.textwidth=50
445 445 hg root
446 446
447 447 print the root (top) of the current working
448 448 directory
449 449
450 450 Print the root directory of the current
451 451 repository.
452 452
453 453 Returns 0 on success.
454 454
455 455 (some details hidden, use --verbose to show
456 456 complete help)
457 457
458 458 Test help option with version option
459 459
460 460 $ hg add -h --version
461 461 Mercurial Distributed SCM (version *) (glob)
462 462 (see https://mercurial-scm.org for more information)
463 463
464 464 Copyright (C) 2005-* Matt Mackall and others (glob)
465 465 This is free software; see the source for copying conditions. There is NO
466 466 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
467 467
468 468 $ hg add --skjdfks
469 469 hg add: option --skjdfks not recognized
470 470 hg add [OPTION]... [FILE]...
471 471
472 472 add the specified files on the next commit
473 473
474 474 options ([+] can be repeated):
475 475
476 476 -I --include PATTERN [+] include names matching the given patterns
477 477 -X --exclude PATTERN [+] exclude names matching the given patterns
478 478 -S --subrepos recurse into subrepositories
479 479 -n --dry-run do not perform actions, just print output
480 480
481 481 (use 'hg add -h' to show more help)
482 482 [255]
483 483
484 484 Test ambiguous command help
485 485
486 486 $ hg help ad
487 487 list of commands:
488 488
489 489 add add the specified files on the next commit
490 490 addremove add all new files, delete all missing files
491 491
492 492 (use 'hg help -v ad' to show built-in aliases and global options)
493 493
494 494 Test command without options
495 495
496 496 $ hg help verify
497 497 hg verify
498 498
499 499 verify the integrity of the repository
500 500
501 501 Verify the integrity of the current repository.
502 502
503 503 This will perform an extensive check of the repository's integrity,
504 504 validating the hashes and checksums of each entry in the changelog,
505 505 manifest, and tracked files, as well as the integrity of their crosslinks
506 506 and indices.
507 507
508 508 Please see https://mercurial-scm.org/wiki/RepositoryCorruption for more
509 509 information about recovery from corruption of the repository.
510 510
511 511 Returns 0 on success, 1 if errors are encountered.
512 512
513 513 (some details hidden, use --verbose to show complete help)
514 514
515 515 $ hg help diff
516 516 hg diff [OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...
517 517
518 518 diff repository (or selected files)
519 519
520 520 Show differences between revisions for the specified files.
521 521
522 522 Differences between files are shown using the unified diff format.
523 523
524 524 Note:
525 525 'hg diff' may generate unexpected results for merges, as it will
526 526 default to comparing against the working directory's first parent
527 527 changeset if no revisions are specified.
528 528
529 529 When two revision arguments are given, then changes are shown between
530 530 those revisions. If only one revision is specified then that revision is
531 531 compared to the working directory, and, when no revisions are specified,
532 532 the working directory files are compared to its first parent.
533 533
534 534 Alternatively you can specify -c/--change with a revision to see the
535 535 changes in that changeset relative to its first parent.
536 536
537 537 Without the -a/--text option, diff will avoid generating diffs of files it
538 538 detects as binary. With -a, diff will generate a diff anyway, probably
539 539 with undesirable results.
540 540
541 541 Use the -g/--git option to generate diffs in the git extended diff format.
542 542 For more information, read 'hg help diffs'.
543 543
544 544 Returns 0 on success.
545 545
546 546 options ([+] can be repeated):
547 547
548 548 -r --rev REV [+] revision
549 549 -c --change REV change made by revision
550 550 -a --text treat all files as text
551 551 -g --git use git extended diff format
552 552 --binary generate binary diffs in git mode (default)
553 553 --nodates omit dates from diff headers
554 554 --noprefix omit a/ and b/ prefixes from filenames
555 555 -p --show-function show which function each change is in
556 556 --reverse produce a diff that undoes the changes
557 557 -w --ignore-all-space ignore white space when comparing lines
558 558 -b --ignore-space-change ignore changes in the amount of white space
559 559 -B --ignore-blank-lines ignore changes whose lines are all blank
560 560 -Z --ignore-space-at-eol ignore changes in whitespace at EOL
561 561 -U --unified NUM number of lines of context to show
562 562 --stat output diffstat-style summary of changes
563 563 --root DIR produce diffs relative to subdirectory
564 564 -I --include PATTERN [+] include names matching the given patterns
565 565 -X --exclude PATTERN [+] exclude names matching the given patterns
566 566 -S --subrepos recurse into subrepositories
567 567
568 568 (some details hidden, use --verbose to show complete help)
569 569
570 570 $ hg help status
571 571 hg status [OPTION]... [FILE]...
572 572
573 573 aliases: st
574 574
575 575 show changed files in the working directory
576 576
577 577 Show status of files in the repository. If names are given, only files
578 578 that match are shown. Files that are clean or ignored or the source of a
579 579 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
580 580 -C/--copies or -A/--all are given. Unless options described with "show
581 581 only ..." are given, the options -mardu are used.
582 582
583 583 Option -q/--quiet hides untracked (unknown and ignored) files unless
584 584 explicitly requested with -u/--unknown or -i/--ignored.
585 585
586 586 Note:
587 587 'hg status' may appear to disagree with diff if permissions have
588 588 changed or a merge has occurred. The standard diff format does not
589 589 report permission changes and diff only reports changes relative to one
590 590 merge parent.
591 591
592 592 If one revision is given, it is used as the base revision. If two
593 593 revisions are given, the differences between them are shown. The --change
594 594 option can also be used as a shortcut to list the changed files of a
595 595 revision from its first parent.
596 596
597 597 The codes used to show the status of files are:
598 598
599 599 M = modified
600 600 A = added
601 601 R = removed
602 602 C = clean
603 603 ! = missing (deleted by non-hg command, but still tracked)
604 604 ? = not tracked
605 605 I = ignored
606 606 = origin of the previous file (with --copies)
607 607
608 608 Returns 0 on success.
609 609
610 610 options ([+] can be repeated):
611 611
612 612 -A --all show status of all files
613 613 -m --modified show only modified files
614 614 -a --added show only added files
615 615 -r --removed show only removed files
616 616 -d --deleted show only deleted (but tracked) files
617 617 -c --clean show only files without changes
618 618 -u --unknown show only unknown (not tracked) files
619 619 -i --ignored show only ignored files
620 620 -n --no-status hide status prefix
621 621 -C --copies show source of copied files
622 622 -0 --print0 end filenames with NUL, for use with xargs
623 623 --rev REV [+] show difference from revision
624 624 --change REV list the changed files of a revision
625 625 -I --include PATTERN [+] include names matching the given patterns
626 626 -X --exclude PATTERN [+] exclude names matching the given patterns
627 627 -S --subrepos recurse into subrepositories
628 628
629 629 (some details hidden, use --verbose to show complete help)
630 630
631 631 $ hg -q help status
632 632 hg status [OPTION]... [FILE]...
633 633
634 634 show changed files in the working directory
635 635
636 636 $ hg help foo
637 637 abort: no such help topic: foo
638 638 (try 'hg help --keyword foo')
639 639 [255]
640 640
641 641 $ hg skjdfks
642 642 hg: unknown command 'skjdfks'
643 643 Mercurial Distributed SCM
644 644
645 645 basic commands:
646 646
647 647 add add the specified files on the next commit
648 648 annotate show changeset information by line for each file
649 649 clone make a copy of an existing repository
650 650 commit commit the specified files or all outstanding changes
651 651 diff diff repository (or selected files)
652 652 export dump the header and diffs for one or more changesets
653 653 forget forget the specified files on the next commit
654 654 init create a new repository in the given directory
655 655 log show revision history of entire repository or files
656 656 merge merge another revision into working directory
657 657 pull pull changes from the specified source
658 658 push push changes to the specified destination
659 659 remove remove the specified files on the next commit
660 660 serve start stand-alone webserver
661 661 status show changed files in the working directory
662 662 summary summarize working directory state
663 663 update update working directory (or switch revisions)
664 664
665 665 (use 'hg help' for the full list of commands or 'hg -v' for details)
666 666 [255]
667 667
668 668 Typoed command gives suggestion
669 669 $ hg puls
670 670 hg: unknown command 'puls'
671 671 (did you mean one of pull, push?)
672 672 [255]
673 673
674 674 Not enabled extension gets suggested
675 675
676 676 $ hg rebase
677 677 hg: unknown command 'rebase'
678 678 'rebase' is provided by the following extension:
679 679
680 680 rebase command to move sets of revisions to a different ancestor
681 681
682 682 (use 'hg help extensions' for information on enabling extensions)
683 683 [255]
684 684
685 685 Disabled extension gets suggested
686 686 $ hg --config extensions.rebase=! rebase
687 687 hg: unknown command 'rebase'
688 688 'rebase' is provided by the following extension:
689 689
690 690 rebase command to move sets of revisions to a different ancestor
691 691
692 692 (use 'hg help extensions' for information on enabling extensions)
693 693 [255]
694 694
695 695 Make sure that we don't run afoul of the help system thinking that
696 696 this is a section and erroring out weirdly.
697 697
698 698 $ hg .log
699 699 hg: unknown command '.log'
700 700 (did you mean log?)
701 701 [255]
702 702
703 703 $ hg log.
704 704 hg: unknown command 'log.'
705 705 (did you mean log?)
706 706 [255]
707 707 $ hg pu.lh
708 708 hg: unknown command 'pu.lh'
709 709 (did you mean one of pull, push?)
710 710 [255]
711 711
712 712 $ cat > helpext.py <<EOF
713 713 > import os
714 714 > from mercurial import commands, registrar
715 715 >
716 716 > cmdtable = {}
717 717 > command = registrar.command(cmdtable)
718 718 >
719 719 > @command(b'nohelp',
720 720 > [(b'', b'longdesc', 3, b'x'*90),
721 721 > (b'n', b'', None, b'normal desc'),
722 722 > (b'', b'newline', b'', b'line1\nline2')],
723 723 > b'hg nohelp',
724 724 > norepo=True)
725 725 > @command(b'debugoptADV', [(b'', b'aopt', None, b'option is (ADVANCED)')])
726 726 > @command(b'debugoptDEP', [(b'', b'dopt', None, b'option is (DEPRECATED)')])
727 727 > @command(b'debugoptEXP', [(b'', b'eopt', None, b'option is (EXPERIMENTAL)')])
728 728 > def nohelp(ui, *args, **kwargs):
729 729 > pass
730 730 >
731 731 > def uisetup(ui):
732 732 > ui.setconfig(b'alias', b'shellalias', b'!echo hi', b'helpext')
733 733 > ui.setconfig(b'alias', b'hgalias', b'summary', b'helpext')
734 734 >
735 735 > EOF
736 736 $ echo '[extensions]' >> $HGRCPATH
737 737 $ echo "helpext = `pwd`/helpext.py" >> $HGRCPATH
738 738
739 739 Test for aliases
740 740
741 741 $ hg help hgalias
742 742 hg hgalias [--remote]
743 743
744 744 alias for: hg summary
745 745
746 746 summarize working directory state
747 747
748 748 This generates a brief summary of the working directory state, including
749 749 parents, branch, commit status, phase and available updates.
750 750
751 751 With the --remote option, this will check the default paths for incoming
752 752 and outgoing changes. This can be time-consuming.
753 753
754 754 Returns 0 on success.
755 755
756 756 defined by: helpext
757 757
758 758 options:
759 759
760 760 --remote check for push and pull
761 761
762 762 (some details hidden, use --verbose to show complete help)
763 763
764 764 $ hg help shellalias
765 765 hg shellalias
766 766
767 767 shell alias for:
768 768
769 769 echo hi
770 770
771 771 defined by: helpext
772 772
773 773 (some details hidden, use --verbose to show complete help)
774 774
775 775 Test command with no help text
776 776
777 777 $ hg help nohelp
778 778 hg nohelp
779 779
780 780 (no help text available)
781 781
782 782 options:
783 783
784 784 --longdesc VALUE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
785 785 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (default: 3)
786 786 -n -- normal desc
787 787 --newline VALUE line1 line2
788 788
789 789 (some details hidden, use --verbose to show complete help)
790 790
791 791 $ hg help -k nohelp
792 792 Commands:
793 793
794 794 nohelp hg nohelp
795 795
796 796 Extension Commands:
797 797
798 798 nohelp (no help text available)
799 799
800 800 Test that default list of commands omits extension commands
801 801
802 802 $ hg help
803 803 Mercurial Distributed SCM
804 804
805 805 list of commands:
806 806
807 807 add add the specified files on the next commit
808 808 addremove add all new files, delete all missing files
809 809 annotate show changeset information by line for each file
810 810 archive create an unversioned archive of a repository revision
811 811 backout reverse effect of earlier changeset
812 812 bisect subdivision search of changesets
813 813 bookmarks create a new bookmark or list existing bookmarks
814 814 branch set or show the current branch name
815 815 branches list repository named branches
816 816 bundle create a bundle file
817 817 cat output the current or given revision of files
818 818 clone make a copy of an existing repository
819 819 commit commit the specified files or all outstanding changes
820 820 config show combined config settings from all hgrc files
821 821 copy mark files as copied for the next commit
822 822 diff diff repository (or selected files)
823 823 export dump the header and diffs for one or more changesets
824 824 files list tracked files
825 825 forget forget the specified files on the next commit
826 826 graft copy changes from other branches onto the current branch
827 827 grep search revision history for a pattern in specified files
828 828 heads show branch heads
829 829 help show help for a given topic or a help overview
830 830 identify identify the working directory or specified revision
831 831 import import an ordered set of patches
832 832 incoming show new changesets found in source
833 833 init create a new repository in the given directory
834 834 log show revision history of entire repository or files
835 835 manifest output the current or given revision of the project manifest
836 836 merge merge another revision into working directory
837 837 outgoing show changesets not found in the destination
838 838 paths show aliases for remote repositories
839 839 phase set or show the current phase name
840 840 pull pull changes from the specified source
841 841 push push changes to the specified destination
842 842 recover roll back an interrupted transaction
843 843 remove remove the specified files on the next commit
844 844 rename rename files; equivalent of copy + remove
845 845 resolve redo merges or set/view the merge status of files
846 846 revert restore files to their checkout state
847 847 root print the root (top) of the current working directory
848 848 serve start stand-alone webserver
849 849 status show changed files in the working directory
850 850 summary summarize working directory state
851 851 tag add one or more tags for the current or given revision
852 852 tags list repository tags
853 853 unbundle apply one or more bundle files
854 854 update update working directory (or switch revisions)
855 855 verify verify the integrity of the repository
856 856 version output version and copyright information
857 857
858 858 enabled extensions:
859 859
860 860 helpext (no help text available)
861 861
862 862 additional help topics:
863 863
864 864 bundlespec Bundle File Formats
865 865 color Colorizing Outputs
866 866 config Configuration Files
867 867 dates Date Formats
868 868 diffs Diff Formats
869 869 environment Environment Variables
870 870 extensions Using Additional Features
871 871 filesets Specifying File Sets
872 872 flags Command-line flags
873 873 glossary Glossary
874 874 hgignore Syntax for Mercurial Ignore Files
875 875 hgweb Configuring hgweb
876 876 internals Technical implementation topics
877 877 merge-tools Merge Tools
878 878 pager Pager Support
879 879 patterns File Name Patterns
880 880 phases Working with Phases
881 881 revisions Specifying Revisions
882 882 scripting Using Mercurial from scripts and automation
883 883 subrepos Subrepositories
884 884 templating Template Usage
885 885 urls URL Paths
886 886
887 887 (use 'hg help -v' to show built-in aliases and global options)
888 888
889 889
890 890 Test list of internal help commands
891 891
892 892 $ hg help debug
893 893 debug commands (internal and unsupported):
894 894
895 895 debugancestor
896 896 find the ancestor revision of two revisions in a given index
897 897 debugapplystreamclonebundle
898 898 apply a stream clone bundle file
899 899 debugbuilddag
900 900 builds a repo with a given DAG from scratch in the current
901 901 empty repo
902 902 debugbundle lists the contents of a bundle
903 903 debugcapabilities
904 904 lists the capabilities of a remote peer
905 905 debugcheckstate
906 906 validate the correctness of the current dirstate
907 907 debugcolor show available color, effects or style
908 908 debugcommands
909 909 list all available commands and options
910 910 debugcomplete
911 911 returns the completion list associated with the given command
912 912 debugcreatestreamclonebundle
913 913 create a stream clone bundle file
914 914 debugdag format the changelog or an index DAG as a concise textual
915 915 description
916 916 debugdata dump the contents of a data file revision
917 917 debugdate parse and display a date
918 918 debugdeltachain
919 919 dump information about delta chains in a revlog
920 920 debugdirstate
921 921 show the contents of the current dirstate
922 922 debugdiscovery
923 923 runs the changeset discovery protocol in isolation
924 924 debugdownload
925 925 download a resource using Mercurial logic and config
926 926 debugextensions
927 927 show information about active extensions
928 928 debugfileset parse and apply a fileset specification
929 929 debugformat display format information about the current repository
930 930 debugfsinfo show information detected about current filesystem
931 931 debuggetbundle
932 932 retrieves a bundle from a repo
933 933 debugignore display the combined ignore pattern and information about
934 934 ignored files
935 935 debugindex dump the contents of an index file
936 936 debugindexdot
937 937 dump an index DAG as a graphviz dot file
938 938 debuginstall test Mercurial installation
939 939 debugknown test whether node ids are known to a repo
940 940 debuglocks show or modify state of locks
941 941 debugmergestate
942 942 print merge state
943 943 debugnamecomplete
944 944 complete "names" - tags, open branch names, bookmark names
945 945 debugobsolete
946 946 create arbitrary obsolete marker
947 947 debugoptADV (no help text available)
948 948 debugoptDEP (no help text available)
949 949 debugoptEXP (no help text available)
950 950 debugpathcomplete
951 951 complete part or all of a tracked path
952 952 debugpeer establish a connection to a peer repository
953 953 debugpickmergetool
954 954 examine which merge tool is chosen for specified file
955 955 debugpushkey access the pushkey key/value protocol
956 956 debugpvec (no help text available)
957 957 debugrebuilddirstate
958 958 rebuild the dirstate as it would look like for the given
959 959 revision
960 960 debugrebuildfncache
961 961 rebuild the fncache file
962 962 debugrename dump rename information
963 963 debugrevlog show data and statistics about a revlog
964 964 debugrevspec parse and apply a revision specification
965 965 debugsetparents
966 966 manually set the parents of the current working directory
967 967 debugssl test a secure connection to a server
968 968 debugsub (no help text available)
969 969 debugsuccessorssets
970 970 show set of successors for revision
971 971 debugtemplate
972 972 parse and apply a template
973 973 debugupdatecaches
974 974 warm all known caches in the repository
975 975 debugupgraderepo
976 976 upgrade a repository to use different features
977 977 debugwalk show how files match on given patterns
978 978 debugwireargs
979 979 (no help text available)
980 980
981 981 (use 'hg help -v debug' to show built-in aliases and global options)
982 982
983 983 internals topic renders index of available sub-topics
984 984
985 985 $ hg help internals
986 986 Technical implementation topics
987 987 """""""""""""""""""""""""""""""
988 988
989 989 To access a subtopic, use "hg help internals.{subtopic-name}"
990 990
991 991 bundles Bundles
992 992 censor Censor
993 993 changegroups Changegroups
994 994 config Config Registrar
995 995 requirements Repository Requirements
996 996 revlogs Revision Logs
997 997 wireprotocol Wire Protocol
998 998
999 999 sub-topics can be accessed
1000 1000
1001 1001 $ hg help internals.changegroups
1002 1002 Changegroups
1003 1003 """"""""""""
1004 1004
1005 1005 Changegroups are representations of repository revlog data, specifically
1006 1006 the changelog data, root/flat manifest data, treemanifest data, and
1007 1007 filelogs.
1008 1008
1009 1009 There are 3 versions of changegroups: "1", "2", and "3". From a high-
1010 1010 level, versions "1" and "2" are almost exactly the same, with the only
1011 1011 difference being an additional item in the *delta header*. Version "3"
1012 1012 adds support for revlog flags in the *delta header* and optionally
1013 1013 exchanging treemanifests (enabled by setting an option on the
1014 1014 "changegroup" part in the bundle2).
1015 1015
1016 1016 Changegroups when not exchanging treemanifests consist of 3 logical
1017 1017 segments:
1018 1018
1019 1019 +---------------------------------+
1020 1020 | | | |
1021 1021 | changeset | manifest | filelogs |
1022 1022 | | | |
1023 1023 | | | |
1024 1024 +---------------------------------+
1025 1025
1026 1026 When exchanging treemanifests, there are 4 logical segments:
1027 1027
1028 1028 +-------------------------------------------------+
1029 1029 | | | | |
1030 1030 | changeset | root | treemanifests | filelogs |
1031 1031 | | manifest | | |
1032 1032 | | | | |
1033 1033 +-------------------------------------------------+
1034 1034
1035 1035 The principle building block of each segment is a *chunk*. A *chunk* is a
1036 1036 framed piece of data:
1037 1037
1038 1038 +---------------------------------------+
1039 1039 | | |
1040 1040 | length | data |
1041 1041 | (4 bytes) | (<length - 4> bytes) |
1042 1042 | | |
1043 1043 +---------------------------------------+
1044 1044
1045 1045 All integers are big-endian signed integers. Each chunk starts with a
1046 1046 32-bit integer indicating the length of the entire chunk (including the
1047 1047 length field itself).
1048 1048
1049 1049 There is a special case chunk that has a value of 0 for the length
1050 1050 ("0x00000000"). We call this an *empty chunk*.
1051 1051
1052 1052 Delta Groups
1053 1053 ============
1054 1054
1055 1055 A *delta group* expresses the content of a revlog as a series of deltas,
1056 1056 or patches against previous revisions.
1057 1057
1058 1058 Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
1059 1059 to signal the end of the delta group:
1060 1060
1061 1061 +------------------------------------------------------------------------+
1062 1062 | | | | | |
1063 1063 | chunk0 length | chunk0 data | chunk1 length | chunk1 data | 0x0 |
1064 1064 | (4 bytes) | (various) | (4 bytes) | (various) | (4 bytes) |
1065 1065 | | | | | |
1066 1066 +------------------------------------------------------------------------+
1067 1067
1068 1068 Each *chunk*'s data consists of the following:
1069 1069
1070 1070 +---------------------------------------+
1071 1071 | | |
1072 1072 | delta header | delta data |
1073 1073 | (various by version) | (various) |
1074 1074 | | |
1075 1075 +---------------------------------------+
1076 1076
1077 1077 The *delta data* is a series of *delta*s that describe a diff from an
1078 1078 existing entry (either that the recipient already has, or previously
1079 1079 specified in the bundle/changegroup).
1080 1080
1081 1081 The *delta header* is different between versions "1", "2", and "3" of the
1082 1082 changegroup format.
1083 1083
1084 1084 Version 1 (headerlen=80):
1085 1085
1086 1086 +------------------------------------------------------+
1087 1087 | | | | |
1088 1088 | node | p1 node | p2 node | link node |
1089 1089 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
1090 1090 | | | | |
1091 1091 +------------------------------------------------------+
1092 1092
1093 1093 Version 2 (headerlen=100):
1094 1094
1095 1095 +------------------------------------------------------------------+
1096 1096 | | | | | |
1097 1097 | node | p1 node | p2 node | base node | link node |
1098 1098 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
1099 1099 | | | | | |
1100 1100 +------------------------------------------------------------------+
1101 1101
1102 1102 Version 3 (headerlen=102):
1103 1103
1104 1104 +------------------------------------------------------------------------------+
1105 1105 | | | | | | |
1106 1106 | node | p1 node | p2 node | base node | link node | flags |
1107 1107 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (2 bytes) |
1108 1108 | | | | | | |
1109 1109 +------------------------------------------------------------------------------+
1110 1110
1111 1111 The *delta data* consists of "chunklen - 4 - headerlen" bytes, which
1112 1112 contain a series of *delta*s, densely packed (no separators). These deltas
1113 1113 describe a diff from an existing entry (either that the recipient already
1114 1114 has, or previously specified in the bundle/changegroup). The format is
1115 1115 described more fully in "hg help internals.bdiff", but briefly:
1116 1116
1117 1117 +---------------------------------------------------------------+
1118 1118 | | | | |
1119 1119 | start offset | end offset | new length | content |
1120 1120 | (4 bytes) | (4 bytes) | (4 bytes) | (<new length> bytes) |
1121 1121 | | | | |
1122 1122 +---------------------------------------------------------------+
1123 1123
1124 1124 Please note that the length field in the delta data does *not* include
1125 1125 itself.
1126 1126
1127 1127 In version 1, the delta is always applied against the previous node from
1128 1128 the changegroup or the first parent if this is the first entry in the
1129 1129 changegroup.
1130 1130
1131 1131 In version 2 and up, the delta base node is encoded in the entry in the
1132 1132 changegroup. This allows the delta to be expressed against any parent,
1133 1133 which can result in smaller deltas and more efficient encoding of data.
1134 1134
1135 1135 Changeset Segment
1136 1136 =================
1137 1137
1138 1138 The *changeset segment* consists of a single *delta group* holding
1139 1139 changelog data. The *empty chunk* at the end of the *delta group* denotes
1140 1140 the boundary to the *manifest segment*.
1141 1141
1142 1142 Manifest Segment
1143 1143 ================
1144 1144
1145 1145 The *manifest segment* consists of a single *delta group* holding manifest
1146 1146 data. If treemanifests are in use, it contains only the manifest for the
1147 1147 root directory of the repository. Otherwise, it contains the entire
1148 1148 manifest data. The *empty chunk* at the end of the *delta group* denotes
1149 1149 the boundary to the next segment (either the *treemanifests segment* or
1150 1150 the *filelogs segment*, depending on version and the request options).
1151 1151
1152 1152 Treemanifests Segment
1153 1153 ---------------------
1154 1154
1155 1155 The *treemanifests segment* only exists in changegroup version "3", and
1156 1156 only if the 'treemanifest' param is part of the bundle2 changegroup part
1157 1157 (it is not possible to use changegroup version 3 outside of bundle2).
1158 1158 Aside from the filenames in the *treemanifests segment* containing a
1159 1159 trailing "/" character, it behaves identically to the *filelogs segment*
1160 1160 (see below). The final sub-segment is followed by an *empty chunk*
1161 1161 (logically, a sub-segment with filename size 0). This denotes the boundary
1162 1162 to the *filelogs segment*.
1163 1163
1164 1164 Filelogs Segment
1165 1165 ================
1166 1166
1167 1167 The *filelogs segment* consists of multiple sub-segments, each
1168 1168 corresponding to an individual file whose data is being described:
1169 1169
1170 1170 +--------------------------------------------------+
1171 1171 | | | | | |
1172 1172 | filelog0 | filelog1 | filelog2 | ... | 0x0 |
1173 1173 | | | | | (4 bytes) |
1174 1174 | | | | | |
1175 1175 +--------------------------------------------------+
1176 1176
1177 1177 The final filelog sub-segment is followed by an *empty chunk* (logically,
1178 1178 a sub-segment with filename size 0). This denotes the end of the segment
1179 1179 and of the overall changegroup.
1180 1180
1181 1181 Each filelog sub-segment consists of the following:
1182 1182
1183 1183 +------------------------------------------------------+
1184 1184 | | | |
1185 1185 | filename length | filename | delta group |
1186 1186 | (4 bytes) | (<length - 4> bytes) | (various) |
1187 1187 | | | |
1188 1188 +------------------------------------------------------+
1189 1189
1190 1190 That is, a *chunk* consisting of the filename (not terminated or padded)
1191 1191 followed by N chunks constituting the *delta group* for this file. The
1192 1192 *empty chunk* at the end of each *delta group* denotes the boundary to the
1193 1193 next filelog sub-segment.
1194 1194
1195 1195 Test list of commands with command with no help text
1196 1196
1197 1197 $ hg help helpext
1198 1198 helpext extension - no help text available
1199 1199
1200 1200 list of commands:
1201 1201
1202 1202 nohelp (no help text available)
1203 1203
1204 1204 (use 'hg help -v helpext' to show built-in aliases and global options)
1205 1205
1206 1206
1207 1207 test advanced, deprecated and experimental options are hidden in command help
1208 1208 $ hg help debugoptADV
1209 1209 hg debugoptADV
1210 1210
1211 1211 (no help text available)
1212 1212
1213 1213 options:
1214 1214
1215 1215 (some details hidden, use --verbose to show complete help)
1216 1216 $ hg help debugoptDEP
1217 1217 hg debugoptDEP
1218 1218
1219 1219 (no help text available)
1220 1220
1221 1221 options:
1222 1222
1223 1223 (some details hidden, use --verbose to show complete help)
1224 1224
1225 1225 $ hg help debugoptEXP
1226 1226 hg debugoptEXP
1227 1227
1228 1228 (no help text available)
1229 1229
1230 1230 options:
1231 1231
1232 1232 (some details hidden, use --verbose to show complete help)
1233 1233
1234 1234 test advanced, deprecated and experimental options are shown with -v
1235 1235 $ hg help -v debugoptADV | grep aopt
1236 1236 --aopt option is (ADVANCED)
1237 1237 $ hg help -v debugoptDEP | grep dopt
1238 1238 --dopt option is (DEPRECATED)
1239 1239 $ hg help -v debugoptEXP | grep eopt
1240 1240 --eopt option is (EXPERIMENTAL)
1241 1241
1242 1242 #if gettext
1243 1243 test deprecated option is hidden with translation with untranslated description
1244 1244 (use many globy for not failing on changed transaction)
1245 1245 $ LANGUAGE=sv hg help debugoptDEP
1246 1246 hg debugoptDEP
1247 1247
1248 1248 (*) (glob)
1249 1249
1250 1250 options:
1251 1251
1252 1252 (some details hidden, use --verbose to show complete help)
1253 1253 #endif
1254 1254
1255 1255 Test commands that collide with topics (issue4240)
1256 1256
1257 1257 $ hg config -hq
1258 1258 hg config [-u] [NAME]...
1259 1259
1260 1260 show combined config settings from all hgrc files
1261 1261 $ hg showconfig -hq
1262 1262 hg config [-u] [NAME]...
1263 1263
1264 1264 show combined config settings from all hgrc files
1265 1265
1266 1266 Test a help topic
1267 1267
1268 1268 $ hg help dates
1269 1269 Date Formats
1270 1270 """"""""""""
1271 1271
1272 1272 Some commands allow the user to specify a date, e.g.:
1273 1273
1274 1274 - backout, commit, import, tag: Specify the commit date.
1275 1275 - log, revert, update: Select revision(s) by date.
1276 1276
1277 1277 Many date formats are valid. Here are some examples:
1278 1278
1279 1279 - "Wed Dec 6 13:18:29 2006" (local timezone assumed)
1280 1280 - "Dec 6 13:18 -0600" (year assumed, time offset provided)
1281 1281 - "Dec 6 13:18 UTC" (UTC and GMT are aliases for +0000)
1282 1282 - "Dec 6" (midnight)
1283 1283 - "13:18" (today assumed)
1284 1284 - "3:39" (3:39AM assumed)
1285 1285 - "3:39pm" (15:39)
1286 1286 - "2006-12-06 13:18:29" (ISO 8601 format)
1287 1287 - "2006-12-6 13:18"
1288 1288 - "2006-12-6"
1289 1289 - "12-6"
1290 1290 - "12/6"
1291 1291 - "12/6/6" (Dec 6 2006)
1292 1292 - "today" (midnight)
1293 1293 - "yesterday" (midnight)
1294 1294 - "now" - right now
1295 1295
1296 1296 Lastly, there is Mercurial's internal format:
1297 1297
1298 1298 - "1165411109 0" (Wed Dec 6 13:18:29 2006 UTC)
1299 1299
1300 1300 This is the internal representation format for dates. The first number is
1301 1301 the number of seconds since the epoch (1970-01-01 00:00 UTC). The second
1302 1302 is the offset of the local timezone, in seconds west of UTC (negative if
1303 1303 the timezone is east of UTC).
1304 1304
1305 1305 The log command also accepts date ranges:
1306 1306
1307 1307 - "<DATE" - at or before a given date/time
1308 1308 - ">DATE" - on or after a given date/time
1309 1309 - "DATE to DATE" - a date range, inclusive
1310 1310 - "-DAYS" - within a given number of days of today
1311 1311
1312 1312 Test repeated config section name
1313 1313
1314 1314 $ hg help config.host
1315 1315 "http_proxy.host"
1316 1316 Host name and (optional) port of the proxy server, for example
1317 1317 "myproxy:8000".
1318 1318
1319 1319 "smtp.host"
1320 1320 Host name of mail server, e.g. "mail.example.com".
1321 1321
1322 1322 Unrelated trailing paragraphs shouldn't be included
1323 1323
1324 1324 $ hg help config.extramsg | grep '^$'
1325 1325
1326 1326
1327 1327 Test capitalized section name
1328 1328
1329 1329 $ hg help scripting.HGPLAIN > /dev/null
1330 1330
1331 1331 Help subsection:
1332 1332
1333 1333 $ hg help config.charsets |grep "Email example:" > /dev/null
1334 1334 [1]
1335 1335
1336 1336 Show nested definitions
1337 1337 ("profiling.type"[break]"ls"[break]"stat"[break])
1338 1338
1339 1339 $ hg help config.type | egrep '^$'|wc -l
1340 1340 \s*3 (re)
1341 1341
1342 1342 Separate sections from subsections
1343 1343
1344 1344 $ hg help config.format | egrep '^ ("|-)|^\s*$' | uniq
1345 1345 "format"
1346 1346 --------
1347 1347
1348 1348 "usegeneraldelta"
1349 1349
1350 1350 "dotencode"
1351 1351
1352 1352 "usefncache"
1353 1353
1354 1354 "usestore"
1355 1355
1356 1356 "profiling"
1357 1357 -----------
1358 1358
1359 1359 "format"
1360 1360
1361 1361 "progress"
1362 1362 ----------
1363 1363
1364 1364 "format"
1365 1365
1366 1366
1367 1367 Last item in help config.*:
1368 1368
1369 1369 $ hg help config.`hg help config|grep '^ "'| \
1370 1370 > tail -1|sed 's![ "]*!!g'`| \
1371 1371 > grep 'hg help -c config' > /dev/null
1372 1372 [1]
1373 1373
1374 1374 note to use help -c for general hg help config:
1375 1375
1376 1376 $ hg help config |grep 'hg help -c config' > /dev/null
1377 1377
1378 1378 Test templating help
1379 1379
1380 1380 $ hg help templating | egrep '(desc|diffstat|firstline|nonempty) '
1381 1381 desc String. The text of the changeset description.
1382 1382 diffstat String. Statistics of changes with the following format:
1383 1383 firstline Any text. Returns the first line of text.
1384 1384 nonempty Any text. Returns '(none)' if the string is empty.
1385 1385
1386 1386 Test deprecated items
1387 1387
1388 1388 $ hg help -v templating | grep currentbookmark
1389 1389 currentbookmark
1390 1390 $ hg help templating | (grep currentbookmark || true)
1391 1391
1392 1392 Test help hooks
1393 1393
1394 1394 $ cat > helphook1.py <<EOF
1395 1395 > from mercurial import help
1396 1396 >
1397 1397 > def rewrite(ui, topic, doc):
1398 1398 > return doc + '\nhelphook1\n'
1399 1399 >
1400 1400 > def extsetup(ui):
1401 1401 > help.addtopichook('revisions', rewrite)
1402 1402 > EOF
1403 1403 $ cat > helphook2.py <<EOF
1404 1404 > from mercurial import help
1405 1405 >
1406 1406 > def rewrite(ui, topic, doc):
1407 1407 > return doc + '\nhelphook2\n'
1408 1408 >
1409 1409 > def extsetup(ui):
1410 1410 > help.addtopichook('revisions', rewrite)
1411 1411 > EOF
1412 1412 $ echo '[extensions]' >> $HGRCPATH
1413 1413 $ echo "helphook1 = `pwd`/helphook1.py" >> $HGRCPATH
1414 1414 $ echo "helphook2 = `pwd`/helphook2.py" >> $HGRCPATH
1415 1415 $ hg help revsets | grep helphook
1416 1416 helphook1
1417 1417 helphook2
1418 1418
1419 1419 help -c should only show debug --debug
1420 1420
1421 1421 $ hg help -c --debug|egrep debug|wc -l|egrep '^\s*0\s*$'
1422 1422 [1]
1423 1423
1424 1424 help -c should only show deprecated for -v
1425 1425
1426 1426 $ hg help -c -v|egrep DEPRECATED|wc -l|egrep '^\s*0\s*$'
1427 1427 [1]
1428 1428
1429 1429 Test -s / --system
1430 1430
1431 1431 $ hg help config.files -s windows |grep 'etc/mercurial' | \
1432 1432 > wc -l | sed -e 's/ //g'
1433 1433 0
1434 1434 $ hg help config.files --system unix | grep 'USER' | \
1435 1435 > wc -l | sed -e 's/ //g'
1436 1436 0
1437 1437
1438 1438 Test -e / -c / -k combinations
1439 1439
1440 1440 $ hg help -c|egrep '^[A-Z].*:|^ debug'
1441 1441 Commands:
1442 1442 $ hg help -e|egrep '^[A-Z].*:|^ debug'
1443 1443 Extensions:
1444 1444 $ hg help -k|egrep '^[A-Z].*:|^ debug'
1445 1445 Topics:
1446 1446 Commands:
1447 1447 Extensions:
1448 1448 Extension Commands:
1449 1449 $ hg help -c schemes
1450 1450 abort: no such help topic: schemes
1451 1451 (try 'hg help --keyword schemes')
1452 1452 [255]
1453 1453 $ hg help -e schemes |head -1
1454 1454 schemes extension - extend schemes with shortcuts to repository swarms
1455 1455 $ hg help -c -k dates |egrep '^(Topics|Extensions|Commands):'
1456 1456 Commands:
1457 1457 $ hg help -e -k a |egrep '^(Topics|Extensions|Commands):'
1458 1458 Extensions:
1459 1459 $ hg help -e -c -k date |egrep '^(Topics|Extensions|Commands):'
1460 1460 Extensions:
1461 1461 Commands:
1462 1462 $ hg help -c commit > /dev/null
1463 1463 $ hg help -e -c commit > /dev/null
1464 1464 $ hg help -e commit > /dev/null
1465 1465 abort: no such help topic: commit
1466 1466 (try 'hg help --keyword commit')
1467 1467 [255]
1468 1468
1469 1469 Test keyword search help
1470 1470
1471 1471 $ cat > prefixedname.py <<EOF
1472 1472 > '''matched against word "clone"
1473 1473 > '''
1474 1474 > EOF
1475 1475 $ echo '[extensions]' >> $HGRCPATH
1476 1476 $ echo "dot.dot.prefixedname = `pwd`/prefixedname.py" >> $HGRCPATH
1477 1477 $ hg help -k clone
1478 1478 Topics:
1479 1479
1480 1480 config Configuration Files
1481 1481 extensions Using Additional Features
1482 1482 glossary Glossary
1483 1483 phases Working with Phases
1484 1484 subrepos Subrepositories
1485 1485 urls URL Paths
1486 1486
1487 1487 Commands:
1488 1488
1489 1489 bookmarks create a new bookmark or list existing bookmarks
1490 1490 clone make a copy of an existing repository
1491 1491 paths show aliases for remote repositories
1492 1492 update update working directory (or switch revisions)
1493 1493
1494 1494 Extensions:
1495 1495
1496 1496 clonebundles advertise pre-generated bundles to seed clones
1497 narrow create clones which fetch history data for subset of files
1498 (EXPERIMENTAL)
1497 1499 prefixedname matched against word "clone"
1498 1500 relink recreates hardlinks between repository clones
1499 1501
1500 1502 Extension Commands:
1501 1503
1502 1504 qclone clone main and patch repository at same time
1503 1505
1504 1506 Test unfound topic
1505 1507
1506 1508 $ hg help nonexistingtopicthatwillneverexisteverever
1507 1509 abort: no such help topic: nonexistingtopicthatwillneverexisteverever
1508 1510 (try 'hg help --keyword nonexistingtopicthatwillneverexisteverever')
1509 1511 [255]
1510 1512
1511 1513 Test unfound keyword
1512 1514
1513 1515 $ hg help --keyword nonexistingwordthatwillneverexisteverever
1514 1516 abort: no matches
1515 1517 (try 'hg help' for a list of topics)
1516 1518 [255]
1517 1519
1518 1520 Test omit indicating for help
1519 1521
1520 1522 $ cat > addverboseitems.py <<EOF
1521 1523 > '''extension to test omit indicating.
1522 1524 >
1523 1525 > This paragraph is never omitted (for extension)
1524 1526 >
1525 1527 > .. container:: verbose
1526 1528 >
1527 1529 > This paragraph is omitted,
1528 1530 > if :hg:\`help\` is invoked without \`\`-v\`\` (for extension)
1529 1531 >
1530 1532 > This paragraph is never omitted, too (for extension)
1531 1533 > '''
1532 1534 > from __future__ import absolute_import
1533 1535 > from mercurial import commands, help
1534 1536 > testtopic = """This paragraph is never omitted (for topic).
1535 1537 >
1536 1538 > .. container:: verbose
1537 1539 >
1538 1540 > This paragraph is omitted,
1539 1541 > if :hg:\`help\` is invoked without \`\`-v\`\` (for topic)
1540 1542 >
1541 1543 > This paragraph is never omitted, too (for topic)
1542 1544 > """
1543 1545 > def extsetup(ui):
1544 1546 > help.helptable.append((["topic-containing-verbose"],
1545 1547 > "This is the topic to test omit indicating.",
1546 1548 > lambda ui: testtopic))
1547 1549 > EOF
1548 1550 $ echo '[extensions]' >> $HGRCPATH
1549 1551 $ echo "addverboseitems = `pwd`/addverboseitems.py" >> $HGRCPATH
1550 1552 $ hg help addverboseitems
1551 1553 addverboseitems extension - extension to test omit indicating.
1552 1554
1553 1555 This paragraph is never omitted (for extension)
1554 1556
1555 1557 This paragraph is never omitted, too (for extension)
1556 1558
1557 1559 (some details hidden, use --verbose to show complete help)
1558 1560
1559 1561 no commands defined
1560 1562 $ hg help -v addverboseitems
1561 1563 addverboseitems extension - extension to test omit indicating.
1562 1564
1563 1565 This paragraph is never omitted (for extension)
1564 1566
1565 1567 This paragraph is omitted, if 'hg help' is invoked without "-v" (for
1566 1568 extension)
1567 1569
1568 1570 This paragraph is never omitted, too (for extension)
1569 1571
1570 1572 no commands defined
1571 1573 $ hg help topic-containing-verbose
1572 1574 This is the topic to test omit indicating.
1573 1575 """"""""""""""""""""""""""""""""""""""""""
1574 1576
1575 1577 This paragraph is never omitted (for topic).
1576 1578
1577 1579 This paragraph is never omitted, too (for topic)
1578 1580
1579 1581 (some details hidden, use --verbose to show complete help)
1580 1582 $ hg help -v topic-containing-verbose
1581 1583 This is the topic to test omit indicating.
1582 1584 """"""""""""""""""""""""""""""""""""""""""
1583 1585
1584 1586 This paragraph is never omitted (for topic).
1585 1587
1586 1588 This paragraph is omitted, if 'hg help' is invoked without "-v" (for
1587 1589 topic)
1588 1590
1589 1591 This paragraph is never omitted, too (for topic)
1590 1592
1591 1593 Test section lookup
1592 1594
1593 1595 $ hg help revset.merge
1594 1596 "merge()"
1595 1597 Changeset is a merge changeset.
1596 1598
1597 1599 $ hg help glossary.dag
1598 1600 DAG
1599 1601 The repository of changesets of a distributed version control system
1600 1602 (DVCS) can be described as a directed acyclic graph (DAG), consisting
1601 1603 of nodes and edges, where nodes correspond to changesets and edges
1602 1604 imply a parent -> child relation. This graph can be visualized by
1603 1605 graphical tools such as 'hg log --graph'. In Mercurial, the DAG is
1604 1606 limited by the requirement for children to have at most two parents.
1605 1607
1606 1608
1607 1609 $ hg help hgrc.paths
1608 1610 "paths"
1609 1611 -------
1610 1612
1611 1613 Assigns symbolic names and behavior to repositories.
1612 1614
1613 1615 Options are symbolic names defining the URL or directory that is the
1614 1616 location of the repository. Example:
1615 1617
1616 1618 [paths]
1617 1619 my_server = https://example.com/my_repo
1618 1620 local_path = /home/me/repo
1619 1621
1620 1622 These symbolic names can be used from the command line. To pull from
1621 1623 "my_server": 'hg pull my_server'. To push to "local_path": 'hg push
1622 1624 local_path'.
1623 1625
1624 1626 Options containing colons (":") denote sub-options that can influence
1625 1627 behavior for that specific path. Example:
1626 1628
1627 1629 [paths]
1628 1630 my_server = https://example.com/my_path
1629 1631 my_server:pushurl = ssh://example.com/my_path
1630 1632
1631 1633 The following sub-options can be defined:
1632 1634
1633 1635 "pushurl"
1634 1636 The URL to use for push operations. If not defined, the location
1635 1637 defined by the path's main entry is used.
1636 1638
1637 1639 "pushrev"
1638 1640 A revset defining which revisions to push by default.
1639 1641
1640 1642 When 'hg push' is executed without a "-r" argument, the revset defined
1641 1643 by this sub-option is evaluated to determine what to push.
1642 1644
1643 1645 For example, a value of "." will push the working directory's revision
1644 1646 by default.
1645 1647
1646 1648 Revsets specifying bookmarks will not result in the bookmark being
1647 1649 pushed.
1648 1650
1649 1651 The following special named paths exist:
1650 1652
1651 1653 "default"
1652 1654 The URL or directory to use when no source or remote is specified.
1653 1655
1654 1656 'hg clone' will automatically define this path to the location the
1655 1657 repository was cloned from.
1656 1658
1657 1659 "default-push"
1658 1660 (deprecated) The URL or directory for the default 'hg push' location.
1659 1661 "default:pushurl" should be used instead.
1660 1662
1661 1663 $ hg help glossary.mcguffin
1662 1664 abort: help section not found: glossary.mcguffin
1663 1665 [255]
1664 1666
1665 1667 $ hg help glossary.mc.guffin
1666 1668 abort: help section not found: glossary.mc.guffin
1667 1669 [255]
1668 1670
1669 1671 $ hg help template.files
1670 1672 files List of strings. All files modified, added, or removed by
1671 1673 this changeset.
1672 1674 files(pattern)
1673 1675 All files of the current changeset matching the pattern. See
1674 1676 'hg help patterns'.
1675 1677
1676 1678 Test section lookup by translated message
1677 1679
1678 1680 str.lower() instead of encoding.lower(str) on translated message might
1679 1681 make message meaningless, because some encoding uses 0x41(A) - 0x5a(Z)
1680 1682 as the second or later byte of multi-byte character.
1681 1683
1682 1684 For example, "\x8bL\x98^" (translation of "record" in ja_JP.cp932)
1683 1685 contains 0x4c (L). str.lower() replaces 0x4c(L) by 0x6c(l) and this
1684 1686 replacement makes message meaningless.
1685 1687
1686 1688 This tests that section lookup by translated string isn't broken by
1687 1689 such str.lower().
1688 1690
1689 1691 $ $PYTHON <<EOF
1690 1692 > def escape(s):
1691 1693 > return ''.join('\u%x' % ord(uc) for uc in s.decode('cp932'))
1692 1694 > # translation of "record" in ja_JP.cp932
1693 1695 > upper = "\x8bL\x98^"
1694 1696 > # str.lower()-ed section name should be treated as different one
1695 1697 > lower = "\x8bl\x98^"
1696 1698 > with open('ambiguous.py', 'w') as fp:
1697 1699 > fp.write("""# ambiguous section names in ja_JP.cp932
1698 1700 > u'''summary of extension
1699 1701 >
1700 1702 > %s
1701 1703 > ----
1702 1704 >
1703 1705 > Upper name should show only this message
1704 1706 >
1705 1707 > %s
1706 1708 > ----
1707 1709 >
1708 1710 > Lower name should show only this message
1709 1711 >
1710 1712 > subsequent section
1711 1713 > ------------------
1712 1714 >
1713 1715 > This should be hidden at 'hg help ambiguous' with section name.
1714 1716 > '''
1715 1717 > """ % (escape(upper), escape(lower)))
1716 1718 > EOF
1717 1719
1718 1720 $ cat >> $HGRCPATH <<EOF
1719 1721 > [extensions]
1720 1722 > ambiguous = ./ambiguous.py
1721 1723 > EOF
1722 1724
1723 1725 $ $PYTHON <<EOF | sh
1724 1726 > upper = "\x8bL\x98^"
1725 1727 > print("hg --encoding cp932 help -e ambiguous.%s" % upper)
1726 1728 > EOF
1727 1729 \x8bL\x98^ (esc)
1728 1730 ----
1729 1731
1730 1732 Upper name should show only this message
1731 1733
1732 1734
1733 1735 $ $PYTHON <<EOF | sh
1734 1736 > lower = "\x8bl\x98^"
1735 1737 > print("hg --encoding cp932 help -e ambiguous.%s" % lower)
1736 1738 > EOF
1737 1739 \x8bl\x98^ (esc)
1738 1740 ----
1739 1741
1740 1742 Lower name should show only this message
1741 1743
1742 1744
1743 1745 $ cat >> $HGRCPATH <<EOF
1744 1746 > [extensions]
1745 1747 > ambiguous = !
1746 1748 > EOF
1747 1749
1748 1750 Show help content of disabled extensions
1749 1751
1750 1752 $ cat >> $HGRCPATH <<EOF
1751 1753 > [extensions]
1752 1754 > ambiguous = !./ambiguous.py
1753 1755 > EOF
1754 1756 $ hg help -e ambiguous
1755 1757 ambiguous extension - (no help text available)
1756 1758
1757 1759 (use 'hg help extensions' for information on enabling extensions)
1758 1760
1759 1761 Test dynamic list of merge tools only shows up once
1760 1762 $ hg help merge-tools
1761 1763 Merge Tools
1762 1764 """""""""""
1763 1765
1764 1766 To merge files Mercurial uses merge tools.
1765 1767
1766 1768 A merge tool combines two different versions of a file into a merged file.
1767 1769 Merge tools are given the two files and the greatest common ancestor of
1768 1770 the two file versions, so they can determine the changes made on both
1769 1771 branches.
1770 1772
1771 1773 Merge tools are used both for 'hg resolve', 'hg merge', 'hg update', 'hg
1772 1774 backout' and in several extensions.
1773 1775
1774 1776 Usually, the merge tool tries to automatically reconcile the files by
1775 1777 combining all non-overlapping changes that occurred separately in the two
1776 1778 different evolutions of the same initial base file. Furthermore, some
1777 1779 interactive merge programs make it easier to manually resolve conflicting
1778 1780 merges, either in a graphical way, or by inserting some conflict markers.
1779 1781 Mercurial does not include any interactive merge programs but relies on
1780 1782 external tools for that.
1781 1783
1782 1784 Available merge tools
1783 1785 =====================
1784 1786
1785 1787 External merge tools and their properties are configured in the merge-
1786 1788 tools configuration section - see hgrc(5) - but they can often just be
1787 1789 named by their executable.
1788 1790
1789 1791 A merge tool is generally usable if its executable can be found on the
1790 1792 system and if it can handle the merge. The executable is found if it is an
1791 1793 absolute or relative executable path or the name of an application in the
1792 1794 executable search path. The tool is assumed to be able to handle the merge
1793 1795 if it can handle symlinks if the file is a symlink, if it can handle
1794 1796 binary files if the file is binary, and if a GUI is available if the tool
1795 1797 requires a GUI.
1796 1798
1797 1799 There are some internal merge tools which can be used. The internal merge
1798 1800 tools are:
1799 1801
1800 1802 ":dump"
1801 1803 Creates three versions of the files to merge, containing the contents of
1802 1804 local, other and base. These files can then be used to perform a merge
1803 1805 manually. If the file to be merged is named "a.txt", these files will
1804 1806 accordingly be named "a.txt.local", "a.txt.other" and "a.txt.base" and
1805 1807 they will be placed in the same directory as "a.txt".
1806 1808
1807 1809 This implies premerge. Therefore, files aren't dumped, if premerge runs
1808 1810 successfully. Use :forcedump to forcibly write files out.
1809 1811
1810 1812 ":fail"
1811 1813 Rather than attempting to merge files that were modified on both
1812 1814 branches, it marks them as unresolved. The resolve command must be used
1813 1815 to resolve these conflicts.
1814 1816
1815 1817 ":forcedump"
1816 1818 Creates three versions of the files as same as :dump, but omits
1817 1819 premerge.
1818 1820
1819 1821 ":local"
1820 1822 Uses the local 'p1()' version of files as the merged version.
1821 1823
1822 1824 ":merge"
1823 1825 Uses the internal non-interactive simple merge algorithm for merging
1824 1826 files. It will fail if there are any conflicts and leave markers in the
1825 1827 partially merged file. Markers will have two sections, one for each side
1826 1828 of merge.
1827 1829
1828 1830 ":merge-local"
1829 1831 Like :merge, but resolve all conflicts non-interactively in favor of the
1830 1832 local 'p1()' changes.
1831 1833
1832 1834 ":merge-other"
1833 1835 Like :merge, but resolve all conflicts non-interactively in favor of the
1834 1836 other 'p2()' changes.
1835 1837
1836 1838 ":merge3"
1837 1839 Uses the internal non-interactive simple merge algorithm for merging
1838 1840 files. It will fail if there are any conflicts and leave markers in the
1839 1841 partially merged file. Marker will have three sections, one from each
1840 1842 side of the merge and one for the base content.
1841 1843
1842 1844 ":other"
1843 1845 Uses the other 'p2()' version of files as the merged version.
1844 1846
1845 1847 ":prompt"
1846 1848 Asks the user which of the local 'p1()' or the other 'p2()' version to
1847 1849 keep as the merged version.
1848 1850
1849 1851 ":tagmerge"
1850 1852 Uses the internal tag merge algorithm (experimental).
1851 1853
1852 1854 ":union"
1853 1855 Uses the internal non-interactive simple merge algorithm for merging
1854 1856 files. It will use both left and right sides for conflict regions. No
1855 1857 markers are inserted.
1856 1858
1857 1859 Internal tools are always available and do not require a GUI but will by
1858 1860 default not handle symlinks or binary files.
1859 1861
1860 1862 Choosing a merge tool
1861 1863 =====================
1862 1864
1863 1865 Mercurial uses these rules when deciding which merge tool to use:
1864 1866
1865 1867 1. If a tool has been specified with the --tool option to merge or
1866 1868 resolve, it is used. If it is the name of a tool in the merge-tools
1867 1869 configuration, its configuration is used. Otherwise the specified tool
1868 1870 must be executable by the shell.
1869 1871 2. If the "HGMERGE" environment variable is present, its value is used and
1870 1872 must be executable by the shell.
1871 1873 3. If the filename of the file to be merged matches any of the patterns in
1872 1874 the merge-patterns configuration section, the first usable merge tool
1873 1875 corresponding to a matching pattern is used. Here, binary capabilities
1874 1876 of the merge tool are not considered.
1875 1877 4. If ui.merge is set it will be considered next. If the value is not the
1876 1878 name of a configured tool, the specified value is used and must be
1877 1879 executable by the shell. Otherwise the named tool is used if it is
1878 1880 usable.
1879 1881 5. If any usable merge tools are present in the merge-tools configuration
1880 1882 section, the one with the highest priority is used.
1881 1883 6. If a program named "hgmerge" can be found on the system, it is used -
1882 1884 but it will by default not be used for symlinks and binary files.
1883 1885 7. If the file to be merged is not binary and is not a symlink, then
1884 1886 internal ":merge" is used.
1885 1887 8. Otherwise, ":prompt" is used.
1886 1888
1887 1889 Note:
1888 1890 After selecting a merge program, Mercurial will by default attempt to
1889 1891 merge the files using a simple merge algorithm first. Only if it
1890 1892 doesn't succeed because of conflicting changes will Mercurial actually
1891 1893 execute the merge program. Whether to use the simple merge algorithm
1892 1894 first can be controlled by the premerge setting of the merge tool.
1893 1895 Premerge is enabled by default unless the file is binary or a symlink.
1894 1896
1895 1897 See the merge-tools and ui sections of hgrc(5) for details on the
1896 1898 configuration of merge tools.
1897 1899
1898 1900 Compression engines listed in `hg help bundlespec`
1899 1901
1900 1902 $ hg help bundlespec | grep gzip
1901 1903 "v1" bundles can only use the "gzip", "bzip2", and "none" compression
1902 1904 An algorithm that produces smaller bundles than "gzip".
1903 1905 This engine will likely produce smaller bundles than "gzip" but will be
1904 1906 "gzip"
1905 1907 better compression than "gzip". It also frequently yields better (?)
1906 1908
1907 1909 Test usage of section marks in help documents
1908 1910
1909 1911 $ cd "$TESTDIR"/../doc
1910 1912 $ $PYTHON check-seclevel.py
1911 1913 $ cd $TESTTMP
1912 1914
1913 1915 #if serve
1914 1916
1915 1917 Test the help pages in hgweb.
1916 1918
1917 1919 Dish up an empty repo; serve it cold.
1918 1920
1919 1921 $ hg init "$TESTTMP/test"
1920 1922 $ hg serve -R "$TESTTMP/test" -n test -p $HGPORT -d --pid-file=hg.pid
1921 1923 $ cat hg.pid >> $DAEMON_PIDS
1922 1924
1923 1925 $ get-with-headers.py $LOCALIP:$HGPORT "help"
1924 1926 200 Script output follows
1925 1927
1926 1928 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1927 1929 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
1928 1930 <head>
1929 1931 <link rel="icon" href="/static/hgicon.png" type="image/png" />
1930 1932 <meta name="robots" content="index, nofollow" />
1931 1933 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
1932 1934 <script type="text/javascript" src="/static/mercurial.js"></script>
1933 1935
1934 1936 <title>Help: Index</title>
1935 1937 </head>
1936 1938 <body>
1937 1939
1938 1940 <div class="container">
1939 1941 <div class="menu">
1940 1942 <div class="logo">
1941 1943 <a href="https://mercurial-scm.org/">
1942 1944 <img src="/static/hglogo.png" alt="mercurial" /></a>
1943 1945 </div>
1944 1946 <ul>
1945 1947 <li><a href="/shortlog">log</a></li>
1946 1948 <li><a href="/graph">graph</a></li>
1947 1949 <li><a href="/tags">tags</a></li>
1948 1950 <li><a href="/bookmarks">bookmarks</a></li>
1949 1951 <li><a href="/branches">branches</a></li>
1950 1952 </ul>
1951 1953 <ul>
1952 1954 <li class="active">help</li>
1953 1955 </ul>
1954 1956 </div>
1955 1957
1956 1958 <div class="main">
1957 1959 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
1958 1960
1959 1961 <form class="search" action="/log">
1960 1962
1961 1963 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
1962 1964 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
1963 1965 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
1964 1966 </form>
1965 1967 <table class="bigtable">
1966 1968 <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr>
1967 1969
1968 1970 <tr><td>
1969 1971 <a href="/help/bundlespec">
1970 1972 bundlespec
1971 1973 </a>
1972 1974 </td><td>
1973 1975 Bundle File Formats
1974 1976 </td></tr>
1975 1977 <tr><td>
1976 1978 <a href="/help/color">
1977 1979 color
1978 1980 </a>
1979 1981 </td><td>
1980 1982 Colorizing Outputs
1981 1983 </td></tr>
1982 1984 <tr><td>
1983 1985 <a href="/help/config">
1984 1986 config
1985 1987 </a>
1986 1988 </td><td>
1987 1989 Configuration Files
1988 1990 </td></tr>
1989 1991 <tr><td>
1990 1992 <a href="/help/dates">
1991 1993 dates
1992 1994 </a>
1993 1995 </td><td>
1994 1996 Date Formats
1995 1997 </td></tr>
1996 1998 <tr><td>
1997 1999 <a href="/help/diffs">
1998 2000 diffs
1999 2001 </a>
2000 2002 </td><td>
2001 2003 Diff Formats
2002 2004 </td></tr>
2003 2005 <tr><td>
2004 2006 <a href="/help/environment">
2005 2007 environment
2006 2008 </a>
2007 2009 </td><td>
2008 2010 Environment Variables
2009 2011 </td></tr>
2010 2012 <tr><td>
2011 2013 <a href="/help/extensions">
2012 2014 extensions
2013 2015 </a>
2014 2016 </td><td>
2015 2017 Using Additional Features
2016 2018 </td></tr>
2017 2019 <tr><td>
2018 2020 <a href="/help/filesets">
2019 2021 filesets
2020 2022 </a>
2021 2023 </td><td>
2022 2024 Specifying File Sets
2023 2025 </td></tr>
2024 2026 <tr><td>
2025 2027 <a href="/help/flags">
2026 2028 flags
2027 2029 </a>
2028 2030 </td><td>
2029 2031 Command-line flags
2030 2032 </td></tr>
2031 2033 <tr><td>
2032 2034 <a href="/help/glossary">
2033 2035 glossary
2034 2036 </a>
2035 2037 </td><td>
2036 2038 Glossary
2037 2039 </td></tr>
2038 2040 <tr><td>
2039 2041 <a href="/help/hgignore">
2040 2042 hgignore
2041 2043 </a>
2042 2044 </td><td>
2043 2045 Syntax for Mercurial Ignore Files
2044 2046 </td></tr>
2045 2047 <tr><td>
2046 2048 <a href="/help/hgweb">
2047 2049 hgweb
2048 2050 </a>
2049 2051 </td><td>
2050 2052 Configuring hgweb
2051 2053 </td></tr>
2052 2054 <tr><td>
2053 2055 <a href="/help/internals">
2054 2056 internals
2055 2057 </a>
2056 2058 </td><td>
2057 2059 Technical implementation topics
2058 2060 </td></tr>
2059 2061 <tr><td>
2060 2062 <a href="/help/merge-tools">
2061 2063 merge-tools
2062 2064 </a>
2063 2065 </td><td>
2064 2066 Merge Tools
2065 2067 </td></tr>
2066 2068 <tr><td>
2067 2069 <a href="/help/pager">
2068 2070 pager
2069 2071 </a>
2070 2072 </td><td>
2071 2073 Pager Support
2072 2074 </td></tr>
2073 2075 <tr><td>
2074 2076 <a href="/help/patterns">
2075 2077 patterns
2076 2078 </a>
2077 2079 </td><td>
2078 2080 File Name Patterns
2079 2081 </td></tr>
2080 2082 <tr><td>
2081 2083 <a href="/help/phases">
2082 2084 phases
2083 2085 </a>
2084 2086 </td><td>
2085 2087 Working with Phases
2086 2088 </td></tr>
2087 2089 <tr><td>
2088 2090 <a href="/help/revisions">
2089 2091 revisions
2090 2092 </a>
2091 2093 </td><td>
2092 2094 Specifying Revisions
2093 2095 </td></tr>
2094 2096 <tr><td>
2095 2097 <a href="/help/scripting">
2096 2098 scripting
2097 2099 </a>
2098 2100 </td><td>
2099 2101 Using Mercurial from scripts and automation
2100 2102 </td></tr>
2101 2103 <tr><td>
2102 2104 <a href="/help/subrepos">
2103 2105 subrepos
2104 2106 </a>
2105 2107 </td><td>
2106 2108 Subrepositories
2107 2109 </td></tr>
2108 2110 <tr><td>
2109 2111 <a href="/help/templating">
2110 2112 templating
2111 2113 </a>
2112 2114 </td><td>
2113 2115 Template Usage
2114 2116 </td></tr>
2115 2117 <tr><td>
2116 2118 <a href="/help/urls">
2117 2119 urls
2118 2120 </a>
2119 2121 </td><td>
2120 2122 URL Paths
2121 2123 </td></tr>
2122 2124 <tr><td>
2123 2125 <a href="/help/topic-containing-verbose">
2124 2126 topic-containing-verbose
2125 2127 </a>
2126 2128 </td><td>
2127 2129 This is the topic to test omit indicating.
2128 2130 </td></tr>
2129 2131
2130 2132
2131 2133 <tr><td colspan="2"><h2><a name="main" href="#main">Main Commands</a></h2></td></tr>
2132 2134
2133 2135 <tr><td>
2134 2136 <a href="/help/add">
2135 2137 add
2136 2138 </a>
2137 2139 </td><td>
2138 2140 add the specified files on the next commit
2139 2141 </td></tr>
2140 2142 <tr><td>
2141 2143 <a href="/help/annotate">
2142 2144 annotate
2143 2145 </a>
2144 2146 </td><td>
2145 2147 show changeset information by line for each file
2146 2148 </td></tr>
2147 2149 <tr><td>
2148 2150 <a href="/help/clone">
2149 2151 clone
2150 2152 </a>
2151 2153 </td><td>
2152 2154 make a copy of an existing repository
2153 2155 </td></tr>
2154 2156 <tr><td>
2155 2157 <a href="/help/commit">
2156 2158 commit
2157 2159 </a>
2158 2160 </td><td>
2159 2161 commit the specified files or all outstanding changes
2160 2162 </td></tr>
2161 2163 <tr><td>
2162 2164 <a href="/help/diff">
2163 2165 diff
2164 2166 </a>
2165 2167 </td><td>
2166 2168 diff repository (or selected files)
2167 2169 </td></tr>
2168 2170 <tr><td>
2169 2171 <a href="/help/export">
2170 2172 export
2171 2173 </a>
2172 2174 </td><td>
2173 2175 dump the header and diffs for one or more changesets
2174 2176 </td></tr>
2175 2177 <tr><td>
2176 2178 <a href="/help/forget">
2177 2179 forget
2178 2180 </a>
2179 2181 </td><td>
2180 2182 forget the specified files on the next commit
2181 2183 </td></tr>
2182 2184 <tr><td>
2183 2185 <a href="/help/init">
2184 2186 init
2185 2187 </a>
2186 2188 </td><td>
2187 2189 create a new repository in the given directory
2188 2190 </td></tr>
2189 2191 <tr><td>
2190 2192 <a href="/help/log">
2191 2193 log
2192 2194 </a>
2193 2195 </td><td>
2194 2196 show revision history of entire repository or files
2195 2197 </td></tr>
2196 2198 <tr><td>
2197 2199 <a href="/help/merge">
2198 2200 merge
2199 2201 </a>
2200 2202 </td><td>
2201 2203 merge another revision into working directory
2202 2204 </td></tr>
2203 2205 <tr><td>
2204 2206 <a href="/help/pull">
2205 2207 pull
2206 2208 </a>
2207 2209 </td><td>
2208 2210 pull changes from the specified source
2209 2211 </td></tr>
2210 2212 <tr><td>
2211 2213 <a href="/help/push">
2212 2214 push
2213 2215 </a>
2214 2216 </td><td>
2215 2217 push changes to the specified destination
2216 2218 </td></tr>
2217 2219 <tr><td>
2218 2220 <a href="/help/remove">
2219 2221 remove
2220 2222 </a>
2221 2223 </td><td>
2222 2224 remove the specified files on the next commit
2223 2225 </td></tr>
2224 2226 <tr><td>
2225 2227 <a href="/help/serve">
2226 2228 serve
2227 2229 </a>
2228 2230 </td><td>
2229 2231 start stand-alone webserver
2230 2232 </td></tr>
2231 2233 <tr><td>
2232 2234 <a href="/help/status">
2233 2235 status
2234 2236 </a>
2235 2237 </td><td>
2236 2238 show changed files in the working directory
2237 2239 </td></tr>
2238 2240 <tr><td>
2239 2241 <a href="/help/summary">
2240 2242 summary
2241 2243 </a>
2242 2244 </td><td>
2243 2245 summarize working directory state
2244 2246 </td></tr>
2245 2247 <tr><td>
2246 2248 <a href="/help/update">
2247 2249 update
2248 2250 </a>
2249 2251 </td><td>
2250 2252 update working directory (or switch revisions)
2251 2253 </td></tr>
2252 2254
2253 2255
2254 2256
2255 2257 <tr><td colspan="2"><h2><a name="other" href="#other">Other Commands</a></h2></td></tr>
2256 2258
2257 2259 <tr><td>
2258 2260 <a href="/help/addremove">
2259 2261 addremove
2260 2262 </a>
2261 2263 </td><td>
2262 2264 add all new files, delete all missing files
2263 2265 </td></tr>
2264 2266 <tr><td>
2265 2267 <a href="/help/archive">
2266 2268 archive
2267 2269 </a>
2268 2270 </td><td>
2269 2271 create an unversioned archive of a repository revision
2270 2272 </td></tr>
2271 2273 <tr><td>
2272 2274 <a href="/help/backout">
2273 2275 backout
2274 2276 </a>
2275 2277 </td><td>
2276 2278 reverse effect of earlier changeset
2277 2279 </td></tr>
2278 2280 <tr><td>
2279 2281 <a href="/help/bisect">
2280 2282 bisect
2281 2283 </a>
2282 2284 </td><td>
2283 2285 subdivision search of changesets
2284 2286 </td></tr>
2285 2287 <tr><td>
2286 2288 <a href="/help/bookmarks">
2287 2289 bookmarks
2288 2290 </a>
2289 2291 </td><td>
2290 2292 create a new bookmark or list existing bookmarks
2291 2293 </td></tr>
2292 2294 <tr><td>
2293 2295 <a href="/help/branch">
2294 2296 branch
2295 2297 </a>
2296 2298 </td><td>
2297 2299 set or show the current branch name
2298 2300 </td></tr>
2299 2301 <tr><td>
2300 2302 <a href="/help/branches">
2301 2303 branches
2302 2304 </a>
2303 2305 </td><td>
2304 2306 list repository named branches
2305 2307 </td></tr>
2306 2308 <tr><td>
2307 2309 <a href="/help/bundle">
2308 2310 bundle
2309 2311 </a>
2310 2312 </td><td>
2311 2313 create a bundle file
2312 2314 </td></tr>
2313 2315 <tr><td>
2314 2316 <a href="/help/cat">
2315 2317 cat
2316 2318 </a>
2317 2319 </td><td>
2318 2320 output the current or given revision of files
2319 2321 </td></tr>
2320 2322 <tr><td>
2321 2323 <a href="/help/config">
2322 2324 config
2323 2325 </a>
2324 2326 </td><td>
2325 2327 show combined config settings from all hgrc files
2326 2328 </td></tr>
2327 2329 <tr><td>
2328 2330 <a href="/help/copy">
2329 2331 copy
2330 2332 </a>
2331 2333 </td><td>
2332 2334 mark files as copied for the next commit
2333 2335 </td></tr>
2334 2336 <tr><td>
2335 2337 <a href="/help/files">
2336 2338 files
2337 2339 </a>
2338 2340 </td><td>
2339 2341 list tracked files
2340 2342 </td></tr>
2341 2343 <tr><td>
2342 2344 <a href="/help/graft">
2343 2345 graft
2344 2346 </a>
2345 2347 </td><td>
2346 2348 copy changes from other branches onto the current branch
2347 2349 </td></tr>
2348 2350 <tr><td>
2349 2351 <a href="/help/grep">
2350 2352 grep
2351 2353 </a>
2352 2354 </td><td>
2353 2355 search revision history for a pattern in specified files
2354 2356 </td></tr>
2355 2357 <tr><td>
2356 2358 <a href="/help/heads">
2357 2359 heads
2358 2360 </a>
2359 2361 </td><td>
2360 2362 show branch heads
2361 2363 </td></tr>
2362 2364 <tr><td>
2363 2365 <a href="/help/help">
2364 2366 help
2365 2367 </a>
2366 2368 </td><td>
2367 2369 show help for a given topic or a help overview
2368 2370 </td></tr>
2369 2371 <tr><td>
2370 2372 <a href="/help/hgalias">
2371 2373 hgalias
2372 2374 </a>
2373 2375 </td><td>
2374 2376 summarize working directory state
2375 2377 </td></tr>
2376 2378 <tr><td>
2377 2379 <a href="/help/identify">
2378 2380 identify
2379 2381 </a>
2380 2382 </td><td>
2381 2383 identify the working directory or specified revision
2382 2384 </td></tr>
2383 2385 <tr><td>
2384 2386 <a href="/help/import">
2385 2387 import
2386 2388 </a>
2387 2389 </td><td>
2388 2390 import an ordered set of patches
2389 2391 </td></tr>
2390 2392 <tr><td>
2391 2393 <a href="/help/incoming">
2392 2394 incoming
2393 2395 </a>
2394 2396 </td><td>
2395 2397 show new changesets found in source
2396 2398 </td></tr>
2397 2399 <tr><td>
2398 2400 <a href="/help/manifest">
2399 2401 manifest
2400 2402 </a>
2401 2403 </td><td>
2402 2404 output the current or given revision of the project manifest
2403 2405 </td></tr>
2404 2406 <tr><td>
2405 2407 <a href="/help/nohelp">
2406 2408 nohelp
2407 2409 </a>
2408 2410 </td><td>
2409 2411 (no help text available)
2410 2412 </td></tr>
2411 2413 <tr><td>
2412 2414 <a href="/help/outgoing">
2413 2415 outgoing
2414 2416 </a>
2415 2417 </td><td>
2416 2418 show changesets not found in the destination
2417 2419 </td></tr>
2418 2420 <tr><td>
2419 2421 <a href="/help/paths">
2420 2422 paths
2421 2423 </a>
2422 2424 </td><td>
2423 2425 show aliases for remote repositories
2424 2426 </td></tr>
2425 2427 <tr><td>
2426 2428 <a href="/help/phase">
2427 2429 phase
2428 2430 </a>
2429 2431 </td><td>
2430 2432 set or show the current phase name
2431 2433 </td></tr>
2432 2434 <tr><td>
2433 2435 <a href="/help/recover">
2434 2436 recover
2435 2437 </a>
2436 2438 </td><td>
2437 2439 roll back an interrupted transaction
2438 2440 </td></tr>
2439 2441 <tr><td>
2440 2442 <a href="/help/rename">
2441 2443 rename
2442 2444 </a>
2443 2445 </td><td>
2444 2446 rename files; equivalent of copy + remove
2445 2447 </td></tr>
2446 2448 <tr><td>
2447 2449 <a href="/help/resolve">
2448 2450 resolve
2449 2451 </a>
2450 2452 </td><td>
2451 2453 redo merges or set/view the merge status of files
2452 2454 </td></tr>
2453 2455 <tr><td>
2454 2456 <a href="/help/revert">
2455 2457 revert
2456 2458 </a>
2457 2459 </td><td>
2458 2460 restore files to their checkout state
2459 2461 </td></tr>
2460 2462 <tr><td>
2461 2463 <a href="/help/root">
2462 2464 root
2463 2465 </a>
2464 2466 </td><td>
2465 2467 print the root (top) of the current working directory
2466 2468 </td></tr>
2467 2469 <tr><td>
2468 2470 <a href="/help/shellalias">
2469 2471 shellalias
2470 2472 </a>
2471 2473 </td><td>
2472 2474 (no help text available)
2473 2475 </td></tr>
2474 2476 <tr><td>
2475 2477 <a href="/help/tag">
2476 2478 tag
2477 2479 </a>
2478 2480 </td><td>
2479 2481 add one or more tags for the current or given revision
2480 2482 </td></tr>
2481 2483 <tr><td>
2482 2484 <a href="/help/tags">
2483 2485 tags
2484 2486 </a>
2485 2487 </td><td>
2486 2488 list repository tags
2487 2489 </td></tr>
2488 2490 <tr><td>
2489 2491 <a href="/help/unbundle">
2490 2492 unbundle
2491 2493 </a>
2492 2494 </td><td>
2493 2495 apply one or more bundle files
2494 2496 </td></tr>
2495 2497 <tr><td>
2496 2498 <a href="/help/verify">
2497 2499 verify
2498 2500 </a>
2499 2501 </td><td>
2500 2502 verify the integrity of the repository
2501 2503 </td></tr>
2502 2504 <tr><td>
2503 2505 <a href="/help/version">
2504 2506 version
2505 2507 </a>
2506 2508 </td><td>
2507 2509 output version and copyright information
2508 2510 </td></tr>
2509 2511
2510 2512
2511 2513 </table>
2512 2514 </div>
2513 2515 </div>
2514 2516
2515 2517
2516 2518
2517 2519 </body>
2518 2520 </html>
2519 2521
2520 2522
2521 2523 $ get-with-headers.py $LOCALIP:$HGPORT "help/add"
2522 2524 200 Script output follows
2523 2525
2524 2526 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2525 2527 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2526 2528 <head>
2527 2529 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2528 2530 <meta name="robots" content="index, nofollow" />
2529 2531 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2530 2532 <script type="text/javascript" src="/static/mercurial.js"></script>
2531 2533
2532 2534 <title>Help: add</title>
2533 2535 </head>
2534 2536 <body>
2535 2537
2536 2538 <div class="container">
2537 2539 <div class="menu">
2538 2540 <div class="logo">
2539 2541 <a href="https://mercurial-scm.org/">
2540 2542 <img src="/static/hglogo.png" alt="mercurial" /></a>
2541 2543 </div>
2542 2544 <ul>
2543 2545 <li><a href="/shortlog">log</a></li>
2544 2546 <li><a href="/graph">graph</a></li>
2545 2547 <li><a href="/tags">tags</a></li>
2546 2548 <li><a href="/bookmarks">bookmarks</a></li>
2547 2549 <li><a href="/branches">branches</a></li>
2548 2550 </ul>
2549 2551 <ul>
2550 2552 <li class="active"><a href="/help">help</a></li>
2551 2553 </ul>
2552 2554 </div>
2553 2555
2554 2556 <div class="main">
2555 2557 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2556 2558 <h3>Help: add</h3>
2557 2559
2558 2560 <form class="search" action="/log">
2559 2561
2560 2562 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
2561 2563 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2562 2564 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2563 2565 </form>
2564 2566 <div id="doc">
2565 2567 <p>
2566 2568 hg add [OPTION]... [FILE]...
2567 2569 </p>
2568 2570 <p>
2569 2571 add the specified files on the next commit
2570 2572 </p>
2571 2573 <p>
2572 2574 Schedule files to be version controlled and added to the
2573 2575 repository.
2574 2576 </p>
2575 2577 <p>
2576 2578 The files will be added to the repository at the next commit. To
2577 2579 undo an add before that, see 'hg forget'.
2578 2580 </p>
2579 2581 <p>
2580 2582 If no names are given, add all files to the repository (except
2581 2583 files matching &quot;.hgignore&quot;).
2582 2584 </p>
2583 2585 <p>
2584 2586 Examples:
2585 2587 </p>
2586 2588 <ul>
2587 2589 <li> New (unknown) files are added automatically by 'hg add':
2588 2590 <pre>
2589 2591 \$ ls (re)
2590 2592 foo.c
2591 2593 \$ hg status (re)
2592 2594 ? foo.c
2593 2595 \$ hg add (re)
2594 2596 adding foo.c
2595 2597 \$ hg status (re)
2596 2598 A foo.c
2597 2599 </pre>
2598 2600 <li> Specific files to be added can be specified:
2599 2601 <pre>
2600 2602 \$ ls (re)
2601 2603 bar.c foo.c
2602 2604 \$ hg status (re)
2603 2605 ? bar.c
2604 2606 ? foo.c
2605 2607 \$ hg add bar.c (re)
2606 2608 \$ hg status (re)
2607 2609 A bar.c
2608 2610 ? foo.c
2609 2611 </pre>
2610 2612 </ul>
2611 2613 <p>
2612 2614 Returns 0 if all files are successfully added.
2613 2615 </p>
2614 2616 <p>
2615 2617 options ([+] can be repeated):
2616 2618 </p>
2617 2619 <table>
2618 2620 <tr><td>-I</td>
2619 2621 <td>--include PATTERN [+]</td>
2620 2622 <td>include names matching the given patterns</td></tr>
2621 2623 <tr><td>-X</td>
2622 2624 <td>--exclude PATTERN [+]</td>
2623 2625 <td>exclude names matching the given patterns</td></tr>
2624 2626 <tr><td>-S</td>
2625 2627 <td>--subrepos</td>
2626 2628 <td>recurse into subrepositories</td></tr>
2627 2629 <tr><td>-n</td>
2628 2630 <td>--dry-run</td>
2629 2631 <td>do not perform actions, just print output</td></tr>
2630 2632 </table>
2631 2633 <p>
2632 2634 global options ([+] can be repeated):
2633 2635 </p>
2634 2636 <table>
2635 2637 <tr><td>-R</td>
2636 2638 <td>--repository REPO</td>
2637 2639 <td>repository root directory or name of overlay bundle file</td></tr>
2638 2640 <tr><td></td>
2639 2641 <td>--cwd DIR</td>
2640 2642 <td>change working directory</td></tr>
2641 2643 <tr><td>-y</td>
2642 2644 <td>--noninteractive</td>
2643 2645 <td>do not prompt, automatically pick the first choice for all prompts</td></tr>
2644 2646 <tr><td>-q</td>
2645 2647 <td>--quiet</td>
2646 2648 <td>suppress output</td></tr>
2647 2649 <tr><td>-v</td>
2648 2650 <td>--verbose</td>
2649 2651 <td>enable additional output</td></tr>
2650 2652 <tr><td></td>
2651 2653 <td>--color TYPE</td>
2652 2654 <td>when to colorize (boolean, always, auto, never, or debug)</td></tr>
2653 2655 <tr><td></td>
2654 2656 <td>--config CONFIG [+]</td>
2655 2657 <td>set/override config option (use 'section.name=value')</td></tr>
2656 2658 <tr><td></td>
2657 2659 <td>--debug</td>
2658 2660 <td>enable debugging output</td></tr>
2659 2661 <tr><td></td>
2660 2662 <td>--debugger</td>
2661 2663 <td>start debugger</td></tr>
2662 2664 <tr><td></td>
2663 2665 <td>--encoding ENCODE</td>
2664 2666 <td>set the charset encoding (default: ascii)</td></tr>
2665 2667 <tr><td></td>
2666 2668 <td>--encodingmode MODE</td>
2667 2669 <td>set the charset encoding mode (default: strict)</td></tr>
2668 2670 <tr><td></td>
2669 2671 <td>--traceback</td>
2670 2672 <td>always print a traceback on exception</td></tr>
2671 2673 <tr><td></td>
2672 2674 <td>--time</td>
2673 2675 <td>time how long the command takes</td></tr>
2674 2676 <tr><td></td>
2675 2677 <td>--profile</td>
2676 2678 <td>print command execution profile</td></tr>
2677 2679 <tr><td></td>
2678 2680 <td>--version</td>
2679 2681 <td>output version information and exit</td></tr>
2680 2682 <tr><td>-h</td>
2681 2683 <td>--help</td>
2682 2684 <td>display help and exit</td></tr>
2683 2685 <tr><td></td>
2684 2686 <td>--hidden</td>
2685 2687 <td>consider hidden changesets</td></tr>
2686 2688 <tr><td></td>
2687 2689 <td>--pager TYPE</td>
2688 2690 <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
2689 2691 </table>
2690 2692
2691 2693 </div>
2692 2694 </div>
2693 2695 </div>
2694 2696
2695 2697
2696 2698
2697 2699 </body>
2698 2700 </html>
2699 2701
2700 2702
2701 2703 $ get-with-headers.py $LOCALIP:$HGPORT "help/remove"
2702 2704 200 Script output follows
2703 2705
2704 2706 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2705 2707 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2706 2708 <head>
2707 2709 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2708 2710 <meta name="robots" content="index, nofollow" />
2709 2711 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2710 2712 <script type="text/javascript" src="/static/mercurial.js"></script>
2711 2713
2712 2714 <title>Help: remove</title>
2713 2715 </head>
2714 2716 <body>
2715 2717
2716 2718 <div class="container">
2717 2719 <div class="menu">
2718 2720 <div class="logo">
2719 2721 <a href="https://mercurial-scm.org/">
2720 2722 <img src="/static/hglogo.png" alt="mercurial" /></a>
2721 2723 </div>
2722 2724 <ul>
2723 2725 <li><a href="/shortlog">log</a></li>
2724 2726 <li><a href="/graph">graph</a></li>
2725 2727 <li><a href="/tags">tags</a></li>
2726 2728 <li><a href="/bookmarks">bookmarks</a></li>
2727 2729 <li><a href="/branches">branches</a></li>
2728 2730 </ul>
2729 2731 <ul>
2730 2732 <li class="active"><a href="/help">help</a></li>
2731 2733 </ul>
2732 2734 </div>
2733 2735
2734 2736 <div class="main">
2735 2737 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2736 2738 <h3>Help: remove</h3>
2737 2739
2738 2740 <form class="search" action="/log">
2739 2741
2740 2742 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
2741 2743 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2742 2744 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2743 2745 </form>
2744 2746 <div id="doc">
2745 2747 <p>
2746 2748 hg remove [OPTION]... FILE...
2747 2749 </p>
2748 2750 <p>
2749 2751 aliases: rm
2750 2752 </p>
2751 2753 <p>
2752 2754 remove the specified files on the next commit
2753 2755 </p>
2754 2756 <p>
2755 2757 Schedule the indicated files for removal from the current branch.
2756 2758 </p>
2757 2759 <p>
2758 2760 This command schedules the files to be removed at the next commit.
2759 2761 To undo a remove before that, see 'hg revert'. To undo added
2760 2762 files, see 'hg forget'.
2761 2763 </p>
2762 2764 <p>
2763 2765 -A/--after can be used to remove only files that have already
2764 2766 been deleted, -f/--force can be used to force deletion, and -Af
2765 2767 can be used to remove files from the next revision without
2766 2768 deleting them from the working directory.
2767 2769 </p>
2768 2770 <p>
2769 2771 The following table details the behavior of remove for different
2770 2772 file states (columns) and option combinations (rows). The file
2771 2773 states are Added [A], Clean [C], Modified [M] and Missing [!]
2772 2774 (as reported by 'hg status'). The actions are Warn, Remove
2773 2775 (from branch) and Delete (from disk):
2774 2776 </p>
2775 2777 <table>
2776 2778 <tr><td>opt/state</td>
2777 2779 <td>A</td>
2778 2780 <td>C</td>
2779 2781 <td>M</td>
2780 2782 <td>!</td></tr>
2781 2783 <tr><td>none</td>
2782 2784 <td>W</td>
2783 2785 <td>RD</td>
2784 2786 <td>W</td>
2785 2787 <td>R</td></tr>
2786 2788 <tr><td>-f</td>
2787 2789 <td>R</td>
2788 2790 <td>RD</td>
2789 2791 <td>RD</td>
2790 2792 <td>R</td></tr>
2791 2793 <tr><td>-A</td>
2792 2794 <td>W</td>
2793 2795 <td>W</td>
2794 2796 <td>W</td>
2795 2797 <td>R</td></tr>
2796 2798 <tr><td>-Af</td>
2797 2799 <td>R</td>
2798 2800 <td>R</td>
2799 2801 <td>R</td>
2800 2802 <td>R</td></tr>
2801 2803 </table>
2802 2804 <p>
2803 2805 <b>Note:</b>
2804 2806 </p>
2805 2807 <p>
2806 2808 'hg remove' never deletes files in Added [A] state from the
2807 2809 working directory, not even if &quot;--force&quot; is specified.
2808 2810 </p>
2809 2811 <p>
2810 2812 Returns 0 on success, 1 if any warnings encountered.
2811 2813 </p>
2812 2814 <p>
2813 2815 options ([+] can be repeated):
2814 2816 </p>
2815 2817 <table>
2816 2818 <tr><td>-A</td>
2817 2819 <td>--after</td>
2818 2820 <td>record delete for missing files</td></tr>
2819 2821 <tr><td>-f</td>
2820 2822 <td>--force</td>
2821 2823 <td>forget added files, delete modified files</td></tr>
2822 2824 <tr><td>-S</td>
2823 2825 <td>--subrepos</td>
2824 2826 <td>recurse into subrepositories</td></tr>
2825 2827 <tr><td>-I</td>
2826 2828 <td>--include PATTERN [+]</td>
2827 2829 <td>include names matching the given patterns</td></tr>
2828 2830 <tr><td>-X</td>
2829 2831 <td>--exclude PATTERN [+]</td>
2830 2832 <td>exclude names matching the given patterns</td></tr>
2831 2833 </table>
2832 2834 <p>
2833 2835 global options ([+] can be repeated):
2834 2836 </p>
2835 2837 <table>
2836 2838 <tr><td>-R</td>
2837 2839 <td>--repository REPO</td>
2838 2840 <td>repository root directory or name of overlay bundle file</td></tr>
2839 2841 <tr><td></td>
2840 2842 <td>--cwd DIR</td>
2841 2843 <td>change working directory</td></tr>
2842 2844 <tr><td>-y</td>
2843 2845 <td>--noninteractive</td>
2844 2846 <td>do not prompt, automatically pick the first choice for all prompts</td></tr>
2845 2847 <tr><td>-q</td>
2846 2848 <td>--quiet</td>
2847 2849 <td>suppress output</td></tr>
2848 2850 <tr><td>-v</td>
2849 2851 <td>--verbose</td>
2850 2852 <td>enable additional output</td></tr>
2851 2853 <tr><td></td>
2852 2854 <td>--color TYPE</td>
2853 2855 <td>when to colorize (boolean, always, auto, never, or debug)</td></tr>
2854 2856 <tr><td></td>
2855 2857 <td>--config CONFIG [+]</td>
2856 2858 <td>set/override config option (use 'section.name=value')</td></tr>
2857 2859 <tr><td></td>
2858 2860 <td>--debug</td>
2859 2861 <td>enable debugging output</td></tr>
2860 2862 <tr><td></td>
2861 2863 <td>--debugger</td>
2862 2864 <td>start debugger</td></tr>
2863 2865 <tr><td></td>
2864 2866 <td>--encoding ENCODE</td>
2865 2867 <td>set the charset encoding (default: ascii)</td></tr>
2866 2868 <tr><td></td>
2867 2869 <td>--encodingmode MODE</td>
2868 2870 <td>set the charset encoding mode (default: strict)</td></tr>
2869 2871 <tr><td></td>
2870 2872 <td>--traceback</td>
2871 2873 <td>always print a traceback on exception</td></tr>
2872 2874 <tr><td></td>
2873 2875 <td>--time</td>
2874 2876 <td>time how long the command takes</td></tr>
2875 2877 <tr><td></td>
2876 2878 <td>--profile</td>
2877 2879 <td>print command execution profile</td></tr>
2878 2880 <tr><td></td>
2879 2881 <td>--version</td>
2880 2882 <td>output version information and exit</td></tr>
2881 2883 <tr><td>-h</td>
2882 2884 <td>--help</td>
2883 2885 <td>display help and exit</td></tr>
2884 2886 <tr><td></td>
2885 2887 <td>--hidden</td>
2886 2888 <td>consider hidden changesets</td></tr>
2887 2889 <tr><td></td>
2888 2890 <td>--pager TYPE</td>
2889 2891 <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
2890 2892 </table>
2891 2893
2892 2894 </div>
2893 2895 </div>
2894 2896 </div>
2895 2897
2896 2898
2897 2899
2898 2900 </body>
2899 2901 </html>
2900 2902
2901 2903
2902 2904 $ get-with-headers.py $LOCALIP:$HGPORT "help/dates"
2903 2905 200 Script output follows
2904 2906
2905 2907 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2906 2908 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2907 2909 <head>
2908 2910 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2909 2911 <meta name="robots" content="index, nofollow" />
2910 2912 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2911 2913 <script type="text/javascript" src="/static/mercurial.js"></script>
2912 2914
2913 2915 <title>Help: dates</title>
2914 2916 </head>
2915 2917 <body>
2916 2918
2917 2919 <div class="container">
2918 2920 <div class="menu">
2919 2921 <div class="logo">
2920 2922 <a href="https://mercurial-scm.org/">
2921 2923 <img src="/static/hglogo.png" alt="mercurial" /></a>
2922 2924 </div>
2923 2925 <ul>
2924 2926 <li><a href="/shortlog">log</a></li>
2925 2927 <li><a href="/graph">graph</a></li>
2926 2928 <li><a href="/tags">tags</a></li>
2927 2929 <li><a href="/bookmarks">bookmarks</a></li>
2928 2930 <li><a href="/branches">branches</a></li>
2929 2931 </ul>
2930 2932 <ul>
2931 2933 <li class="active"><a href="/help">help</a></li>
2932 2934 </ul>
2933 2935 </div>
2934 2936
2935 2937 <div class="main">
2936 2938 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2937 2939 <h3>Help: dates</h3>
2938 2940
2939 2941 <form class="search" action="/log">
2940 2942
2941 2943 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
2942 2944 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2943 2945 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2944 2946 </form>
2945 2947 <div id="doc">
2946 2948 <h1>Date Formats</h1>
2947 2949 <p>
2948 2950 Some commands allow the user to specify a date, e.g.:
2949 2951 </p>
2950 2952 <ul>
2951 2953 <li> backout, commit, import, tag: Specify the commit date.
2952 2954 <li> log, revert, update: Select revision(s) by date.
2953 2955 </ul>
2954 2956 <p>
2955 2957 Many date formats are valid. Here are some examples:
2956 2958 </p>
2957 2959 <ul>
2958 2960 <li> &quot;Wed Dec 6 13:18:29 2006&quot; (local timezone assumed)
2959 2961 <li> &quot;Dec 6 13:18 -0600&quot; (year assumed, time offset provided)
2960 2962 <li> &quot;Dec 6 13:18 UTC&quot; (UTC and GMT are aliases for +0000)
2961 2963 <li> &quot;Dec 6&quot; (midnight)
2962 2964 <li> &quot;13:18&quot; (today assumed)
2963 2965 <li> &quot;3:39&quot; (3:39AM assumed)
2964 2966 <li> &quot;3:39pm&quot; (15:39)
2965 2967 <li> &quot;2006-12-06 13:18:29&quot; (ISO 8601 format)
2966 2968 <li> &quot;2006-12-6 13:18&quot;
2967 2969 <li> &quot;2006-12-6&quot;
2968 2970 <li> &quot;12-6&quot;
2969 2971 <li> &quot;12/6&quot;
2970 2972 <li> &quot;12/6/6&quot; (Dec 6 2006)
2971 2973 <li> &quot;today&quot; (midnight)
2972 2974 <li> &quot;yesterday&quot; (midnight)
2973 2975 <li> &quot;now&quot; - right now
2974 2976 </ul>
2975 2977 <p>
2976 2978 Lastly, there is Mercurial's internal format:
2977 2979 </p>
2978 2980 <ul>
2979 2981 <li> &quot;1165411109 0&quot; (Wed Dec 6 13:18:29 2006 UTC)
2980 2982 </ul>
2981 2983 <p>
2982 2984 This is the internal representation format for dates. The first number
2983 2985 is the number of seconds since the epoch (1970-01-01 00:00 UTC). The
2984 2986 second is the offset of the local timezone, in seconds west of UTC
2985 2987 (negative if the timezone is east of UTC).
2986 2988 </p>
2987 2989 <p>
2988 2990 The log command also accepts date ranges:
2989 2991 </p>
2990 2992 <ul>
2991 2993 <li> &quot;&lt;DATE&quot; - at or before a given date/time
2992 2994 <li> &quot;&gt;DATE&quot; - on or after a given date/time
2993 2995 <li> &quot;DATE to DATE&quot; - a date range, inclusive
2994 2996 <li> &quot;-DAYS&quot; - within a given number of days of today
2995 2997 </ul>
2996 2998
2997 2999 </div>
2998 3000 </div>
2999 3001 </div>
3000 3002
3001 3003
3002 3004
3003 3005 </body>
3004 3006 </html>
3005 3007
3006 3008
3007 3009 Sub-topic indexes rendered properly
3008 3010
3009 3011 $ get-with-headers.py $LOCALIP:$HGPORT "help/internals"
3010 3012 200 Script output follows
3011 3013
3012 3014 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3013 3015 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3014 3016 <head>
3015 3017 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3016 3018 <meta name="robots" content="index, nofollow" />
3017 3019 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3018 3020 <script type="text/javascript" src="/static/mercurial.js"></script>
3019 3021
3020 3022 <title>Help: internals</title>
3021 3023 </head>
3022 3024 <body>
3023 3025
3024 3026 <div class="container">
3025 3027 <div class="menu">
3026 3028 <div class="logo">
3027 3029 <a href="https://mercurial-scm.org/">
3028 3030 <img src="/static/hglogo.png" alt="mercurial" /></a>
3029 3031 </div>
3030 3032 <ul>
3031 3033 <li><a href="/shortlog">log</a></li>
3032 3034 <li><a href="/graph">graph</a></li>
3033 3035 <li><a href="/tags">tags</a></li>
3034 3036 <li><a href="/bookmarks">bookmarks</a></li>
3035 3037 <li><a href="/branches">branches</a></li>
3036 3038 </ul>
3037 3039 <ul>
3038 3040 <li><a href="/help">help</a></li>
3039 3041 </ul>
3040 3042 </div>
3041 3043
3042 3044 <div class="main">
3043 3045 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3044 3046
3045 3047 <form class="search" action="/log">
3046 3048
3047 3049 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3048 3050 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3049 3051 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3050 3052 </form>
3051 3053 <table class="bigtable">
3052 3054 <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr>
3053 3055
3054 3056 <tr><td>
3055 3057 <a href="/help/internals.bundles">
3056 3058 bundles
3057 3059 </a>
3058 3060 </td><td>
3059 3061 Bundles
3060 3062 </td></tr>
3061 3063 <tr><td>
3062 3064 <a href="/help/internals.censor">
3063 3065 censor
3064 3066 </a>
3065 3067 </td><td>
3066 3068 Censor
3067 3069 </td></tr>
3068 3070 <tr><td>
3069 3071 <a href="/help/internals.changegroups">
3070 3072 changegroups
3071 3073 </a>
3072 3074 </td><td>
3073 3075 Changegroups
3074 3076 </td></tr>
3075 3077 <tr><td>
3076 3078 <a href="/help/internals.config">
3077 3079 config
3078 3080 </a>
3079 3081 </td><td>
3080 3082 Config Registrar
3081 3083 </td></tr>
3082 3084 <tr><td>
3083 3085 <a href="/help/internals.requirements">
3084 3086 requirements
3085 3087 </a>
3086 3088 </td><td>
3087 3089 Repository Requirements
3088 3090 </td></tr>
3089 3091 <tr><td>
3090 3092 <a href="/help/internals.revlogs">
3091 3093 revlogs
3092 3094 </a>
3093 3095 </td><td>
3094 3096 Revision Logs
3095 3097 </td></tr>
3096 3098 <tr><td>
3097 3099 <a href="/help/internals.wireprotocol">
3098 3100 wireprotocol
3099 3101 </a>
3100 3102 </td><td>
3101 3103 Wire Protocol
3102 3104 </td></tr>
3103 3105
3104 3106
3105 3107
3106 3108
3107 3109
3108 3110 </table>
3109 3111 </div>
3110 3112 </div>
3111 3113
3112 3114
3113 3115
3114 3116 </body>
3115 3117 </html>
3116 3118
3117 3119
3118 3120 Sub-topic topics rendered properly
3119 3121
3120 3122 $ get-with-headers.py $LOCALIP:$HGPORT "help/internals.changegroups"
3121 3123 200 Script output follows
3122 3124
3123 3125 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3124 3126 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3125 3127 <head>
3126 3128 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3127 3129 <meta name="robots" content="index, nofollow" />
3128 3130 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3129 3131 <script type="text/javascript" src="/static/mercurial.js"></script>
3130 3132
3131 3133 <title>Help: internals.changegroups</title>
3132 3134 </head>
3133 3135 <body>
3134 3136
3135 3137 <div class="container">
3136 3138 <div class="menu">
3137 3139 <div class="logo">
3138 3140 <a href="https://mercurial-scm.org/">
3139 3141 <img src="/static/hglogo.png" alt="mercurial" /></a>
3140 3142 </div>
3141 3143 <ul>
3142 3144 <li><a href="/shortlog">log</a></li>
3143 3145 <li><a href="/graph">graph</a></li>
3144 3146 <li><a href="/tags">tags</a></li>
3145 3147 <li><a href="/bookmarks">bookmarks</a></li>
3146 3148 <li><a href="/branches">branches</a></li>
3147 3149 </ul>
3148 3150 <ul>
3149 3151 <li class="active"><a href="/help">help</a></li>
3150 3152 </ul>
3151 3153 </div>
3152 3154
3153 3155 <div class="main">
3154 3156 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3155 3157 <h3>Help: internals.changegroups</h3>
3156 3158
3157 3159 <form class="search" action="/log">
3158 3160
3159 3161 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3160 3162 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3161 3163 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3162 3164 </form>
3163 3165 <div id="doc">
3164 3166 <h1>Changegroups</h1>
3165 3167 <p>
3166 3168 Changegroups are representations of repository revlog data, specifically
3167 3169 the changelog data, root/flat manifest data, treemanifest data, and
3168 3170 filelogs.
3169 3171 </p>
3170 3172 <p>
3171 3173 There are 3 versions of changegroups: &quot;1&quot;, &quot;2&quot;, and &quot;3&quot;. From a
3172 3174 high-level, versions &quot;1&quot; and &quot;2&quot; are almost exactly the same, with the
3173 3175 only difference being an additional item in the *delta header*. Version
3174 3176 &quot;3&quot; adds support for revlog flags in the *delta header* and optionally
3175 3177 exchanging treemanifests (enabled by setting an option on the
3176 3178 &quot;changegroup&quot; part in the bundle2).
3177 3179 </p>
3178 3180 <p>
3179 3181 Changegroups when not exchanging treemanifests consist of 3 logical
3180 3182 segments:
3181 3183 </p>
3182 3184 <pre>
3183 3185 +---------------------------------+
3184 3186 | | | |
3185 3187 | changeset | manifest | filelogs |
3186 3188 | | | |
3187 3189 | | | |
3188 3190 +---------------------------------+
3189 3191 </pre>
3190 3192 <p>
3191 3193 When exchanging treemanifests, there are 4 logical segments:
3192 3194 </p>
3193 3195 <pre>
3194 3196 +-------------------------------------------------+
3195 3197 | | | | |
3196 3198 | changeset | root | treemanifests | filelogs |
3197 3199 | | manifest | | |
3198 3200 | | | | |
3199 3201 +-------------------------------------------------+
3200 3202 </pre>
3201 3203 <p>
3202 3204 The principle building block of each segment is a *chunk*. A *chunk*
3203 3205 is a framed piece of data:
3204 3206 </p>
3205 3207 <pre>
3206 3208 +---------------------------------------+
3207 3209 | | |
3208 3210 | length | data |
3209 3211 | (4 bytes) | (&lt;length - 4&gt; bytes) |
3210 3212 | | |
3211 3213 +---------------------------------------+
3212 3214 </pre>
3213 3215 <p>
3214 3216 All integers are big-endian signed integers. Each chunk starts with a 32-bit
3215 3217 integer indicating the length of the entire chunk (including the length field
3216 3218 itself).
3217 3219 </p>
3218 3220 <p>
3219 3221 There is a special case chunk that has a value of 0 for the length
3220 3222 (&quot;0x00000000&quot;). We call this an *empty chunk*.
3221 3223 </p>
3222 3224 <h2>Delta Groups</h2>
3223 3225 <p>
3224 3226 A *delta group* expresses the content of a revlog as a series of deltas,
3225 3227 or patches against previous revisions.
3226 3228 </p>
3227 3229 <p>
3228 3230 Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
3229 3231 to signal the end of the delta group:
3230 3232 </p>
3231 3233 <pre>
3232 3234 +------------------------------------------------------------------------+
3233 3235 | | | | | |
3234 3236 | chunk0 length | chunk0 data | chunk1 length | chunk1 data | 0x0 |
3235 3237 | (4 bytes) | (various) | (4 bytes) | (various) | (4 bytes) |
3236 3238 | | | | | |
3237 3239 +------------------------------------------------------------------------+
3238 3240 </pre>
3239 3241 <p>
3240 3242 Each *chunk*'s data consists of the following:
3241 3243 </p>
3242 3244 <pre>
3243 3245 +---------------------------------------+
3244 3246 | | |
3245 3247 | delta header | delta data |
3246 3248 | (various by version) | (various) |
3247 3249 | | |
3248 3250 +---------------------------------------+
3249 3251 </pre>
3250 3252 <p>
3251 3253 The *delta data* is a series of *delta*s that describe a diff from an existing
3252 3254 entry (either that the recipient already has, or previously specified in the
3253 3255 bundle/changegroup).
3254 3256 </p>
3255 3257 <p>
3256 3258 The *delta header* is different between versions &quot;1&quot;, &quot;2&quot;, and
3257 3259 &quot;3&quot; of the changegroup format.
3258 3260 </p>
3259 3261 <p>
3260 3262 Version 1 (headerlen=80):
3261 3263 </p>
3262 3264 <pre>
3263 3265 +------------------------------------------------------+
3264 3266 | | | | |
3265 3267 | node | p1 node | p2 node | link node |
3266 3268 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
3267 3269 | | | | |
3268 3270 +------------------------------------------------------+
3269 3271 </pre>
3270 3272 <p>
3271 3273 Version 2 (headerlen=100):
3272 3274 </p>
3273 3275 <pre>
3274 3276 +------------------------------------------------------------------+
3275 3277 | | | | | |
3276 3278 | node | p1 node | p2 node | base node | link node |
3277 3279 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
3278 3280 | | | | | |
3279 3281 +------------------------------------------------------------------+
3280 3282 </pre>
3281 3283 <p>
3282 3284 Version 3 (headerlen=102):
3283 3285 </p>
3284 3286 <pre>
3285 3287 +------------------------------------------------------------------------------+
3286 3288 | | | | | | |
3287 3289 | node | p1 node | p2 node | base node | link node | flags |
3288 3290 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (2 bytes) |
3289 3291 | | | | | | |
3290 3292 +------------------------------------------------------------------------------+
3291 3293 </pre>
3292 3294 <p>
3293 3295 The *delta data* consists of &quot;chunklen - 4 - headerlen&quot; bytes, which contain a
3294 3296 series of *delta*s, densely packed (no separators). These deltas describe a diff
3295 3297 from an existing entry (either that the recipient already has, or previously
3296 3298 specified in the bundle/changegroup). The format is described more fully in
3297 3299 &quot;hg help internals.bdiff&quot;, but briefly:
3298 3300 </p>
3299 3301 <pre>
3300 3302 +---------------------------------------------------------------+
3301 3303 | | | | |
3302 3304 | start offset | end offset | new length | content |
3303 3305 | (4 bytes) | (4 bytes) | (4 bytes) | (&lt;new length&gt; bytes) |
3304 3306 | | | | |
3305 3307 +---------------------------------------------------------------+
3306 3308 </pre>
3307 3309 <p>
3308 3310 Please note that the length field in the delta data does *not* include itself.
3309 3311 </p>
3310 3312 <p>
3311 3313 In version 1, the delta is always applied against the previous node from
3312 3314 the changegroup or the first parent if this is the first entry in the
3313 3315 changegroup.
3314 3316 </p>
3315 3317 <p>
3316 3318 In version 2 and up, the delta base node is encoded in the entry in the
3317 3319 changegroup. This allows the delta to be expressed against any parent,
3318 3320 which can result in smaller deltas and more efficient encoding of data.
3319 3321 </p>
3320 3322 <h2>Changeset Segment</h2>
3321 3323 <p>
3322 3324 The *changeset segment* consists of a single *delta group* holding
3323 3325 changelog data. The *empty chunk* at the end of the *delta group* denotes
3324 3326 the boundary to the *manifest segment*.
3325 3327 </p>
3326 3328 <h2>Manifest Segment</h2>
3327 3329 <p>
3328 3330 The *manifest segment* consists of a single *delta group* holding manifest
3329 3331 data. If treemanifests are in use, it contains only the manifest for the
3330 3332 root directory of the repository. Otherwise, it contains the entire
3331 3333 manifest data. The *empty chunk* at the end of the *delta group* denotes
3332 3334 the boundary to the next segment (either the *treemanifests segment* or the
3333 3335 *filelogs segment*, depending on version and the request options).
3334 3336 </p>
3335 3337 <h3>Treemanifests Segment</h3>
3336 3338 <p>
3337 3339 The *treemanifests segment* only exists in changegroup version &quot;3&quot;, and
3338 3340 only if the 'treemanifest' param is part of the bundle2 changegroup part
3339 3341 (it is not possible to use changegroup version 3 outside of bundle2).
3340 3342 Aside from the filenames in the *treemanifests segment* containing a
3341 3343 trailing &quot;/&quot; character, it behaves identically to the *filelogs segment*
3342 3344 (see below). The final sub-segment is followed by an *empty chunk* (logically,
3343 3345 a sub-segment with filename size 0). This denotes the boundary to the
3344 3346 *filelogs segment*.
3345 3347 </p>
3346 3348 <h2>Filelogs Segment</h2>
3347 3349 <p>
3348 3350 The *filelogs segment* consists of multiple sub-segments, each
3349 3351 corresponding to an individual file whose data is being described:
3350 3352 </p>
3351 3353 <pre>
3352 3354 +--------------------------------------------------+
3353 3355 | | | | | |
3354 3356 | filelog0 | filelog1 | filelog2 | ... | 0x0 |
3355 3357 | | | | | (4 bytes) |
3356 3358 | | | | | |
3357 3359 +--------------------------------------------------+
3358 3360 </pre>
3359 3361 <p>
3360 3362 The final filelog sub-segment is followed by an *empty chunk* (logically,
3361 3363 a sub-segment with filename size 0). This denotes the end of the segment
3362 3364 and of the overall changegroup.
3363 3365 </p>
3364 3366 <p>
3365 3367 Each filelog sub-segment consists of the following:
3366 3368 </p>
3367 3369 <pre>
3368 3370 +------------------------------------------------------+
3369 3371 | | | |
3370 3372 | filename length | filename | delta group |
3371 3373 | (4 bytes) | (&lt;length - 4&gt; bytes) | (various) |
3372 3374 | | | |
3373 3375 +------------------------------------------------------+
3374 3376 </pre>
3375 3377 <p>
3376 3378 That is, a *chunk* consisting of the filename (not terminated or padded)
3377 3379 followed by N chunks constituting the *delta group* for this file. The
3378 3380 *empty chunk* at the end of each *delta group* denotes the boundary to the
3379 3381 next filelog sub-segment.
3380 3382 </p>
3381 3383
3382 3384 </div>
3383 3385 </div>
3384 3386 </div>
3385 3387
3386 3388
3387 3389
3388 3390 </body>
3389 3391 </html>
3390 3392
3391 3393
3392 3394 $ killdaemons.py
3393 3395
3394 3396 #endif
General Comments 0
You need to be logged in to leave comments. Login now