##// END OF EJS Templates
clone: use "official" API to create local clone destination...
marmoute -
r48235:d1c1fd7a default
parent child Browse files
Show More
@@ -1,1602 +1,1599 b''
1 # hg.py - repository classes for mercurial
1 # hg.py - repository classes for mercurial
2 #
2 #
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
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.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import errno
11 import errno
12 import os
12 import os
13 import shutil
13 import shutil
14 import stat
14 import stat
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 hex,
18 hex,
19 sha1nodeconstants,
19 sha1nodeconstants,
20 short,
20 short,
21 )
21 )
22 from .pycompat import getattr
22 from .pycompat import getattr
23
23
24 from . import (
24 from . import (
25 bookmarks,
25 bookmarks,
26 bundlerepo,
26 bundlerepo,
27 cacheutil,
27 cacheutil,
28 cmdutil,
28 cmdutil,
29 destutil,
29 destutil,
30 discovery,
30 discovery,
31 error,
31 error,
32 exchange,
32 exchange,
33 extensions,
33 extensions,
34 graphmod,
34 graphmod,
35 httppeer,
35 httppeer,
36 localrepo,
36 localrepo,
37 lock,
37 lock,
38 logcmdutil,
38 logcmdutil,
39 logexchange,
39 logexchange,
40 merge as mergemod,
40 merge as mergemod,
41 mergestate as mergestatemod,
41 mergestate as mergestatemod,
42 narrowspec,
42 narrowspec,
43 phases,
43 phases,
44 requirements,
44 requirements,
45 scmutil,
45 scmutil,
46 sshpeer,
46 sshpeer,
47 statichttprepo,
47 statichttprepo,
48 ui as uimod,
48 ui as uimod,
49 unionrepo,
49 unionrepo,
50 url,
50 url,
51 util,
51 util,
52 verify as verifymod,
52 verify as verifymod,
53 vfs as vfsmod,
53 vfs as vfsmod,
54 )
54 )
55 from .interfaces import repository as repositorymod
55 from .interfaces import repository as repositorymod
56 from .utils import (
56 from .utils import (
57 hashutil,
57 hashutil,
58 stringutil,
58 stringutil,
59 urlutil,
59 urlutil,
60 )
60 )
61
61
62
62
63 release = lock.release
63 release = lock.release
64
64
65 # shared features
65 # shared features
66 sharedbookmarks = b'bookmarks'
66 sharedbookmarks = b'bookmarks'
67
67
68
68
69 def _local(path):
69 def _local(path):
70 path = util.expandpath(urlutil.urllocalpath(path))
70 path = util.expandpath(urlutil.urllocalpath(path))
71
71
72 try:
72 try:
73 # we use os.stat() directly here instead of os.path.isfile()
73 # we use os.stat() directly here instead of os.path.isfile()
74 # because the latter started returning `False` on invalid path
74 # because the latter started returning `False` on invalid path
75 # exceptions starting in 3.8 and we care about handling
75 # exceptions starting in 3.8 and we care about handling
76 # invalid paths specially here.
76 # invalid paths specially here.
77 st = os.stat(path)
77 st = os.stat(path)
78 isfile = stat.S_ISREG(st.st_mode)
78 isfile = stat.S_ISREG(st.st_mode)
79 # Python 2 raises TypeError, Python 3 ValueError.
79 # Python 2 raises TypeError, Python 3 ValueError.
80 except (TypeError, ValueError) as e:
80 except (TypeError, ValueError) as e:
81 raise error.Abort(
81 raise error.Abort(
82 _(b'invalid path %s: %s') % (path, stringutil.forcebytestr(e))
82 _(b'invalid path %s: %s') % (path, stringutil.forcebytestr(e))
83 )
83 )
84 except OSError:
84 except OSError:
85 isfile = False
85 isfile = False
86
86
87 return isfile and bundlerepo or localrepo
87 return isfile and bundlerepo or localrepo
88
88
89
89
90 def addbranchrevs(lrepo, other, branches, revs):
90 def addbranchrevs(lrepo, other, branches, revs):
91 peer = other.peer() # a courtesy to callers using a localrepo for other
91 peer = other.peer() # a courtesy to callers using a localrepo for other
92 hashbranch, branches = branches
92 hashbranch, branches = branches
93 if not hashbranch and not branches:
93 if not hashbranch and not branches:
94 x = revs or None
94 x = revs or None
95 if revs:
95 if revs:
96 y = revs[0]
96 y = revs[0]
97 else:
97 else:
98 y = None
98 y = None
99 return x, y
99 return x, y
100 if revs:
100 if revs:
101 revs = list(revs)
101 revs = list(revs)
102 else:
102 else:
103 revs = []
103 revs = []
104
104
105 if not peer.capable(b'branchmap'):
105 if not peer.capable(b'branchmap'):
106 if branches:
106 if branches:
107 raise error.Abort(_(b"remote branch lookup not supported"))
107 raise error.Abort(_(b"remote branch lookup not supported"))
108 revs.append(hashbranch)
108 revs.append(hashbranch)
109 return revs, revs[0]
109 return revs, revs[0]
110
110
111 with peer.commandexecutor() as e:
111 with peer.commandexecutor() as e:
112 branchmap = e.callcommand(b'branchmap', {}).result()
112 branchmap = e.callcommand(b'branchmap', {}).result()
113
113
114 def primary(branch):
114 def primary(branch):
115 if branch == b'.':
115 if branch == b'.':
116 if not lrepo:
116 if not lrepo:
117 raise error.Abort(_(b"dirstate branch not accessible"))
117 raise error.Abort(_(b"dirstate branch not accessible"))
118 branch = lrepo.dirstate.branch()
118 branch = lrepo.dirstate.branch()
119 if branch in branchmap:
119 if branch in branchmap:
120 revs.extend(hex(r) for r in reversed(branchmap[branch]))
120 revs.extend(hex(r) for r in reversed(branchmap[branch]))
121 return True
121 return True
122 else:
122 else:
123 return False
123 return False
124
124
125 for branch in branches:
125 for branch in branches:
126 if not primary(branch):
126 if not primary(branch):
127 raise error.RepoLookupError(_(b"unknown branch '%s'") % branch)
127 raise error.RepoLookupError(_(b"unknown branch '%s'") % branch)
128 if hashbranch:
128 if hashbranch:
129 if not primary(hashbranch):
129 if not primary(hashbranch):
130 revs.append(hashbranch)
130 revs.append(hashbranch)
131 return revs, revs[0]
131 return revs, revs[0]
132
132
133
133
134 def parseurl(path, branches=None):
134 def parseurl(path, branches=None):
135 '''parse url#branch, returning (url, (branch, branches))'''
135 '''parse url#branch, returning (url, (branch, branches))'''
136 msg = b'parseurl(...) moved to mercurial.utils.urlutil'
136 msg = b'parseurl(...) moved to mercurial.utils.urlutil'
137 util.nouideprecwarn(msg, b'6.0', stacklevel=2)
137 util.nouideprecwarn(msg, b'6.0', stacklevel=2)
138 return urlutil.parseurl(path, branches=branches)
138 return urlutil.parseurl(path, branches=branches)
139
139
140
140
141 schemes = {
141 schemes = {
142 b'bundle': bundlerepo,
142 b'bundle': bundlerepo,
143 b'union': unionrepo,
143 b'union': unionrepo,
144 b'file': _local,
144 b'file': _local,
145 b'http': httppeer,
145 b'http': httppeer,
146 b'https': httppeer,
146 b'https': httppeer,
147 b'ssh': sshpeer,
147 b'ssh': sshpeer,
148 b'static-http': statichttprepo,
148 b'static-http': statichttprepo,
149 }
149 }
150
150
151
151
152 def _peerlookup(path):
152 def _peerlookup(path):
153 u = urlutil.url(path)
153 u = urlutil.url(path)
154 scheme = u.scheme or b'file'
154 scheme = u.scheme or b'file'
155 thing = schemes.get(scheme) or schemes[b'file']
155 thing = schemes.get(scheme) or schemes[b'file']
156 try:
156 try:
157 return thing(path)
157 return thing(path)
158 except TypeError:
158 except TypeError:
159 # we can't test callable(thing) because 'thing' can be an unloaded
159 # we can't test callable(thing) because 'thing' can be an unloaded
160 # module that implements __call__
160 # module that implements __call__
161 if not util.safehasattr(thing, b'instance'):
161 if not util.safehasattr(thing, b'instance'):
162 raise
162 raise
163 return thing
163 return thing
164
164
165
165
166 def islocal(repo):
166 def islocal(repo):
167 '''return true if repo (or path pointing to repo) is local'''
167 '''return true if repo (or path pointing to repo) is local'''
168 if isinstance(repo, bytes):
168 if isinstance(repo, bytes):
169 try:
169 try:
170 return _peerlookup(repo).islocal(repo)
170 return _peerlookup(repo).islocal(repo)
171 except AttributeError:
171 except AttributeError:
172 return False
172 return False
173 return repo.local()
173 return repo.local()
174
174
175
175
176 def openpath(ui, path, sendaccept=True):
176 def openpath(ui, path, sendaccept=True):
177 '''open path with open if local, url.open if remote'''
177 '''open path with open if local, url.open if remote'''
178 pathurl = urlutil.url(path, parsequery=False, parsefragment=False)
178 pathurl = urlutil.url(path, parsequery=False, parsefragment=False)
179 if pathurl.islocal():
179 if pathurl.islocal():
180 return util.posixfile(pathurl.localpath(), b'rb')
180 return util.posixfile(pathurl.localpath(), b'rb')
181 else:
181 else:
182 return url.open(ui, path, sendaccept=sendaccept)
182 return url.open(ui, path, sendaccept=sendaccept)
183
183
184
184
185 # a list of (ui, repo) functions called for wire peer initialization
185 # a list of (ui, repo) functions called for wire peer initialization
186 wirepeersetupfuncs = []
186 wirepeersetupfuncs = []
187
187
188
188
189 def _peerorrepo(
189 def _peerorrepo(
190 ui, path, create=False, presetupfuncs=None, intents=None, createopts=None
190 ui, path, create=False, presetupfuncs=None, intents=None, createopts=None
191 ):
191 ):
192 """return a repository object for the specified path"""
192 """return a repository object for the specified path"""
193 obj = _peerlookup(path).instance(
193 obj = _peerlookup(path).instance(
194 ui, path, create, intents=intents, createopts=createopts
194 ui, path, create, intents=intents, createopts=createopts
195 )
195 )
196 ui = getattr(obj, "ui", ui)
196 ui = getattr(obj, "ui", ui)
197 for f in presetupfuncs or []:
197 for f in presetupfuncs or []:
198 f(ui, obj)
198 f(ui, obj)
199 ui.log(b'extension', b'- executing reposetup hooks\n')
199 ui.log(b'extension', b'- executing reposetup hooks\n')
200 with util.timedcm('all reposetup') as allreposetupstats:
200 with util.timedcm('all reposetup') as allreposetupstats:
201 for name, module in extensions.extensions(ui):
201 for name, module in extensions.extensions(ui):
202 ui.log(b'extension', b' - running reposetup for %s\n', name)
202 ui.log(b'extension', b' - running reposetup for %s\n', name)
203 hook = getattr(module, 'reposetup', None)
203 hook = getattr(module, 'reposetup', None)
204 if hook:
204 if hook:
205 with util.timedcm('reposetup %r', name) as stats:
205 with util.timedcm('reposetup %r', name) as stats:
206 hook(ui, obj)
206 hook(ui, obj)
207 ui.log(
207 ui.log(
208 b'extension', b' > reposetup for %s took %s\n', name, stats
208 b'extension', b' > reposetup for %s took %s\n', name, stats
209 )
209 )
210 ui.log(b'extension', b'> all reposetup took %s\n', allreposetupstats)
210 ui.log(b'extension', b'> all reposetup took %s\n', allreposetupstats)
211 if not obj.local():
211 if not obj.local():
212 for f in wirepeersetupfuncs:
212 for f in wirepeersetupfuncs:
213 f(ui, obj)
213 f(ui, obj)
214 return obj
214 return obj
215
215
216
216
217 def repository(
217 def repository(
218 ui,
218 ui,
219 path=b'',
219 path=b'',
220 create=False,
220 create=False,
221 presetupfuncs=None,
221 presetupfuncs=None,
222 intents=None,
222 intents=None,
223 createopts=None,
223 createopts=None,
224 ):
224 ):
225 """return a repository object for the specified path"""
225 """return a repository object for the specified path"""
226 peer = _peerorrepo(
226 peer = _peerorrepo(
227 ui,
227 ui,
228 path,
228 path,
229 create,
229 create,
230 presetupfuncs=presetupfuncs,
230 presetupfuncs=presetupfuncs,
231 intents=intents,
231 intents=intents,
232 createopts=createopts,
232 createopts=createopts,
233 )
233 )
234 repo = peer.local()
234 repo = peer.local()
235 if not repo:
235 if not repo:
236 raise error.Abort(
236 raise error.Abort(
237 _(b"repository '%s' is not local") % (path or peer.url())
237 _(b"repository '%s' is not local") % (path or peer.url())
238 )
238 )
239 return repo.filtered(b'visible')
239 return repo.filtered(b'visible')
240
240
241
241
242 def peer(uiorrepo, opts, path, create=False, intents=None, createopts=None):
242 def peer(uiorrepo, opts, path, create=False, intents=None, createopts=None):
243 '''return a repository peer for the specified path'''
243 '''return a repository peer for the specified path'''
244 rui = remoteui(uiorrepo, opts)
244 rui = remoteui(uiorrepo, opts)
245 return _peerorrepo(
245 return _peerorrepo(
246 rui, path, create, intents=intents, createopts=createopts
246 rui, path, create, intents=intents, createopts=createopts
247 ).peer()
247 ).peer()
248
248
249
249
250 def defaultdest(source):
250 def defaultdest(source):
251 """return default destination of clone if none is given
251 """return default destination of clone if none is given
252
252
253 >>> defaultdest(b'foo')
253 >>> defaultdest(b'foo')
254 'foo'
254 'foo'
255 >>> defaultdest(b'/foo/bar')
255 >>> defaultdest(b'/foo/bar')
256 'bar'
256 'bar'
257 >>> defaultdest(b'/')
257 >>> defaultdest(b'/')
258 ''
258 ''
259 >>> defaultdest(b'')
259 >>> defaultdest(b'')
260 ''
260 ''
261 >>> defaultdest(b'http://example.org/')
261 >>> defaultdest(b'http://example.org/')
262 ''
262 ''
263 >>> defaultdest(b'http://example.org/foo/')
263 >>> defaultdest(b'http://example.org/foo/')
264 'foo'
264 'foo'
265 """
265 """
266 path = urlutil.url(source).path
266 path = urlutil.url(source).path
267 if not path:
267 if not path:
268 return b''
268 return b''
269 return os.path.basename(os.path.normpath(path))
269 return os.path.basename(os.path.normpath(path))
270
270
271
271
272 def sharedreposource(repo):
272 def sharedreposource(repo):
273 """Returns repository object for source repository of a shared repo.
273 """Returns repository object for source repository of a shared repo.
274
274
275 If repo is not a shared repository, returns None.
275 If repo is not a shared repository, returns None.
276 """
276 """
277 if repo.sharedpath == repo.path:
277 if repo.sharedpath == repo.path:
278 return None
278 return None
279
279
280 if util.safehasattr(repo, b'srcrepo') and repo.srcrepo:
280 if util.safehasattr(repo, b'srcrepo') and repo.srcrepo:
281 return repo.srcrepo
281 return repo.srcrepo
282
282
283 # the sharedpath always ends in the .hg; we want the path to the repo
283 # the sharedpath always ends in the .hg; we want the path to the repo
284 source = repo.vfs.split(repo.sharedpath)[0]
284 source = repo.vfs.split(repo.sharedpath)[0]
285 srcurl, branches = urlutil.parseurl(source)
285 srcurl, branches = urlutil.parseurl(source)
286 srcrepo = repository(repo.ui, srcurl)
286 srcrepo = repository(repo.ui, srcurl)
287 repo.srcrepo = srcrepo
287 repo.srcrepo = srcrepo
288 return srcrepo
288 return srcrepo
289
289
290
290
291 def share(
291 def share(
292 ui,
292 ui,
293 source,
293 source,
294 dest=None,
294 dest=None,
295 update=True,
295 update=True,
296 bookmarks=True,
296 bookmarks=True,
297 defaultpath=None,
297 defaultpath=None,
298 relative=False,
298 relative=False,
299 ):
299 ):
300 '''create a shared repository'''
300 '''create a shared repository'''
301
301
302 if not islocal(source):
302 if not islocal(source):
303 raise error.Abort(_(b'can only share local repositories'))
303 raise error.Abort(_(b'can only share local repositories'))
304
304
305 if not dest:
305 if not dest:
306 dest = defaultdest(source)
306 dest = defaultdest(source)
307 else:
307 else:
308 dest = urlutil.get_clone_path(ui, dest)[1]
308 dest = urlutil.get_clone_path(ui, dest)[1]
309
309
310 if isinstance(source, bytes):
310 if isinstance(source, bytes):
311 origsource, source, branches = urlutil.get_clone_path(ui, source)
311 origsource, source, branches = urlutil.get_clone_path(ui, source)
312 srcrepo = repository(ui, source)
312 srcrepo = repository(ui, source)
313 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
313 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
314 else:
314 else:
315 srcrepo = source.local()
315 srcrepo = source.local()
316 checkout = None
316 checkout = None
317
317
318 shareditems = set()
318 shareditems = set()
319 if bookmarks:
319 if bookmarks:
320 shareditems.add(sharedbookmarks)
320 shareditems.add(sharedbookmarks)
321
321
322 r = repository(
322 r = repository(
323 ui,
323 ui,
324 dest,
324 dest,
325 create=True,
325 create=True,
326 createopts={
326 createopts={
327 b'sharedrepo': srcrepo,
327 b'sharedrepo': srcrepo,
328 b'sharedrelative': relative,
328 b'sharedrelative': relative,
329 b'shareditems': shareditems,
329 b'shareditems': shareditems,
330 },
330 },
331 )
331 )
332
332
333 postshare(srcrepo, r, defaultpath=defaultpath)
333 postshare(srcrepo, r, defaultpath=defaultpath)
334 r = repository(ui, dest)
334 r = repository(ui, dest)
335 _postshareupdate(r, update, checkout=checkout)
335 _postshareupdate(r, update, checkout=checkout)
336 return r
336 return r
337
337
338
338
339 def _prependsourcehgrc(repo):
339 def _prependsourcehgrc(repo):
340 """copies the source repo config and prepend it in current repo .hg/hgrc
340 """copies the source repo config and prepend it in current repo .hg/hgrc
341 on unshare. This is only done if the share was perfomed using share safe
341 on unshare. This is only done if the share was perfomed using share safe
342 method where we share config of source in shares"""
342 method where we share config of source in shares"""
343 srcvfs = vfsmod.vfs(repo.sharedpath)
343 srcvfs = vfsmod.vfs(repo.sharedpath)
344 dstvfs = vfsmod.vfs(repo.path)
344 dstvfs = vfsmod.vfs(repo.path)
345
345
346 if not srcvfs.exists(b'hgrc'):
346 if not srcvfs.exists(b'hgrc'):
347 return
347 return
348
348
349 currentconfig = b''
349 currentconfig = b''
350 if dstvfs.exists(b'hgrc'):
350 if dstvfs.exists(b'hgrc'):
351 currentconfig = dstvfs.read(b'hgrc')
351 currentconfig = dstvfs.read(b'hgrc')
352
352
353 with dstvfs(b'hgrc', b'wb') as fp:
353 with dstvfs(b'hgrc', b'wb') as fp:
354 sourceconfig = srcvfs.read(b'hgrc')
354 sourceconfig = srcvfs.read(b'hgrc')
355 fp.write(b"# Config copied from shared source\n")
355 fp.write(b"# Config copied from shared source\n")
356 fp.write(sourceconfig)
356 fp.write(sourceconfig)
357 fp.write(b'\n')
357 fp.write(b'\n')
358 fp.write(currentconfig)
358 fp.write(currentconfig)
359
359
360
360
361 def unshare(ui, repo):
361 def unshare(ui, repo):
362 """convert a shared repository to a normal one
362 """convert a shared repository to a normal one
363
363
364 Copy the store data to the repo and remove the sharedpath data.
364 Copy the store data to the repo and remove the sharedpath data.
365
365
366 Returns a new repository object representing the unshared repository.
366 Returns a new repository object representing the unshared repository.
367
367
368 The passed repository object is not usable after this function is
368 The passed repository object is not usable after this function is
369 called.
369 called.
370 """
370 """
371
371
372 with repo.lock():
372 with repo.lock():
373 # we use locks here because if we race with commit, we
373 # we use locks here because if we race with commit, we
374 # can end up with extra data in the cloned revlogs that's
374 # can end up with extra data in the cloned revlogs that's
375 # not pointed to by changesets, thus causing verify to
375 # not pointed to by changesets, thus causing verify to
376 # fail
376 # fail
377 destlock = copystore(ui, repo, repo.path)
377 destlock = copystore(ui, repo, repo.path)
378 with destlock or util.nullcontextmanager():
378 with destlock or util.nullcontextmanager():
379 if requirements.SHARESAFE_REQUIREMENT in repo.requirements:
379 if requirements.SHARESAFE_REQUIREMENT in repo.requirements:
380 # we were sharing .hg/hgrc of the share source with the current
380 # we were sharing .hg/hgrc of the share source with the current
381 # repo. We need to copy that while unsharing otherwise it can
381 # repo. We need to copy that while unsharing otherwise it can
382 # disable hooks and other checks
382 # disable hooks and other checks
383 _prependsourcehgrc(repo)
383 _prependsourcehgrc(repo)
384
384
385 sharefile = repo.vfs.join(b'sharedpath')
385 sharefile = repo.vfs.join(b'sharedpath')
386 util.rename(sharefile, sharefile + b'.old')
386 util.rename(sharefile, sharefile + b'.old')
387
387
388 repo.requirements.discard(requirements.SHARED_REQUIREMENT)
388 repo.requirements.discard(requirements.SHARED_REQUIREMENT)
389 repo.requirements.discard(requirements.RELATIVE_SHARED_REQUIREMENT)
389 repo.requirements.discard(requirements.RELATIVE_SHARED_REQUIREMENT)
390 scmutil.writereporequirements(repo)
390 scmutil.writereporequirements(repo)
391
391
392 # Removing share changes some fundamental properties of the repo instance.
392 # Removing share changes some fundamental properties of the repo instance.
393 # So we instantiate a new repo object and operate on it rather than
393 # So we instantiate a new repo object and operate on it rather than
394 # try to keep the existing repo usable.
394 # try to keep the existing repo usable.
395 newrepo = repository(repo.baseui, repo.root, create=False)
395 newrepo = repository(repo.baseui, repo.root, create=False)
396
396
397 # TODO: figure out how to access subrepos that exist, but were previously
397 # TODO: figure out how to access subrepos that exist, but were previously
398 # removed from .hgsub
398 # removed from .hgsub
399 c = newrepo[b'.']
399 c = newrepo[b'.']
400 subs = c.substate
400 subs = c.substate
401 for s in sorted(subs):
401 for s in sorted(subs):
402 c.sub(s).unshare()
402 c.sub(s).unshare()
403
403
404 localrepo.poisonrepository(repo)
404 localrepo.poisonrepository(repo)
405
405
406 return newrepo
406 return newrepo
407
407
408
408
409 def postshare(sourcerepo, destrepo, defaultpath=None):
409 def postshare(sourcerepo, destrepo, defaultpath=None):
410 """Called after a new shared repo is created.
410 """Called after a new shared repo is created.
411
411
412 The new repo only has a requirements file and pointer to the source.
412 The new repo only has a requirements file and pointer to the source.
413 This function configures additional shared data.
413 This function configures additional shared data.
414
414
415 Extensions can wrap this function and write additional entries to
415 Extensions can wrap this function and write additional entries to
416 destrepo/.hg/shared to indicate additional pieces of data to be shared.
416 destrepo/.hg/shared to indicate additional pieces of data to be shared.
417 """
417 """
418 default = defaultpath or sourcerepo.ui.config(b'paths', b'default')
418 default = defaultpath or sourcerepo.ui.config(b'paths', b'default')
419 if default:
419 if default:
420 template = b'[paths]\ndefault = %s\n'
420 template = b'[paths]\ndefault = %s\n'
421 destrepo.vfs.write(b'hgrc', util.tonativeeol(template % default))
421 destrepo.vfs.write(b'hgrc', util.tonativeeol(template % default))
422 if requirements.NARROW_REQUIREMENT in sourcerepo.requirements:
422 if requirements.NARROW_REQUIREMENT in sourcerepo.requirements:
423 with destrepo.wlock():
423 with destrepo.wlock():
424 narrowspec.copytoworkingcopy(destrepo)
424 narrowspec.copytoworkingcopy(destrepo)
425
425
426
426
427 def _postshareupdate(repo, update, checkout=None):
427 def _postshareupdate(repo, update, checkout=None):
428 """Maybe perform a working directory update after a shared repo is created.
428 """Maybe perform a working directory update after a shared repo is created.
429
429
430 ``update`` can be a boolean or a revision to update to.
430 ``update`` can be a boolean or a revision to update to.
431 """
431 """
432 if not update:
432 if not update:
433 return
433 return
434
434
435 repo.ui.status(_(b"updating working directory\n"))
435 repo.ui.status(_(b"updating working directory\n"))
436 if update is not True:
436 if update is not True:
437 checkout = update
437 checkout = update
438 for test in (checkout, b'default', b'tip'):
438 for test in (checkout, b'default', b'tip'):
439 if test is None:
439 if test is None:
440 continue
440 continue
441 try:
441 try:
442 uprev = repo.lookup(test)
442 uprev = repo.lookup(test)
443 break
443 break
444 except error.RepoLookupError:
444 except error.RepoLookupError:
445 continue
445 continue
446 _update(repo, uprev)
446 _update(repo, uprev)
447
447
448
448
449 def copystore(ui, srcrepo, destpath):
449 def copystore(ui, srcrepo, destpath):
450 """copy files from store of srcrepo in destpath
450 """copy files from store of srcrepo in destpath
451
451
452 returns destlock
452 returns destlock
453 """
453 """
454 destlock = None
454 destlock = None
455 try:
455 try:
456 hardlink = None
456 hardlink = None
457 topic = _(b'linking') if hardlink else _(b'copying')
457 topic = _(b'linking') if hardlink else _(b'copying')
458 with ui.makeprogress(topic, unit=_(b'files')) as progress:
458 with ui.makeprogress(topic, unit=_(b'files')) as progress:
459 num = 0
459 num = 0
460 srcpublishing = srcrepo.publishing()
460 srcpublishing = srcrepo.publishing()
461 srcvfs = vfsmod.vfs(srcrepo.sharedpath)
461 srcvfs = vfsmod.vfs(srcrepo.sharedpath)
462 dstvfs = vfsmod.vfs(destpath)
462 dstvfs = vfsmod.vfs(destpath)
463 for f in srcrepo.store.copylist():
463 for f in srcrepo.store.copylist():
464 if srcpublishing and f.endswith(b'phaseroots'):
464 if srcpublishing and f.endswith(b'phaseroots'):
465 continue
465 continue
466 dstbase = os.path.dirname(f)
466 dstbase = os.path.dirname(f)
467 if dstbase and not dstvfs.exists(dstbase):
467 if dstbase and not dstvfs.exists(dstbase):
468 dstvfs.mkdir(dstbase)
468 dstvfs.mkdir(dstbase)
469 if srcvfs.exists(f):
469 if srcvfs.exists(f):
470 if f.endswith(b'data'):
470 if f.endswith(b'data'):
471 # 'dstbase' may be empty (e.g. revlog format 0)
471 # 'dstbase' may be empty (e.g. revlog format 0)
472 lockfile = os.path.join(dstbase, b"lock")
472 lockfile = os.path.join(dstbase, b"lock")
473 # lock to avoid premature writing to the target
473 # lock to avoid premature writing to the target
474 destlock = lock.lock(dstvfs, lockfile)
474 destlock = lock.lock(dstvfs, lockfile)
475 hardlink, n = util.copyfiles(
475 hardlink, n = util.copyfiles(
476 srcvfs.join(f), dstvfs.join(f), hardlink, progress
476 srcvfs.join(f), dstvfs.join(f), hardlink, progress
477 )
477 )
478 num += n
478 num += n
479 if hardlink:
479 if hardlink:
480 ui.debug(b"linked %d files\n" % num)
480 ui.debug(b"linked %d files\n" % num)
481 else:
481 else:
482 ui.debug(b"copied %d files\n" % num)
482 ui.debug(b"copied %d files\n" % num)
483 return destlock
483 return destlock
484 except: # re-raises
484 except: # re-raises
485 release(destlock)
485 release(destlock)
486 raise
486 raise
487
487
488
488
489 def clonewithshare(
489 def clonewithshare(
490 ui,
490 ui,
491 peeropts,
491 peeropts,
492 sharepath,
492 sharepath,
493 source,
493 source,
494 srcpeer,
494 srcpeer,
495 dest,
495 dest,
496 pull=False,
496 pull=False,
497 rev=None,
497 rev=None,
498 update=True,
498 update=True,
499 stream=False,
499 stream=False,
500 ):
500 ):
501 """Perform a clone using a shared repo.
501 """Perform a clone using a shared repo.
502
502
503 The store for the repository will be located at <sharepath>/.hg. The
503 The store for the repository will be located at <sharepath>/.hg. The
504 specified revisions will be cloned or pulled from "source". A shared repo
504 specified revisions will be cloned or pulled from "source". A shared repo
505 will be created at "dest" and a working copy will be created if "update" is
505 will be created at "dest" and a working copy will be created if "update" is
506 True.
506 True.
507 """
507 """
508 revs = None
508 revs = None
509 if rev:
509 if rev:
510 if not srcpeer.capable(b'lookup'):
510 if not srcpeer.capable(b'lookup'):
511 raise error.Abort(
511 raise error.Abort(
512 _(
512 _(
513 b"src repository does not support "
513 b"src repository does not support "
514 b"revision lookup and so doesn't "
514 b"revision lookup and so doesn't "
515 b"support clone by revision"
515 b"support clone by revision"
516 )
516 )
517 )
517 )
518
518
519 # TODO this is batchable.
519 # TODO this is batchable.
520 remoterevs = []
520 remoterevs = []
521 for r in rev:
521 for r in rev:
522 with srcpeer.commandexecutor() as e:
522 with srcpeer.commandexecutor() as e:
523 remoterevs.append(
523 remoterevs.append(
524 e.callcommand(
524 e.callcommand(
525 b'lookup',
525 b'lookup',
526 {
526 {
527 b'key': r,
527 b'key': r,
528 },
528 },
529 ).result()
529 ).result()
530 )
530 )
531 revs = remoterevs
531 revs = remoterevs
532
532
533 # Obtain a lock before checking for or cloning the pooled repo otherwise
533 # Obtain a lock before checking for or cloning the pooled repo otherwise
534 # 2 clients may race creating or populating it.
534 # 2 clients may race creating or populating it.
535 pooldir = os.path.dirname(sharepath)
535 pooldir = os.path.dirname(sharepath)
536 # lock class requires the directory to exist.
536 # lock class requires the directory to exist.
537 try:
537 try:
538 util.makedir(pooldir, False)
538 util.makedir(pooldir, False)
539 except OSError as e:
539 except OSError as e:
540 if e.errno != errno.EEXIST:
540 if e.errno != errno.EEXIST:
541 raise
541 raise
542
542
543 poolvfs = vfsmod.vfs(pooldir)
543 poolvfs = vfsmod.vfs(pooldir)
544 basename = os.path.basename(sharepath)
544 basename = os.path.basename(sharepath)
545
545
546 with lock.lock(poolvfs, b'%s.lock' % basename):
546 with lock.lock(poolvfs, b'%s.lock' % basename):
547 if os.path.exists(sharepath):
547 if os.path.exists(sharepath):
548 ui.status(
548 ui.status(
549 _(b'(sharing from existing pooled repository %s)\n') % basename
549 _(b'(sharing from existing pooled repository %s)\n') % basename
550 )
550 )
551 else:
551 else:
552 ui.status(
552 ui.status(
553 _(b'(sharing from new pooled repository %s)\n') % basename
553 _(b'(sharing from new pooled repository %s)\n') % basename
554 )
554 )
555 # Always use pull mode because hardlinks in share mode don't work
555 # Always use pull mode because hardlinks in share mode don't work
556 # well. Never update because working copies aren't necessary in
556 # well. Never update because working copies aren't necessary in
557 # share mode.
557 # share mode.
558 clone(
558 clone(
559 ui,
559 ui,
560 peeropts,
560 peeropts,
561 source,
561 source,
562 dest=sharepath,
562 dest=sharepath,
563 pull=True,
563 pull=True,
564 revs=rev,
564 revs=rev,
565 update=False,
565 update=False,
566 stream=stream,
566 stream=stream,
567 )
567 )
568
568
569 # Resolve the value to put in [paths] section for the source.
569 # Resolve the value to put in [paths] section for the source.
570 if islocal(source):
570 if islocal(source):
571 defaultpath = os.path.abspath(urlutil.urllocalpath(source))
571 defaultpath = os.path.abspath(urlutil.urllocalpath(source))
572 else:
572 else:
573 defaultpath = source
573 defaultpath = source
574
574
575 sharerepo = repository(ui, path=sharepath)
575 sharerepo = repository(ui, path=sharepath)
576 destrepo = share(
576 destrepo = share(
577 ui,
577 ui,
578 sharerepo,
578 sharerepo,
579 dest=dest,
579 dest=dest,
580 update=False,
580 update=False,
581 bookmarks=False,
581 bookmarks=False,
582 defaultpath=defaultpath,
582 defaultpath=defaultpath,
583 )
583 )
584
584
585 # We need to perform a pull against the dest repo to fetch bookmarks
585 # We need to perform a pull against the dest repo to fetch bookmarks
586 # and other non-store data that isn't shared by default. In the case of
586 # and other non-store data that isn't shared by default. In the case of
587 # non-existing shared repo, this means we pull from the remote twice. This
587 # non-existing shared repo, this means we pull from the remote twice. This
588 # is a bit weird. But at the time it was implemented, there wasn't an easy
588 # is a bit weird. But at the time it was implemented, there wasn't an easy
589 # way to pull just non-changegroup data.
589 # way to pull just non-changegroup data.
590 exchange.pull(destrepo, srcpeer, heads=revs)
590 exchange.pull(destrepo, srcpeer, heads=revs)
591
591
592 _postshareupdate(destrepo, update)
592 _postshareupdate(destrepo, update)
593
593
594 return srcpeer, peer(ui, peeropts, dest)
594 return srcpeer, peer(ui, peeropts, dest)
595
595
596
596
597 # Recomputing caches is often slow on big repos, so copy them.
597 # Recomputing caches is often slow on big repos, so copy them.
598 def _copycache(srcrepo, dstcachedir, fname):
598 def _copycache(srcrepo, dstcachedir, fname):
599 """copy a cache from srcrepo to destcachedir (if it exists)"""
599 """copy a cache from srcrepo to destcachedir (if it exists)"""
600 srcfname = srcrepo.cachevfs.join(fname)
600 srcfname = srcrepo.cachevfs.join(fname)
601 dstfname = os.path.join(dstcachedir, fname)
601 dstfname = os.path.join(dstcachedir, fname)
602 if os.path.exists(srcfname):
602 if os.path.exists(srcfname):
603 if not os.path.exists(dstcachedir):
603 if not os.path.exists(dstcachedir):
604 os.mkdir(dstcachedir)
604 os.mkdir(dstcachedir)
605 util.copyfile(srcfname, dstfname)
605 util.copyfile(srcfname, dstfname)
606
606
607
607
608 def clone(
608 def clone(
609 ui,
609 ui,
610 peeropts,
610 peeropts,
611 source,
611 source,
612 dest=None,
612 dest=None,
613 pull=False,
613 pull=False,
614 revs=None,
614 revs=None,
615 update=True,
615 update=True,
616 stream=False,
616 stream=False,
617 branch=None,
617 branch=None,
618 shareopts=None,
618 shareopts=None,
619 storeincludepats=None,
619 storeincludepats=None,
620 storeexcludepats=None,
620 storeexcludepats=None,
621 depth=None,
621 depth=None,
622 ):
622 ):
623 """Make a copy of an existing repository.
623 """Make a copy of an existing repository.
624
624
625 Create a copy of an existing repository in a new directory. The
625 Create a copy of an existing repository in a new directory. The
626 source and destination are URLs, as passed to the repository
626 source and destination are URLs, as passed to the repository
627 function. Returns a pair of repository peers, the source and
627 function. Returns a pair of repository peers, the source and
628 newly created destination.
628 newly created destination.
629
629
630 The location of the source is added to the new repository's
630 The location of the source is added to the new repository's
631 .hg/hgrc file, as the default to be used for future pulls and
631 .hg/hgrc file, as the default to be used for future pulls and
632 pushes.
632 pushes.
633
633
634 If an exception is raised, the partly cloned/updated destination
634 If an exception is raised, the partly cloned/updated destination
635 repository will be deleted.
635 repository will be deleted.
636
636
637 Arguments:
637 Arguments:
638
638
639 source: repository object or URL
639 source: repository object or URL
640
640
641 dest: URL of destination repository to create (defaults to base
641 dest: URL of destination repository to create (defaults to base
642 name of source repository)
642 name of source repository)
643
643
644 pull: always pull from source repository, even in local case or if the
644 pull: always pull from source repository, even in local case or if the
645 server prefers streaming
645 server prefers streaming
646
646
647 stream: stream raw data uncompressed from repository (fast over
647 stream: stream raw data uncompressed from repository (fast over
648 LAN, slow over WAN)
648 LAN, slow over WAN)
649
649
650 revs: revision to clone up to (implies pull=True)
650 revs: revision to clone up to (implies pull=True)
651
651
652 update: update working directory after clone completes, if
652 update: update working directory after clone completes, if
653 destination is local repository (True means update to default rev,
653 destination is local repository (True means update to default rev,
654 anything else is treated as a revision)
654 anything else is treated as a revision)
655
655
656 branch: branches to clone
656 branch: branches to clone
657
657
658 shareopts: dict of options to control auto sharing behavior. The "pool" key
658 shareopts: dict of options to control auto sharing behavior. The "pool" key
659 activates auto sharing mode and defines the directory for stores. The
659 activates auto sharing mode and defines the directory for stores. The
660 "mode" key determines how to construct the directory name of the shared
660 "mode" key determines how to construct the directory name of the shared
661 repository. "identity" means the name is derived from the node of the first
661 repository. "identity" means the name is derived from the node of the first
662 changeset in the repository. "remote" means the name is derived from the
662 changeset in the repository. "remote" means the name is derived from the
663 remote's path/URL. Defaults to "identity."
663 remote's path/URL. Defaults to "identity."
664
664
665 storeincludepats and storeexcludepats: sets of file patterns to include and
665 storeincludepats and storeexcludepats: sets of file patterns to include and
666 exclude in the repository copy, respectively. If not defined, all files
666 exclude in the repository copy, respectively. If not defined, all files
667 will be included (a "full" clone). Otherwise a "narrow" clone containing
667 will be included (a "full" clone). Otherwise a "narrow" clone containing
668 only the requested files will be performed. If ``storeincludepats`` is not
668 only the requested files will be performed. If ``storeincludepats`` is not
669 defined but ``storeexcludepats`` is, ``storeincludepats`` is assumed to be
669 defined but ``storeexcludepats`` is, ``storeincludepats`` is assumed to be
670 ``path:.``. If both are empty sets, no files will be cloned.
670 ``path:.``. If both are empty sets, no files will be cloned.
671 """
671 """
672
672
673 if isinstance(source, bytes):
673 if isinstance(source, bytes):
674 src = urlutil.get_clone_path(ui, source, branch)
674 src = urlutil.get_clone_path(ui, source, branch)
675 origsource, source, branches = src
675 origsource, source, branches = src
676 srcpeer = peer(ui, peeropts, source)
676 srcpeer = peer(ui, peeropts, source)
677 else:
677 else:
678 srcpeer = source.peer() # in case we were called with a localrepo
678 srcpeer = source.peer() # in case we were called with a localrepo
679 branches = (None, branch or [])
679 branches = (None, branch or [])
680 origsource = source = srcpeer.url()
680 origsource = source = srcpeer.url()
681 srclock = destlock = cleandir = None
681 srclock = destlock = cleandir = None
682 destpeer = None
682 destpeer = None
683 try:
683 try:
684 revs, checkout = addbranchrevs(srcpeer, srcpeer, branches, revs)
684 revs, checkout = addbranchrevs(srcpeer, srcpeer, branches, revs)
685
685
686 if dest is None:
686 if dest is None:
687 dest = defaultdest(source)
687 dest = defaultdest(source)
688 if dest:
688 if dest:
689 ui.status(_(b"destination directory: %s\n") % dest)
689 ui.status(_(b"destination directory: %s\n") % dest)
690 else:
690 else:
691 dest = urlutil.get_clone_path(ui, dest)[0]
691 dest = urlutil.get_clone_path(ui, dest)[0]
692
692
693 dest = urlutil.urllocalpath(dest)
693 dest = urlutil.urllocalpath(dest)
694 source = urlutil.urllocalpath(source)
694 source = urlutil.urllocalpath(source)
695
695
696 if not dest:
696 if not dest:
697 raise error.InputError(_(b"empty destination path is not valid"))
697 raise error.InputError(_(b"empty destination path is not valid"))
698
698
699 destvfs = vfsmod.vfs(dest, expandpath=True)
699 destvfs = vfsmod.vfs(dest, expandpath=True)
700 if destvfs.lexists():
700 if destvfs.lexists():
701 if not destvfs.isdir():
701 if not destvfs.isdir():
702 raise error.InputError(
702 raise error.InputError(
703 _(b"destination '%s' already exists") % dest
703 _(b"destination '%s' already exists") % dest
704 )
704 )
705 elif destvfs.listdir():
705 elif destvfs.listdir():
706 raise error.InputError(
706 raise error.InputError(
707 _(b"destination '%s' is not empty") % dest
707 _(b"destination '%s' is not empty") % dest
708 )
708 )
709
709
710 createopts = {}
710 createopts = {}
711 narrow = False
711 narrow = False
712
712
713 if storeincludepats is not None:
713 if storeincludepats is not None:
714 narrowspec.validatepatterns(storeincludepats)
714 narrowspec.validatepatterns(storeincludepats)
715 narrow = True
715 narrow = True
716
716
717 if storeexcludepats is not None:
717 if storeexcludepats is not None:
718 narrowspec.validatepatterns(storeexcludepats)
718 narrowspec.validatepatterns(storeexcludepats)
719 narrow = True
719 narrow = True
720
720
721 if narrow:
721 if narrow:
722 # Include everything by default if only exclusion patterns defined.
722 # Include everything by default if only exclusion patterns defined.
723 if storeexcludepats and not storeincludepats:
723 if storeexcludepats and not storeincludepats:
724 storeincludepats = {b'path:.'}
724 storeincludepats = {b'path:.'}
725
725
726 createopts[b'narrowfiles'] = True
726 createopts[b'narrowfiles'] = True
727
727
728 if depth:
728 if depth:
729 createopts[b'shallowfilestore'] = True
729 createopts[b'shallowfilestore'] = True
730
730
731 if srcpeer.capable(b'lfs-serve'):
731 if srcpeer.capable(b'lfs-serve'):
732 # Repository creation honors the config if it disabled the extension, so
732 # Repository creation honors the config if it disabled the extension, so
733 # we can't just announce that lfs will be enabled. This check avoids
733 # we can't just announce that lfs will be enabled. This check avoids
734 # saying that lfs will be enabled, and then saying it's an unknown
734 # saying that lfs will be enabled, and then saying it's an unknown
735 # feature. The lfs creation option is set in either case so that a
735 # feature. The lfs creation option is set in either case so that a
736 # requirement is added. If the extension is explicitly disabled but the
736 # requirement is added. If the extension is explicitly disabled but the
737 # requirement is set, the clone aborts early, before transferring any
737 # requirement is set, the clone aborts early, before transferring any
738 # data.
738 # data.
739 createopts[b'lfs'] = True
739 createopts[b'lfs'] = True
740
740
741 if extensions.disabled_help(b'lfs'):
741 if extensions.disabled_help(b'lfs'):
742 ui.status(
742 ui.status(
743 _(
743 _(
744 b'(remote is using large file support (lfs), but it is '
744 b'(remote is using large file support (lfs), but it is '
745 b'explicitly disabled in the local configuration)\n'
745 b'explicitly disabled in the local configuration)\n'
746 )
746 )
747 )
747 )
748 else:
748 else:
749 ui.status(
749 ui.status(
750 _(
750 _(
751 b'(remote is using large file support (lfs); lfs will '
751 b'(remote is using large file support (lfs); lfs will '
752 b'be enabled for this repository)\n'
752 b'be enabled for this repository)\n'
753 )
753 )
754 )
754 )
755
755
756 shareopts = shareopts or {}
756 shareopts = shareopts or {}
757 sharepool = shareopts.get(b'pool')
757 sharepool = shareopts.get(b'pool')
758 sharenamemode = shareopts.get(b'mode')
758 sharenamemode = shareopts.get(b'mode')
759 if sharepool and islocal(dest):
759 if sharepool and islocal(dest):
760 sharepath = None
760 sharepath = None
761 if sharenamemode == b'identity':
761 if sharenamemode == b'identity':
762 # Resolve the name from the initial changeset in the remote
762 # Resolve the name from the initial changeset in the remote
763 # repository. This returns nullid when the remote is empty. It
763 # repository. This returns nullid when the remote is empty. It
764 # raises RepoLookupError if revision 0 is filtered or otherwise
764 # raises RepoLookupError if revision 0 is filtered or otherwise
765 # not available. If we fail to resolve, sharing is not enabled.
765 # not available. If we fail to resolve, sharing is not enabled.
766 try:
766 try:
767 with srcpeer.commandexecutor() as e:
767 with srcpeer.commandexecutor() as e:
768 rootnode = e.callcommand(
768 rootnode = e.callcommand(
769 b'lookup',
769 b'lookup',
770 {
770 {
771 b'key': b'0',
771 b'key': b'0',
772 },
772 },
773 ).result()
773 ).result()
774
774
775 if rootnode != sha1nodeconstants.nullid:
775 if rootnode != sha1nodeconstants.nullid:
776 sharepath = os.path.join(sharepool, hex(rootnode))
776 sharepath = os.path.join(sharepool, hex(rootnode))
777 else:
777 else:
778 ui.status(
778 ui.status(
779 _(
779 _(
780 b'(not using pooled storage: '
780 b'(not using pooled storage: '
781 b'remote appears to be empty)\n'
781 b'remote appears to be empty)\n'
782 )
782 )
783 )
783 )
784 except error.RepoLookupError:
784 except error.RepoLookupError:
785 ui.status(
785 ui.status(
786 _(
786 _(
787 b'(not using pooled storage: '
787 b'(not using pooled storage: '
788 b'unable to resolve identity of remote)\n'
788 b'unable to resolve identity of remote)\n'
789 )
789 )
790 )
790 )
791 elif sharenamemode == b'remote':
791 elif sharenamemode == b'remote':
792 sharepath = os.path.join(
792 sharepath = os.path.join(
793 sharepool, hex(hashutil.sha1(source).digest())
793 sharepool, hex(hashutil.sha1(source).digest())
794 )
794 )
795 else:
795 else:
796 raise error.Abort(
796 raise error.Abort(
797 _(b'unknown share naming mode: %s') % sharenamemode
797 _(b'unknown share naming mode: %s') % sharenamemode
798 )
798 )
799
799
800 # TODO this is a somewhat arbitrary restriction.
800 # TODO this is a somewhat arbitrary restriction.
801 if narrow:
801 if narrow:
802 ui.status(
802 ui.status(
803 _(b'(pooled storage not supported for narrow clones)\n')
803 _(b'(pooled storage not supported for narrow clones)\n')
804 )
804 )
805 sharepath = None
805 sharepath = None
806
806
807 if sharepath:
807 if sharepath:
808 return clonewithshare(
808 return clonewithshare(
809 ui,
809 ui,
810 peeropts,
810 peeropts,
811 sharepath,
811 sharepath,
812 source,
812 source,
813 srcpeer,
813 srcpeer,
814 dest,
814 dest,
815 pull=pull,
815 pull=pull,
816 rev=revs,
816 rev=revs,
817 update=update,
817 update=update,
818 stream=stream,
818 stream=stream,
819 )
819 )
820
820
821 srcrepo = srcpeer.local()
821 srcrepo = srcpeer.local()
822
822
823 abspath = origsource
823 abspath = origsource
824 if islocal(origsource):
824 if islocal(origsource):
825 abspath = os.path.abspath(urlutil.urllocalpath(origsource))
825 abspath = os.path.abspath(urlutil.urllocalpath(origsource))
826
826
827 if islocal(dest):
827 if islocal(dest):
828 if os.path.exists(dest):
828 if os.path.exists(dest):
829 # only clean up directories we create ourselves
829 # only clean up directories we create ourselves
830 hgdir = os.path.realpath(os.path.join(dest, b".hg"))
830 hgdir = os.path.realpath(os.path.join(dest, b".hg"))
831 cleandir = hgdir
831 cleandir = hgdir
832 else:
832 else:
833 cleandir = dest
833 cleandir = dest
834
834
835 copy = False
835 copy = False
836 if (
836 if (
837 srcrepo
837 srcrepo
838 and srcrepo.cancopy()
838 and srcrepo.cancopy()
839 and islocal(dest)
839 and islocal(dest)
840 and not phases.hassecret(srcrepo)
840 and not phases.hassecret(srcrepo)
841 ):
841 ):
842 copy = not pull and not revs
842 copy = not pull and not revs
843
843
844 # TODO this is a somewhat arbitrary restriction.
844 # TODO this is a somewhat arbitrary restriction.
845 if narrow:
845 if narrow:
846 copy = False
846 copy = False
847
847
848 if copy:
848 if copy:
849 try:
849 try:
850 # we use a lock here because if we race with commit, we
850 # we use a lock here because if we race with commit, we
851 # can end up with extra data in the cloned revlogs that's
851 # can end up with extra data in the cloned revlogs that's
852 # not pointed to by changesets, thus causing verify to
852 # not pointed to by changesets, thus causing verify to
853 # fail
853 # fail
854 srclock = srcrepo.lock(wait=False)
854 srclock = srcrepo.lock(wait=False)
855 except error.LockError:
855 except error.LockError:
856 copy = False
856 copy = False
857
857
858 if copy:
858 if copy:
859 srcrepo.hook(b'preoutgoing', throw=True, source=b'clone')
859 srcrepo.hook(b'preoutgoing', throw=True, source=b'clone')
860 hgdir = os.path.realpath(os.path.join(dest, b".hg"))
860
861 if not os.path.exists(dest):
861 destrootpath = urlutil.urllocalpath(dest)
862 util.makedirs(dest)
862 dest_reqs = localrepo.clone_requirements(ui, createopts, srcrepo)
863 try:
863 localrepo.createrepository(
864 destpath = hgdir
864 ui,
865 util.makedir(destpath, notindexed=True)
865 destrootpath,
866 except OSError as inst:
866 requirements=dest_reqs,
867 if inst.errno == errno.EEXIST:
868 cleandir = None
869 raise error.Abort(
870 _(b"destination '%s' already exists") % dest
871 )
867 )
872 raise
868 destrepo = localrepo.makelocalrepository(ui, destrootpath)
873
869
870 destpath = destrepo.vfs.base
874 destlock = copystore(ui, srcrepo, destpath)
871 destlock = copystore(ui, srcrepo, destpath)
875 # copy bookmarks over
872 # copy bookmarks over
876 srcbookmarks = srcrepo.vfs.join(b'bookmarks')
873 srcbookmarks = srcrepo.vfs.join(b'bookmarks')
877 dstbookmarks = os.path.join(destpath, b'bookmarks')
874 dstbookmarks = os.path.join(destpath, b'bookmarks')
878 if os.path.exists(srcbookmarks):
875 if os.path.exists(srcbookmarks):
879 util.copyfile(srcbookmarks, dstbookmarks)
876 util.copyfile(srcbookmarks, dstbookmarks)
880
877
881 dstcachedir = os.path.join(destpath, b'cache')
878 dstcachedir = os.path.join(destpath, b'cache')
882 for cache in cacheutil.cachetocopy(srcrepo):
879 for cache in cacheutil.cachetocopy(srcrepo):
883 _copycache(srcrepo, dstcachedir, cache)
880 _copycache(srcrepo, dstcachedir, cache)
884
881
885 # we need to re-init the repo after manually copying the data
882 # we need to re-init the repo after manually copying the data
886 # into it
883 # into it
887 destpeer = peer(srcrepo, peeropts, dest)
884 destpeer = peer(srcrepo, peeropts, dest)
888 srcrepo.hook(
885 srcrepo.hook(
889 b'outgoing', source=b'clone', node=srcrepo.nodeconstants.nullhex
886 b'outgoing', source=b'clone', node=srcrepo.nodeconstants.nullhex
890 )
887 )
891 else:
888 else:
892 try:
889 try:
893 # only pass ui when no srcrepo
890 # only pass ui when no srcrepo
894 destpeer = peer(
891 destpeer = peer(
895 srcrepo or ui,
892 srcrepo or ui,
896 peeropts,
893 peeropts,
897 dest,
894 dest,
898 create=True,
895 create=True,
899 createopts=createopts,
896 createopts=createopts,
900 )
897 )
901 except OSError as inst:
898 except OSError as inst:
902 if inst.errno == errno.EEXIST:
899 if inst.errno == errno.EEXIST:
903 cleandir = None
900 cleandir = None
904 raise error.Abort(
901 raise error.Abort(
905 _(b"destination '%s' already exists") % dest
902 _(b"destination '%s' already exists") % dest
906 )
903 )
907 raise
904 raise
908
905
909 if revs:
906 if revs:
910 if not srcpeer.capable(b'lookup'):
907 if not srcpeer.capable(b'lookup'):
911 raise error.Abort(
908 raise error.Abort(
912 _(
909 _(
913 b"src repository does not support "
910 b"src repository does not support "
914 b"revision lookup and so doesn't "
911 b"revision lookup and so doesn't "
915 b"support clone by revision"
912 b"support clone by revision"
916 )
913 )
917 )
914 )
918
915
919 # TODO this is batchable.
916 # TODO this is batchable.
920 remoterevs = []
917 remoterevs = []
921 for rev in revs:
918 for rev in revs:
922 with srcpeer.commandexecutor() as e:
919 with srcpeer.commandexecutor() as e:
923 remoterevs.append(
920 remoterevs.append(
924 e.callcommand(
921 e.callcommand(
925 b'lookup',
922 b'lookup',
926 {
923 {
927 b'key': rev,
924 b'key': rev,
928 },
925 },
929 ).result()
926 ).result()
930 )
927 )
931 revs = remoterevs
928 revs = remoterevs
932
929
933 checkout = revs[0]
930 checkout = revs[0]
934 else:
931 else:
935 revs = None
932 revs = None
936 local = destpeer.local()
933 local = destpeer.local()
937 if local:
934 if local:
938 if narrow:
935 if narrow:
939 with local.wlock(), local.lock():
936 with local.wlock(), local.lock():
940 local.setnarrowpats(storeincludepats, storeexcludepats)
937 local.setnarrowpats(storeincludepats, storeexcludepats)
941 narrowspec.copytoworkingcopy(local)
938 narrowspec.copytoworkingcopy(local)
942
939
943 u = urlutil.url(abspath)
940 u = urlutil.url(abspath)
944 defaulturl = bytes(u)
941 defaulturl = bytes(u)
945 local.ui.setconfig(b'paths', b'default', defaulturl, b'clone')
942 local.ui.setconfig(b'paths', b'default', defaulturl, b'clone')
946 if not stream:
943 if not stream:
947 if pull:
944 if pull:
948 stream = False
945 stream = False
949 else:
946 else:
950 stream = None
947 stream = None
951 # internal config: ui.quietbookmarkmove
948 # internal config: ui.quietbookmarkmove
952 overrides = {(b'ui', b'quietbookmarkmove'): True}
949 overrides = {(b'ui', b'quietbookmarkmove'): True}
953 with local.ui.configoverride(overrides, b'clone'):
950 with local.ui.configoverride(overrides, b'clone'):
954 exchange.pull(
951 exchange.pull(
955 local,
952 local,
956 srcpeer,
953 srcpeer,
957 revs,
954 revs,
958 streamclonerequested=stream,
955 streamclonerequested=stream,
959 includepats=storeincludepats,
956 includepats=storeincludepats,
960 excludepats=storeexcludepats,
957 excludepats=storeexcludepats,
961 depth=depth,
958 depth=depth,
962 )
959 )
963 elif srcrepo:
960 elif srcrepo:
964 # TODO lift restriction once exchange.push() accepts narrow
961 # TODO lift restriction once exchange.push() accepts narrow
965 # push.
962 # push.
966 if narrow:
963 if narrow:
967 raise error.Abort(
964 raise error.Abort(
968 _(
965 _(
969 b'narrow clone not available for '
966 b'narrow clone not available for '
970 b'remote destinations'
967 b'remote destinations'
971 )
968 )
972 )
969 )
973
970
974 exchange.push(
971 exchange.push(
975 srcrepo,
972 srcrepo,
976 destpeer,
973 destpeer,
977 revs=revs,
974 revs=revs,
978 bookmarks=srcrepo._bookmarks.keys(),
975 bookmarks=srcrepo._bookmarks.keys(),
979 )
976 )
980 else:
977 else:
981 raise error.Abort(
978 raise error.Abort(
982 _(b"clone from remote to remote not supported")
979 _(b"clone from remote to remote not supported")
983 )
980 )
984
981
985 cleandir = None
982 cleandir = None
986
983
987 destrepo = destpeer.local()
984 destrepo = destpeer.local()
988 if destrepo:
985 if destrepo:
989 template = uimod.samplehgrcs[b'cloned']
986 template = uimod.samplehgrcs[b'cloned']
990 u = urlutil.url(abspath)
987 u = urlutil.url(abspath)
991 u.passwd = None
988 u.passwd = None
992 defaulturl = bytes(u)
989 defaulturl = bytes(u)
993 destrepo.vfs.write(b'hgrc', util.tonativeeol(template % defaulturl))
990 destrepo.vfs.write(b'hgrc', util.tonativeeol(template % defaulturl))
994 destrepo.ui.setconfig(b'paths', b'default', defaulturl, b'clone')
991 destrepo.ui.setconfig(b'paths', b'default', defaulturl, b'clone')
995
992
996 if ui.configbool(b'experimental', b'remotenames'):
993 if ui.configbool(b'experimental', b'remotenames'):
997 logexchange.pullremotenames(destrepo, srcpeer)
994 logexchange.pullremotenames(destrepo, srcpeer)
998
995
999 if update:
996 if update:
1000 if update is not True:
997 if update is not True:
1001 with srcpeer.commandexecutor() as e:
998 with srcpeer.commandexecutor() as e:
1002 checkout = e.callcommand(
999 checkout = e.callcommand(
1003 b'lookup',
1000 b'lookup',
1004 {
1001 {
1005 b'key': update,
1002 b'key': update,
1006 },
1003 },
1007 ).result()
1004 ).result()
1008
1005
1009 uprev = None
1006 uprev = None
1010 status = None
1007 status = None
1011 if checkout is not None:
1008 if checkout is not None:
1012 # Some extensions (at least hg-git and hg-subversion) have
1009 # Some extensions (at least hg-git and hg-subversion) have
1013 # a peer.lookup() implementation that returns a name instead
1010 # a peer.lookup() implementation that returns a name instead
1014 # of a nodeid. We work around it here until we've figured
1011 # of a nodeid. We work around it here until we've figured
1015 # out a better solution.
1012 # out a better solution.
1016 if len(checkout) == 20 and checkout in destrepo:
1013 if len(checkout) == 20 and checkout in destrepo:
1017 uprev = checkout
1014 uprev = checkout
1018 elif scmutil.isrevsymbol(destrepo, checkout):
1015 elif scmutil.isrevsymbol(destrepo, checkout):
1019 uprev = scmutil.revsymbol(destrepo, checkout).node()
1016 uprev = scmutil.revsymbol(destrepo, checkout).node()
1020 else:
1017 else:
1021 if update is not True:
1018 if update is not True:
1022 try:
1019 try:
1023 uprev = destrepo.lookup(update)
1020 uprev = destrepo.lookup(update)
1024 except error.RepoLookupError:
1021 except error.RepoLookupError:
1025 pass
1022 pass
1026 if uprev is None:
1023 if uprev is None:
1027 try:
1024 try:
1028 if destrepo._activebookmark:
1025 if destrepo._activebookmark:
1029 uprev = destrepo.lookup(destrepo._activebookmark)
1026 uprev = destrepo.lookup(destrepo._activebookmark)
1030 update = destrepo._activebookmark
1027 update = destrepo._activebookmark
1031 else:
1028 else:
1032 uprev = destrepo._bookmarks[b'@']
1029 uprev = destrepo._bookmarks[b'@']
1033 update = b'@'
1030 update = b'@'
1034 bn = destrepo[uprev].branch()
1031 bn = destrepo[uprev].branch()
1035 if bn == b'default':
1032 if bn == b'default':
1036 status = _(b"updating to bookmark %s\n" % update)
1033 status = _(b"updating to bookmark %s\n" % update)
1037 else:
1034 else:
1038 status = (
1035 status = (
1039 _(b"updating to bookmark %s on branch %s\n")
1036 _(b"updating to bookmark %s on branch %s\n")
1040 ) % (update, bn)
1037 ) % (update, bn)
1041 except KeyError:
1038 except KeyError:
1042 try:
1039 try:
1043 uprev = destrepo.branchtip(b'default')
1040 uprev = destrepo.branchtip(b'default')
1044 except error.RepoLookupError:
1041 except error.RepoLookupError:
1045 uprev = destrepo.lookup(b'tip')
1042 uprev = destrepo.lookup(b'tip')
1046 if not status:
1043 if not status:
1047 bn = destrepo[uprev].branch()
1044 bn = destrepo[uprev].branch()
1048 status = _(b"updating to branch %s\n") % bn
1045 status = _(b"updating to branch %s\n") % bn
1049 destrepo.ui.status(status)
1046 destrepo.ui.status(status)
1050 _update(destrepo, uprev)
1047 _update(destrepo, uprev)
1051 if update in destrepo._bookmarks:
1048 if update in destrepo._bookmarks:
1052 bookmarks.activate(destrepo, update)
1049 bookmarks.activate(destrepo, update)
1053 if destlock is not None:
1050 if destlock is not None:
1054 release(destlock)
1051 release(destlock)
1055 # here is a tiny windows were someone could end up writing the
1052 # here is a tiny windows were someone could end up writing the
1056 # repository before the cache are sure to be warm. This is "fine"
1053 # repository before the cache are sure to be warm. This is "fine"
1057 # as the only "bad" outcome would be some slowness. That potential
1054 # as the only "bad" outcome would be some slowness. That potential
1058 # slowness already affect reader.
1055 # slowness already affect reader.
1059 with destrepo.lock():
1056 with destrepo.lock():
1060 destrepo.updatecaches(caches=repositorymod.CACHES_POST_CLONE)
1057 destrepo.updatecaches(caches=repositorymod.CACHES_POST_CLONE)
1061 finally:
1058 finally:
1062 release(srclock, destlock)
1059 release(srclock, destlock)
1063 if cleandir is not None:
1060 if cleandir is not None:
1064 shutil.rmtree(cleandir, True)
1061 shutil.rmtree(cleandir, True)
1065 if srcpeer is not None:
1062 if srcpeer is not None:
1066 srcpeer.close()
1063 srcpeer.close()
1067 if destpeer and destpeer.local() is None:
1064 if destpeer and destpeer.local() is None:
1068 destpeer.close()
1065 destpeer.close()
1069 return srcpeer, destpeer
1066 return srcpeer, destpeer
1070
1067
1071
1068
1072 def _showstats(repo, stats, quietempty=False):
1069 def _showstats(repo, stats, quietempty=False):
1073 if quietempty and stats.isempty():
1070 if quietempty and stats.isempty():
1074 return
1071 return
1075 repo.ui.status(
1072 repo.ui.status(
1076 _(
1073 _(
1077 b"%d files updated, %d files merged, "
1074 b"%d files updated, %d files merged, "
1078 b"%d files removed, %d files unresolved\n"
1075 b"%d files removed, %d files unresolved\n"
1079 )
1076 )
1080 % (
1077 % (
1081 stats.updatedcount,
1078 stats.updatedcount,
1082 stats.mergedcount,
1079 stats.mergedcount,
1083 stats.removedcount,
1080 stats.removedcount,
1084 stats.unresolvedcount,
1081 stats.unresolvedcount,
1085 )
1082 )
1086 )
1083 )
1087
1084
1088
1085
1089 def updaterepo(repo, node, overwrite, updatecheck=None):
1086 def updaterepo(repo, node, overwrite, updatecheck=None):
1090 """Update the working directory to node.
1087 """Update the working directory to node.
1091
1088
1092 When overwrite is set, changes are clobbered, merged else
1089 When overwrite is set, changes are clobbered, merged else
1093
1090
1094 returns stats (see pydoc mercurial.merge.applyupdates)"""
1091 returns stats (see pydoc mercurial.merge.applyupdates)"""
1095 repo.ui.deprecwarn(
1092 repo.ui.deprecwarn(
1096 b'prefer merge.update() or merge.clean_update() over hg.updaterepo()',
1093 b'prefer merge.update() or merge.clean_update() over hg.updaterepo()',
1097 b'5.7',
1094 b'5.7',
1098 )
1095 )
1099 return mergemod._update(
1096 return mergemod._update(
1100 repo,
1097 repo,
1101 node,
1098 node,
1102 branchmerge=False,
1099 branchmerge=False,
1103 force=overwrite,
1100 force=overwrite,
1104 labels=[b'working copy', b'destination'],
1101 labels=[b'working copy', b'destination'],
1105 updatecheck=updatecheck,
1102 updatecheck=updatecheck,
1106 )
1103 )
1107
1104
1108
1105
1109 def update(repo, node, quietempty=False, updatecheck=None):
1106 def update(repo, node, quietempty=False, updatecheck=None):
1110 """update the working directory to node"""
1107 """update the working directory to node"""
1111 stats = mergemod.update(repo[node], updatecheck=updatecheck)
1108 stats = mergemod.update(repo[node], updatecheck=updatecheck)
1112 _showstats(repo, stats, quietempty)
1109 _showstats(repo, stats, quietempty)
1113 if stats.unresolvedcount:
1110 if stats.unresolvedcount:
1114 repo.ui.status(_(b"use 'hg resolve' to retry unresolved file merges\n"))
1111 repo.ui.status(_(b"use 'hg resolve' to retry unresolved file merges\n"))
1115 return stats.unresolvedcount > 0
1112 return stats.unresolvedcount > 0
1116
1113
1117
1114
1118 # naming conflict in clone()
1115 # naming conflict in clone()
1119 _update = update
1116 _update = update
1120
1117
1121
1118
1122 def clean(repo, node, show_stats=True, quietempty=False):
1119 def clean(repo, node, show_stats=True, quietempty=False):
1123 """forcibly switch the working directory to node, clobbering changes"""
1120 """forcibly switch the working directory to node, clobbering changes"""
1124 stats = mergemod.clean_update(repo[node])
1121 stats = mergemod.clean_update(repo[node])
1125 assert stats.unresolvedcount == 0
1122 assert stats.unresolvedcount == 0
1126 if show_stats:
1123 if show_stats:
1127 _showstats(repo, stats, quietempty)
1124 _showstats(repo, stats, quietempty)
1128 return False
1125 return False
1129
1126
1130
1127
1131 # naming conflict in updatetotally()
1128 # naming conflict in updatetotally()
1132 _clean = clean
1129 _clean = clean
1133
1130
1134 _VALID_UPDATECHECKS = {
1131 _VALID_UPDATECHECKS = {
1135 mergemod.UPDATECHECK_ABORT,
1132 mergemod.UPDATECHECK_ABORT,
1136 mergemod.UPDATECHECK_NONE,
1133 mergemod.UPDATECHECK_NONE,
1137 mergemod.UPDATECHECK_LINEAR,
1134 mergemod.UPDATECHECK_LINEAR,
1138 mergemod.UPDATECHECK_NO_CONFLICT,
1135 mergemod.UPDATECHECK_NO_CONFLICT,
1139 }
1136 }
1140
1137
1141
1138
1142 def updatetotally(ui, repo, checkout, brev, clean=False, updatecheck=None):
1139 def updatetotally(ui, repo, checkout, brev, clean=False, updatecheck=None):
1143 """Update the working directory with extra care for non-file components
1140 """Update the working directory with extra care for non-file components
1144
1141
1145 This takes care of non-file components below:
1142 This takes care of non-file components below:
1146
1143
1147 :bookmark: might be advanced or (in)activated
1144 :bookmark: might be advanced or (in)activated
1148
1145
1149 This takes arguments below:
1146 This takes arguments below:
1150
1147
1151 :checkout: to which revision the working directory is updated
1148 :checkout: to which revision the working directory is updated
1152 :brev: a name, which might be a bookmark to be activated after updating
1149 :brev: a name, which might be a bookmark to be activated after updating
1153 :clean: whether changes in the working directory can be discarded
1150 :clean: whether changes in the working directory can be discarded
1154 :updatecheck: how to deal with a dirty working directory
1151 :updatecheck: how to deal with a dirty working directory
1155
1152
1156 Valid values for updatecheck are the UPDATECHECK_* constants
1153 Valid values for updatecheck are the UPDATECHECK_* constants
1157 defined in the merge module. Passing `None` will result in using the
1154 defined in the merge module. Passing `None` will result in using the
1158 configured default.
1155 configured default.
1159
1156
1160 * ABORT: abort if the working directory is dirty
1157 * ABORT: abort if the working directory is dirty
1161 * NONE: don't check (merge working directory changes into destination)
1158 * NONE: don't check (merge working directory changes into destination)
1162 * LINEAR: check that update is linear before merging working directory
1159 * LINEAR: check that update is linear before merging working directory
1163 changes into destination
1160 changes into destination
1164 * NO_CONFLICT: check that the update does not result in file merges
1161 * NO_CONFLICT: check that the update does not result in file merges
1165
1162
1166 This returns whether conflict is detected at updating or not.
1163 This returns whether conflict is detected at updating or not.
1167 """
1164 """
1168 if updatecheck is None:
1165 if updatecheck is None:
1169 updatecheck = ui.config(b'commands', b'update.check')
1166 updatecheck = ui.config(b'commands', b'update.check')
1170 if updatecheck not in _VALID_UPDATECHECKS:
1167 if updatecheck not in _VALID_UPDATECHECKS:
1171 # If not configured, or invalid value configured
1168 # If not configured, or invalid value configured
1172 updatecheck = mergemod.UPDATECHECK_LINEAR
1169 updatecheck = mergemod.UPDATECHECK_LINEAR
1173 if updatecheck not in _VALID_UPDATECHECKS:
1170 if updatecheck not in _VALID_UPDATECHECKS:
1174 raise ValueError(
1171 raise ValueError(
1175 r'Invalid updatecheck value %r (can accept %r)'
1172 r'Invalid updatecheck value %r (can accept %r)'
1176 % (updatecheck, _VALID_UPDATECHECKS)
1173 % (updatecheck, _VALID_UPDATECHECKS)
1177 )
1174 )
1178 with repo.wlock():
1175 with repo.wlock():
1179 movemarkfrom = None
1176 movemarkfrom = None
1180 warndest = False
1177 warndest = False
1181 if checkout is None:
1178 if checkout is None:
1182 updata = destutil.destupdate(repo, clean=clean)
1179 updata = destutil.destupdate(repo, clean=clean)
1183 checkout, movemarkfrom, brev = updata
1180 checkout, movemarkfrom, brev = updata
1184 warndest = True
1181 warndest = True
1185
1182
1186 if clean:
1183 if clean:
1187 ret = _clean(repo, checkout)
1184 ret = _clean(repo, checkout)
1188 else:
1185 else:
1189 if updatecheck == mergemod.UPDATECHECK_ABORT:
1186 if updatecheck == mergemod.UPDATECHECK_ABORT:
1190 cmdutil.bailifchanged(repo, merge=False)
1187 cmdutil.bailifchanged(repo, merge=False)
1191 updatecheck = mergemod.UPDATECHECK_NONE
1188 updatecheck = mergemod.UPDATECHECK_NONE
1192 ret = _update(repo, checkout, updatecheck=updatecheck)
1189 ret = _update(repo, checkout, updatecheck=updatecheck)
1193
1190
1194 if not ret and movemarkfrom:
1191 if not ret and movemarkfrom:
1195 if movemarkfrom == repo[b'.'].node():
1192 if movemarkfrom == repo[b'.'].node():
1196 pass # no-op update
1193 pass # no-op update
1197 elif bookmarks.update(repo, [movemarkfrom], repo[b'.'].node()):
1194 elif bookmarks.update(repo, [movemarkfrom], repo[b'.'].node()):
1198 b = ui.label(repo._activebookmark, b'bookmarks.active')
1195 b = ui.label(repo._activebookmark, b'bookmarks.active')
1199 ui.status(_(b"updating bookmark %s\n") % b)
1196 ui.status(_(b"updating bookmark %s\n") % b)
1200 else:
1197 else:
1201 # this can happen with a non-linear update
1198 # this can happen with a non-linear update
1202 b = ui.label(repo._activebookmark, b'bookmarks')
1199 b = ui.label(repo._activebookmark, b'bookmarks')
1203 ui.status(_(b"(leaving bookmark %s)\n") % b)
1200 ui.status(_(b"(leaving bookmark %s)\n") % b)
1204 bookmarks.deactivate(repo)
1201 bookmarks.deactivate(repo)
1205 elif brev in repo._bookmarks:
1202 elif brev in repo._bookmarks:
1206 if brev != repo._activebookmark:
1203 if brev != repo._activebookmark:
1207 b = ui.label(brev, b'bookmarks.active')
1204 b = ui.label(brev, b'bookmarks.active')
1208 ui.status(_(b"(activating bookmark %s)\n") % b)
1205 ui.status(_(b"(activating bookmark %s)\n") % b)
1209 bookmarks.activate(repo, brev)
1206 bookmarks.activate(repo, brev)
1210 elif brev:
1207 elif brev:
1211 if repo._activebookmark:
1208 if repo._activebookmark:
1212 b = ui.label(repo._activebookmark, b'bookmarks')
1209 b = ui.label(repo._activebookmark, b'bookmarks')
1213 ui.status(_(b"(leaving bookmark %s)\n") % b)
1210 ui.status(_(b"(leaving bookmark %s)\n") % b)
1214 bookmarks.deactivate(repo)
1211 bookmarks.deactivate(repo)
1215
1212
1216 if warndest:
1213 if warndest:
1217 destutil.statusotherdests(ui, repo)
1214 destutil.statusotherdests(ui, repo)
1218
1215
1219 return ret
1216 return ret
1220
1217
1221
1218
1222 def merge(
1219 def merge(
1223 ctx,
1220 ctx,
1224 force=False,
1221 force=False,
1225 remind=True,
1222 remind=True,
1226 labels=None,
1223 labels=None,
1227 ):
1224 ):
1228 """Branch merge with node, resolving changes. Return true if any
1225 """Branch merge with node, resolving changes. Return true if any
1229 unresolved conflicts."""
1226 unresolved conflicts."""
1230 repo = ctx.repo()
1227 repo = ctx.repo()
1231 stats = mergemod.merge(ctx, force=force, labels=labels)
1228 stats = mergemod.merge(ctx, force=force, labels=labels)
1232 _showstats(repo, stats)
1229 _showstats(repo, stats)
1233 if stats.unresolvedcount:
1230 if stats.unresolvedcount:
1234 repo.ui.status(
1231 repo.ui.status(
1235 _(
1232 _(
1236 b"use 'hg resolve' to retry unresolved file merges "
1233 b"use 'hg resolve' to retry unresolved file merges "
1237 b"or 'hg merge --abort' to abandon\n"
1234 b"or 'hg merge --abort' to abandon\n"
1238 )
1235 )
1239 )
1236 )
1240 elif remind:
1237 elif remind:
1241 repo.ui.status(_(b"(branch merge, don't forget to commit)\n"))
1238 repo.ui.status(_(b"(branch merge, don't forget to commit)\n"))
1242 return stats.unresolvedcount > 0
1239 return stats.unresolvedcount > 0
1243
1240
1244
1241
1245 def abortmerge(ui, repo):
1242 def abortmerge(ui, repo):
1246 ms = mergestatemod.mergestate.read(repo)
1243 ms = mergestatemod.mergestate.read(repo)
1247 if ms.active():
1244 if ms.active():
1248 # there were conflicts
1245 # there were conflicts
1249 node = ms.localctx.hex()
1246 node = ms.localctx.hex()
1250 else:
1247 else:
1251 # there were no conficts, mergestate was not stored
1248 # there were no conficts, mergestate was not stored
1252 node = repo[b'.'].hex()
1249 node = repo[b'.'].hex()
1253
1250
1254 repo.ui.status(_(b"aborting the merge, updating back to %s\n") % node[:12])
1251 repo.ui.status(_(b"aborting the merge, updating back to %s\n") % node[:12])
1255 stats = mergemod.clean_update(repo[node])
1252 stats = mergemod.clean_update(repo[node])
1256 assert stats.unresolvedcount == 0
1253 assert stats.unresolvedcount == 0
1257 _showstats(repo, stats)
1254 _showstats(repo, stats)
1258
1255
1259
1256
1260 def _incoming(
1257 def _incoming(
1261 displaychlist,
1258 displaychlist,
1262 subreporecurse,
1259 subreporecurse,
1263 ui,
1260 ui,
1264 repo,
1261 repo,
1265 source,
1262 source,
1266 opts,
1263 opts,
1267 buffered=False,
1264 buffered=False,
1268 subpath=None,
1265 subpath=None,
1269 ):
1266 ):
1270 """
1267 """
1271 Helper for incoming / gincoming.
1268 Helper for incoming / gincoming.
1272 displaychlist gets called with
1269 displaychlist gets called with
1273 (remoterepo, incomingchangesetlist, displayer) parameters,
1270 (remoterepo, incomingchangesetlist, displayer) parameters,
1274 and is supposed to contain only code that can't be unified.
1271 and is supposed to contain only code that can't be unified.
1275 """
1272 """
1276 srcs = urlutil.get_pull_paths(repo, ui, [source], opts.get(b'branch'))
1273 srcs = urlutil.get_pull_paths(repo, ui, [source], opts.get(b'branch'))
1277 srcs = list(srcs)
1274 srcs = list(srcs)
1278 if len(srcs) != 1:
1275 if len(srcs) != 1:
1279 msg = _(b'for now, incoming supports only a single source, %d provided')
1276 msg = _(b'for now, incoming supports only a single source, %d provided')
1280 msg %= len(srcs)
1277 msg %= len(srcs)
1281 raise error.Abort(msg)
1278 raise error.Abort(msg)
1282 source, branches = srcs[0]
1279 source, branches = srcs[0]
1283 if subpath is not None:
1280 if subpath is not None:
1284 subpath = urlutil.url(subpath)
1281 subpath = urlutil.url(subpath)
1285 if subpath.isabs():
1282 if subpath.isabs():
1286 source = bytes(subpath)
1283 source = bytes(subpath)
1287 else:
1284 else:
1288 p = urlutil.url(source)
1285 p = urlutil.url(source)
1289 p.path = os.path.normpath(b'%s/%s' % (p.path, subpath))
1286 p.path = os.path.normpath(b'%s/%s' % (p.path, subpath))
1290 source = bytes(p)
1287 source = bytes(p)
1291 other = peer(repo, opts, source)
1288 other = peer(repo, opts, source)
1292 cleanupfn = other.close
1289 cleanupfn = other.close
1293 try:
1290 try:
1294 ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(source))
1291 ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(source))
1295 revs, checkout = addbranchrevs(repo, other, branches, opts.get(b'rev'))
1292 revs, checkout = addbranchrevs(repo, other, branches, opts.get(b'rev'))
1296
1293
1297 if revs:
1294 if revs:
1298 revs = [other.lookup(rev) for rev in revs]
1295 revs = [other.lookup(rev) for rev in revs]
1299 other, chlist, cleanupfn = bundlerepo.getremotechanges(
1296 other, chlist, cleanupfn = bundlerepo.getremotechanges(
1300 ui, repo, other, revs, opts[b"bundle"], opts[b"force"]
1297 ui, repo, other, revs, opts[b"bundle"], opts[b"force"]
1301 )
1298 )
1302
1299
1303 if not chlist:
1300 if not chlist:
1304 ui.status(_(b"no changes found\n"))
1301 ui.status(_(b"no changes found\n"))
1305 return subreporecurse()
1302 return subreporecurse()
1306 ui.pager(b'incoming')
1303 ui.pager(b'incoming')
1307 displayer = logcmdutil.changesetdisplayer(
1304 displayer = logcmdutil.changesetdisplayer(
1308 ui, other, opts, buffered=buffered
1305 ui, other, opts, buffered=buffered
1309 )
1306 )
1310 displaychlist(other, chlist, displayer)
1307 displaychlist(other, chlist, displayer)
1311 displayer.close()
1308 displayer.close()
1312 finally:
1309 finally:
1313 cleanupfn()
1310 cleanupfn()
1314 subreporecurse()
1311 subreporecurse()
1315 return 0 # exit code is zero since we found incoming changes
1312 return 0 # exit code is zero since we found incoming changes
1316
1313
1317
1314
1318 def incoming(ui, repo, source, opts, subpath=None):
1315 def incoming(ui, repo, source, opts, subpath=None):
1319 def subreporecurse():
1316 def subreporecurse():
1320 ret = 1
1317 ret = 1
1321 if opts.get(b'subrepos'):
1318 if opts.get(b'subrepos'):
1322 ctx = repo[None]
1319 ctx = repo[None]
1323 for subpath in sorted(ctx.substate):
1320 for subpath in sorted(ctx.substate):
1324 sub = ctx.sub(subpath)
1321 sub = ctx.sub(subpath)
1325 ret = min(ret, sub.incoming(ui, source, opts))
1322 ret = min(ret, sub.incoming(ui, source, opts))
1326 return ret
1323 return ret
1327
1324
1328 def display(other, chlist, displayer):
1325 def display(other, chlist, displayer):
1329 limit = logcmdutil.getlimit(opts)
1326 limit = logcmdutil.getlimit(opts)
1330 if opts.get(b'newest_first'):
1327 if opts.get(b'newest_first'):
1331 chlist.reverse()
1328 chlist.reverse()
1332 count = 0
1329 count = 0
1333 for n in chlist:
1330 for n in chlist:
1334 if limit is not None and count >= limit:
1331 if limit is not None and count >= limit:
1335 break
1332 break
1336 parents = [
1333 parents = [
1337 p for p in other.changelog.parents(n) if p != repo.nullid
1334 p for p in other.changelog.parents(n) if p != repo.nullid
1338 ]
1335 ]
1339 if opts.get(b'no_merges') and len(parents) == 2:
1336 if opts.get(b'no_merges') and len(parents) == 2:
1340 continue
1337 continue
1341 count += 1
1338 count += 1
1342 displayer.show(other[n])
1339 displayer.show(other[n])
1343
1340
1344 return _incoming(
1341 return _incoming(
1345 display, subreporecurse, ui, repo, source, opts, subpath=subpath
1342 display, subreporecurse, ui, repo, source, opts, subpath=subpath
1346 )
1343 )
1347
1344
1348
1345
1349 def _outgoing(ui, repo, dests, opts, subpath=None):
1346 def _outgoing(ui, repo, dests, opts, subpath=None):
1350 out = set()
1347 out = set()
1351 others = []
1348 others = []
1352 for path in urlutil.get_push_paths(repo, ui, dests):
1349 for path in urlutil.get_push_paths(repo, ui, dests):
1353 dest = path.pushloc or path.loc
1350 dest = path.pushloc or path.loc
1354 if subpath is not None:
1351 if subpath is not None:
1355 subpath = urlutil.url(subpath)
1352 subpath = urlutil.url(subpath)
1356 if subpath.isabs():
1353 if subpath.isabs():
1357 dest = bytes(subpath)
1354 dest = bytes(subpath)
1358 else:
1355 else:
1359 p = urlutil.url(dest)
1356 p = urlutil.url(dest)
1360 p.path = os.path.normpath(b'%s/%s' % (p.path, subpath))
1357 p.path = os.path.normpath(b'%s/%s' % (p.path, subpath))
1361 dest = bytes(p)
1358 dest = bytes(p)
1362 branches = path.branch, opts.get(b'branch') or []
1359 branches = path.branch, opts.get(b'branch') or []
1363
1360
1364 ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(dest))
1361 ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(dest))
1365 revs, checkout = addbranchrevs(repo, repo, branches, opts.get(b'rev'))
1362 revs, checkout = addbranchrevs(repo, repo, branches, opts.get(b'rev'))
1366 if revs:
1363 if revs:
1367 revs = [repo[rev].node() for rev in scmutil.revrange(repo, revs)]
1364 revs = [repo[rev].node() for rev in scmutil.revrange(repo, revs)]
1368
1365
1369 other = peer(repo, opts, dest)
1366 other = peer(repo, opts, dest)
1370 try:
1367 try:
1371 outgoing = discovery.findcommonoutgoing(
1368 outgoing = discovery.findcommonoutgoing(
1372 repo, other, revs, force=opts.get(b'force')
1369 repo, other, revs, force=opts.get(b'force')
1373 )
1370 )
1374 o = outgoing.missing
1371 o = outgoing.missing
1375 out.update(o)
1372 out.update(o)
1376 if not o:
1373 if not o:
1377 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
1374 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
1378 others.append(other)
1375 others.append(other)
1379 except: # re-raises
1376 except: # re-raises
1380 other.close()
1377 other.close()
1381 raise
1378 raise
1382 # make sure this is ordered by revision number
1379 # make sure this is ordered by revision number
1383 outgoing_revs = list(out)
1380 outgoing_revs = list(out)
1384 cl = repo.changelog
1381 cl = repo.changelog
1385 outgoing_revs.sort(key=cl.rev)
1382 outgoing_revs.sort(key=cl.rev)
1386 return outgoing_revs, others
1383 return outgoing_revs, others
1387
1384
1388
1385
1389 def _outgoing_recurse(ui, repo, dests, opts):
1386 def _outgoing_recurse(ui, repo, dests, opts):
1390 ret = 1
1387 ret = 1
1391 if opts.get(b'subrepos'):
1388 if opts.get(b'subrepos'):
1392 ctx = repo[None]
1389 ctx = repo[None]
1393 for subpath in sorted(ctx.substate):
1390 for subpath in sorted(ctx.substate):
1394 sub = ctx.sub(subpath)
1391 sub = ctx.sub(subpath)
1395 ret = min(ret, sub.outgoing(ui, dests, opts))
1392 ret = min(ret, sub.outgoing(ui, dests, opts))
1396 return ret
1393 return ret
1397
1394
1398
1395
1399 def _outgoing_filter(repo, revs, opts):
1396 def _outgoing_filter(repo, revs, opts):
1400 """apply revision filtering/ordering option for outgoing"""
1397 """apply revision filtering/ordering option for outgoing"""
1401 limit = logcmdutil.getlimit(opts)
1398 limit = logcmdutil.getlimit(opts)
1402 no_merges = opts.get(b'no_merges')
1399 no_merges = opts.get(b'no_merges')
1403 if opts.get(b'newest_first'):
1400 if opts.get(b'newest_first'):
1404 revs.reverse()
1401 revs.reverse()
1405 if limit is None and not no_merges:
1402 if limit is None and not no_merges:
1406 for r in revs:
1403 for r in revs:
1407 yield r
1404 yield r
1408 return
1405 return
1409
1406
1410 count = 0
1407 count = 0
1411 cl = repo.changelog
1408 cl = repo.changelog
1412 for n in revs:
1409 for n in revs:
1413 if limit is not None and count >= limit:
1410 if limit is not None and count >= limit:
1414 break
1411 break
1415 parents = [p for p in cl.parents(n) if p != repo.nullid]
1412 parents = [p for p in cl.parents(n) if p != repo.nullid]
1416 if no_merges and len(parents) == 2:
1413 if no_merges and len(parents) == 2:
1417 continue
1414 continue
1418 count += 1
1415 count += 1
1419 yield n
1416 yield n
1420
1417
1421
1418
1422 def outgoing(ui, repo, dests, opts, subpath=None):
1419 def outgoing(ui, repo, dests, opts, subpath=None):
1423 if opts.get(b'graph'):
1420 if opts.get(b'graph'):
1424 logcmdutil.checkunsupportedgraphflags([], opts)
1421 logcmdutil.checkunsupportedgraphflags([], opts)
1425 o, others = _outgoing(ui, repo, dests, opts, subpath=subpath)
1422 o, others = _outgoing(ui, repo, dests, opts, subpath=subpath)
1426 ret = 1
1423 ret = 1
1427 try:
1424 try:
1428 if o:
1425 if o:
1429 ret = 0
1426 ret = 0
1430
1427
1431 if opts.get(b'graph'):
1428 if opts.get(b'graph'):
1432 revdag = logcmdutil.graphrevs(repo, o, opts)
1429 revdag = logcmdutil.graphrevs(repo, o, opts)
1433 ui.pager(b'outgoing')
1430 ui.pager(b'outgoing')
1434 displayer = logcmdutil.changesetdisplayer(
1431 displayer = logcmdutil.changesetdisplayer(
1435 ui, repo, opts, buffered=True
1432 ui, repo, opts, buffered=True
1436 )
1433 )
1437 logcmdutil.displaygraph(
1434 logcmdutil.displaygraph(
1438 ui, repo, revdag, displayer, graphmod.asciiedges
1435 ui, repo, revdag, displayer, graphmod.asciiedges
1439 )
1436 )
1440 else:
1437 else:
1441 ui.pager(b'outgoing')
1438 ui.pager(b'outgoing')
1442 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
1439 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
1443 for n in _outgoing_filter(repo, o, opts):
1440 for n in _outgoing_filter(repo, o, opts):
1444 displayer.show(repo[n])
1441 displayer.show(repo[n])
1445 displayer.close()
1442 displayer.close()
1446 for oth in others:
1443 for oth in others:
1447 cmdutil.outgoinghooks(ui, repo, oth, opts, o)
1444 cmdutil.outgoinghooks(ui, repo, oth, opts, o)
1448 ret = min(ret, _outgoing_recurse(ui, repo, dests, opts))
1445 ret = min(ret, _outgoing_recurse(ui, repo, dests, opts))
1449 return ret # exit code is zero since we found outgoing changes
1446 return ret # exit code is zero since we found outgoing changes
1450 finally:
1447 finally:
1451 for oth in others:
1448 for oth in others:
1452 oth.close()
1449 oth.close()
1453
1450
1454
1451
1455 def verify(repo, level=None):
1452 def verify(repo, level=None):
1456 """verify the consistency of a repository"""
1453 """verify the consistency of a repository"""
1457 ret = verifymod.verify(repo, level=level)
1454 ret = verifymod.verify(repo, level=level)
1458
1455
1459 # Broken subrepo references in hidden csets don't seem worth worrying about,
1456 # Broken subrepo references in hidden csets don't seem worth worrying about,
1460 # since they can't be pushed/pulled, and --hidden can be used if they are a
1457 # since they can't be pushed/pulled, and --hidden can be used if they are a
1461 # concern.
1458 # concern.
1462
1459
1463 # pathto() is needed for -R case
1460 # pathto() is needed for -R case
1464 revs = repo.revs(
1461 revs = repo.revs(
1465 b"filelog(%s)", util.pathto(repo.root, repo.getcwd(), b'.hgsubstate')
1462 b"filelog(%s)", util.pathto(repo.root, repo.getcwd(), b'.hgsubstate')
1466 )
1463 )
1467
1464
1468 if revs:
1465 if revs:
1469 repo.ui.status(_(b'checking subrepo links\n'))
1466 repo.ui.status(_(b'checking subrepo links\n'))
1470 for rev in revs:
1467 for rev in revs:
1471 ctx = repo[rev]
1468 ctx = repo[rev]
1472 try:
1469 try:
1473 for subpath in ctx.substate:
1470 for subpath in ctx.substate:
1474 try:
1471 try:
1475 ret = (
1472 ret = (
1476 ctx.sub(subpath, allowcreate=False).verify() or ret
1473 ctx.sub(subpath, allowcreate=False).verify() or ret
1477 )
1474 )
1478 except error.RepoError as e:
1475 except error.RepoError as e:
1479 repo.ui.warn(b'%d: %s\n' % (rev, e))
1476 repo.ui.warn(b'%d: %s\n' % (rev, e))
1480 except Exception:
1477 except Exception:
1481 repo.ui.warn(
1478 repo.ui.warn(
1482 _(b'.hgsubstate is corrupt in revision %s\n')
1479 _(b'.hgsubstate is corrupt in revision %s\n')
1483 % short(ctx.node())
1480 % short(ctx.node())
1484 )
1481 )
1485
1482
1486 return ret
1483 return ret
1487
1484
1488
1485
1489 def remoteui(src, opts):
1486 def remoteui(src, opts):
1490 """build a remote ui from ui or repo and opts"""
1487 """build a remote ui from ui or repo and opts"""
1491 if util.safehasattr(src, b'baseui'): # looks like a repository
1488 if util.safehasattr(src, b'baseui'): # looks like a repository
1492 dst = src.baseui.copy() # drop repo-specific config
1489 dst = src.baseui.copy() # drop repo-specific config
1493 src = src.ui # copy target options from repo
1490 src = src.ui # copy target options from repo
1494 else: # assume it's a global ui object
1491 else: # assume it's a global ui object
1495 dst = src.copy() # keep all global options
1492 dst = src.copy() # keep all global options
1496
1493
1497 # copy ssh-specific options
1494 # copy ssh-specific options
1498 for o in b'ssh', b'remotecmd':
1495 for o in b'ssh', b'remotecmd':
1499 v = opts.get(o) or src.config(b'ui', o)
1496 v = opts.get(o) or src.config(b'ui', o)
1500 if v:
1497 if v:
1501 dst.setconfig(b"ui", o, v, b'copied')
1498 dst.setconfig(b"ui", o, v, b'copied')
1502
1499
1503 # copy bundle-specific options
1500 # copy bundle-specific options
1504 r = src.config(b'bundle', b'mainreporoot')
1501 r = src.config(b'bundle', b'mainreporoot')
1505 if r:
1502 if r:
1506 dst.setconfig(b'bundle', b'mainreporoot', r, b'copied')
1503 dst.setconfig(b'bundle', b'mainreporoot', r, b'copied')
1507
1504
1508 # copy selected local settings to the remote ui
1505 # copy selected local settings to the remote ui
1509 for sect in (b'auth', b'hostfingerprints', b'hostsecurity', b'http_proxy'):
1506 for sect in (b'auth', b'hostfingerprints', b'hostsecurity', b'http_proxy'):
1510 for key, val in src.configitems(sect):
1507 for key, val in src.configitems(sect):
1511 dst.setconfig(sect, key, val, b'copied')
1508 dst.setconfig(sect, key, val, b'copied')
1512 v = src.config(b'web', b'cacerts')
1509 v = src.config(b'web', b'cacerts')
1513 if v:
1510 if v:
1514 dst.setconfig(b'web', b'cacerts', util.expandpath(v), b'copied')
1511 dst.setconfig(b'web', b'cacerts', util.expandpath(v), b'copied')
1515
1512
1516 return dst
1513 return dst
1517
1514
1518
1515
1519 # Files of interest
1516 # Files of interest
1520 # Used to check if the repository has changed looking at mtime and size of
1517 # Used to check if the repository has changed looking at mtime and size of
1521 # these files.
1518 # these files.
1522 foi = [
1519 foi = [
1523 (b'spath', b'00changelog.i'),
1520 (b'spath', b'00changelog.i'),
1524 (b'spath', b'phaseroots'), # ! phase can change content at the same size
1521 (b'spath', b'phaseroots'), # ! phase can change content at the same size
1525 (b'spath', b'obsstore'),
1522 (b'spath', b'obsstore'),
1526 (b'path', b'bookmarks'), # ! bookmark can change content at the same size
1523 (b'path', b'bookmarks'), # ! bookmark can change content at the same size
1527 ]
1524 ]
1528
1525
1529
1526
1530 class cachedlocalrepo(object):
1527 class cachedlocalrepo(object):
1531 """Holds a localrepository that can be cached and reused."""
1528 """Holds a localrepository that can be cached and reused."""
1532
1529
1533 def __init__(self, repo):
1530 def __init__(self, repo):
1534 """Create a new cached repo from an existing repo.
1531 """Create a new cached repo from an existing repo.
1535
1532
1536 We assume the passed in repo was recently created. If the
1533 We assume the passed in repo was recently created. If the
1537 repo has changed between when it was created and when it was
1534 repo has changed between when it was created and when it was
1538 turned into a cache, it may not refresh properly.
1535 turned into a cache, it may not refresh properly.
1539 """
1536 """
1540 assert isinstance(repo, localrepo.localrepository)
1537 assert isinstance(repo, localrepo.localrepository)
1541 self._repo = repo
1538 self._repo = repo
1542 self._state, self.mtime = self._repostate()
1539 self._state, self.mtime = self._repostate()
1543 self._filtername = repo.filtername
1540 self._filtername = repo.filtername
1544
1541
1545 def fetch(self):
1542 def fetch(self):
1546 """Refresh (if necessary) and return a repository.
1543 """Refresh (if necessary) and return a repository.
1547
1544
1548 If the cached instance is out of date, it will be recreated
1545 If the cached instance is out of date, it will be recreated
1549 automatically and returned.
1546 automatically and returned.
1550
1547
1551 Returns a tuple of the repo and a boolean indicating whether a new
1548 Returns a tuple of the repo and a boolean indicating whether a new
1552 repo instance was created.
1549 repo instance was created.
1553 """
1550 """
1554 # We compare the mtimes and sizes of some well-known files to
1551 # We compare the mtimes and sizes of some well-known files to
1555 # determine if the repo changed. This is not precise, as mtimes
1552 # determine if the repo changed. This is not precise, as mtimes
1556 # are susceptible to clock skew and imprecise filesystems and
1553 # are susceptible to clock skew and imprecise filesystems and
1557 # file content can change while maintaining the same size.
1554 # file content can change while maintaining the same size.
1558
1555
1559 state, mtime = self._repostate()
1556 state, mtime = self._repostate()
1560 if state == self._state:
1557 if state == self._state:
1561 return self._repo, False
1558 return self._repo, False
1562
1559
1563 repo = repository(self._repo.baseui, self._repo.url())
1560 repo = repository(self._repo.baseui, self._repo.url())
1564 if self._filtername:
1561 if self._filtername:
1565 self._repo = repo.filtered(self._filtername)
1562 self._repo = repo.filtered(self._filtername)
1566 else:
1563 else:
1567 self._repo = repo.unfiltered()
1564 self._repo = repo.unfiltered()
1568 self._state = state
1565 self._state = state
1569 self.mtime = mtime
1566 self.mtime = mtime
1570
1567
1571 return self._repo, True
1568 return self._repo, True
1572
1569
1573 def _repostate(self):
1570 def _repostate(self):
1574 state = []
1571 state = []
1575 maxmtime = -1
1572 maxmtime = -1
1576 for attr, fname in foi:
1573 for attr, fname in foi:
1577 prefix = getattr(self._repo, attr)
1574 prefix = getattr(self._repo, attr)
1578 p = os.path.join(prefix, fname)
1575 p = os.path.join(prefix, fname)
1579 try:
1576 try:
1580 st = os.stat(p)
1577 st = os.stat(p)
1581 except OSError:
1578 except OSError:
1582 st = os.stat(prefix)
1579 st = os.stat(prefix)
1583 state.append((st[stat.ST_MTIME], st.st_size))
1580 state.append((st[stat.ST_MTIME], st.st_size))
1584 maxmtime = max(maxmtime, st[stat.ST_MTIME])
1581 maxmtime = max(maxmtime, st[stat.ST_MTIME])
1585
1582
1586 return tuple(state), maxmtime
1583 return tuple(state), maxmtime
1587
1584
1588 def copy(self):
1585 def copy(self):
1589 """Obtain a copy of this class instance.
1586 """Obtain a copy of this class instance.
1590
1587
1591 A new localrepository instance is obtained. The new instance should be
1588 A new localrepository instance is obtained. The new instance should be
1592 completely independent of the original.
1589 completely independent of the original.
1593 """
1590 """
1594 repo = repository(self._repo.baseui, self._repo.origroot)
1591 repo = repository(self._repo.baseui, self._repo.origroot)
1595 if self._filtername:
1592 if self._filtername:
1596 repo = repo.filtered(self._filtername)
1593 repo = repo.filtered(self._filtername)
1597 else:
1594 else:
1598 repo = repo.unfiltered()
1595 repo = repo.unfiltered()
1599 c = cachedlocalrepo(repo)
1596 c = cachedlocalrepo(repo)
1600 c._state = self._state
1597 c._state = self._state
1601 c.mtime = self.mtime
1598 c.mtime = self.mtime
1602 return c
1599 return c
@@ -1,56 +1,57 b''
1 Create an empty repo:
1 Create an empty repo:
2
2
3 $ hg init a
3 $ hg init a
4 $ cd a
4 $ cd a
5
5
6 Try some commands:
6 Try some commands:
7
7
8 $ hg log
8 $ hg log
9 $ hg grep wah
9 $ hg grep wah
10 [1]
10 [1]
11 $ hg manifest
11 $ hg manifest
12 $ hg verify
12 $ hg verify
13 checking changesets
13 checking changesets
14 checking manifests
14 checking manifests
15 crosschecking files in changesets and manifests
15 crosschecking files in changesets and manifests
16 checking files
16 checking files
17 checked 0 changesets with 0 changes to 0 files
17 checked 0 changesets with 0 changes to 0 files
18
18
19 Check the basic files created:
19 Check the basic files created:
20
20
21 $ ls .hg
21 $ ls .hg
22 00changelog.i
22 00changelog.i
23 cache
23 cache
24 requires
24 requires
25 store
25 store
26 wcache
26 wcache
27
27
28 Should be empty:
28 Should be empty:
29
29
30 $ ls .hg/store
30 $ ls .hg/store
31
31
32 Poke at a clone:
32 Poke at a clone:
33
33
34 $ cd ..
34 $ cd ..
35 $ hg clone a b
35 $ hg clone a b
36 updating to branch default
36 updating to branch default
37 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
37 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
38 $ cd b
38 $ cd b
39 $ hg verify
39 $ hg verify
40 checking changesets
40 checking changesets
41 checking manifests
41 checking manifests
42 crosschecking files in changesets and manifests
42 crosschecking files in changesets and manifests
43 checking files
43 checking files
44 checked 0 changesets with 0 changes to 0 files
44 checked 0 changesets with 0 changes to 0 files
45 $ ls .hg
45 $ ls .hg
46 00changelog.i
46 00changelog.i
47 cache
47 cache
48 hgrc
48 hgrc
49 requires
49 requires
50 store
50 store
51 wcache
51
52
52 Should be empty:
53 Should be empty:
53
54
54 $ ls .hg/store
55 $ ls .hg/store
55
56
56 $ cd ..
57 $ cd ..
@@ -1,1908 +1,1909 b''
1 Test basic extension support
1 Test basic extension support
2 $ cat > unflush.py <<EOF
2 $ cat > unflush.py <<EOF
3 > import sys
3 > import sys
4 > from mercurial import pycompat
4 > from mercurial import pycompat
5 > if pycompat.ispy3:
5 > if pycompat.ispy3:
6 > # no changes required
6 > # no changes required
7 > sys.exit(0)
7 > sys.exit(0)
8 > with open(sys.argv[1], 'rb') as f:
8 > with open(sys.argv[1], 'rb') as f:
9 > data = f.read()
9 > data = f.read()
10 > with open(sys.argv[1], 'wb') as f:
10 > with open(sys.argv[1], 'wb') as f:
11 > f.write(data.replace(b', flush=True', b''))
11 > f.write(data.replace(b', flush=True', b''))
12 > EOF
12 > EOF
13
13
14 $ cat > foobar.py <<EOF
14 $ cat > foobar.py <<EOF
15 > import os
15 > import os
16 > from mercurial import commands, exthelper, registrar
16 > from mercurial import commands, exthelper, registrar
17 >
17 >
18 > eh = exthelper.exthelper()
18 > eh = exthelper.exthelper()
19 > eh.configitem(b'tests', b'foo', default=b"Foo")
19 > eh.configitem(b'tests', b'foo', default=b"Foo")
20 >
20 >
21 > uisetup = eh.finaluisetup
21 > uisetup = eh.finaluisetup
22 > uipopulate = eh.finaluipopulate
22 > uipopulate = eh.finaluipopulate
23 > reposetup = eh.finalreposetup
23 > reposetup = eh.finalreposetup
24 > cmdtable = eh.cmdtable
24 > cmdtable = eh.cmdtable
25 > configtable = eh.configtable
25 > configtable = eh.configtable
26 >
26 >
27 > @eh.uisetup
27 > @eh.uisetup
28 > def _uisetup(ui):
28 > def _uisetup(ui):
29 > ui.debug(b"uisetup called [debug]\\n")
29 > ui.debug(b"uisetup called [debug]\\n")
30 > ui.write(b"uisetup called\\n")
30 > ui.write(b"uisetup called\\n")
31 > ui.status(b"uisetup called [status]\\n")
31 > ui.status(b"uisetup called [status]\\n")
32 > ui.flush()
32 > ui.flush()
33 > @eh.uipopulate
33 > @eh.uipopulate
34 > def _uipopulate(ui):
34 > def _uipopulate(ui):
35 > ui._populatecnt = getattr(ui, "_populatecnt", 0) + 1
35 > ui._populatecnt = getattr(ui, "_populatecnt", 0) + 1
36 > ui.write(b"uipopulate called (%d times)\n" % ui._populatecnt)
36 > ui.write(b"uipopulate called (%d times)\n" % ui._populatecnt)
37 > @eh.reposetup
37 > @eh.reposetup
38 > def _reposetup(ui, repo):
38 > def _reposetup(ui, repo):
39 > ui.write(b"reposetup called for %s\\n" % os.path.basename(repo.root))
39 > ui.write(b"reposetup called for %s\\n" % os.path.basename(repo.root))
40 > ui.write(b"ui %s= repo.ui\\n" % (ui == repo.ui and b"=" or b"!"))
40 > ui.write(b"ui %s= repo.ui\\n" % (ui == repo.ui and b"=" or b"!"))
41 > ui.flush()
41 > ui.flush()
42 > @eh.command(b'foo', [], b'hg foo')
42 > @eh.command(b'foo', [], b'hg foo')
43 > def foo(ui, *args, **kwargs):
43 > def foo(ui, *args, **kwargs):
44 > foo = ui.config(b'tests', b'foo')
44 > foo = ui.config(b'tests', b'foo')
45 > ui.write(foo)
45 > ui.write(foo)
46 > ui.write(b"\\n")
46 > ui.write(b"\\n")
47 > @eh.command(b'bar', [], b'hg bar', norepo=True)
47 > @eh.command(b'bar', [], b'hg bar', norepo=True)
48 > def bar(ui, *args, **kwargs):
48 > def bar(ui, *args, **kwargs):
49 > ui.write(b"Bar\\n")
49 > ui.write(b"Bar\\n")
50 > EOF
50 > EOF
51 $ abspath=`pwd`/foobar.py
51 $ abspath=`pwd`/foobar.py
52
52
53 $ mkdir barfoo
53 $ mkdir barfoo
54 $ cp foobar.py barfoo/__init__.py
54 $ cp foobar.py barfoo/__init__.py
55 $ barfoopath=`pwd`/barfoo
55 $ barfoopath=`pwd`/barfoo
56
56
57 $ hg init a
57 $ hg init a
58 $ cd a
58 $ cd a
59 $ echo foo > file
59 $ echo foo > file
60 $ hg add file
60 $ hg add file
61 $ hg commit -m 'add file'
61 $ hg commit -m 'add file'
62
62
63 $ echo '[extensions]' >> $HGRCPATH
63 $ echo '[extensions]' >> $HGRCPATH
64 $ echo "foobar = $abspath" >> $HGRCPATH
64 $ echo "foobar = $abspath" >> $HGRCPATH
65 $ hg foo
65 $ hg foo
66 uisetup called
66 uisetup called
67 uisetup called [status]
67 uisetup called [status]
68 uipopulate called (1 times)
68 uipopulate called (1 times)
69 uipopulate called (1 times)
69 uipopulate called (1 times)
70 uipopulate called (1 times)
70 uipopulate called (1 times)
71 reposetup called for a
71 reposetup called for a
72 ui == repo.ui
72 ui == repo.ui
73 uipopulate called (1 times) (chg !)
73 uipopulate called (1 times) (chg !)
74 uipopulate called (1 times) (chg !)
74 uipopulate called (1 times) (chg !)
75 uipopulate called (1 times) (chg !)
75 uipopulate called (1 times) (chg !)
76 uipopulate called (1 times) (chg !)
76 uipopulate called (1 times) (chg !)
77 uipopulate called (1 times) (chg !)
77 uipopulate called (1 times) (chg !)
78 reposetup called for a (chg !)
78 reposetup called for a (chg !)
79 ui == repo.ui (chg !)
79 ui == repo.ui (chg !)
80 Foo
80 Foo
81 $ hg foo --quiet
81 $ hg foo --quiet
82 uisetup called (no-chg !)
82 uisetup called (no-chg !)
83 uipopulate called (1 times)
83 uipopulate called (1 times)
84 uipopulate called (1 times)
84 uipopulate called (1 times)
85 uipopulate called (1 times) (chg !)
85 uipopulate called (1 times) (chg !)
86 uipopulate called (1 times) (chg !)
86 uipopulate called (1 times) (chg !)
87 uipopulate called (1 times)
87 uipopulate called (1 times)
88 reposetup called for a
88 reposetup called for a
89 ui == repo.ui
89 ui == repo.ui
90 Foo
90 Foo
91 $ hg foo --debug
91 $ hg foo --debug
92 uisetup called [debug] (no-chg !)
92 uisetup called [debug] (no-chg !)
93 uisetup called (no-chg !)
93 uisetup called (no-chg !)
94 uisetup called [status] (no-chg !)
94 uisetup called [status] (no-chg !)
95 uipopulate called (1 times)
95 uipopulate called (1 times)
96 uipopulate called (1 times)
96 uipopulate called (1 times)
97 uipopulate called (1 times) (chg !)
97 uipopulate called (1 times) (chg !)
98 uipopulate called (1 times) (chg !)
98 uipopulate called (1 times) (chg !)
99 uipopulate called (1 times)
99 uipopulate called (1 times)
100 reposetup called for a
100 reposetup called for a
101 ui == repo.ui
101 ui == repo.ui
102 Foo
102 Foo
103
103
104 $ cd ..
104 $ cd ..
105 $ hg clone a b
105 $ hg clone a b
106 uisetup called (no-chg !)
106 uisetup called (no-chg !)
107 uisetup called [status] (no-chg !)
107 uisetup called [status] (no-chg !)
108 uipopulate called (1 times)
108 uipopulate called (1 times)
109 uipopulate called (1 times) (chg !)
109 uipopulate called (1 times) (chg !)
110 uipopulate called (1 times)
110 uipopulate called (1 times)
111 reposetup called for a
111 reposetup called for a
112 ui == repo.ui
112 ui == repo.ui
113 uipopulate called (1 times)
113 uipopulate called (1 times)
114 uipopulate called (1 times)
114 reposetup called for b
115 reposetup called for b
115 ui == repo.ui
116 ui == repo.ui
116 updating to branch default
117 updating to branch default
117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
118
119
119 $ hg bar
120 $ hg bar
120 uisetup called (no-chg !)
121 uisetup called (no-chg !)
121 uisetup called [status] (no-chg !)
122 uisetup called [status] (no-chg !)
122 uipopulate called (1 times)
123 uipopulate called (1 times)
123 uipopulate called (1 times) (chg !)
124 uipopulate called (1 times) (chg !)
124 Bar
125 Bar
125 $ echo 'foobar = !' >> $HGRCPATH
126 $ echo 'foobar = !' >> $HGRCPATH
126
127
127 module/__init__.py-style
128 module/__init__.py-style
128
129
129 $ echo "barfoo = $barfoopath" >> $HGRCPATH
130 $ echo "barfoo = $barfoopath" >> $HGRCPATH
130 $ cd a
131 $ cd a
131 $ hg foo
132 $ hg foo
132 uisetup called
133 uisetup called
133 uisetup called [status]
134 uisetup called [status]
134 uipopulate called (1 times)
135 uipopulate called (1 times)
135 uipopulate called (1 times)
136 uipopulate called (1 times)
136 uipopulate called (1 times)
137 uipopulate called (1 times)
137 reposetup called for a
138 reposetup called for a
138 ui == repo.ui
139 ui == repo.ui
139 uipopulate called (1 times) (chg !)
140 uipopulate called (1 times) (chg !)
140 uipopulate called (1 times) (chg !)
141 uipopulate called (1 times) (chg !)
141 uipopulate called (1 times) (chg !)
142 uipopulate called (1 times) (chg !)
142 uipopulate called (1 times) (chg !)
143 uipopulate called (1 times) (chg !)
143 uipopulate called (1 times) (chg !)
144 uipopulate called (1 times) (chg !)
144 reposetup called for a (chg !)
145 reposetup called for a (chg !)
145 ui == repo.ui (chg !)
146 ui == repo.ui (chg !)
146 Foo
147 Foo
147 $ echo 'barfoo = !' >> $HGRCPATH
148 $ echo 'barfoo = !' >> $HGRCPATH
148
149
149 Check that extensions are loaded in phases:
150 Check that extensions are loaded in phases:
150
151
151 $ cat > foo.py <<EOF
152 $ cat > foo.py <<EOF
152 > from __future__ import print_function
153 > from __future__ import print_function
153 > import os
154 > import os
154 > from mercurial import exthelper
155 > from mercurial import exthelper
155 > from mercurial.utils import procutil
156 > from mercurial.utils import procutil
156 >
157 >
157 > def write(msg):
158 > def write(msg):
158 > procutil.stdout.write(msg)
159 > procutil.stdout.write(msg)
159 > procutil.stdout.flush()
160 > procutil.stdout.flush()
160 >
161 >
161 > name = os.path.basename(__file__).rsplit('.', 1)[0]
162 > name = os.path.basename(__file__).rsplit('.', 1)[0]
162 > bytesname = name.encode('utf-8')
163 > bytesname = name.encode('utf-8')
163 > write(b"1) %s imported\n" % bytesname)
164 > write(b"1) %s imported\n" % bytesname)
164 > eh = exthelper.exthelper()
165 > eh = exthelper.exthelper()
165 > @eh.uisetup
166 > @eh.uisetup
166 > def _uisetup(ui):
167 > def _uisetup(ui):
167 > write(b"2) %s uisetup\n" % bytesname)
168 > write(b"2) %s uisetup\n" % bytesname)
168 > @eh.extsetup
169 > @eh.extsetup
169 > def _extsetup(ui):
170 > def _extsetup(ui):
170 > write(b"3) %s extsetup\n" % bytesname)
171 > write(b"3) %s extsetup\n" % bytesname)
171 > @eh.uipopulate
172 > @eh.uipopulate
172 > def _uipopulate(ui):
173 > def _uipopulate(ui):
173 > write(b"4) %s uipopulate\n" % bytesname)
174 > write(b"4) %s uipopulate\n" % bytesname)
174 > @eh.reposetup
175 > @eh.reposetup
175 > def _reposetup(ui, repo):
176 > def _reposetup(ui, repo):
176 > write(b"5) %s reposetup\n" % bytesname)
177 > write(b"5) %s reposetup\n" % bytesname)
177 >
178 >
178 > extsetup = eh.finalextsetup
179 > extsetup = eh.finalextsetup
179 > reposetup = eh.finalreposetup
180 > reposetup = eh.finalreposetup
180 > uipopulate = eh.finaluipopulate
181 > uipopulate = eh.finaluipopulate
181 > uisetup = eh.finaluisetup
182 > uisetup = eh.finaluisetup
182 > revsetpredicate = eh.revsetpredicate
183 > revsetpredicate = eh.revsetpredicate
183 >
184 >
184 > # custom predicate to check registration of functions at loading
185 > # custom predicate to check registration of functions at loading
185 > from mercurial import (
186 > from mercurial import (
186 > smartset,
187 > smartset,
187 > )
188 > )
188 > @eh.revsetpredicate(bytesname, safe=True) # safe=True for query via hgweb
189 > @eh.revsetpredicate(bytesname, safe=True) # safe=True for query via hgweb
189 > def custompredicate(repo, subset, x):
190 > def custompredicate(repo, subset, x):
190 > return smartset.baseset([r for r in subset if r in {0}])
191 > return smartset.baseset([r for r in subset if r in {0}])
191 > EOF
192 > EOF
192 $ "$PYTHON" $TESTTMP/unflush.py foo.py
193 $ "$PYTHON" $TESTTMP/unflush.py foo.py
193
194
194 $ cp foo.py bar.py
195 $ cp foo.py bar.py
195 $ echo 'foo = foo.py' >> $HGRCPATH
196 $ echo 'foo = foo.py' >> $HGRCPATH
196 $ echo 'bar = bar.py' >> $HGRCPATH
197 $ echo 'bar = bar.py' >> $HGRCPATH
197
198
198 Check normal command's load order of extensions and registration of functions
199 Check normal command's load order of extensions and registration of functions
199
200
200 On chg server, extension should be first set up by the server. Then
201 On chg server, extension should be first set up by the server. Then
201 object-level setup should follow in the worker process.
202 object-level setup should follow in the worker process.
202
203
203 $ hg log -r "foo() and bar()" -q
204 $ hg log -r "foo() and bar()" -q
204 1) foo imported
205 1) foo imported
205 1) bar imported
206 1) bar imported
206 2) foo uisetup
207 2) foo uisetup
207 2) bar uisetup
208 2) bar uisetup
208 3) foo extsetup
209 3) foo extsetup
209 3) bar extsetup
210 3) bar extsetup
210 4) foo uipopulate
211 4) foo uipopulate
211 4) bar uipopulate
212 4) bar uipopulate
212 4) foo uipopulate
213 4) foo uipopulate
213 4) bar uipopulate
214 4) bar uipopulate
214 4) foo uipopulate
215 4) foo uipopulate
215 4) bar uipopulate
216 4) bar uipopulate
216 5) foo reposetup
217 5) foo reposetup
217 5) bar reposetup
218 5) bar reposetup
218 4) foo uipopulate (chg !)
219 4) foo uipopulate (chg !)
219 4) bar uipopulate (chg !)
220 4) bar uipopulate (chg !)
220 4) foo uipopulate (chg !)
221 4) foo uipopulate (chg !)
221 4) bar uipopulate (chg !)
222 4) bar uipopulate (chg !)
222 4) foo uipopulate (chg !)
223 4) foo uipopulate (chg !)
223 4) bar uipopulate (chg !)
224 4) bar uipopulate (chg !)
224 4) foo uipopulate (chg !)
225 4) foo uipopulate (chg !)
225 4) bar uipopulate (chg !)
226 4) bar uipopulate (chg !)
226 4) foo uipopulate (chg !)
227 4) foo uipopulate (chg !)
227 4) bar uipopulate (chg !)
228 4) bar uipopulate (chg !)
228 5) foo reposetup (chg !)
229 5) foo reposetup (chg !)
229 5) bar reposetup (chg !)
230 5) bar reposetup (chg !)
230 0:c24b9ac61126
231 0:c24b9ac61126
231
232
232 Check hgweb's load order of extensions and registration of functions
233 Check hgweb's load order of extensions and registration of functions
233
234
234 $ cat > hgweb.cgi <<EOF
235 $ cat > hgweb.cgi <<EOF
235 > #!$PYTHON
236 > #!$PYTHON
236 > from mercurial import demandimport; demandimport.enable()
237 > from mercurial import demandimport; demandimport.enable()
237 > from mercurial.hgweb import hgweb
238 > from mercurial.hgweb import hgweb
238 > from mercurial.hgweb import wsgicgi
239 > from mercurial.hgweb import wsgicgi
239 > application = hgweb(b'.', b'test repo')
240 > application = hgweb(b'.', b'test repo')
240 > wsgicgi.launch(application)
241 > wsgicgi.launch(application)
241 > EOF
242 > EOF
242 $ . "$TESTDIR/cgienv"
243 $ . "$TESTDIR/cgienv"
243
244
244 $ PATH_INFO='/' SCRIPT_NAME='' "$PYTHON" hgweb.cgi \
245 $ PATH_INFO='/' SCRIPT_NAME='' "$PYTHON" hgweb.cgi \
245 > | grep '^[0-9]) ' # ignores HTML output
246 > | grep '^[0-9]) ' # ignores HTML output
246 1) foo imported
247 1) foo imported
247 1) bar imported
248 1) bar imported
248 2) foo uisetup
249 2) foo uisetup
249 2) bar uisetup
250 2) bar uisetup
250 3) foo extsetup
251 3) foo extsetup
251 3) bar extsetup
252 3) bar extsetup
252 4) foo uipopulate
253 4) foo uipopulate
253 4) bar uipopulate
254 4) bar uipopulate
254 4) foo uipopulate
255 4) foo uipopulate
255 4) bar uipopulate
256 4) bar uipopulate
256 5) foo reposetup
257 5) foo reposetup
257 5) bar reposetup
258 5) bar reposetup
258
259
259 (check that revset predicate foo() and bar() are available)
260 (check that revset predicate foo() and bar() are available)
260
261
261 #if msys
262 #if msys
262 $ PATH_INFO='//shortlog'
263 $ PATH_INFO='//shortlog'
263 #else
264 #else
264 $ PATH_INFO='/shortlog'
265 $ PATH_INFO='/shortlog'
265 #endif
266 #endif
266 $ export PATH_INFO
267 $ export PATH_INFO
267 $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' "$PYTHON" hgweb.cgi \
268 $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' "$PYTHON" hgweb.cgi \
268 > | grep '<a href="/rev/[0-9a-z]*">'
269 > | grep '<a href="/rev/[0-9a-z]*">'
269 <a href="/rev/c24b9ac61126">add file</a>
270 <a href="/rev/c24b9ac61126">add file</a>
270
271
271 $ echo 'foo = !' >> $HGRCPATH
272 $ echo 'foo = !' >> $HGRCPATH
272 $ echo 'bar = !' >> $HGRCPATH
273 $ echo 'bar = !' >> $HGRCPATH
273
274
274 Check "from __future__ import absolute_import" support for external libraries
275 Check "from __future__ import absolute_import" support for external libraries
275
276
276 (import-checker.py reports issues for some of heredoc python code
277 (import-checker.py reports issues for some of heredoc python code
277 fragments below, because import-checker.py does not know test specific
278 fragments below, because import-checker.py does not know test specific
278 package hierarchy. NO_CHECK_* should be used as a limit mark of
279 package hierarchy. NO_CHECK_* should be used as a limit mark of
279 heredoc, in order to make import-checker.py ignore them. For
280 heredoc, in order to make import-checker.py ignore them. For
280 simplicity, all python code fragments below are generated with such
281 simplicity, all python code fragments below are generated with such
281 limit mark, regardless of importing module or not.)
282 limit mark, regardless of importing module or not.)
282
283
283 #if windows
284 #if windows
284 $ PATHSEP=";"
285 $ PATHSEP=";"
285 #else
286 #else
286 $ PATHSEP=":"
287 $ PATHSEP=":"
287 #endif
288 #endif
288 $ export PATHSEP
289 $ export PATHSEP
289
290
290 $ mkdir $TESTTMP/libroot
291 $ mkdir $TESTTMP/libroot
291 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
292 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
292 $ mkdir $TESTTMP/libroot/mod
293 $ mkdir $TESTTMP/libroot/mod
293 $ touch $TESTTMP/libroot/mod/__init__.py
294 $ touch $TESTTMP/libroot/mod/__init__.py
294 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
295 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
295
296
296 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<NO_CHECK_EOF
297 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<NO_CHECK_EOF
297 > from __future__ import absolute_import, print_function
298 > from __future__ import absolute_import, print_function
298 > import ambig # should load "libroot/ambig.py"
299 > import ambig # should load "libroot/ambig.py"
299 > s = ambig.s
300 > s = ambig.s
300 > NO_CHECK_EOF
301 > NO_CHECK_EOF
301 $ cat > loadabs.py <<NO_CHECK_EOF
302 $ cat > loadabs.py <<NO_CHECK_EOF
302 > import mod.ambigabs as ambigabs
303 > import mod.ambigabs as ambigabs
303 > def extsetup(ui):
304 > def extsetup(ui):
304 > print('ambigabs.s=%s' % ambigabs.s, flush=True)
305 > print('ambigabs.s=%s' % ambigabs.s, flush=True)
305 > NO_CHECK_EOF
306 > NO_CHECK_EOF
306 $ "$PYTHON" $TESTTMP/unflush.py loadabs.py
307 $ "$PYTHON" $TESTTMP/unflush.py loadabs.py
307 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
308 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
308 ambigabs.s=libroot/ambig.py
309 ambigabs.s=libroot/ambig.py
309 $TESTTMP/a
310 $TESTTMP/a
310
311
311 #if no-py3
312 #if no-py3
312 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<NO_CHECK_EOF
313 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<NO_CHECK_EOF
313 > from __future__ import print_function
314 > from __future__ import print_function
314 > import ambig # should load "libroot/mod/ambig.py"
315 > import ambig # should load "libroot/mod/ambig.py"
315 > s = ambig.s
316 > s = ambig.s
316 > NO_CHECK_EOF
317 > NO_CHECK_EOF
317 $ cat > loadrel.py <<NO_CHECK_EOF
318 $ cat > loadrel.py <<NO_CHECK_EOF
318 > import mod.ambigrel as ambigrel
319 > import mod.ambigrel as ambigrel
319 > def extsetup(ui):
320 > def extsetup(ui):
320 > print('ambigrel.s=%s' % ambigrel.s, flush=True)
321 > print('ambigrel.s=%s' % ambigrel.s, flush=True)
321 > NO_CHECK_EOF
322 > NO_CHECK_EOF
322 $ "$PYTHON" $TESTTMP/unflush.py loadrel.py
323 $ "$PYTHON" $TESTTMP/unflush.py loadrel.py
323 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
324 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
324 ambigrel.s=libroot/mod/ambig.py
325 ambigrel.s=libroot/mod/ambig.py
325 $TESTTMP/a
326 $TESTTMP/a
326 #endif
327 #endif
327
328
328 Check absolute/relative import of extension specific modules
329 Check absolute/relative import of extension specific modules
329
330
330 $ mkdir $TESTTMP/extroot
331 $ mkdir $TESTTMP/extroot
331 $ cat > $TESTTMP/extroot/bar.py <<NO_CHECK_EOF
332 $ cat > $TESTTMP/extroot/bar.py <<NO_CHECK_EOF
332 > s = b'this is extroot.bar'
333 > s = b'this is extroot.bar'
333 > NO_CHECK_EOF
334 > NO_CHECK_EOF
334 $ mkdir $TESTTMP/extroot/sub1
335 $ mkdir $TESTTMP/extroot/sub1
335 $ cat > $TESTTMP/extroot/sub1/__init__.py <<NO_CHECK_EOF
336 $ cat > $TESTTMP/extroot/sub1/__init__.py <<NO_CHECK_EOF
336 > s = b'this is extroot.sub1.__init__'
337 > s = b'this is extroot.sub1.__init__'
337 > NO_CHECK_EOF
338 > NO_CHECK_EOF
338 $ cat > $TESTTMP/extroot/sub1/baz.py <<NO_CHECK_EOF
339 $ cat > $TESTTMP/extroot/sub1/baz.py <<NO_CHECK_EOF
339 > s = b'this is extroot.sub1.baz'
340 > s = b'this is extroot.sub1.baz'
340 > NO_CHECK_EOF
341 > NO_CHECK_EOF
341 $ cat > $TESTTMP/extroot/__init__.py <<NO_CHECK_EOF
342 $ cat > $TESTTMP/extroot/__init__.py <<NO_CHECK_EOF
342 > from __future__ import absolute_import
343 > from __future__ import absolute_import
343 > s = b'this is extroot.__init__'
344 > s = b'this is extroot.__init__'
344 > from . import foo
345 > from . import foo
345 > def extsetup(ui):
346 > def extsetup(ui):
346 > ui.write(b'(extroot) ', foo.func(), b'\n')
347 > ui.write(b'(extroot) ', foo.func(), b'\n')
347 > ui.flush()
348 > ui.flush()
348 > NO_CHECK_EOF
349 > NO_CHECK_EOF
349
350
350 $ cat > $TESTTMP/extroot/foo.py <<NO_CHECK_EOF
351 $ cat > $TESTTMP/extroot/foo.py <<NO_CHECK_EOF
351 > # test absolute import
352 > # test absolute import
352 > buf = []
353 > buf = []
353 > def func():
354 > def func():
354 > # "not locals" case
355 > # "not locals" case
355 > import extroot.bar
356 > import extroot.bar
356 > buf.append(b'import extroot.bar in func(): %s' % extroot.bar.s)
357 > buf.append(b'import extroot.bar in func(): %s' % extroot.bar.s)
357 > return b'\n(extroot) '.join(buf)
358 > return b'\n(extroot) '.join(buf)
358 > # b"fromlist == ('*',)" case
359 > # b"fromlist == ('*',)" case
359 > from extroot.bar import *
360 > from extroot.bar import *
360 > buf.append(b'from extroot.bar import *: %s' % s)
361 > buf.append(b'from extroot.bar import *: %s' % s)
361 > # "not fromlist" and "if '.' in name" case
362 > # "not fromlist" and "if '.' in name" case
362 > import extroot.sub1.baz
363 > import extroot.sub1.baz
363 > buf.append(b'import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
364 > buf.append(b'import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
364 > # "not fromlist" and NOT "if '.' in name" case
365 > # "not fromlist" and NOT "if '.' in name" case
365 > import extroot
366 > import extroot
366 > buf.append(b'import extroot: %s' % extroot.s)
367 > buf.append(b'import extroot: %s' % extroot.s)
367 > # NOT "not fromlist" and NOT "level != -1" case
368 > # NOT "not fromlist" and NOT "level != -1" case
368 > from extroot.bar import s
369 > from extroot.bar import s
369 > buf.append(b'from extroot.bar import s: %s' % s)
370 > buf.append(b'from extroot.bar import s: %s' % s)
370 > NO_CHECK_EOF
371 > NO_CHECK_EOF
371 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
372 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
372 (extroot) from extroot.bar import *: this is extroot.bar
373 (extroot) from extroot.bar import *: this is extroot.bar
373 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
374 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
374 (extroot) import extroot: this is extroot.__init__
375 (extroot) import extroot: this is extroot.__init__
375 (extroot) from extroot.bar import s: this is extroot.bar
376 (extroot) from extroot.bar import s: this is extroot.bar
376 (extroot) import extroot.bar in func(): this is extroot.bar
377 (extroot) import extroot.bar in func(): this is extroot.bar
377 $TESTTMP/a
378 $TESTTMP/a
378
379
379 #if no-py3
380 #if no-py3
380 $ rm "$TESTTMP"/extroot/foo.*
381 $ rm "$TESTTMP"/extroot/foo.*
381 $ rm -Rf "$TESTTMP/extroot/__pycache__"
382 $ rm -Rf "$TESTTMP/extroot/__pycache__"
382 $ cat > $TESTTMP/extroot/foo.py <<NO_CHECK_EOF
383 $ cat > $TESTTMP/extroot/foo.py <<NO_CHECK_EOF
383 > # test relative import
384 > # test relative import
384 > buf = []
385 > buf = []
385 > def func():
386 > def func():
386 > # "not locals" case
387 > # "not locals" case
387 > import bar
388 > import bar
388 > buf.append('import bar in func(): %s' % bar.s)
389 > buf.append('import bar in func(): %s' % bar.s)
389 > return '\n(extroot) '.join(buf)
390 > return '\n(extroot) '.join(buf)
390 > # "fromlist == ('*',)" case
391 > # "fromlist == ('*',)" case
391 > from bar import *
392 > from bar import *
392 > buf.append('from bar import *: %s' % s)
393 > buf.append('from bar import *: %s' % s)
393 > # "not fromlist" and "if '.' in name" case
394 > # "not fromlist" and "if '.' in name" case
394 > import sub1.baz
395 > import sub1.baz
395 > buf.append('import sub1.baz: %s' % sub1.baz.s)
396 > buf.append('import sub1.baz: %s' % sub1.baz.s)
396 > # "not fromlist" and NOT "if '.' in name" case
397 > # "not fromlist" and NOT "if '.' in name" case
397 > import sub1
398 > import sub1
398 > buf.append('import sub1: %s' % sub1.s)
399 > buf.append('import sub1: %s' % sub1.s)
399 > # NOT "not fromlist" and NOT "level != -1" case
400 > # NOT "not fromlist" and NOT "level != -1" case
400 > from bar import s
401 > from bar import s
401 > buf.append('from bar import s: %s' % s)
402 > buf.append('from bar import s: %s' % s)
402 > NO_CHECK_EOF
403 > NO_CHECK_EOF
403 $ hg --config extensions.extroot=$TESTTMP/extroot root
404 $ hg --config extensions.extroot=$TESTTMP/extroot root
404 (extroot) from bar import *: this is extroot.bar
405 (extroot) from bar import *: this is extroot.bar
405 (extroot) import sub1.baz: this is extroot.sub1.baz
406 (extroot) import sub1.baz: this is extroot.sub1.baz
406 (extroot) import sub1: this is extroot.sub1.__init__
407 (extroot) import sub1: this is extroot.sub1.__init__
407 (extroot) from bar import s: this is extroot.bar
408 (extroot) from bar import s: this is extroot.bar
408 (extroot) import bar in func(): this is extroot.bar
409 (extroot) import bar in func(): this is extroot.bar
409 $TESTTMP/a
410 $TESTTMP/a
410 #endif
411 #endif
411
412
412 #if demandimport
413 #if demandimport
413
414
414 Examine whether module loading is delayed until actual referring, even
415 Examine whether module loading is delayed until actual referring, even
415 though module is imported with "absolute_import" feature.
416 though module is imported with "absolute_import" feature.
416
417
417 Files below in each packages are used for described purpose:
418 Files below in each packages are used for described purpose:
418
419
419 - "called": examine whether "from MODULE import ATTR" works correctly
420 - "called": examine whether "from MODULE import ATTR" works correctly
420 - "unused": examine whether loading is delayed correctly
421 - "unused": examine whether loading is delayed correctly
421 - "used": examine whether "from PACKAGE import MODULE" works correctly
422 - "used": examine whether "from PACKAGE import MODULE" works correctly
422
423
423 Package hierarchy is needed to examine whether demand importing works
424 Package hierarchy is needed to examine whether demand importing works
424 as expected for "from SUB.PACK.AGE import MODULE".
425 as expected for "from SUB.PACK.AGE import MODULE".
425
426
426 Setup "external library" to be imported with "absolute_import"
427 Setup "external library" to be imported with "absolute_import"
427 feature.
428 feature.
428
429
429 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
430 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
430 $ touch $TESTTMP/extlibroot/__init__.py
431 $ touch $TESTTMP/extlibroot/__init__.py
431 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
432 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
432 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
433 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
433
434
434 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<NO_CHECK_EOF
435 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<NO_CHECK_EOF
435 > def func():
436 > def func():
436 > return b"this is extlibroot.lsub1.lsub2.called.func()"
437 > return b"this is extlibroot.lsub1.lsub2.called.func()"
437 > NO_CHECK_EOF
438 > NO_CHECK_EOF
438 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<NO_CHECK_EOF
439 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<NO_CHECK_EOF
439 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
440 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
440 > NO_CHECK_EOF
441 > NO_CHECK_EOF
441 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<NO_CHECK_EOF
442 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<NO_CHECK_EOF
442 > detail = b"this is extlibroot.lsub1.lsub2.used"
443 > detail = b"this is extlibroot.lsub1.lsub2.used"
443 > NO_CHECK_EOF
444 > NO_CHECK_EOF
444
445
445 Setup sub-package of "external library", which causes instantiation of
446 Setup sub-package of "external library", which causes instantiation of
446 demandmod in "recurse down the module chain" code path. Relative
447 demandmod in "recurse down the module chain" code path. Relative
447 importing with "absolute_import" feature isn't tested, because "level
448 importing with "absolute_import" feature isn't tested, because "level
448 >=1 " doesn't cause instantiation of demandmod.
449 >=1 " doesn't cause instantiation of demandmod.
449
450
450 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
451 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
451 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<NO_CHECK_EOF
452 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<NO_CHECK_EOF
452 > detail = b"this is extlibroot.recursedown.abs.used"
453 > detail = b"this is extlibroot.recursedown.abs.used"
453 > NO_CHECK_EOF
454 > NO_CHECK_EOF
454 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<NO_CHECK_EOF
455 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<NO_CHECK_EOF
455 > from __future__ import absolute_import
456 > from __future__ import absolute_import
456 > from extlibroot.recursedown.abs.used import detail
457 > from extlibroot.recursedown.abs.used import detail
457 > NO_CHECK_EOF
458 > NO_CHECK_EOF
458
459
459 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
460 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
460 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<NO_CHECK_EOF
461 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<NO_CHECK_EOF
461 > detail = b"this is extlibroot.recursedown.legacy.used"
462 > detail = b"this is extlibroot.recursedown.legacy.used"
462 > NO_CHECK_EOF
463 > NO_CHECK_EOF
463 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<NO_CHECK_EOF
464 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<NO_CHECK_EOF
464 > # legacy style (level == -1) import
465 > # legacy style (level == -1) import
465 > from extlibroot.recursedown.legacy.used import detail
466 > from extlibroot.recursedown.legacy.used import detail
466 > NO_CHECK_EOF
467 > NO_CHECK_EOF
467
468
468 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<NO_CHECK_EOF
469 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<NO_CHECK_EOF
469 > from __future__ import absolute_import
470 > from __future__ import absolute_import
470 > from extlibroot.recursedown.abs import detail as absdetail
471 > from extlibroot.recursedown.abs import detail as absdetail
471 > from .legacy import detail as legacydetail
472 > from .legacy import detail as legacydetail
472 > NO_CHECK_EOF
473 > NO_CHECK_EOF
473
474
474 Setup package that re-exports an attribute of its submodule as the same
475 Setup package that re-exports an attribute of its submodule as the same
475 name. This leaves 'shadowing.used' pointing to 'used.detail', but still
476 name. This leaves 'shadowing.used' pointing to 'used.detail', but still
476 the submodule 'used' should be somehow accessible. (issue5617)
477 the submodule 'used' should be somehow accessible. (issue5617)
477
478
478 $ mkdir -p $TESTTMP/extlibroot/shadowing
479 $ mkdir -p $TESTTMP/extlibroot/shadowing
479 $ cat > $TESTTMP/extlibroot/shadowing/used.py <<NO_CHECK_EOF
480 $ cat > $TESTTMP/extlibroot/shadowing/used.py <<NO_CHECK_EOF
480 > detail = b"this is extlibroot.shadowing.used"
481 > detail = b"this is extlibroot.shadowing.used"
481 > NO_CHECK_EOF
482 > NO_CHECK_EOF
482 $ cat > $TESTTMP/extlibroot/shadowing/proxied.py <<NO_CHECK_EOF
483 $ cat > $TESTTMP/extlibroot/shadowing/proxied.py <<NO_CHECK_EOF
483 > from __future__ import absolute_import
484 > from __future__ import absolute_import
484 > from extlibroot.shadowing.used import detail
485 > from extlibroot.shadowing.used import detail
485 > NO_CHECK_EOF
486 > NO_CHECK_EOF
486 $ cat > $TESTTMP/extlibroot/shadowing/__init__.py <<NO_CHECK_EOF
487 $ cat > $TESTTMP/extlibroot/shadowing/__init__.py <<NO_CHECK_EOF
487 > from __future__ import absolute_import
488 > from __future__ import absolute_import
488 > from .used import detail as used
489 > from .used import detail as used
489 > NO_CHECK_EOF
490 > NO_CHECK_EOF
490
491
491 Setup extension local modules to be imported with "absolute_import"
492 Setup extension local modules to be imported with "absolute_import"
492 feature.
493 feature.
493
494
494 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
495 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
495 $ touch $TESTTMP/absextroot/xsub1/__init__.py
496 $ touch $TESTTMP/absextroot/xsub1/__init__.py
496 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
497 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
497
498
498 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<NO_CHECK_EOF
499 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<NO_CHECK_EOF
499 > def func():
500 > def func():
500 > return b"this is absextroot.xsub1.xsub2.called.func()"
501 > return b"this is absextroot.xsub1.xsub2.called.func()"
501 > NO_CHECK_EOF
502 > NO_CHECK_EOF
502 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<NO_CHECK_EOF
503 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<NO_CHECK_EOF
503 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
504 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
504 > NO_CHECK_EOF
505 > NO_CHECK_EOF
505 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<NO_CHECK_EOF
506 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<NO_CHECK_EOF
506 > detail = b"this is absextroot.xsub1.xsub2.used"
507 > detail = b"this is absextroot.xsub1.xsub2.used"
507 > NO_CHECK_EOF
508 > NO_CHECK_EOF
508
509
509 Setup extension local modules to examine whether demand importing
510 Setup extension local modules to examine whether demand importing
510 works as expected in "level > 1" case.
511 works as expected in "level > 1" case.
511
512
512 $ cat > $TESTTMP/absextroot/relimportee.py <<NO_CHECK_EOF
513 $ cat > $TESTTMP/absextroot/relimportee.py <<NO_CHECK_EOF
513 > detail = b"this is absextroot.relimportee"
514 > detail = b"this is absextroot.relimportee"
514 > NO_CHECK_EOF
515 > NO_CHECK_EOF
515 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<NO_CHECK_EOF
516 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<NO_CHECK_EOF
516 > from __future__ import absolute_import
517 > from __future__ import absolute_import
517 > from mercurial import pycompat
518 > from mercurial import pycompat
518 > from ... import relimportee
519 > from ... import relimportee
519 > detail = b"this relimporter imports %r" % (
520 > detail = b"this relimporter imports %r" % (
520 > pycompat.bytestr(relimportee.detail))
521 > pycompat.bytestr(relimportee.detail))
521 > NO_CHECK_EOF
522 > NO_CHECK_EOF
522
523
523 Setup modules, which actually import extension local modules at
524 Setup modules, which actually import extension local modules at
524 runtime.
525 runtime.
525
526
526 $ cat > $TESTTMP/absextroot/absolute.py << NO_CHECK_EOF
527 $ cat > $TESTTMP/absextroot/absolute.py << NO_CHECK_EOF
527 > from __future__ import absolute_import
528 > from __future__ import absolute_import
528 >
529 >
529 > # import extension local modules absolutely (level = 0)
530 > # import extension local modules absolutely (level = 0)
530 > from absextroot.xsub1.xsub2 import used, unused
531 > from absextroot.xsub1.xsub2 import used, unused
531 > from absextroot.xsub1.xsub2.called import func
532 > from absextroot.xsub1.xsub2.called import func
532 >
533 >
533 > def getresult():
534 > def getresult():
534 > result = []
535 > result = []
535 > result.append(used.detail)
536 > result.append(used.detail)
536 > result.append(func())
537 > result.append(func())
537 > return result
538 > return result
538 > NO_CHECK_EOF
539 > NO_CHECK_EOF
539
540
540 $ cat > $TESTTMP/absextroot/relative.py << NO_CHECK_EOF
541 $ cat > $TESTTMP/absextroot/relative.py << NO_CHECK_EOF
541 > from __future__ import absolute_import
542 > from __future__ import absolute_import
542 >
543 >
543 > # import extension local modules relatively (level == 1)
544 > # import extension local modules relatively (level == 1)
544 > from .xsub1.xsub2 import used, unused
545 > from .xsub1.xsub2 import used, unused
545 > from .xsub1.xsub2.called import func
546 > from .xsub1.xsub2.called import func
546 >
547 >
547 > # import a module, which implies "importing with level > 1"
548 > # import a module, which implies "importing with level > 1"
548 > from .xsub1.xsub2 import relimporter
549 > from .xsub1.xsub2 import relimporter
549 >
550 >
550 > def getresult():
551 > def getresult():
551 > result = []
552 > result = []
552 > result.append(used.detail)
553 > result.append(used.detail)
553 > result.append(func())
554 > result.append(func())
554 > result.append(relimporter.detail)
555 > result.append(relimporter.detail)
555 > return result
556 > return result
556 > NO_CHECK_EOF
557 > NO_CHECK_EOF
557
558
558 Setup main procedure of extension.
559 Setup main procedure of extension.
559
560
560 $ cat > $TESTTMP/absextroot/__init__.py <<NO_CHECK_EOF
561 $ cat > $TESTTMP/absextroot/__init__.py <<NO_CHECK_EOF
561 > from __future__ import absolute_import
562 > from __future__ import absolute_import
562 > from mercurial import registrar
563 > from mercurial import registrar
563 > cmdtable = {}
564 > cmdtable = {}
564 > command = registrar.command(cmdtable)
565 > command = registrar.command(cmdtable)
565 >
566 >
566 > # "absolute" and "relative" shouldn't be imported before actual
567 > # "absolute" and "relative" shouldn't be imported before actual
567 > # command execution, because (1) they import same modules, and (2)
568 > # command execution, because (1) they import same modules, and (2)
568 > # preceding import (= instantiate "demandmod" object instead of
569 > # preceding import (= instantiate "demandmod" object instead of
569 > # real "module" object) might hide problem of succeeding import.
570 > # real "module" object) might hide problem of succeeding import.
570 >
571 >
571 > @command(b'showabsolute', [], norepo=True)
572 > @command(b'showabsolute', [], norepo=True)
572 > def showabsolute(ui, *args, **opts):
573 > def showabsolute(ui, *args, **opts):
573 > from absextroot import absolute
574 > from absextroot import absolute
574 > ui.write(b'ABS: %s\n' % b'\nABS: '.join(absolute.getresult()))
575 > ui.write(b'ABS: %s\n' % b'\nABS: '.join(absolute.getresult()))
575 >
576 >
576 > @command(b'showrelative', [], norepo=True)
577 > @command(b'showrelative', [], norepo=True)
577 > def showrelative(ui, *args, **opts):
578 > def showrelative(ui, *args, **opts):
578 > from . import relative
579 > from . import relative
579 > ui.write(b'REL: %s\n' % b'\nREL: '.join(relative.getresult()))
580 > ui.write(b'REL: %s\n' % b'\nREL: '.join(relative.getresult()))
580 >
581 >
581 > # import modules from external library
582 > # import modules from external library
582 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
583 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
583 > from extlibroot.lsub1.lsub2.called import func as lfunc
584 > from extlibroot.lsub1.lsub2.called import func as lfunc
584 > from extlibroot.recursedown import absdetail, legacydetail
585 > from extlibroot.recursedown import absdetail, legacydetail
585 > from extlibroot.shadowing import proxied
586 > from extlibroot.shadowing import proxied
586 >
587 >
587 > def uisetup(ui):
588 > def uisetup(ui):
588 > result = []
589 > result = []
589 > result.append(lused.detail)
590 > result.append(lused.detail)
590 > result.append(lfunc())
591 > result.append(lfunc())
591 > result.append(absdetail)
592 > result.append(absdetail)
592 > result.append(legacydetail)
593 > result.append(legacydetail)
593 > result.append(proxied.detail)
594 > result.append(proxied.detail)
594 > ui.write(b'LIB: %s\n' % b'\nLIB: '.join(result))
595 > ui.write(b'LIB: %s\n' % b'\nLIB: '.join(result))
595 > NO_CHECK_EOF
596 > NO_CHECK_EOF
596
597
597 Examine module importing.
598 Examine module importing.
598
599
599 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
600 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
600 LIB: this is extlibroot.lsub1.lsub2.used
601 LIB: this is extlibroot.lsub1.lsub2.used
601 LIB: this is extlibroot.lsub1.lsub2.called.func()
602 LIB: this is extlibroot.lsub1.lsub2.called.func()
602 LIB: this is extlibroot.recursedown.abs.used
603 LIB: this is extlibroot.recursedown.abs.used
603 LIB: this is extlibroot.recursedown.legacy.used
604 LIB: this is extlibroot.recursedown.legacy.used
604 LIB: this is extlibroot.shadowing.used
605 LIB: this is extlibroot.shadowing.used
605 ABS: this is absextroot.xsub1.xsub2.used
606 ABS: this is absextroot.xsub1.xsub2.used
606 ABS: this is absextroot.xsub1.xsub2.called.func()
607 ABS: this is absextroot.xsub1.xsub2.called.func()
607
608
608 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
609 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
609 LIB: this is extlibroot.lsub1.lsub2.used
610 LIB: this is extlibroot.lsub1.lsub2.used
610 LIB: this is extlibroot.lsub1.lsub2.called.func()
611 LIB: this is extlibroot.lsub1.lsub2.called.func()
611 LIB: this is extlibroot.recursedown.abs.used
612 LIB: this is extlibroot.recursedown.abs.used
612 LIB: this is extlibroot.recursedown.legacy.used
613 LIB: this is extlibroot.recursedown.legacy.used
613 LIB: this is extlibroot.shadowing.used
614 LIB: this is extlibroot.shadowing.used
614 REL: this is absextroot.xsub1.xsub2.used
615 REL: this is absextroot.xsub1.xsub2.used
615 REL: this is absextroot.xsub1.xsub2.called.func()
616 REL: this is absextroot.xsub1.xsub2.called.func()
616 REL: this relimporter imports 'this is absextroot.relimportee'
617 REL: this relimporter imports 'this is absextroot.relimportee'
617
618
618 Examine whether sub-module is imported relatively as expected.
619 Examine whether sub-module is imported relatively as expected.
619
620
620 See also issue5208 for detail about example case on Python 3.x.
621 See also issue5208 for detail about example case on Python 3.x.
621
622
622 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
623 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
623 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
624 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
624
625
625 $ cat > $TESTTMP/notexist.py <<NO_CHECK_EOF
626 $ cat > $TESTTMP/notexist.py <<NO_CHECK_EOF
626 > text = 'notexist.py at root is loaded unintentionally\n'
627 > text = 'notexist.py at root is loaded unintentionally\n'
627 > NO_CHECK_EOF
628 > NO_CHECK_EOF
628
629
629 $ cat > $TESTTMP/checkrelativity.py <<NO_CHECK_EOF
630 $ cat > $TESTTMP/checkrelativity.py <<NO_CHECK_EOF
630 > from mercurial import registrar
631 > from mercurial import registrar
631 > cmdtable = {}
632 > cmdtable = {}
632 > command = registrar.command(cmdtable)
633 > command = registrar.command(cmdtable)
633 >
634 >
634 > # demand import avoids failure of importing notexist here, but only on
635 > # demand import avoids failure of importing notexist here, but only on
635 > # Python 2.
636 > # Python 2.
636 > import extlibroot.lsub1.lsub2.notexist
637 > import extlibroot.lsub1.lsub2.notexist
637 >
638 >
638 > @command(b'checkrelativity', [], norepo=True)
639 > @command(b'checkrelativity', [], norepo=True)
639 > def checkrelativity(ui, *args, **opts):
640 > def checkrelativity(ui, *args, **opts):
640 > try:
641 > try:
641 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
642 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
642 > return 1 # unintentional success
643 > return 1 # unintentional success
643 > except ImportError:
644 > except ImportError:
644 > pass # intentional failure
645 > pass # intentional failure
645 > NO_CHECK_EOF
646 > NO_CHECK_EOF
646
647
647 Python 3's lazy importer verifies modules exist before returning the lazy
648 Python 3's lazy importer verifies modules exist before returning the lazy
648 module stub. Our custom lazy importer for Python 2 always returns a stub.
649 module stub. Our custom lazy importer for Python 2 always returns a stub.
649
650
650 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity) || true
651 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity) || true
651 *** failed to import extension checkrelativity from $TESTTMP/checkrelativity.py: No module named 'extlibroot.lsub1.lsub2.notexist' (py3 !)
652 *** failed to import extension checkrelativity from $TESTTMP/checkrelativity.py: No module named 'extlibroot.lsub1.lsub2.notexist' (py3 !)
652 hg: unknown command 'checkrelativity' (py3 !)
653 hg: unknown command 'checkrelativity' (py3 !)
653 (use 'hg help' for a list of commands) (py3 !)
654 (use 'hg help' for a list of commands) (py3 !)
654
655
655 #endif
656 #endif
656
657
657 (Here, module importing tests are finished. Therefore, use other than
658 (Here, module importing tests are finished. Therefore, use other than
658 NO_CHECK_* limit mark for heredoc python files, in order to apply
659 NO_CHECK_* limit mark for heredoc python files, in order to apply
659 import-checker.py or so on their contents)
660 import-checker.py or so on their contents)
660
661
661 Make sure a broken uisetup doesn't globally break hg:
662 Make sure a broken uisetup doesn't globally break hg:
662 $ cat > $TESTTMP/baduisetup.py <<EOF
663 $ cat > $TESTTMP/baduisetup.py <<EOF
663 > def uisetup(ui):
664 > def uisetup(ui):
664 > 1 / 0
665 > 1 / 0
665 > EOF
666 > EOF
666
667
667 Even though the extension fails during uisetup, hg is still basically usable:
668 Even though the extension fails during uisetup, hg is still basically usable:
668 $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version
669 $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version
669 Traceback (most recent call last):
670 Traceback (most recent call last):
670 File "*/mercurial/extensions.py", line *, in _runuisetup (glob)
671 File "*/mercurial/extensions.py", line *, in _runuisetup (glob)
671 uisetup(ui)
672 uisetup(ui)
672 File "$TESTTMP/baduisetup.py", line 2, in uisetup
673 File "$TESTTMP/baduisetup.py", line 2, in uisetup
673 1 / 0
674 1 / 0
674 ZeroDivisionError: * by zero (glob)
675 ZeroDivisionError: * by zero (glob)
675 *** failed to set up extension baduisetup: * by zero (glob)
676 *** failed to set up extension baduisetup: * by zero (glob)
676 Mercurial Distributed SCM (version *) (glob)
677 Mercurial Distributed SCM (version *) (glob)
677 (see https://mercurial-scm.org for more information)
678 (see https://mercurial-scm.org for more information)
678
679
679 Copyright (C) 2005-* Olivia Mackall and others (glob)
680 Copyright (C) 2005-* Olivia Mackall and others (glob)
680 This is free software; see the source for copying conditions. There is NO
681 This is free software; see the source for copying conditions. There is NO
681 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
682 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
682
683
683 $ cd ..
684 $ cd ..
684
685
685 hide outer repo
686 hide outer repo
686 $ hg init
687 $ hg init
687
688
688 $ cat > empty.py <<EOF
689 $ cat > empty.py <<EOF
689 > '''empty cmdtable
690 > '''empty cmdtable
690 > '''
691 > '''
691 > cmdtable = {}
692 > cmdtable = {}
692 > EOF
693 > EOF
693 $ emptypath=`pwd`/empty.py
694 $ emptypath=`pwd`/empty.py
694 $ echo "empty = $emptypath" >> $HGRCPATH
695 $ echo "empty = $emptypath" >> $HGRCPATH
695 $ hg help empty
696 $ hg help empty
696 empty extension - empty cmdtable
697 empty extension - empty cmdtable
697
698
698 no commands defined
699 no commands defined
699
700
700
701
701 $ echo 'empty = !' >> $HGRCPATH
702 $ echo 'empty = !' >> $HGRCPATH
702
703
703 $ cat > debugextension.py <<EOF
704 $ cat > debugextension.py <<EOF
704 > '''only debugcommands
705 > '''only debugcommands
705 > '''
706 > '''
706 > from mercurial import registrar
707 > from mercurial import registrar
707 > cmdtable = {}
708 > cmdtable = {}
708 > command = registrar.command(cmdtable)
709 > command = registrar.command(cmdtable)
709 > @command(b'debugfoobar', [], b'hg debugfoobar')
710 > @command(b'debugfoobar', [], b'hg debugfoobar')
710 > def debugfoobar(ui, repo, *args, **opts):
711 > def debugfoobar(ui, repo, *args, **opts):
711 > "yet another debug command"
712 > "yet another debug command"
712 > @command(b'foo', [], b'hg foo')
713 > @command(b'foo', [], b'hg foo')
713 > def foo(ui, repo, *args, **opts):
714 > def foo(ui, repo, *args, **opts):
714 > """yet another foo command
715 > """yet another foo command
715 > This command has been DEPRECATED since forever.
716 > This command has been DEPRECATED since forever.
716 > """
717 > """
717 > EOF
718 > EOF
718 $ debugpath=`pwd`/debugextension.py
719 $ debugpath=`pwd`/debugextension.py
719 $ echo "debugextension = $debugpath" >> $HGRCPATH
720 $ echo "debugextension = $debugpath" >> $HGRCPATH
720
721
721 $ hg help debugextension
722 $ hg help debugextension
722 hg debugextensions
723 hg debugextensions
723
724
724 show information about active extensions
725 show information about active extensions
725
726
726 options:
727 options:
727
728
728 -T --template TEMPLATE display with template
729 -T --template TEMPLATE display with template
729
730
730 (some details hidden, use --verbose to show complete help)
731 (some details hidden, use --verbose to show complete help)
731
732
732
733
733 $ hg --verbose help debugextension
734 $ hg --verbose help debugextension
734 hg debugextensions
735 hg debugextensions
735
736
736 show information about active extensions
737 show information about active extensions
737
738
738 options:
739 options:
739
740
740 -T --template TEMPLATE display with template
741 -T --template TEMPLATE display with template
741
742
742 global options ([+] can be repeated):
743 global options ([+] can be repeated):
743
744
744 -R --repository REPO repository root directory or name of overlay bundle
745 -R --repository REPO repository root directory or name of overlay bundle
745 file
746 file
746 --cwd DIR change working directory
747 --cwd DIR change working directory
747 -y --noninteractive do not prompt, automatically pick the first choice for
748 -y --noninteractive do not prompt, automatically pick the first choice for
748 all prompts
749 all prompts
749 -q --quiet suppress output
750 -q --quiet suppress output
750 -v --verbose enable additional output
751 -v --verbose enable additional output
751 --color TYPE when to colorize (boolean, always, auto, never, or
752 --color TYPE when to colorize (boolean, always, auto, never, or
752 debug)
753 debug)
753 --config CONFIG [+] set/override config option (use 'section.name=value')
754 --config CONFIG [+] set/override config option (use 'section.name=value')
754 --debug enable debugging output
755 --debug enable debugging output
755 --debugger start debugger
756 --debugger start debugger
756 --encoding ENCODE set the charset encoding (default: ascii)
757 --encoding ENCODE set the charset encoding (default: ascii)
757 --encodingmode MODE set the charset encoding mode (default: strict)
758 --encodingmode MODE set the charset encoding mode (default: strict)
758 --traceback always print a traceback on exception
759 --traceback always print a traceback on exception
759 --time time how long the command takes
760 --time time how long the command takes
760 --profile print command execution profile
761 --profile print command execution profile
761 --version output version information and exit
762 --version output version information and exit
762 -h --help display help and exit
763 -h --help display help and exit
763 --hidden consider hidden changesets
764 --hidden consider hidden changesets
764 --pager TYPE when to paginate (boolean, always, auto, or never)
765 --pager TYPE when to paginate (boolean, always, auto, or never)
765 (default: auto)
766 (default: auto)
766
767
767
768
768
769
769
770
770
771
771
772
772 $ hg --debug help debugextension
773 $ hg --debug help debugextension
773 hg debugextensions
774 hg debugextensions
774
775
775 show information about active extensions
776 show information about active extensions
776
777
777 options:
778 options:
778
779
779 -T --template TEMPLATE display with template
780 -T --template TEMPLATE display with template
780
781
781 global options ([+] can be repeated):
782 global options ([+] can be repeated):
782
783
783 -R --repository REPO repository root directory or name of overlay bundle
784 -R --repository REPO repository root directory or name of overlay bundle
784 file
785 file
785 --cwd DIR change working directory
786 --cwd DIR change working directory
786 -y --noninteractive do not prompt, automatically pick the first choice for
787 -y --noninteractive do not prompt, automatically pick the first choice for
787 all prompts
788 all prompts
788 -q --quiet suppress output
789 -q --quiet suppress output
789 -v --verbose enable additional output
790 -v --verbose enable additional output
790 --color TYPE when to colorize (boolean, always, auto, never, or
791 --color TYPE when to colorize (boolean, always, auto, never, or
791 debug)
792 debug)
792 --config CONFIG [+] set/override config option (use 'section.name=value')
793 --config CONFIG [+] set/override config option (use 'section.name=value')
793 --debug enable debugging output
794 --debug enable debugging output
794 --debugger start debugger
795 --debugger start debugger
795 --encoding ENCODE set the charset encoding (default: ascii)
796 --encoding ENCODE set the charset encoding (default: ascii)
796 --encodingmode MODE set the charset encoding mode (default: strict)
797 --encodingmode MODE set the charset encoding mode (default: strict)
797 --traceback always print a traceback on exception
798 --traceback always print a traceback on exception
798 --time time how long the command takes
799 --time time how long the command takes
799 --profile print command execution profile
800 --profile print command execution profile
800 --version output version information and exit
801 --version output version information and exit
801 -h --help display help and exit
802 -h --help display help and exit
802 --hidden consider hidden changesets
803 --hidden consider hidden changesets
803 --pager TYPE when to paginate (boolean, always, auto, or never)
804 --pager TYPE when to paginate (boolean, always, auto, or never)
804 (default: auto)
805 (default: auto)
805
806
806
807
807
808
808
809
809
810
810 $ echo 'debugextension = !' >> $HGRCPATH
811 $ echo 'debugextension = !' >> $HGRCPATH
811
812
812 Asking for help about a deprecated extension should do something useful:
813 Asking for help about a deprecated extension should do something useful:
813
814
814 $ hg help glog
815 $ hg help glog
815 'glog' is provided by the following extension:
816 'glog' is provided by the following extension:
816
817
817 graphlog command to view revision graphs from a shell (DEPRECATED)
818 graphlog command to view revision graphs from a shell (DEPRECATED)
818
819
819 (use 'hg help extensions' for information on enabling extensions)
820 (use 'hg help extensions' for information on enabling extensions)
820
821
821 Extension module help vs command help:
822 Extension module help vs command help:
822
823
823 $ echo 'extdiff =' >> $HGRCPATH
824 $ echo 'extdiff =' >> $HGRCPATH
824 $ hg help extdiff
825 $ hg help extdiff
825 hg extdiff [OPT]... [FILE]...
826 hg extdiff [OPT]... [FILE]...
826
827
827 use external program to diff repository (or selected files)
828 use external program to diff repository (or selected files)
828
829
829 Show differences between revisions for the specified files, using an
830 Show differences between revisions for the specified files, using an
830 external program. The default program used is diff, with default options
831 external program. The default program used is diff, with default options
831 "-Npru".
832 "-Npru".
832
833
833 To select a different program, use the -p/--program option. The program
834 To select a different program, use the -p/--program option. The program
834 will be passed the names of two directories to compare, unless the --per-
835 will be passed the names of two directories to compare, unless the --per-
835 file option is specified (see below). To pass additional options to the
836 file option is specified (see below). To pass additional options to the
836 program, use -o/--option. These will be passed before the names of the
837 program, use -o/--option. These will be passed before the names of the
837 directories or files to compare.
838 directories or files to compare.
838
839
839 The --from, --to, and --change options work the same way they do for 'hg
840 The --from, --to, and --change options work the same way they do for 'hg
840 diff'.
841 diff'.
841
842
842 The --per-file option runs the external program repeatedly on each file to
843 The --per-file option runs the external program repeatedly on each file to
843 diff, instead of once on two directories. By default, this happens one by
844 diff, instead of once on two directories. By default, this happens one by
844 one, where the next file diff is open in the external program only once
845 one, where the next file diff is open in the external program only once
845 the previous external program (for the previous file diff) has exited. If
846 the previous external program (for the previous file diff) has exited. If
846 the external program has a graphical interface, it can open all the file
847 the external program has a graphical interface, it can open all the file
847 diffs at once instead of one by one. See 'hg help -e extdiff' for
848 diffs at once instead of one by one. See 'hg help -e extdiff' for
848 information about how to tell Mercurial that a given program has a
849 information about how to tell Mercurial that a given program has a
849 graphical interface.
850 graphical interface.
850
851
851 The --confirm option will prompt the user before each invocation of the
852 The --confirm option will prompt the user before each invocation of the
852 external program. It is ignored if --per-file isn't specified.
853 external program. It is ignored if --per-file isn't specified.
853
854
854 (use 'hg help -e extdiff' to show help for the extdiff extension)
855 (use 'hg help -e extdiff' to show help for the extdiff extension)
855
856
856 options ([+] can be repeated):
857 options ([+] can be repeated):
857
858
858 -p --program CMD comparison program to run
859 -p --program CMD comparison program to run
859 -o --option OPT [+] pass option to comparison program
860 -o --option OPT [+] pass option to comparison program
860 --from REV1 revision to diff from
861 --from REV1 revision to diff from
861 --to REV2 revision to diff to
862 --to REV2 revision to diff to
862 -c --change REV change made by revision
863 -c --change REV change made by revision
863 --per-file compare each file instead of revision snapshots
864 --per-file compare each file instead of revision snapshots
864 --confirm prompt user before each external program invocation
865 --confirm prompt user before each external program invocation
865 --patch compare patches for two revisions
866 --patch compare patches for two revisions
866 -I --include PATTERN [+] include names matching the given patterns
867 -I --include PATTERN [+] include names matching the given patterns
867 -X --exclude PATTERN [+] exclude names matching the given patterns
868 -X --exclude PATTERN [+] exclude names matching the given patterns
868 -S --subrepos recurse into subrepositories
869 -S --subrepos recurse into subrepositories
869
870
870 (some details hidden, use --verbose to show complete help)
871 (some details hidden, use --verbose to show complete help)
871
872
872
873
873
874
874
875
875
876
876
877
877
878
878
879
879
880
880
881
881 $ hg help --extension extdiff
882 $ hg help --extension extdiff
882 extdiff extension - command to allow external programs to compare revisions
883 extdiff extension - command to allow external programs to compare revisions
883
884
884 The extdiff Mercurial extension allows you to use external programs to compare
885 The extdiff Mercurial extension allows you to use external programs to compare
885 revisions, or revision with working directory. The external diff programs are
886 revisions, or revision with working directory. The external diff programs are
886 called with a configurable set of options and two non-option arguments: paths
887 called with a configurable set of options and two non-option arguments: paths
887 to directories containing snapshots of files to compare.
888 to directories containing snapshots of files to compare.
888
889
889 If there is more than one file being compared and the "child" revision is the
890 If there is more than one file being compared and the "child" revision is the
890 working directory, any modifications made in the external diff program will be
891 working directory, any modifications made in the external diff program will be
891 copied back to the working directory from the temporary directory.
892 copied back to the working directory from the temporary directory.
892
893
893 The extdiff extension also allows you to configure new diff commands, so you
894 The extdiff extension also allows you to configure new diff commands, so you
894 do not need to type 'hg extdiff -p kdiff3' always.
895 do not need to type 'hg extdiff -p kdiff3' always.
895
896
896 [extdiff]
897 [extdiff]
897 # add new command that runs GNU diff(1) in 'context diff' mode
898 # add new command that runs GNU diff(1) in 'context diff' mode
898 cdiff = gdiff -Nprc5
899 cdiff = gdiff -Nprc5
899 ## or the old way:
900 ## or the old way:
900 #cmd.cdiff = gdiff
901 #cmd.cdiff = gdiff
901 #opts.cdiff = -Nprc5
902 #opts.cdiff = -Nprc5
902
903
903 # add new command called meld, runs meld (no need to name twice). If
904 # add new command called meld, runs meld (no need to name twice). If
904 # the meld executable is not available, the meld tool in [merge-tools]
905 # the meld executable is not available, the meld tool in [merge-tools]
905 # will be used, if available
906 # will be used, if available
906 meld =
907 meld =
907
908
908 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
909 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
909 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
910 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
910 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
911 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
911 # your .vimrc
912 # your .vimrc
912 vimdiff = gvim -f "+next" \
913 vimdiff = gvim -f "+next" \
913 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
914 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
914
915
915 Tool arguments can include variables that are expanded at runtime:
916 Tool arguments can include variables that are expanded at runtime:
916
917
917 $parent1, $plabel1 - filename, descriptive label of first parent
918 $parent1, $plabel1 - filename, descriptive label of first parent
918 $child, $clabel - filename, descriptive label of child revision
919 $child, $clabel - filename, descriptive label of child revision
919 $parent2, $plabel2 - filename, descriptive label of second parent
920 $parent2, $plabel2 - filename, descriptive label of second parent
920 $root - repository root
921 $root - repository root
921 $parent is an alias for $parent1.
922 $parent is an alias for $parent1.
922
923
923 The extdiff extension will look in your [diff-tools] and [merge-tools]
924 The extdiff extension will look in your [diff-tools] and [merge-tools]
924 sections for diff tool arguments, when none are specified in [extdiff].
925 sections for diff tool arguments, when none are specified in [extdiff].
925
926
926 [extdiff]
927 [extdiff]
927 kdiff3 =
928 kdiff3 =
928
929
929 [diff-tools]
930 [diff-tools]
930 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
931 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
931
932
932 If a program has a graphical interface, it might be interesting to tell
933 If a program has a graphical interface, it might be interesting to tell
933 Mercurial about it. It will prevent the program from being mistakenly used in
934 Mercurial about it. It will prevent the program from being mistakenly used in
934 a terminal-only environment (such as an SSH terminal session), and will make
935 a terminal-only environment (such as an SSH terminal session), and will make
935 'hg extdiff --per-file' open multiple file diffs at once instead of one by one
936 'hg extdiff --per-file' open multiple file diffs at once instead of one by one
936 (if you still want to open file diffs one by one, you can use the --confirm
937 (if you still want to open file diffs one by one, you can use the --confirm
937 option).
938 option).
938
939
939 Declaring that a tool has a graphical interface can be done with the "gui"
940 Declaring that a tool has a graphical interface can be done with the "gui"
940 flag next to where "diffargs" are specified:
941 flag next to where "diffargs" are specified:
941
942
942 [diff-tools]
943 [diff-tools]
943 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
944 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
944 kdiff3.gui = true
945 kdiff3.gui = true
945
946
946 You can use -I/-X and list of file or directory names like normal 'hg diff'
947 You can use -I/-X and list of file or directory names like normal 'hg diff'
947 command. The extdiff extension makes snapshots of only needed files, so
948 command. The extdiff extension makes snapshots of only needed files, so
948 running the external diff program will actually be pretty fast (at least
949 running the external diff program will actually be pretty fast (at least
949 faster than having to compare the entire tree).
950 faster than having to compare the entire tree).
950
951
951 list of commands:
952 list of commands:
952
953
953 extdiff use external program to diff repository (or selected files)
954 extdiff use external program to diff repository (or selected files)
954
955
955 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
956 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
956
957
957
958
958
959
959
960
960
961
961
962
962
963
963
964
964
965
965
966
966
967
967
968
968
969
969
970
970
971
971
972
972 $ echo 'extdiff = !' >> $HGRCPATH
973 $ echo 'extdiff = !' >> $HGRCPATH
973
974
974 Test help topic with same name as extension
975 Test help topic with same name as extension
975
976
976 $ cat > multirevs.py <<EOF
977 $ cat > multirevs.py <<EOF
977 > from mercurial import commands, registrar
978 > from mercurial import commands, registrar
978 > cmdtable = {}
979 > cmdtable = {}
979 > command = registrar.command(cmdtable)
980 > command = registrar.command(cmdtable)
980 > """multirevs extension
981 > """multirevs extension
981 > Big multi-line module docstring."""
982 > Big multi-line module docstring."""
982 > @command(b'multirevs', [], b'ARG', norepo=True)
983 > @command(b'multirevs', [], b'ARG', norepo=True)
983 > def multirevs(ui, repo, arg, *args, **opts):
984 > def multirevs(ui, repo, arg, *args, **opts):
984 > """multirevs command"""
985 > """multirevs command"""
985 > EOF
986 > EOF
986 $ echo "multirevs = multirevs.py" >> $HGRCPATH
987 $ echo "multirevs = multirevs.py" >> $HGRCPATH
987
988
988 $ hg help multirevs | tail
989 $ hg help multirevs | tail
989 used):
990 used):
990
991
991 hg update :@
992 hg update :@
992
993
993 - Show diff between tags 1.3 and 1.5 (this works because the first and the
994 - Show diff between tags 1.3 and 1.5 (this works because the first and the
994 last revisions of the revset are used):
995 last revisions of the revset are used):
995
996
996 hg diff -r 1.3::1.5
997 hg diff -r 1.3::1.5
997
998
998 use 'hg help -c multirevs' to see help for the multirevs command
999 use 'hg help -c multirevs' to see help for the multirevs command
999
1000
1000
1001
1001
1002
1002
1003
1003
1004
1004
1005
1005 $ hg help -c multirevs
1006 $ hg help -c multirevs
1006 hg multirevs ARG
1007 hg multirevs ARG
1007
1008
1008 multirevs command
1009 multirevs command
1009
1010
1010 (some details hidden, use --verbose to show complete help)
1011 (some details hidden, use --verbose to show complete help)
1011
1012
1012
1013
1013
1014
1014 $ hg multirevs
1015 $ hg multirevs
1015 hg multirevs: invalid arguments
1016 hg multirevs: invalid arguments
1016 hg multirevs ARG
1017 hg multirevs ARG
1017
1018
1018 multirevs command
1019 multirevs command
1019
1020
1020 (use 'hg multirevs -h' to show more help)
1021 (use 'hg multirevs -h' to show more help)
1021 [10]
1022 [10]
1022
1023
1023
1024
1024
1025
1025 $ echo "multirevs = !" >> $HGRCPATH
1026 $ echo "multirevs = !" >> $HGRCPATH
1026
1027
1027 Issue811: Problem loading extensions twice (by site and by user)
1028 Issue811: Problem loading extensions twice (by site and by user)
1028
1029
1029 $ cat <<EOF >> $HGRCPATH
1030 $ cat <<EOF >> $HGRCPATH
1030 > mq =
1031 > mq =
1031 > strip =
1032 > strip =
1032 > hgext.mq =
1033 > hgext.mq =
1033 > hgext/mq =
1034 > hgext/mq =
1034 > EOF
1035 > EOF
1035
1036
1036 Show extensions:
1037 Show extensions:
1037 (note that mq force load strip, also checking it's not loaded twice)
1038 (note that mq force load strip, also checking it's not loaded twice)
1038
1039
1039 #if no-extraextensions
1040 #if no-extraextensions
1040 $ hg debugextensions
1041 $ hg debugextensions
1041 mq
1042 mq
1042 strip
1043 strip
1043 #endif
1044 #endif
1044
1045
1045 For extensions, which name matches one of its commands, help
1046 For extensions, which name matches one of its commands, help
1046 message should ask '-v -e' to get list of built-in aliases
1047 message should ask '-v -e' to get list of built-in aliases
1047 along with extension help itself
1048 along with extension help itself
1048
1049
1049 $ mkdir $TESTTMP/d
1050 $ mkdir $TESTTMP/d
1050 $ cat > $TESTTMP/d/dodo.py <<EOF
1051 $ cat > $TESTTMP/d/dodo.py <<EOF
1051 > """
1052 > """
1052 > This is an awesome 'dodo' extension. It does nothing and
1053 > This is an awesome 'dodo' extension. It does nothing and
1053 > writes 'Foo foo'
1054 > writes 'Foo foo'
1054 > """
1055 > """
1055 > from mercurial import commands, registrar
1056 > from mercurial import commands, registrar
1056 > cmdtable = {}
1057 > cmdtable = {}
1057 > command = registrar.command(cmdtable)
1058 > command = registrar.command(cmdtable)
1058 > @command(b'dodo', [], b'hg dodo')
1059 > @command(b'dodo', [], b'hg dodo')
1059 > def dodo(ui, *args, **kwargs):
1060 > def dodo(ui, *args, **kwargs):
1060 > """Does nothing"""
1061 > """Does nothing"""
1061 > ui.write(b"I do nothing. Yay\\n")
1062 > ui.write(b"I do nothing. Yay\\n")
1062 > @command(b'foofoo', [], b'hg foofoo')
1063 > @command(b'foofoo', [], b'hg foofoo')
1063 > def foofoo(ui, *args, **kwargs):
1064 > def foofoo(ui, *args, **kwargs):
1064 > """Writes 'Foo foo'"""
1065 > """Writes 'Foo foo'"""
1065 > ui.write(b"Foo foo\\n")
1066 > ui.write(b"Foo foo\\n")
1066 > EOF
1067 > EOF
1067 $ dodopath=$TESTTMP/d/dodo.py
1068 $ dodopath=$TESTTMP/d/dodo.py
1068
1069
1069 $ echo "dodo = $dodopath" >> $HGRCPATH
1070 $ echo "dodo = $dodopath" >> $HGRCPATH
1070
1071
1071 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
1072 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
1072 $ hg help -e dodo
1073 $ hg help -e dodo
1073 dodo extension -
1074 dodo extension -
1074
1075
1075 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
1076 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
1076
1077
1077 list of commands:
1078 list of commands:
1078
1079
1079 dodo Does nothing
1080 dodo Does nothing
1080 foofoo Writes 'Foo foo'
1081 foofoo Writes 'Foo foo'
1081
1082
1082 (use 'hg help -v -e dodo' to show built-in aliases and global options)
1083 (use 'hg help -v -e dodo' to show built-in aliases and global options)
1083
1084
1084 Make sure that '-v -e' prints list of built-in aliases along with
1085 Make sure that '-v -e' prints list of built-in aliases along with
1085 extension help itself
1086 extension help itself
1086 $ hg help -v -e dodo
1087 $ hg help -v -e dodo
1087 dodo extension -
1088 dodo extension -
1088
1089
1089 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
1090 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
1090
1091
1091 list of commands:
1092 list of commands:
1092
1093
1093 dodo Does nothing
1094 dodo Does nothing
1094 foofoo Writes 'Foo foo'
1095 foofoo Writes 'Foo foo'
1095
1096
1096 global options ([+] can be repeated):
1097 global options ([+] can be repeated):
1097
1098
1098 -R --repository REPO repository root directory or name of overlay bundle
1099 -R --repository REPO repository root directory or name of overlay bundle
1099 file
1100 file
1100 --cwd DIR change working directory
1101 --cwd DIR change working directory
1101 -y --noninteractive do not prompt, automatically pick the first choice for
1102 -y --noninteractive do not prompt, automatically pick the first choice for
1102 all prompts
1103 all prompts
1103 -q --quiet suppress output
1104 -q --quiet suppress output
1104 -v --verbose enable additional output
1105 -v --verbose enable additional output
1105 --color TYPE when to colorize (boolean, always, auto, never, or
1106 --color TYPE when to colorize (boolean, always, auto, never, or
1106 debug)
1107 debug)
1107 --config CONFIG [+] set/override config option (use 'section.name=value')
1108 --config CONFIG [+] set/override config option (use 'section.name=value')
1108 --debug enable debugging output
1109 --debug enable debugging output
1109 --debugger start debugger
1110 --debugger start debugger
1110 --encoding ENCODE set the charset encoding (default: ascii)
1111 --encoding ENCODE set the charset encoding (default: ascii)
1111 --encodingmode MODE set the charset encoding mode (default: strict)
1112 --encodingmode MODE set the charset encoding mode (default: strict)
1112 --traceback always print a traceback on exception
1113 --traceback always print a traceback on exception
1113 --time time how long the command takes
1114 --time time how long the command takes
1114 --profile print command execution profile
1115 --profile print command execution profile
1115 --version output version information and exit
1116 --version output version information and exit
1116 -h --help display help and exit
1117 -h --help display help and exit
1117 --hidden consider hidden changesets
1118 --hidden consider hidden changesets
1118 --pager TYPE when to paginate (boolean, always, auto, or never)
1119 --pager TYPE when to paginate (boolean, always, auto, or never)
1119 (default: auto)
1120 (default: auto)
1120
1121
1121 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
1122 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
1122 $ hg help -v dodo
1123 $ hg help -v dodo
1123 hg dodo
1124 hg dodo
1124
1125
1125 Does nothing
1126 Does nothing
1126
1127
1127 (use 'hg help -e dodo' to show help for the dodo extension)
1128 (use 'hg help -e dodo' to show help for the dodo extension)
1128
1129
1129 options:
1130 options:
1130
1131
1131 --mq operate on patch repository
1132 --mq operate on patch repository
1132
1133
1133 global options ([+] can be repeated):
1134 global options ([+] can be repeated):
1134
1135
1135 -R --repository REPO repository root directory or name of overlay bundle
1136 -R --repository REPO repository root directory or name of overlay bundle
1136 file
1137 file
1137 --cwd DIR change working directory
1138 --cwd DIR change working directory
1138 -y --noninteractive do not prompt, automatically pick the first choice for
1139 -y --noninteractive do not prompt, automatically pick the first choice for
1139 all prompts
1140 all prompts
1140 -q --quiet suppress output
1141 -q --quiet suppress output
1141 -v --verbose enable additional output
1142 -v --verbose enable additional output
1142 --color TYPE when to colorize (boolean, always, auto, never, or
1143 --color TYPE when to colorize (boolean, always, auto, never, or
1143 debug)
1144 debug)
1144 --config CONFIG [+] set/override config option (use 'section.name=value')
1145 --config CONFIG [+] set/override config option (use 'section.name=value')
1145 --debug enable debugging output
1146 --debug enable debugging output
1146 --debugger start debugger
1147 --debugger start debugger
1147 --encoding ENCODE set the charset encoding (default: ascii)
1148 --encoding ENCODE set the charset encoding (default: ascii)
1148 --encodingmode MODE set the charset encoding mode (default: strict)
1149 --encodingmode MODE set the charset encoding mode (default: strict)
1149 --traceback always print a traceback on exception
1150 --traceback always print a traceback on exception
1150 --time time how long the command takes
1151 --time time how long the command takes
1151 --profile print command execution profile
1152 --profile print command execution profile
1152 --version output version information and exit
1153 --version output version information and exit
1153 -h --help display help and exit
1154 -h --help display help and exit
1154 --hidden consider hidden changesets
1155 --hidden consider hidden changesets
1155 --pager TYPE when to paginate (boolean, always, auto, or never)
1156 --pager TYPE when to paginate (boolean, always, auto, or never)
1156 (default: auto)
1157 (default: auto)
1157
1158
1158 In case when extension name doesn't match any of its commands,
1159 In case when extension name doesn't match any of its commands,
1159 help message should ask for '-v' to get list of built-in aliases
1160 help message should ask for '-v' to get list of built-in aliases
1160 along with extension help
1161 along with extension help
1161 $ cat > $TESTTMP/d/dudu.py <<EOF
1162 $ cat > $TESTTMP/d/dudu.py <<EOF
1162 > """
1163 > """
1163 > This is an awesome 'dudu' extension. It does something and
1164 > This is an awesome 'dudu' extension. It does something and
1164 > also writes 'Beep beep'
1165 > also writes 'Beep beep'
1165 > """
1166 > """
1166 > from mercurial import commands, registrar
1167 > from mercurial import commands, registrar
1167 > cmdtable = {}
1168 > cmdtable = {}
1168 > command = registrar.command(cmdtable)
1169 > command = registrar.command(cmdtable)
1169 > @command(b'something', [], b'hg something')
1170 > @command(b'something', [], b'hg something')
1170 > def something(ui, *args, **kwargs):
1171 > def something(ui, *args, **kwargs):
1171 > """Does something"""
1172 > """Does something"""
1172 > ui.write(b"I do something. Yaaay\\n")
1173 > ui.write(b"I do something. Yaaay\\n")
1173 > @command(b'beep', [], b'hg beep')
1174 > @command(b'beep', [], b'hg beep')
1174 > def beep(ui, *args, **kwargs):
1175 > def beep(ui, *args, **kwargs):
1175 > """Writes 'Beep beep'"""
1176 > """Writes 'Beep beep'"""
1176 > ui.write(b"Beep beep\\n")
1177 > ui.write(b"Beep beep\\n")
1177 > EOF
1178 > EOF
1178 $ dudupath=$TESTTMP/d/dudu.py
1179 $ dudupath=$TESTTMP/d/dudu.py
1179
1180
1180 $ echo "dudu = $dudupath" >> $HGRCPATH
1181 $ echo "dudu = $dudupath" >> $HGRCPATH
1181
1182
1182 $ hg help -e dudu
1183 $ hg help -e dudu
1183 dudu extension -
1184 dudu extension -
1184
1185
1185 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1186 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1186 beep'
1187 beep'
1187
1188
1188 list of commands:
1189 list of commands:
1189
1190
1190 beep Writes 'Beep beep'
1191 beep Writes 'Beep beep'
1191 something Does something
1192 something Does something
1192
1193
1193 (use 'hg help -v dudu' to show built-in aliases and global options)
1194 (use 'hg help -v dudu' to show built-in aliases and global options)
1194
1195
1195 In case when extension name doesn't match any of its commands,
1196 In case when extension name doesn't match any of its commands,
1196 help options '-v' and '-v -e' should be equivalent
1197 help options '-v' and '-v -e' should be equivalent
1197 $ hg help -v dudu
1198 $ hg help -v dudu
1198 dudu extension -
1199 dudu extension -
1199
1200
1200 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1201 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1201 beep'
1202 beep'
1202
1203
1203 list of commands:
1204 list of commands:
1204
1205
1205 beep Writes 'Beep beep'
1206 beep Writes 'Beep beep'
1206 something Does something
1207 something Does something
1207
1208
1208 global options ([+] can be repeated):
1209 global options ([+] can be repeated):
1209
1210
1210 -R --repository REPO repository root directory or name of overlay bundle
1211 -R --repository REPO repository root directory or name of overlay bundle
1211 file
1212 file
1212 --cwd DIR change working directory
1213 --cwd DIR change working directory
1213 -y --noninteractive do not prompt, automatically pick the first choice for
1214 -y --noninteractive do not prompt, automatically pick the first choice for
1214 all prompts
1215 all prompts
1215 -q --quiet suppress output
1216 -q --quiet suppress output
1216 -v --verbose enable additional output
1217 -v --verbose enable additional output
1217 --color TYPE when to colorize (boolean, always, auto, never, or
1218 --color TYPE when to colorize (boolean, always, auto, never, or
1218 debug)
1219 debug)
1219 --config CONFIG [+] set/override config option (use 'section.name=value')
1220 --config CONFIG [+] set/override config option (use 'section.name=value')
1220 --debug enable debugging output
1221 --debug enable debugging output
1221 --debugger start debugger
1222 --debugger start debugger
1222 --encoding ENCODE set the charset encoding (default: ascii)
1223 --encoding ENCODE set the charset encoding (default: ascii)
1223 --encodingmode MODE set the charset encoding mode (default: strict)
1224 --encodingmode MODE set the charset encoding mode (default: strict)
1224 --traceback always print a traceback on exception
1225 --traceback always print a traceback on exception
1225 --time time how long the command takes
1226 --time time how long the command takes
1226 --profile print command execution profile
1227 --profile print command execution profile
1227 --version output version information and exit
1228 --version output version information and exit
1228 -h --help display help and exit
1229 -h --help display help and exit
1229 --hidden consider hidden changesets
1230 --hidden consider hidden changesets
1230 --pager TYPE when to paginate (boolean, always, auto, or never)
1231 --pager TYPE when to paginate (boolean, always, auto, or never)
1231 (default: auto)
1232 (default: auto)
1232
1233
1233 $ hg help -v -e dudu
1234 $ hg help -v -e dudu
1234 dudu extension -
1235 dudu extension -
1235
1236
1236 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1237 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1237 beep'
1238 beep'
1238
1239
1239 list of commands:
1240 list of commands:
1240
1241
1241 beep Writes 'Beep beep'
1242 beep Writes 'Beep beep'
1242 something Does something
1243 something Does something
1243
1244
1244 global options ([+] can be repeated):
1245 global options ([+] can be repeated):
1245
1246
1246 -R --repository REPO repository root directory or name of overlay bundle
1247 -R --repository REPO repository root directory or name of overlay bundle
1247 file
1248 file
1248 --cwd DIR change working directory
1249 --cwd DIR change working directory
1249 -y --noninteractive do not prompt, automatically pick the first choice for
1250 -y --noninteractive do not prompt, automatically pick the first choice for
1250 all prompts
1251 all prompts
1251 -q --quiet suppress output
1252 -q --quiet suppress output
1252 -v --verbose enable additional output
1253 -v --verbose enable additional output
1253 --color TYPE when to colorize (boolean, always, auto, never, or
1254 --color TYPE when to colorize (boolean, always, auto, never, or
1254 debug)
1255 debug)
1255 --config CONFIG [+] set/override config option (use 'section.name=value')
1256 --config CONFIG [+] set/override config option (use 'section.name=value')
1256 --debug enable debugging output
1257 --debug enable debugging output
1257 --debugger start debugger
1258 --debugger start debugger
1258 --encoding ENCODE set the charset encoding (default: ascii)
1259 --encoding ENCODE set the charset encoding (default: ascii)
1259 --encodingmode MODE set the charset encoding mode (default: strict)
1260 --encodingmode MODE set the charset encoding mode (default: strict)
1260 --traceback always print a traceback on exception
1261 --traceback always print a traceback on exception
1261 --time time how long the command takes
1262 --time time how long the command takes
1262 --profile print command execution profile
1263 --profile print command execution profile
1263 --version output version information and exit
1264 --version output version information and exit
1264 -h --help display help and exit
1265 -h --help display help and exit
1265 --hidden consider hidden changesets
1266 --hidden consider hidden changesets
1266 --pager TYPE when to paginate (boolean, always, auto, or never)
1267 --pager TYPE when to paginate (boolean, always, auto, or never)
1267 (default: auto)
1268 (default: auto)
1268
1269
1269 Disabled extension commands:
1270 Disabled extension commands:
1270
1271
1271 $ ORGHGRCPATH=$HGRCPATH
1272 $ ORGHGRCPATH=$HGRCPATH
1272 $ HGRCPATH=
1273 $ HGRCPATH=
1273 $ export HGRCPATH
1274 $ export HGRCPATH
1274 $ hg help email
1275 $ hg help email
1275 'email' is provided by the following extension:
1276 'email' is provided by the following extension:
1276
1277
1277 patchbomb command to send changesets as (a series of) patch emails
1278 patchbomb command to send changesets as (a series of) patch emails
1278
1279
1279 (use 'hg help extensions' for information on enabling extensions)
1280 (use 'hg help extensions' for information on enabling extensions)
1280
1281
1281
1282
1282 $ hg qdel
1283 $ hg qdel
1283 hg: unknown command 'qdel'
1284 hg: unknown command 'qdel'
1284 'qdelete' is provided by the following extension:
1285 'qdelete' is provided by the following extension:
1285
1286
1286 mq manage a stack of patches
1287 mq manage a stack of patches
1287
1288
1288 (use 'hg help extensions' for information on enabling extensions)
1289 (use 'hg help extensions' for information on enabling extensions)
1289 [255]
1290 [255]
1290
1291
1291
1292
1292 $ hg churn
1293 $ hg churn
1293 hg: unknown command 'churn'
1294 hg: unknown command 'churn'
1294 'churn' is provided by the following extension:
1295 'churn' is provided by the following extension:
1295
1296
1296 churn command to display statistics about repository history
1297 churn command to display statistics about repository history
1297
1298
1298 (use 'hg help extensions' for information on enabling extensions)
1299 (use 'hg help extensions' for information on enabling extensions)
1299 [255]
1300 [255]
1300
1301
1301
1302
1302
1303
1303 Disabled extensions:
1304 Disabled extensions:
1304
1305
1305 $ hg help churn
1306 $ hg help churn
1306 churn extension - command to display statistics about repository history
1307 churn extension - command to display statistics about repository history
1307
1308
1308 (use 'hg help extensions' for information on enabling extensions)
1309 (use 'hg help extensions' for information on enabling extensions)
1309
1310
1310 $ hg help patchbomb
1311 $ hg help patchbomb
1311 patchbomb extension - command to send changesets as (a series of) patch emails
1312 patchbomb extension - command to send changesets as (a series of) patch emails
1312
1313
1313 The series is started off with a "[PATCH 0 of N]" introduction, which
1314 The series is started off with a "[PATCH 0 of N]" introduction, which
1314 describes the series as a whole.
1315 describes the series as a whole.
1315
1316
1316 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1317 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1317 line of the changeset description as the subject text. The message contains
1318 line of the changeset description as the subject text. The message contains
1318 two or three body parts:
1319 two or three body parts:
1319
1320
1320 - The changeset description.
1321 - The changeset description.
1321 - [Optional] The result of running diffstat on the patch.
1322 - [Optional] The result of running diffstat on the patch.
1322 - The patch itself, as generated by 'hg export'.
1323 - The patch itself, as generated by 'hg export'.
1323
1324
1324 Each message refers to the first in the series using the In-Reply-To and
1325 Each message refers to the first in the series using the In-Reply-To and
1325 References headers, so they will show up as a sequence in threaded mail and
1326 References headers, so they will show up as a sequence in threaded mail and
1326 news readers, and in mail archives.
1327 news readers, and in mail archives.
1327
1328
1328 To configure other defaults, add a section like this to your configuration
1329 To configure other defaults, add a section like this to your configuration
1329 file:
1330 file:
1330
1331
1331 [email]
1332 [email]
1332 from = My Name <my@email>
1333 from = My Name <my@email>
1333 to = recipient1, recipient2, ...
1334 to = recipient1, recipient2, ...
1334 cc = cc1, cc2, ...
1335 cc = cc1, cc2, ...
1335 bcc = bcc1, bcc2, ...
1336 bcc = bcc1, bcc2, ...
1336 reply-to = address1, address2, ...
1337 reply-to = address1, address2, ...
1337
1338
1338 Use "[patchbomb]" as configuration section name if you need to override global
1339 Use "[patchbomb]" as configuration section name if you need to override global
1339 "[email]" address settings.
1340 "[email]" address settings.
1340
1341
1341 Then you can use the 'hg email' command to mail a series of changesets as a
1342 Then you can use the 'hg email' command to mail a series of changesets as a
1342 patchbomb.
1343 patchbomb.
1343
1344
1344 You can also either configure the method option in the email section to be a
1345 You can also either configure the method option in the email section to be a
1345 sendmail compatible mailer or fill out the [smtp] section so that the
1346 sendmail compatible mailer or fill out the [smtp] section so that the
1346 patchbomb extension can automatically send patchbombs directly from the
1347 patchbomb extension can automatically send patchbombs directly from the
1347 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1348 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1348
1349
1349 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1350 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1350 supply one via configuration or the command line. You can override this to
1351 supply one via configuration or the command line. You can override this to
1351 never prompt by configuring an empty value:
1352 never prompt by configuring an empty value:
1352
1353
1353 [email]
1354 [email]
1354 cc =
1355 cc =
1355
1356
1356 You can control the default inclusion of an introduction message with the
1357 You can control the default inclusion of an introduction message with the
1357 "patchbomb.intro" configuration option. The configuration is always
1358 "patchbomb.intro" configuration option. The configuration is always
1358 overwritten by command line flags like --intro and --desc:
1359 overwritten by command line flags like --intro and --desc:
1359
1360
1360 [patchbomb]
1361 [patchbomb]
1361 intro=auto # include introduction message if more than 1 patch (default)
1362 intro=auto # include introduction message if more than 1 patch (default)
1362 intro=never # never include an introduction message
1363 intro=never # never include an introduction message
1363 intro=always # always include an introduction message
1364 intro=always # always include an introduction message
1364
1365
1365 You can specify a template for flags to be added in subject prefixes. Flags
1366 You can specify a template for flags to be added in subject prefixes. Flags
1366 specified by --flag option are exported as "{flags}" keyword:
1367 specified by --flag option are exported as "{flags}" keyword:
1367
1368
1368 [patchbomb]
1369 [patchbomb]
1369 flagtemplate = "{separate(' ',
1370 flagtemplate = "{separate(' ',
1370 ifeq(branch, 'default', '', branch|upper),
1371 ifeq(branch, 'default', '', branch|upper),
1371 flags)}"
1372 flags)}"
1372
1373
1373 You can set patchbomb to always ask for confirmation by setting
1374 You can set patchbomb to always ask for confirmation by setting
1374 "patchbomb.confirm" to true.
1375 "patchbomb.confirm" to true.
1375
1376
1376 (use 'hg help extensions' for information on enabling extensions)
1377 (use 'hg help extensions' for information on enabling extensions)
1377
1378
1378
1379
1379 Broken disabled extension and command:
1380 Broken disabled extension and command:
1380
1381
1381 $ mkdir hgext
1382 $ mkdir hgext
1382 $ echo > hgext/__init__.py
1383 $ echo > hgext/__init__.py
1383 $ cat > hgext/broken.py <<NO_CHECK_EOF
1384 $ cat > hgext/broken.py <<NO_CHECK_EOF
1384 > "broken extension'
1385 > "broken extension'
1385 > NO_CHECK_EOF
1386 > NO_CHECK_EOF
1386 $ cat > path.py <<EOF
1387 $ cat > path.py <<EOF
1387 > import os
1388 > import os
1388 > import sys
1389 > import sys
1389 > sys.path.insert(0, os.environ['HGEXTPATH'])
1390 > sys.path.insert(0, os.environ['HGEXTPATH'])
1390 > EOF
1391 > EOF
1391 $ HGEXTPATH=`pwd`
1392 $ HGEXTPATH=`pwd`
1392 $ export HGEXTPATH
1393 $ export HGEXTPATH
1393
1394
1394 $ hg --config extensions.path=./path.py help broken
1395 $ hg --config extensions.path=./path.py help broken
1395 broken extension - (no help text available)
1396 broken extension - (no help text available)
1396
1397
1397 (use 'hg help extensions' for information on enabling extensions)
1398 (use 'hg help extensions' for information on enabling extensions)
1398
1399
1399
1400
1400 $ cat > hgext/forest.py <<EOF
1401 $ cat > hgext/forest.py <<EOF
1401 > cmdtable = None
1402 > cmdtable = None
1402 > @command()
1403 > @command()
1403 > def f():
1404 > def f():
1404 > pass
1405 > pass
1405 > @command(123)
1406 > @command(123)
1406 > def g():
1407 > def g():
1407 > pass
1408 > pass
1408 > EOF
1409 > EOF
1409 $ hg --config extensions.path=./path.py help foo
1410 $ hg --config extensions.path=./path.py help foo
1410 abort: no such help topic: foo
1411 abort: no such help topic: foo
1411 (try 'hg help --keyword foo')
1412 (try 'hg help --keyword foo')
1412 [255]
1413 [255]
1413
1414
1414 $ cat > throw.py <<EOF
1415 $ cat > throw.py <<EOF
1415 > from mercurial import commands, registrar, util
1416 > from mercurial import commands, registrar, util
1416 > cmdtable = {}
1417 > cmdtable = {}
1417 > command = registrar.command(cmdtable)
1418 > command = registrar.command(cmdtable)
1418 > class Bogon(Exception): pass
1419 > class Bogon(Exception): pass
1419 > # NB: version should be bytes; simulating extension not ported to py3
1420 > # NB: version should be bytes; simulating extension not ported to py3
1420 > __version__ = '1.0.0'
1421 > __version__ = '1.0.0'
1421 > @command(b'throw', [], b'hg throw', norepo=True)
1422 > @command(b'throw', [], b'hg throw', norepo=True)
1422 > def throw(ui, **opts):
1423 > def throw(ui, **opts):
1423 > """throws an exception"""
1424 > """throws an exception"""
1424 > raise Bogon()
1425 > raise Bogon()
1425 > EOF
1426 > EOF
1426
1427
1427 Test extension without proper byteification of key attributes doesn't crash when
1428 Test extension without proper byteification of key attributes doesn't crash when
1428 accessed.
1429 accessed.
1429
1430
1430 $ hg version -v --config extensions.throw=throw.py | grep '^ '
1431 $ hg version -v --config extensions.throw=throw.py | grep '^ '
1431 throw external 1.0.0
1432 throw external 1.0.0
1432
1433
1433 No declared supported version, extension complains:
1434 No declared supported version, extension complains:
1434 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1435 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1435 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1436 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1436 ** which supports versions unknown of Mercurial.
1437 ** which supports versions unknown of Mercurial.
1437 ** Please disable "throw" and try your action again.
1438 ** Please disable "throw" and try your action again.
1438 ** If that fixes the bug please report it to the extension author.
1439 ** If that fixes the bug please report it to the extension author.
1439 ** Python * (glob)
1440 ** Python * (glob)
1440 ** Mercurial Distributed SCM * (glob)
1441 ** Mercurial Distributed SCM * (glob)
1441 ** Extensions loaded: throw 1.0.0
1442 ** Extensions loaded: throw 1.0.0
1442
1443
1443 empty declaration of supported version, extension complains (but doesn't choke if
1444 empty declaration of supported version, extension complains (but doesn't choke if
1444 the value is improperly a str instead of bytes):
1445 the value is improperly a str instead of bytes):
1445 $ echo "testedwith = ''" >> throw.py
1446 $ echo "testedwith = ''" >> throw.py
1446 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1447 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1447 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1448 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1448 ** which supports versions unknown of Mercurial.
1449 ** which supports versions unknown of Mercurial.
1449 ** Please disable "throw" and try your action again.
1450 ** Please disable "throw" and try your action again.
1450 ** If that fixes the bug please report it to the extension author.
1451 ** If that fixes the bug please report it to the extension author.
1451 ** Python * (glob)
1452 ** Python * (glob)
1452 ** Mercurial Distributed SCM (*) (glob)
1453 ** Mercurial Distributed SCM (*) (glob)
1453 ** Extensions loaded: throw 1.0.0
1454 ** Extensions loaded: throw 1.0.0
1454
1455
1455 If the extension specifies a buglink, show that (but don't choke if the value is
1456 If the extension specifies a buglink, show that (but don't choke if the value is
1456 improperly a str instead of bytes):
1457 improperly a str instead of bytes):
1457 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1458 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1458 $ rm -f throw.pyc throw.pyo
1459 $ rm -f throw.pyc throw.pyo
1459 $ rm -Rf __pycache__
1460 $ rm -Rf __pycache__
1460 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1461 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1461 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1462 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1462 ** which supports versions unknown of Mercurial.
1463 ** which supports versions unknown of Mercurial.
1463 ** Please disable "throw" and try your action again.
1464 ** Please disable "throw" and try your action again.
1464 ** If that fixes the bug please report it to http://example.com/bts
1465 ** If that fixes the bug please report it to http://example.com/bts
1465 ** Python * (glob)
1466 ** Python * (glob)
1466 ** Mercurial Distributed SCM (*) (glob)
1467 ** Mercurial Distributed SCM (*) (glob)
1467 ** Extensions loaded: throw 1.0.0
1468 ** Extensions loaded: throw 1.0.0
1468
1469
1469 If the extensions declare outdated versions, accuse the older extension first:
1470 If the extensions declare outdated versions, accuse the older extension first:
1470 $ echo "from mercurial import util" >> older.py
1471 $ echo "from mercurial import util" >> older.py
1471 $ echo "util.version = lambda:b'2.2'" >> older.py
1472 $ echo "util.version = lambda:b'2.2'" >> older.py
1472 $ echo "testedwith = b'1.9.3'" >> older.py
1473 $ echo "testedwith = b'1.9.3'" >> older.py
1473 $ echo "testedwith = b'2.1.1'" >> throw.py
1474 $ echo "testedwith = b'2.1.1'" >> throw.py
1474 $ rm -f throw.pyc throw.pyo
1475 $ rm -f throw.pyc throw.pyo
1475 $ rm -Rf __pycache__
1476 $ rm -Rf __pycache__
1476 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1477 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1477 > throw 2>&1 | egrep '^\*\*'
1478 > throw 2>&1 | egrep '^\*\*'
1478 ** Unknown exception encountered with possibly-broken third-party extension "older" (version N/A)
1479 ** Unknown exception encountered with possibly-broken third-party extension "older" (version N/A)
1479 ** which supports versions 1.9 of Mercurial.
1480 ** which supports versions 1.9 of Mercurial.
1480 ** Please disable "older" and try your action again.
1481 ** Please disable "older" and try your action again.
1481 ** If that fixes the bug please report it to the extension author.
1482 ** If that fixes the bug please report it to the extension author.
1482 ** Python * (glob)
1483 ** Python * (glob)
1483 ** Mercurial Distributed SCM (version 2.2)
1484 ** Mercurial Distributed SCM (version 2.2)
1484 ** Extensions loaded: older, throw 1.0.0
1485 ** Extensions loaded: older, throw 1.0.0
1485
1486
1486 One extension only tested with older, one only with newer versions:
1487 One extension only tested with older, one only with newer versions:
1487 $ echo "util.version = lambda:b'2.1'" >> older.py
1488 $ echo "util.version = lambda:b'2.1'" >> older.py
1488 $ rm -f older.pyc older.pyo
1489 $ rm -f older.pyc older.pyo
1489 $ rm -Rf __pycache__
1490 $ rm -Rf __pycache__
1490 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1491 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1491 > throw 2>&1 | egrep '^\*\*'
1492 > throw 2>&1 | egrep '^\*\*'
1492 ** Unknown exception encountered with possibly-broken third-party extension "older" (version N/A)
1493 ** Unknown exception encountered with possibly-broken third-party extension "older" (version N/A)
1493 ** which supports versions 1.9 of Mercurial.
1494 ** which supports versions 1.9 of Mercurial.
1494 ** Please disable "older" and try your action again.
1495 ** Please disable "older" and try your action again.
1495 ** If that fixes the bug please report it to the extension author.
1496 ** If that fixes the bug please report it to the extension author.
1496 ** Python * (glob)
1497 ** Python * (glob)
1497 ** Mercurial Distributed SCM (version 2.1)
1498 ** Mercurial Distributed SCM (version 2.1)
1498 ** Extensions loaded: older, throw 1.0.0
1499 ** Extensions loaded: older, throw 1.0.0
1499
1500
1500 Older extension is tested with current version, the other only with newer:
1501 Older extension is tested with current version, the other only with newer:
1501 $ echo "util.version = lambda:b'1.9.3'" >> older.py
1502 $ echo "util.version = lambda:b'1.9.3'" >> older.py
1502 $ rm -f older.pyc older.pyo
1503 $ rm -f older.pyc older.pyo
1503 $ rm -Rf __pycache__
1504 $ rm -Rf __pycache__
1504 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1505 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1505 > throw 2>&1 | egrep '^\*\*'
1506 > throw 2>&1 | egrep '^\*\*'
1506 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1507 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1507 ** which supports versions 2.1 of Mercurial.
1508 ** which supports versions 2.1 of Mercurial.
1508 ** Please disable "throw" and try your action again.
1509 ** Please disable "throw" and try your action again.
1509 ** If that fixes the bug please report it to http://example.com/bts
1510 ** If that fixes the bug please report it to http://example.com/bts
1510 ** Python * (glob)
1511 ** Python * (glob)
1511 ** Mercurial Distributed SCM (version 1.9.3)
1512 ** Mercurial Distributed SCM (version 1.9.3)
1512 ** Extensions loaded: older, throw 1.0.0
1513 ** Extensions loaded: older, throw 1.0.0
1513
1514
1514 Ability to point to a different point
1515 Ability to point to a different point
1515 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1516 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1516 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1517 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1517 ** unknown exception encountered, please report by visiting
1518 ** unknown exception encountered, please report by visiting
1518 ** Your Local Goat Lenders
1519 ** Your Local Goat Lenders
1519 ** Python * (glob)
1520 ** Python * (glob)
1520 ** Mercurial Distributed SCM (*) (glob)
1521 ** Mercurial Distributed SCM (*) (glob)
1521 ** Extensions loaded: older, throw 1.0.0
1522 ** Extensions loaded: older, throw 1.0.0
1522
1523
1523 Declare the version as supporting this hg version, show regular bts link:
1524 Declare the version as supporting this hg version, show regular bts link:
1524 $ hgver=`hg debuginstall -T '{hgver}'`
1525 $ hgver=`hg debuginstall -T '{hgver}'`
1525 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1526 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1526 $ if [ -z "$hgver" ]; then
1527 $ if [ -z "$hgver" ]; then
1527 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1528 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1528 > fi
1529 > fi
1529 $ rm -f throw.pyc throw.pyo
1530 $ rm -f throw.pyc throw.pyo
1530 $ rm -Rf __pycache__
1531 $ rm -Rf __pycache__
1531 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1532 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1532 ** unknown exception encountered, please report by visiting
1533 ** unknown exception encountered, please report by visiting
1533 ** https://mercurial-scm.org/wiki/BugTracker
1534 ** https://mercurial-scm.org/wiki/BugTracker
1534 ** Python * (glob)
1535 ** Python * (glob)
1535 ** Mercurial Distributed SCM (*) (glob)
1536 ** Mercurial Distributed SCM (*) (glob)
1536 ** Extensions loaded: throw 1.0.0
1537 ** Extensions loaded: throw 1.0.0
1537
1538
1538 Patch version is ignored during compatibility check
1539 Patch version is ignored during compatibility check
1539 $ echo "testedwith = b'3.2'" >> throw.py
1540 $ echo "testedwith = b'3.2'" >> throw.py
1540 $ echo "util.version = lambda:b'3.2.2'" >> throw.py
1541 $ echo "util.version = lambda:b'3.2.2'" >> throw.py
1541 $ rm -f throw.pyc throw.pyo
1542 $ rm -f throw.pyc throw.pyo
1542 $ rm -Rf __pycache__
1543 $ rm -Rf __pycache__
1543 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1544 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1544 ** unknown exception encountered, please report by visiting
1545 ** unknown exception encountered, please report by visiting
1545 ** https://mercurial-scm.org/wiki/BugTracker
1546 ** https://mercurial-scm.org/wiki/BugTracker
1546 ** Python * (glob)
1547 ** Python * (glob)
1547 ** Mercurial Distributed SCM (*) (glob)
1548 ** Mercurial Distributed SCM (*) (glob)
1548 ** Extensions loaded: throw 1.0.0
1549 ** Extensions loaded: throw 1.0.0
1549
1550
1550 Test version number support in 'hg version':
1551 Test version number support in 'hg version':
1551 $ echo '__version__ = (1, 2, 3)' >> throw.py
1552 $ echo '__version__ = (1, 2, 3)' >> throw.py
1552 $ rm -f throw.pyc throw.pyo
1553 $ rm -f throw.pyc throw.pyo
1553 $ rm -Rf __pycache__
1554 $ rm -Rf __pycache__
1554 $ hg version -v
1555 $ hg version -v
1555 Mercurial Distributed SCM (version *) (glob)
1556 Mercurial Distributed SCM (version *) (glob)
1556 (see https://mercurial-scm.org for more information)
1557 (see https://mercurial-scm.org for more information)
1557
1558
1558 Copyright (C) 2005-* Olivia Mackall and others (glob)
1559 Copyright (C) 2005-* Olivia Mackall and others (glob)
1559 This is free software; see the source for copying conditions. There is NO
1560 This is free software; see the source for copying conditions. There is NO
1560 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1561 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1561
1562
1562 Enabled extensions:
1563 Enabled extensions:
1563
1564
1564
1565
1565 $ hg version -v --config extensions.throw=throw.py
1566 $ hg version -v --config extensions.throw=throw.py
1566 Mercurial Distributed SCM (version *) (glob)
1567 Mercurial Distributed SCM (version *) (glob)
1567 (see https://mercurial-scm.org for more information)
1568 (see https://mercurial-scm.org for more information)
1568
1569
1569 Copyright (C) 2005-* Olivia Mackall and others (glob)
1570 Copyright (C) 2005-* Olivia Mackall and others (glob)
1570 This is free software; see the source for copying conditions. There is NO
1571 This is free software; see the source for copying conditions. There is NO
1571 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1572 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1572
1573
1573 Enabled extensions:
1574 Enabled extensions:
1574
1575
1575 throw external 1.2.3
1576 throw external 1.2.3
1576 $ echo 'getversion = lambda: b"1.twentythree"' >> throw.py
1577 $ echo 'getversion = lambda: b"1.twentythree"' >> throw.py
1577 $ rm -f throw.pyc throw.pyo
1578 $ rm -f throw.pyc throw.pyo
1578 $ rm -Rf __pycache__
1579 $ rm -Rf __pycache__
1579 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1580 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1580 Mercurial Distributed SCM (version *) (glob)
1581 Mercurial Distributed SCM (version *) (glob)
1581 (see https://mercurial-scm.org for more information)
1582 (see https://mercurial-scm.org for more information)
1582
1583
1583 Copyright (C) 2005-* Olivia Mackall and others (glob)
1584 Copyright (C) 2005-* Olivia Mackall and others (glob)
1584 This is free software; see the source for copying conditions. There is NO
1585 This is free software; see the source for copying conditions. There is NO
1585 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1586 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1586
1587
1587 Enabled extensions:
1588 Enabled extensions:
1588
1589
1589 strip internal
1590 strip internal
1590 throw external 1.twentythree
1591 throw external 1.twentythree
1591
1592
1592 $ hg version -q --config extensions.throw=throw.py
1593 $ hg version -q --config extensions.throw=throw.py
1593 Mercurial Distributed SCM (version *) (glob)
1594 Mercurial Distributed SCM (version *) (glob)
1594
1595
1595 Test template output:
1596 Test template output:
1596
1597
1597 $ hg version --config extensions.strip= -T'{extensions}'
1598 $ hg version --config extensions.strip= -T'{extensions}'
1598 strip
1599 strip
1599
1600
1600 Test JSON output of version:
1601 Test JSON output of version:
1601
1602
1602 $ hg version -Tjson
1603 $ hg version -Tjson
1603 [
1604 [
1604 {
1605 {
1605 "extensions": [],
1606 "extensions": [],
1606 "ver": "*" (glob)
1607 "ver": "*" (glob)
1607 }
1608 }
1608 ]
1609 ]
1609
1610
1610 $ hg version --config extensions.throw=throw.py -Tjson
1611 $ hg version --config extensions.throw=throw.py -Tjson
1611 [
1612 [
1612 {
1613 {
1613 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1614 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1614 "ver": "3.2.2"
1615 "ver": "3.2.2"
1615 }
1616 }
1616 ]
1617 ]
1617
1618
1618 $ hg version --config extensions.strip= -Tjson
1619 $ hg version --config extensions.strip= -Tjson
1619 [
1620 [
1620 {
1621 {
1621 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1622 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1622 "ver": "*" (glob)
1623 "ver": "*" (glob)
1623 }
1624 }
1624 ]
1625 ]
1625
1626
1626 Test template output of version:
1627 Test template output of version:
1627
1628
1628 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1629 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1629 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1630 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1630 strip (internal)
1631 strip (internal)
1631 throw 1.twentythree (external)
1632 throw 1.twentythree (external)
1632
1633
1633 Refuse to load extensions with minimum version requirements
1634 Refuse to load extensions with minimum version requirements
1634
1635
1635 $ cat > minversion1.py << EOF
1636 $ cat > minversion1.py << EOF
1636 > from mercurial import util
1637 > from mercurial import util
1637 > util.version = lambda: b'3.5.2'
1638 > util.version = lambda: b'3.5.2'
1638 > minimumhgversion = b'3.6'
1639 > minimumhgversion = b'3.6'
1639 > EOF
1640 > EOF
1640 $ hg --config extensions.minversion=minversion1.py version
1641 $ hg --config extensions.minversion=minversion1.py version
1641 (third party extension minversion requires version 3.6 or newer of Mercurial (current: 3.5.2); disabling)
1642 (third party extension minversion requires version 3.6 or newer of Mercurial (current: 3.5.2); disabling)
1642 Mercurial Distributed SCM (version 3.5.2)
1643 Mercurial Distributed SCM (version 3.5.2)
1643 (see https://mercurial-scm.org for more information)
1644 (see https://mercurial-scm.org for more information)
1644
1645
1645 Copyright (C) 2005-* Olivia Mackall and others (glob)
1646 Copyright (C) 2005-* Olivia Mackall and others (glob)
1646 This is free software; see the source for copying conditions. There is NO
1647 This is free software; see the source for copying conditions. There is NO
1647 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1648 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1648
1649
1649 $ cat > minversion2.py << EOF
1650 $ cat > minversion2.py << EOF
1650 > from mercurial import util
1651 > from mercurial import util
1651 > util.version = lambda: b'3.6'
1652 > util.version = lambda: b'3.6'
1652 > minimumhgversion = b'3.7'
1653 > minimumhgversion = b'3.7'
1653 > EOF
1654 > EOF
1654 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1655 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1655 (third party extension minversion requires version 3.7 or newer of Mercurial (current: 3.6); disabling)
1656 (third party extension minversion requires version 3.7 or newer of Mercurial (current: 3.6); disabling)
1656
1657
1657 Can load version that is only off by point release
1658 Can load version that is only off by point release
1658
1659
1659 $ cat > minversion2.py << EOF
1660 $ cat > minversion2.py << EOF
1660 > from mercurial import util
1661 > from mercurial import util
1661 > util.version = lambda: b'3.6.1'
1662 > util.version = lambda: b'3.6.1'
1662 > minimumhgversion = b'3.6'
1663 > minimumhgversion = b'3.6'
1663 > EOF
1664 > EOF
1664 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1665 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1665 [1]
1666 [1]
1666
1667
1667 Can load minimum version identical to current
1668 Can load minimum version identical to current
1668
1669
1669 $ cat > minversion3.py << EOF
1670 $ cat > minversion3.py << EOF
1670 > from mercurial import util
1671 > from mercurial import util
1671 > util.version = lambda: b'3.5'
1672 > util.version = lambda: b'3.5'
1672 > minimumhgversion = b'3.5'
1673 > minimumhgversion = b'3.5'
1673 > EOF
1674 > EOF
1674 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1675 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1675 [1]
1676 [1]
1676
1677
1677 Restore HGRCPATH
1678 Restore HGRCPATH
1678
1679
1679 $ HGRCPATH=$ORGHGRCPATH
1680 $ HGRCPATH=$ORGHGRCPATH
1680 $ export HGRCPATH
1681 $ export HGRCPATH
1681
1682
1682 Commands handling multiple repositories at a time should invoke only
1683 Commands handling multiple repositories at a time should invoke only
1683 "reposetup()" of extensions enabling in the target repository.
1684 "reposetup()" of extensions enabling in the target repository.
1684
1685
1685 $ mkdir reposetup-test
1686 $ mkdir reposetup-test
1686 $ cd reposetup-test
1687 $ cd reposetup-test
1687
1688
1688 $ cat > $TESTTMP/reposetuptest.py <<EOF
1689 $ cat > $TESTTMP/reposetuptest.py <<EOF
1689 > from mercurial import extensions
1690 > from mercurial import extensions
1690 > def reposetup(ui, repo):
1691 > def reposetup(ui, repo):
1691 > ui.write(b'reposetup() for %s\n' % (repo.root))
1692 > ui.write(b'reposetup() for %s\n' % (repo.root))
1692 > ui.flush()
1693 > ui.flush()
1693 > EOF
1694 > EOF
1694 $ hg init src
1695 $ hg init src
1695 $ echo a > src/a
1696 $ echo a > src/a
1696 $ hg -R src commit -Am '#0 at src/a'
1697 $ hg -R src commit -Am '#0 at src/a'
1697 adding a
1698 adding a
1698 $ echo '[extensions]' >> src/.hg/hgrc
1699 $ echo '[extensions]' >> src/.hg/hgrc
1699 $ echo '# enable extension locally' >> src/.hg/hgrc
1700 $ echo '# enable extension locally' >> src/.hg/hgrc
1700 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1701 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1701 $ hg -R src status
1702 $ hg -R src status
1702 reposetup() for $TESTTMP/reposetup-test/src
1703 reposetup() for $TESTTMP/reposetup-test/src
1703 reposetup() for $TESTTMP/reposetup-test/src (chg !)
1704 reposetup() for $TESTTMP/reposetup-test/src (chg !)
1704
1705
1705 #if no-extraextensions
1706 #if no-extraextensions
1706 $ hg --cwd src debugextensions
1707 $ hg --cwd src debugextensions
1707 reposetup() for $TESTTMP/reposetup-test/src
1708 reposetup() for $TESTTMP/reposetup-test/src
1708 dodo (untested!)
1709 dodo (untested!)
1709 dudu (untested!)
1710 dudu (untested!)
1710 mq
1711 mq
1711 reposetuptest (untested!)
1712 reposetuptest (untested!)
1712 strip
1713 strip
1713 #endif
1714 #endif
1714
1715
1715 $ hg clone -U src clone-dst1
1716 $ hg clone -U src clone-dst1
1716 reposetup() for $TESTTMP/reposetup-test/src
1717 reposetup() for $TESTTMP/reposetup-test/src
1717 $ hg init push-dst1
1718 $ hg init push-dst1
1718 $ hg -q -R src push push-dst1
1719 $ hg -q -R src push push-dst1
1719 reposetup() for $TESTTMP/reposetup-test/src
1720 reposetup() for $TESTTMP/reposetup-test/src
1720 $ hg init pull-src1
1721 $ hg init pull-src1
1721 $ hg -q -R pull-src1 pull src
1722 $ hg -q -R pull-src1 pull src
1722 reposetup() for $TESTTMP/reposetup-test/src
1723 reposetup() for $TESTTMP/reposetup-test/src
1723
1724
1724 $ cat <<EOF >> $HGRCPATH
1725 $ cat <<EOF >> $HGRCPATH
1725 > [extensions]
1726 > [extensions]
1726 > # disable extension globally and explicitly
1727 > # disable extension globally and explicitly
1727 > reposetuptest = !
1728 > reposetuptest = !
1728 > EOF
1729 > EOF
1729 $ hg clone -U src clone-dst2
1730 $ hg clone -U src clone-dst2
1730 reposetup() for $TESTTMP/reposetup-test/src
1731 reposetup() for $TESTTMP/reposetup-test/src
1731 $ hg init push-dst2
1732 $ hg init push-dst2
1732 $ hg -q -R src push push-dst2
1733 $ hg -q -R src push push-dst2
1733 reposetup() for $TESTTMP/reposetup-test/src
1734 reposetup() for $TESTTMP/reposetup-test/src
1734 $ hg init pull-src2
1735 $ hg init pull-src2
1735 $ hg -q -R pull-src2 pull src
1736 $ hg -q -R pull-src2 pull src
1736 reposetup() for $TESTTMP/reposetup-test/src
1737 reposetup() for $TESTTMP/reposetup-test/src
1737
1738
1738 $ cat <<EOF >> $HGRCPATH
1739 $ cat <<EOF >> $HGRCPATH
1739 > [extensions]
1740 > [extensions]
1740 > # enable extension globally
1741 > # enable extension globally
1741 > reposetuptest = $TESTTMP/reposetuptest.py
1742 > reposetuptest = $TESTTMP/reposetuptest.py
1742 > EOF
1743 > EOF
1743 $ hg clone -U src clone-dst3
1744 $ hg clone -U src clone-dst3
1744 reposetup() for $TESTTMP/reposetup-test/src
1745 reposetup() for $TESTTMP/reposetup-test/src
1745 reposetup() for $TESTTMP/reposetup-test/clone-dst3
1746 reposetup() for $TESTTMP/reposetup-test/clone-dst3
1746 $ hg init push-dst3
1747 $ hg init push-dst3
1747 reposetup() for $TESTTMP/reposetup-test/push-dst3
1748 reposetup() for $TESTTMP/reposetup-test/push-dst3
1748 $ hg -q -R src push push-dst3
1749 $ hg -q -R src push push-dst3
1749 reposetup() for $TESTTMP/reposetup-test/src
1750 reposetup() for $TESTTMP/reposetup-test/src
1750 reposetup() for $TESTTMP/reposetup-test/push-dst3
1751 reposetup() for $TESTTMP/reposetup-test/push-dst3
1751 $ hg init pull-src3
1752 $ hg init pull-src3
1752 reposetup() for $TESTTMP/reposetup-test/pull-src3
1753 reposetup() for $TESTTMP/reposetup-test/pull-src3
1753 $ hg -q -R pull-src3 pull src
1754 $ hg -q -R pull-src3 pull src
1754 reposetup() for $TESTTMP/reposetup-test/pull-src3
1755 reposetup() for $TESTTMP/reposetup-test/pull-src3
1755 reposetup() for $TESTTMP/reposetup-test/src
1756 reposetup() for $TESTTMP/reposetup-test/src
1756
1757
1757 $ echo '[extensions]' >> src/.hg/hgrc
1758 $ echo '[extensions]' >> src/.hg/hgrc
1758 $ echo '# disable extension locally' >> src/.hg/hgrc
1759 $ echo '# disable extension locally' >> src/.hg/hgrc
1759 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1760 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1760 $ hg clone -U src clone-dst4
1761 $ hg clone -U src clone-dst4
1761 reposetup() for $TESTTMP/reposetup-test/clone-dst4
1762 reposetup() for $TESTTMP/reposetup-test/clone-dst4
1762 $ hg init push-dst4
1763 $ hg init push-dst4
1763 reposetup() for $TESTTMP/reposetup-test/push-dst4
1764 reposetup() for $TESTTMP/reposetup-test/push-dst4
1764 $ hg -q -R src push push-dst4
1765 $ hg -q -R src push push-dst4
1765 reposetup() for $TESTTMP/reposetup-test/push-dst4
1766 reposetup() for $TESTTMP/reposetup-test/push-dst4
1766 $ hg init pull-src4
1767 $ hg init pull-src4
1767 reposetup() for $TESTTMP/reposetup-test/pull-src4
1768 reposetup() for $TESTTMP/reposetup-test/pull-src4
1768 $ hg -q -R pull-src4 pull src
1769 $ hg -q -R pull-src4 pull src
1769 reposetup() for $TESTTMP/reposetup-test/pull-src4
1770 reposetup() for $TESTTMP/reposetup-test/pull-src4
1770
1771
1771 disabling in command line overlays with all configuration
1772 disabling in command line overlays with all configuration
1772 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1773 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1773 $ hg --config extensions.reposetuptest=! init push-dst5
1774 $ hg --config extensions.reposetuptest=! init push-dst5
1774 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1775 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1775 $ hg --config extensions.reposetuptest=! init pull-src5
1776 $ hg --config extensions.reposetuptest=! init pull-src5
1776 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1777 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1777
1778
1778 $ cat <<EOF >> $HGRCPATH
1779 $ cat <<EOF >> $HGRCPATH
1779 > [extensions]
1780 > [extensions]
1780 > # disable extension globally and explicitly
1781 > # disable extension globally and explicitly
1781 > reposetuptest = !
1782 > reposetuptest = !
1782 > EOF
1783 > EOF
1783 $ hg init parent
1784 $ hg init parent
1784 $ hg init parent/sub1
1785 $ hg init parent/sub1
1785 $ echo 1 > parent/sub1/1
1786 $ echo 1 > parent/sub1/1
1786 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1787 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1787 adding 1
1788 adding 1
1788 $ hg init parent/sub2
1789 $ hg init parent/sub2
1789 $ hg init parent/sub2/sub21
1790 $ hg init parent/sub2/sub21
1790 $ echo 21 > parent/sub2/sub21/21
1791 $ echo 21 > parent/sub2/sub21/21
1791 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1792 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1792 adding 21
1793 adding 21
1793 $ cat > parent/sub2/.hgsub <<EOF
1794 $ cat > parent/sub2/.hgsub <<EOF
1794 > sub21 = sub21
1795 > sub21 = sub21
1795 > EOF
1796 > EOF
1796 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1797 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1797 adding .hgsub
1798 adding .hgsub
1798 $ hg init parent/sub3
1799 $ hg init parent/sub3
1799 $ echo 3 > parent/sub3/3
1800 $ echo 3 > parent/sub3/3
1800 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1801 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1801 adding 3
1802 adding 3
1802 $ cat > parent/.hgsub <<EOF
1803 $ cat > parent/.hgsub <<EOF
1803 > sub1 = sub1
1804 > sub1 = sub1
1804 > sub2 = sub2
1805 > sub2 = sub2
1805 > sub3 = sub3
1806 > sub3 = sub3
1806 > EOF
1807 > EOF
1807 $ hg -R parent commit -Am '#0 at parent'
1808 $ hg -R parent commit -Am '#0 at parent'
1808 adding .hgsub
1809 adding .hgsub
1809 $ echo '[extensions]' >> parent/.hg/hgrc
1810 $ echo '[extensions]' >> parent/.hg/hgrc
1810 $ echo '# enable extension locally' >> parent/.hg/hgrc
1811 $ echo '# enable extension locally' >> parent/.hg/hgrc
1811 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1812 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1812 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1813 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1813 $ hg -R parent status -S -A
1814 $ hg -R parent status -S -A
1814 reposetup() for $TESTTMP/reposetup-test/parent
1815 reposetup() for $TESTTMP/reposetup-test/parent
1815 reposetup() for $TESTTMP/reposetup-test/parent/sub2
1816 reposetup() for $TESTTMP/reposetup-test/parent/sub2
1816 C .hgsub
1817 C .hgsub
1817 C .hgsubstate
1818 C .hgsubstate
1818 C sub1/1
1819 C sub1/1
1819 C sub2/.hgsub
1820 C sub2/.hgsub
1820 C sub2/.hgsubstate
1821 C sub2/.hgsubstate
1821 C sub2/sub21/21
1822 C sub2/sub21/21
1822 C sub3/3
1823 C sub3/3
1823
1824
1824 $ cd ..
1825 $ cd ..
1825
1826
1826 Prohibit registration of commands that don't use @command (issue5137)
1827 Prohibit registration of commands that don't use @command (issue5137)
1827
1828
1828 $ hg init deprecated
1829 $ hg init deprecated
1829 $ cd deprecated
1830 $ cd deprecated
1830
1831
1831 $ cat <<EOF > deprecatedcmd.py
1832 $ cat <<EOF > deprecatedcmd.py
1832 > def deprecatedcmd(repo, ui):
1833 > def deprecatedcmd(repo, ui):
1833 > pass
1834 > pass
1834 > cmdtable = {
1835 > cmdtable = {
1835 > b'deprecatedcmd': (deprecatedcmd, [], b''),
1836 > b'deprecatedcmd': (deprecatedcmd, [], b''),
1836 > }
1837 > }
1837 > EOF
1838 > EOF
1838 $ cat <<EOF > .hg/hgrc
1839 $ cat <<EOF > .hg/hgrc
1839 > [extensions]
1840 > [extensions]
1840 > deprecatedcmd = `pwd`/deprecatedcmd.py
1841 > deprecatedcmd = `pwd`/deprecatedcmd.py
1841 > mq = !
1842 > mq = !
1842 > hgext.mq = !
1843 > hgext.mq = !
1843 > hgext/mq = !
1844 > hgext/mq = !
1844 > EOF
1845 > EOF
1845
1846
1846 $ hg deprecatedcmd > /dev/null
1847 $ hg deprecatedcmd > /dev/null
1847 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1848 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1848 *** (use @command decorator to register 'deprecatedcmd')
1849 *** (use @command decorator to register 'deprecatedcmd')
1849 hg: unknown command 'deprecatedcmd'
1850 hg: unknown command 'deprecatedcmd'
1850 (use 'hg help' for a list of commands)
1851 (use 'hg help' for a list of commands)
1851 [10]
1852 [10]
1852
1853
1853 the extension shouldn't be loaded at all so the mq works:
1854 the extension shouldn't be loaded at all so the mq works:
1854
1855
1855 $ hg qseries --config extensions.mq= > /dev/null
1856 $ hg qseries --config extensions.mq= > /dev/null
1856 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1857 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1857 *** (use @command decorator to register 'deprecatedcmd')
1858 *** (use @command decorator to register 'deprecatedcmd')
1858
1859
1859 $ cd ..
1860 $ cd ..
1860
1861
1861 Test synopsis and docstring extending
1862 Test synopsis and docstring extending
1862
1863
1863 $ hg init exthelp
1864 $ hg init exthelp
1864 $ cat > exthelp.py <<EOF
1865 $ cat > exthelp.py <<EOF
1865 > from mercurial import commands, extensions
1866 > from mercurial import commands, extensions
1866 > def exbookmarks(orig, *args, **opts):
1867 > def exbookmarks(orig, *args, **opts):
1867 > return orig(*args, **opts)
1868 > return orig(*args, **opts)
1868 > def uisetup(ui):
1869 > def uisetup(ui):
1869 > synopsis = b' GREPME [--foo] [-x]'
1870 > synopsis = b' GREPME [--foo] [-x]'
1870 > docstring = '''
1871 > docstring = '''
1871 > GREPME make sure that this is in the help!
1872 > GREPME make sure that this is in the help!
1872 > '''
1873 > '''
1873 > extensions.wrapcommand(commands.table, b'bookmarks', exbookmarks,
1874 > extensions.wrapcommand(commands.table, b'bookmarks', exbookmarks,
1874 > synopsis, docstring)
1875 > synopsis, docstring)
1875 > EOF
1876 > EOF
1876 $ abspath=`pwd`/exthelp.py
1877 $ abspath=`pwd`/exthelp.py
1877 $ echo '[extensions]' >> $HGRCPATH
1878 $ echo '[extensions]' >> $HGRCPATH
1878 $ echo "exthelp = $abspath" >> $HGRCPATH
1879 $ echo "exthelp = $abspath" >> $HGRCPATH
1879 $ cd exthelp
1880 $ cd exthelp
1880 $ hg help bookmarks | grep GREPME
1881 $ hg help bookmarks | grep GREPME
1881 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1882 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1882 GREPME make sure that this is in the help!
1883 GREPME make sure that this is in the help!
1883 $ cd ..
1884 $ cd ..
1884
1885
1885 Prohibit the use of unicode strings as the default value of options
1886 Prohibit the use of unicode strings as the default value of options
1886
1887
1887 $ hg init $TESTTMP/opt-unicode-default
1888 $ hg init $TESTTMP/opt-unicode-default
1888
1889
1889 $ cat > $TESTTMP/test_unicode_default_value.py << EOF
1890 $ cat > $TESTTMP/test_unicode_default_value.py << EOF
1890 > from __future__ import print_function
1891 > from __future__ import print_function
1891 > from mercurial import registrar
1892 > from mercurial import registrar
1892 > cmdtable = {}
1893 > cmdtable = {}
1893 > command = registrar.command(cmdtable)
1894 > command = registrar.command(cmdtable)
1894 > @command(b'dummy', [(b'', b'opt', u'value', u'help')], 'ext [OPTIONS]')
1895 > @command(b'dummy', [(b'', b'opt', u'value', u'help')], 'ext [OPTIONS]')
1895 > def ext(*args, **opts):
1896 > def ext(*args, **opts):
1896 > print(opts[b'opt'], flush=True)
1897 > print(opts[b'opt'], flush=True)
1897 > EOF
1898 > EOF
1898 $ "$PYTHON" $TESTTMP/unflush.py $TESTTMP/test_unicode_default_value.py
1899 $ "$PYTHON" $TESTTMP/unflush.py $TESTTMP/test_unicode_default_value.py
1899 $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF
1900 $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF
1900 > [extensions]
1901 > [extensions]
1901 > test_unicode_default_value = $TESTTMP/test_unicode_default_value.py
1902 > test_unicode_default_value = $TESTTMP/test_unicode_default_value.py
1902 > EOF
1903 > EOF
1903 $ hg -R $TESTTMP/opt-unicode-default dummy
1904 $ hg -R $TESTTMP/opt-unicode-default dummy
1904 *** failed to import extension test_unicode_default_value from $TESTTMP/test_unicode_default_value.py: unicode *'value' found in cmdtable.dummy (glob)
1905 *** failed to import extension test_unicode_default_value from $TESTTMP/test_unicode_default_value.py: unicode *'value' found in cmdtable.dummy (glob)
1905 *** (use b'' to make it byte string)
1906 *** (use b'' to make it byte string)
1906 hg: unknown command 'dummy'
1907 hg: unknown command 'dummy'
1907 (did you mean summary?)
1908 (did you mean summary?)
1908 [10]
1909 [10]
General Comments 0
You need to be logged in to leave comments. Login now