##// END OF EJS Templates
extensions: deprecate extsetup without a `ui` argument (API)...
Matt Harbison -
r41098:28a4fb79 default
parent child Browse files
Show More
@@ -1,96 +1,96 b''
1 # highlight - syntax highlighting in hgweb, based on Pygments
1 # highlight - syntax highlighting in hgweb, based on Pygments
2 #
2 #
3 # Copyright 2008, 2009 Patrick Mezard <pmezard@gmail.com> and others
3 # Copyright 2008, 2009 Patrick Mezard <pmezard@gmail.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 #
7 #
8 # The original module was split in an interface and an implementation
8 # The original module was split in an interface and an implementation
9 # file to defer pygments loading and speedup extension setup.
9 # file to defer pygments loading and speedup extension setup.
10
10
11 """syntax highlighting for hgweb (requires Pygments)
11 """syntax highlighting for hgweb (requires Pygments)
12
12
13 It depends on the Pygments syntax highlighting library:
13 It depends on the Pygments syntax highlighting library:
14 http://pygments.org/
14 http://pygments.org/
15
15
16 There are the following configuration options::
16 There are the following configuration options::
17
17
18 [web]
18 [web]
19 pygments_style = <style> (default: colorful)
19 pygments_style = <style> (default: colorful)
20 highlightfiles = <fileset> (default: size('<5M'))
20 highlightfiles = <fileset> (default: size('<5M'))
21 highlightonlymatchfilename = <bool> (default False)
21 highlightonlymatchfilename = <bool> (default False)
22
22
23 ``highlightonlymatchfilename`` will only highlight files if their type could
23 ``highlightonlymatchfilename`` will only highlight files if their type could
24 be identified by their filename. When this is not enabled (the default),
24 be identified by their filename. When this is not enabled (the default),
25 Pygments will try very hard to identify the file type from content and any
25 Pygments will try very hard to identify the file type from content and any
26 match (even matches with a low confidence score) will be used.
26 match (even matches with a low confidence score) will be used.
27 """
27 """
28
28
29 from __future__ import absolute_import
29 from __future__ import absolute_import
30
30
31 from . import highlight
31 from . import highlight
32 from mercurial.hgweb import (
32 from mercurial.hgweb import (
33 webcommands,
33 webcommands,
34 webutil,
34 webutil,
35 )
35 )
36
36
37 from mercurial import (
37 from mercurial import (
38 extensions,
38 extensions,
39 )
39 )
40
40
41 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
41 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
42 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
42 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
43 # be specifying the version(s) of Mercurial they are tested with, or
43 # be specifying the version(s) of Mercurial they are tested with, or
44 # leave the attribute unspecified.
44 # leave the attribute unspecified.
45 testedwith = 'ships-with-hg-core'
45 testedwith = 'ships-with-hg-core'
46
46
47 def pygmentize(web, field, fctx, tmpl):
47 def pygmentize(web, field, fctx, tmpl):
48 style = web.config('web', 'pygments_style', 'colorful')
48 style = web.config('web', 'pygments_style', 'colorful')
49 expr = web.config('web', 'highlightfiles', "size('<5M')")
49 expr = web.config('web', 'highlightfiles', "size('<5M')")
50 filenameonly = web.configbool('web', 'highlightonlymatchfilename', False)
50 filenameonly = web.configbool('web', 'highlightonlymatchfilename', False)
51
51
52 ctx = fctx.changectx()
52 ctx = fctx.changectx()
53 m = ctx.matchfileset(expr)
53 m = ctx.matchfileset(expr)
54 if m(fctx.path()):
54 if m(fctx.path()):
55 highlight.pygmentize(field, fctx, style, tmpl,
55 highlight.pygmentize(field, fctx, style, tmpl,
56 guessfilenameonly=filenameonly)
56 guessfilenameonly=filenameonly)
57
57
58 def filerevision_highlight(orig, web, fctx):
58 def filerevision_highlight(orig, web, fctx):
59 mt = web.res.headers['Content-Type']
59 mt = web.res.headers['Content-Type']
60 # only pygmentize for mimetype containing 'html' so we both match
60 # only pygmentize for mimetype containing 'html' so we both match
61 # 'text/html' and possibly 'application/xhtml+xml' in the future
61 # 'text/html' and possibly 'application/xhtml+xml' in the future
62 # so that we don't have to touch the extension when the mimetype
62 # so that we don't have to touch the extension when the mimetype
63 # for a template changes; also hgweb optimizes the case that a
63 # for a template changes; also hgweb optimizes the case that a
64 # raw file is sent using rawfile() and doesn't call us, so we
64 # raw file is sent using rawfile() and doesn't call us, so we
65 # can't clash with the file's content-type here in case we
65 # can't clash with the file's content-type here in case we
66 # pygmentize a html file
66 # pygmentize a html file
67 if 'html' in mt:
67 if 'html' in mt:
68 pygmentize(web, 'fileline', fctx, web.tmpl)
68 pygmentize(web, 'fileline', fctx, web.tmpl)
69
69
70 return orig(web, fctx)
70 return orig(web, fctx)
71
71
72 def annotate_highlight(orig, web):
72 def annotate_highlight(orig, web):
73 mt = web.res.headers['Content-Type']
73 mt = web.res.headers['Content-Type']
74 if 'html' in mt:
74 if 'html' in mt:
75 fctx = webutil.filectx(web.repo, web.req)
75 fctx = webutil.filectx(web.repo, web.req)
76 pygmentize(web, 'annotateline', fctx, web.tmpl)
76 pygmentize(web, 'annotateline', fctx, web.tmpl)
77
77
78 return orig(web)
78 return orig(web)
79
79
80 def generate_css(web):
80 def generate_css(web):
81 pg_style = web.config('web', 'pygments_style', 'colorful')
81 pg_style = web.config('web', 'pygments_style', 'colorful')
82 fmter = highlight.HtmlFormatter(style=pg_style)
82 fmter = highlight.HtmlFormatter(style=pg_style)
83 web.res.headers['Content-Type'] = 'text/css'
83 web.res.headers['Content-Type'] = 'text/css'
84 web.res.setbodybytes(''.join([
84 web.res.setbodybytes(''.join([
85 '/* pygments_style = %s */\n\n' % pg_style,
85 '/* pygments_style = %s */\n\n' % pg_style,
86 fmter.get_style_defs(''),
86 fmter.get_style_defs(''),
87 ]))
87 ]))
88 return web.res.sendresponse()
88 return web.res.sendresponse()
89
89
90 def extsetup():
90 def extsetup(ui):
91 # monkeypatch in the new version
91 # monkeypatch in the new version
92 extensions.wrapfunction(webcommands, '_filerevision',
92 extensions.wrapfunction(webcommands, '_filerevision',
93 filerevision_highlight)
93 filerevision_highlight)
94 extensions.wrapfunction(webcommands, 'annotate', annotate_highlight)
94 extensions.wrapfunction(webcommands, 'annotate', annotate_highlight)
95 webcommands.highlightcss = generate_css
95 webcommands.highlightcss = generate_css
96 webcommands.__all__.append('highlightcss')
96 webcommands.__all__.append('highlightcss')
@@ -1,844 +1,846 b''
1 # extensions.py - extension handling for mercurial
1 # extensions.py - extension handling for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import ast
10 import ast
11 import collections
11 import collections
12 import functools
12 import functools
13 import imp
13 import imp
14 import inspect
14 import inspect
15 import os
15 import os
16
16
17 from .i18n import (
17 from .i18n import (
18 _,
18 _,
19 gettext,
19 gettext,
20 )
20 )
21
21
22 from . import (
22 from . import (
23 cmdutil,
23 cmdutil,
24 configitems,
24 configitems,
25 error,
25 error,
26 pycompat,
26 pycompat,
27 util,
27 util,
28 )
28 )
29
29
30 from .utils import (
30 from .utils import (
31 stringutil,
31 stringutil,
32 )
32 )
33
33
34 _extensions = {}
34 _extensions = {}
35 _disabledextensions = {}
35 _disabledextensions = {}
36 _aftercallbacks = {}
36 _aftercallbacks = {}
37 _order = []
37 _order = []
38 _builtin = {
38 _builtin = {
39 'hbisect',
39 'hbisect',
40 'bookmarks',
40 'bookmarks',
41 'color',
41 'color',
42 'parentrevspec',
42 'parentrevspec',
43 'progress',
43 'progress',
44 'interhg',
44 'interhg',
45 'inotify',
45 'inotify',
46 'hgcia'
46 'hgcia'
47 }
47 }
48
48
49 def extensions(ui=None):
49 def extensions(ui=None):
50 if ui:
50 if ui:
51 def enabled(name):
51 def enabled(name):
52 for format in ['%s', 'hgext.%s']:
52 for format in ['%s', 'hgext.%s']:
53 conf = ui.config('extensions', format % name)
53 conf = ui.config('extensions', format % name)
54 if conf is not None and not conf.startswith('!'):
54 if conf is not None and not conf.startswith('!'):
55 return True
55 return True
56 else:
56 else:
57 enabled = lambda name: True
57 enabled = lambda name: True
58 for name in _order:
58 for name in _order:
59 module = _extensions[name]
59 module = _extensions[name]
60 if module and enabled(name):
60 if module and enabled(name):
61 yield name, module
61 yield name, module
62
62
63 def find(name):
63 def find(name):
64 '''return module with given extension name'''
64 '''return module with given extension name'''
65 mod = None
65 mod = None
66 try:
66 try:
67 mod = _extensions[name]
67 mod = _extensions[name]
68 except KeyError:
68 except KeyError:
69 for k, v in _extensions.iteritems():
69 for k, v in _extensions.iteritems():
70 if k.endswith('.' + name) or k.endswith('/' + name):
70 if k.endswith('.' + name) or k.endswith('/' + name):
71 mod = v
71 mod = v
72 break
72 break
73 if not mod:
73 if not mod:
74 raise KeyError(name)
74 raise KeyError(name)
75 return mod
75 return mod
76
76
77 def loadpath(path, module_name):
77 def loadpath(path, module_name):
78 module_name = module_name.replace('.', '_')
78 module_name = module_name.replace('.', '_')
79 path = util.normpath(util.expandpath(path))
79 path = util.normpath(util.expandpath(path))
80 module_name = pycompat.fsdecode(module_name)
80 module_name = pycompat.fsdecode(module_name)
81 path = pycompat.fsdecode(path)
81 path = pycompat.fsdecode(path)
82 if os.path.isdir(path):
82 if os.path.isdir(path):
83 # module/__init__.py style
83 # module/__init__.py style
84 d, f = os.path.split(path)
84 d, f = os.path.split(path)
85 fd, fpath, desc = imp.find_module(f, [d])
85 fd, fpath, desc = imp.find_module(f, [d])
86 return imp.load_module(module_name, fd, fpath, desc)
86 return imp.load_module(module_name, fd, fpath, desc)
87 else:
87 else:
88 try:
88 try:
89 return imp.load_source(module_name, path)
89 return imp.load_source(module_name, path)
90 except IOError as exc:
90 except IOError as exc:
91 if not exc.filename:
91 if not exc.filename:
92 exc.filename = path # python does not fill this
92 exc.filename = path # python does not fill this
93 raise
93 raise
94
94
95 def _importh(name):
95 def _importh(name):
96 """import and return the <name> module"""
96 """import and return the <name> module"""
97 mod = __import__(pycompat.sysstr(name))
97 mod = __import__(pycompat.sysstr(name))
98 components = name.split('.')
98 components = name.split('.')
99 for comp in components[1:]:
99 for comp in components[1:]:
100 mod = getattr(mod, comp)
100 mod = getattr(mod, comp)
101 return mod
101 return mod
102
102
103 def _importext(name, path=None, reportfunc=None):
103 def _importext(name, path=None, reportfunc=None):
104 if path:
104 if path:
105 # the module will be loaded in sys.modules
105 # the module will be loaded in sys.modules
106 # choose an unique name so that it doesn't
106 # choose an unique name so that it doesn't
107 # conflicts with other modules
107 # conflicts with other modules
108 mod = loadpath(path, 'hgext.%s' % name)
108 mod = loadpath(path, 'hgext.%s' % name)
109 else:
109 else:
110 try:
110 try:
111 mod = _importh("hgext.%s" % name)
111 mod = _importh("hgext.%s" % name)
112 except ImportError as err:
112 except ImportError as err:
113 if reportfunc:
113 if reportfunc:
114 reportfunc(err, "hgext.%s" % name, "hgext3rd.%s" % name)
114 reportfunc(err, "hgext.%s" % name, "hgext3rd.%s" % name)
115 try:
115 try:
116 mod = _importh("hgext3rd.%s" % name)
116 mod = _importh("hgext3rd.%s" % name)
117 except ImportError as err:
117 except ImportError as err:
118 if reportfunc:
118 if reportfunc:
119 reportfunc(err, "hgext3rd.%s" % name, name)
119 reportfunc(err, "hgext3rd.%s" % name, name)
120 mod = _importh(name)
120 mod = _importh(name)
121 return mod
121 return mod
122
122
123 def _reportimporterror(ui, err, failed, next):
123 def _reportimporterror(ui, err, failed, next):
124 # note: this ui.log happens before --debug is processed,
124 # note: this ui.log happens before --debug is processed,
125 # Use --config ui.debug=1 to see them.
125 # Use --config ui.debug=1 to see them.
126 ui.log(b'extension', b' - could not import %s (%s): trying %s\n',
126 ui.log(b'extension', b' - could not import %s (%s): trying %s\n',
127 failed, stringutil.forcebytestr(err), next)
127 failed, stringutil.forcebytestr(err), next)
128 if ui.debugflag and ui.configbool('devel', 'debug.extensions'):
128 if ui.debugflag and ui.configbool('devel', 'debug.extensions'):
129 ui.traceback()
129 ui.traceback()
130
130
131 def _rejectunicode(name, xs):
131 def _rejectunicode(name, xs):
132 if isinstance(xs, (list, set, tuple)):
132 if isinstance(xs, (list, set, tuple)):
133 for x in xs:
133 for x in xs:
134 _rejectunicode(name, x)
134 _rejectunicode(name, x)
135 elif isinstance(xs, dict):
135 elif isinstance(xs, dict):
136 for k, v in xs.items():
136 for k, v in xs.items():
137 _rejectunicode(name, k)
137 _rejectunicode(name, k)
138 _rejectunicode(b'%s.%s' % (name, stringutil.forcebytestr(k)), v)
138 _rejectunicode(b'%s.%s' % (name, stringutil.forcebytestr(k)), v)
139 elif isinstance(xs, type(u'')):
139 elif isinstance(xs, type(u'')):
140 raise error.ProgrammingError(b"unicode %r found in %s" % (xs, name),
140 raise error.ProgrammingError(b"unicode %r found in %s" % (xs, name),
141 hint="use b'' to make it byte string")
141 hint="use b'' to make it byte string")
142
142
143 # attributes set by registrar.command
143 # attributes set by registrar.command
144 _cmdfuncattrs = ('norepo', 'optionalrepo', 'inferrepo')
144 _cmdfuncattrs = ('norepo', 'optionalrepo', 'inferrepo')
145
145
146 def _validatecmdtable(ui, cmdtable):
146 def _validatecmdtable(ui, cmdtable):
147 """Check if extension commands have required attributes"""
147 """Check if extension commands have required attributes"""
148 for c, e in cmdtable.iteritems():
148 for c, e in cmdtable.iteritems():
149 f = e[0]
149 f = e[0]
150 missing = [a for a in _cmdfuncattrs if not util.safehasattr(f, a)]
150 missing = [a for a in _cmdfuncattrs if not util.safehasattr(f, a)]
151 if not missing:
151 if not missing:
152 continue
152 continue
153 raise error.ProgrammingError(
153 raise error.ProgrammingError(
154 'missing attributes: %s' % ', '.join(missing),
154 'missing attributes: %s' % ', '.join(missing),
155 hint="use @command decorator to register '%s'" % c)
155 hint="use @command decorator to register '%s'" % c)
156
156
157 def _validatetables(ui, mod):
157 def _validatetables(ui, mod):
158 """Sanity check for loadable tables provided by extension module"""
158 """Sanity check for loadable tables provided by extension module"""
159 for t in ['cmdtable', 'colortable', 'configtable']:
159 for t in ['cmdtable', 'colortable', 'configtable']:
160 _rejectunicode(t, getattr(mod, t, {}))
160 _rejectunicode(t, getattr(mod, t, {}))
161 for t in ['filesetpredicate', 'internalmerge', 'revsetpredicate',
161 for t in ['filesetpredicate', 'internalmerge', 'revsetpredicate',
162 'templatefilter', 'templatefunc', 'templatekeyword']:
162 'templatefilter', 'templatefunc', 'templatekeyword']:
163 o = getattr(mod, t, None)
163 o = getattr(mod, t, None)
164 if o:
164 if o:
165 _rejectunicode(t, o._table)
165 _rejectunicode(t, o._table)
166 _validatecmdtable(ui, getattr(mod, 'cmdtable', {}))
166 _validatecmdtable(ui, getattr(mod, 'cmdtable', {}))
167
167
168 def load(ui, name, path, loadingtime=None):
168 def load(ui, name, path, loadingtime=None):
169 if name.startswith('hgext.') or name.startswith('hgext/'):
169 if name.startswith('hgext.') or name.startswith('hgext/'):
170 shortname = name[6:]
170 shortname = name[6:]
171 else:
171 else:
172 shortname = name
172 shortname = name
173 if shortname in _builtin:
173 if shortname in _builtin:
174 return None
174 return None
175 if shortname in _extensions:
175 if shortname in _extensions:
176 return _extensions[shortname]
176 return _extensions[shortname]
177 ui.log(b'extension', b' - loading extension: %s\n', shortname)
177 ui.log(b'extension', b' - loading extension: %s\n', shortname)
178 _extensions[shortname] = None
178 _extensions[shortname] = None
179 with util.timedcm('load extension %s', shortname) as stats:
179 with util.timedcm('load extension %s', shortname) as stats:
180 mod = _importext(name, path, bind(_reportimporterror, ui))
180 mod = _importext(name, path, bind(_reportimporterror, ui))
181 ui.log(b'extension', b' > %s extension loaded in %s\n', shortname, stats)
181 ui.log(b'extension', b' > %s extension loaded in %s\n', shortname, stats)
182 if loadingtime is not None:
182 if loadingtime is not None:
183 loadingtime[shortname] += stats.elapsed
183 loadingtime[shortname] += stats.elapsed
184
184
185 # Before we do anything with the extension, check against minimum stated
185 # Before we do anything with the extension, check against minimum stated
186 # compatibility. This gives extension authors a mechanism to have their
186 # compatibility. This gives extension authors a mechanism to have their
187 # extensions short circuit when loaded with a known incompatible version
187 # extensions short circuit when loaded with a known incompatible version
188 # of Mercurial.
188 # of Mercurial.
189 minver = getattr(mod, 'minimumhgversion', None)
189 minver = getattr(mod, 'minimumhgversion', None)
190 if minver and util.versiontuple(minver, 2) > util.versiontuple(n=2):
190 if minver and util.versiontuple(minver, 2) > util.versiontuple(n=2):
191 msg = _('(third party extension %s requires version %s or newer '
191 msg = _('(third party extension %s requires version %s or newer '
192 'of Mercurial (current: %s); disabling)\n')
192 'of Mercurial (current: %s); disabling)\n')
193 ui.warn(msg % (shortname, minver, util.version()))
193 ui.warn(msg % (shortname, minver, util.version()))
194 return
194 return
195 ui.log(b'extension', b' - validating extension tables: %s\n', shortname)
195 ui.log(b'extension', b' - validating extension tables: %s\n', shortname)
196 _validatetables(ui, mod)
196 _validatetables(ui, mod)
197
197
198 _extensions[shortname] = mod
198 _extensions[shortname] = mod
199 _order.append(shortname)
199 _order.append(shortname)
200 ui.log(b'extension', b' - invoking registered callbacks: %s\n',
200 ui.log(b'extension', b' - invoking registered callbacks: %s\n',
201 shortname)
201 shortname)
202 with util.timedcm('callbacks extension %s', shortname) as stats:
202 with util.timedcm('callbacks extension %s', shortname) as stats:
203 for fn in _aftercallbacks.get(shortname, []):
203 for fn in _aftercallbacks.get(shortname, []):
204 fn(loaded=True)
204 fn(loaded=True)
205 ui.log(b'extension', b' > callbacks completed in %s\n', stats)
205 ui.log(b'extension', b' > callbacks completed in %s\n', stats)
206 return mod
206 return mod
207
207
208 def _runuisetup(name, ui):
208 def _runuisetup(name, ui):
209 uisetup = getattr(_extensions[name], 'uisetup', None)
209 uisetup = getattr(_extensions[name], 'uisetup', None)
210 if uisetup:
210 if uisetup:
211 try:
211 try:
212 uisetup(ui)
212 uisetup(ui)
213 except Exception as inst:
213 except Exception as inst:
214 ui.traceback(force=True)
214 ui.traceback(force=True)
215 msg = stringutil.forcebytestr(inst)
215 msg = stringutil.forcebytestr(inst)
216 ui.warn(_("*** failed to set up extension %s: %s\n") % (name, msg))
216 ui.warn(_("*** failed to set up extension %s: %s\n") % (name, msg))
217 return False
217 return False
218 return True
218 return True
219
219
220 def _runextsetup(name, ui):
220 def _runextsetup(name, ui):
221 extsetup = getattr(_extensions[name], 'extsetup', None)
221 extsetup = getattr(_extensions[name], 'extsetup', None)
222 if extsetup:
222 if extsetup:
223 try:
223 try:
224 try:
224 try:
225 extsetup(ui)
225 extsetup(ui)
226 except TypeError:
226 except TypeError:
227 if pycompat.getargspec(extsetup).args:
227 if pycompat.getargspec(extsetup).args:
228 raise
228 raise
229 ui.deprecwarn("extsetup for '%s' must take a ui argument"
230 % name, "4.9")
229 extsetup() # old extsetup with no ui argument
231 extsetup() # old extsetup with no ui argument
230 except Exception as inst:
232 except Exception as inst:
231 ui.traceback(force=True)
233 ui.traceback(force=True)
232 msg = stringutil.forcebytestr(inst)
234 msg = stringutil.forcebytestr(inst)
233 ui.warn(_("*** failed to set up extension %s: %s\n") % (name, msg))
235 ui.warn(_("*** failed to set up extension %s: %s\n") % (name, msg))
234 return False
236 return False
235 return True
237 return True
236
238
237 def loadall(ui, whitelist=None):
239 def loadall(ui, whitelist=None):
238 loadingtime = collections.defaultdict(int)
240 loadingtime = collections.defaultdict(int)
239 result = ui.configitems("extensions")
241 result = ui.configitems("extensions")
240 if whitelist is not None:
242 if whitelist is not None:
241 result = [(k, v) for (k, v) in result if k in whitelist]
243 result = [(k, v) for (k, v) in result if k in whitelist]
242 newindex = len(_order)
244 newindex = len(_order)
243 ui.log(b'extension', b'loading %sextensions\n',
245 ui.log(b'extension', b'loading %sextensions\n',
244 'additional ' if newindex else '')
246 'additional ' if newindex else '')
245 ui.log(b'extension', b'- processing %d entries\n', len(result))
247 ui.log(b'extension', b'- processing %d entries\n', len(result))
246 with util.timedcm('load all extensions') as stats:
248 with util.timedcm('load all extensions') as stats:
247 for (name, path) in result:
249 for (name, path) in result:
248 if path:
250 if path:
249 if path[0:1] == '!':
251 if path[0:1] == '!':
250 if name not in _disabledextensions:
252 if name not in _disabledextensions:
251 ui.log(b'extension',
253 ui.log(b'extension',
252 b' - skipping disabled extension: %s\n', name)
254 b' - skipping disabled extension: %s\n', name)
253 _disabledextensions[name] = path[1:]
255 _disabledextensions[name] = path[1:]
254 continue
256 continue
255 try:
257 try:
256 load(ui, name, path, loadingtime)
258 load(ui, name, path, loadingtime)
257 except Exception as inst:
259 except Exception as inst:
258 msg = stringutil.forcebytestr(inst)
260 msg = stringutil.forcebytestr(inst)
259 if path:
261 if path:
260 ui.warn(_("*** failed to import extension %s from %s: %s\n")
262 ui.warn(_("*** failed to import extension %s from %s: %s\n")
261 % (name, path, msg))
263 % (name, path, msg))
262 else:
264 else:
263 ui.warn(_("*** failed to import extension %s: %s\n")
265 ui.warn(_("*** failed to import extension %s: %s\n")
264 % (name, msg))
266 % (name, msg))
265 if isinstance(inst, error.Hint) and inst.hint:
267 if isinstance(inst, error.Hint) and inst.hint:
266 ui.warn(_("*** (%s)\n") % inst.hint)
268 ui.warn(_("*** (%s)\n") % inst.hint)
267 ui.traceback()
269 ui.traceback()
268
270
269 ui.log(b'extension', b'> loaded %d extensions, total time %s\n',
271 ui.log(b'extension', b'> loaded %d extensions, total time %s\n',
270 len(_order) - newindex, stats)
272 len(_order) - newindex, stats)
271 # list of (objname, loadermod, loadername) tuple:
273 # list of (objname, loadermod, loadername) tuple:
272 # - objname is the name of an object in extension module,
274 # - objname is the name of an object in extension module,
273 # from which extra information is loaded
275 # from which extra information is loaded
274 # - loadermod is the module where loader is placed
276 # - loadermod is the module where loader is placed
275 # - loadername is the name of the function,
277 # - loadername is the name of the function,
276 # which takes (ui, extensionname, extraobj) arguments
278 # which takes (ui, extensionname, extraobj) arguments
277 #
279 #
278 # This one is for the list of item that must be run before running any setup
280 # This one is for the list of item that must be run before running any setup
279 earlyextraloaders = [
281 earlyextraloaders = [
280 ('configtable', configitems, 'loadconfigtable'),
282 ('configtable', configitems, 'loadconfigtable'),
281 ]
283 ]
282
284
283 ui.log(b'extension', b'- loading configtable attributes\n')
285 ui.log(b'extension', b'- loading configtable attributes\n')
284 _loadextra(ui, newindex, earlyextraloaders)
286 _loadextra(ui, newindex, earlyextraloaders)
285
287
286 broken = set()
288 broken = set()
287 ui.log(b'extension', b'- executing uisetup hooks\n')
289 ui.log(b'extension', b'- executing uisetup hooks\n')
288 with util.timedcm('all uisetup') as alluisetupstats:
290 with util.timedcm('all uisetup') as alluisetupstats:
289 for name in _order[newindex:]:
291 for name in _order[newindex:]:
290 ui.log(b'extension', b' - running uisetup for %s\n', name)
292 ui.log(b'extension', b' - running uisetup for %s\n', name)
291 with util.timedcm('uisetup %s', name) as stats:
293 with util.timedcm('uisetup %s', name) as stats:
292 if not _runuisetup(name, ui):
294 if not _runuisetup(name, ui):
293 ui.log(b'extension',
295 ui.log(b'extension',
294 b' - the %s extension uisetup failed\n', name)
296 b' - the %s extension uisetup failed\n', name)
295 broken.add(name)
297 broken.add(name)
296 ui.log(b'extension', b' > uisetup for %s took %s\n', name, stats)
298 ui.log(b'extension', b' > uisetup for %s took %s\n', name, stats)
297 loadingtime[name] += stats.elapsed
299 loadingtime[name] += stats.elapsed
298 ui.log(b'extension', b'> all uisetup took %s\n', alluisetupstats)
300 ui.log(b'extension', b'> all uisetup took %s\n', alluisetupstats)
299
301
300 ui.log(b'extension', b'- executing extsetup hooks\n')
302 ui.log(b'extension', b'- executing extsetup hooks\n')
301 with util.timedcm('all extsetup') as allextetupstats:
303 with util.timedcm('all extsetup') as allextetupstats:
302 for name in _order[newindex:]:
304 for name in _order[newindex:]:
303 if name in broken:
305 if name in broken:
304 continue
306 continue
305 ui.log(b'extension', b' - running extsetup for %s\n', name)
307 ui.log(b'extension', b' - running extsetup for %s\n', name)
306 with util.timedcm('extsetup %s', name) as stats:
308 with util.timedcm('extsetup %s', name) as stats:
307 if not _runextsetup(name, ui):
309 if not _runextsetup(name, ui):
308 ui.log(b'extension',
310 ui.log(b'extension',
309 b' - the %s extension extsetup failed\n', name)
311 b' - the %s extension extsetup failed\n', name)
310 broken.add(name)
312 broken.add(name)
311 ui.log(b'extension', b' > extsetup for %s took %s\n', name, stats)
313 ui.log(b'extension', b' > extsetup for %s took %s\n', name, stats)
312 loadingtime[name] += stats.elapsed
314 loadingtime[name] += stats.elapsed
313 ui.log(b'extension', b'> all extsetup took %s\n', allextetupstats)
315 ui.log(b'extension', b'> all extsetup took %s\n', allextetupstats)
314
316
315 for name in broken:
317 for name in broken:
316 ui.log(b'extension', b' - disabling broken %s extension\n', name)
318 ui.log(b'extension', b' - disabling broken %s extension\n', name)
317 _extensions[name] = None
319 _extensions[name] = None
318
320
319 # Call aftercallbacks that were never met.
321 # Call aftercallbacks that were never met.
320 ui.log(b'extension', b'- executing remaining aftercallbacks\n')
322 ui.log(b'extension', b'- executing remaining aftercallbacks\n')
321 with util.timedcm('aftercallbacks') as stats:
323 with util.timedcm('aftercallbacks') as stats:
322 for shortname in _aftercallbacks:
324 for shortname in _aftercallbacks:
323 if shortname in _extensions:
325 if shortname in _extensions:
324 continue
326 continue
325
327
326 for fn in _aftercallbacks[shortname]:
328 for fn in _aftercallbacks[shortname]:
327 ui.log(b'extension',
329 ui.log(b'extension',
328 b' - extension %s not loaded, notify callbacks\n',
330 b' - extension %s not loaded, notify callbacks\n',
329 shortname)
331 shortname)
330 fn(loaded=False)
332 fn(loaded=False)
331 ui.log(b'extension', b'> remaining aftercallbacks completed in %s\n', stats)
333 ui.log(b'extension', b'> remaining aftercallbacks completed in %s\n', stats)
332
334
333 # loadall() is called multiple times and lingering _aftercallbacks
335 # loadall() is called multiple times and lingering _aftercallbacks
334 # entries could result in double execution. See issue4646.
336 # entries could result in double execution. See issue4646.
335 _aftercallbacks.clear()
337 _aftercallbacks.clear()
336
338
337 # delay importing avoids cyclic dependency (especially commands)
339 # delay importing avoids cyclic dependency (especially commands)
338 from . import (
340 from . import (
339 color,
341 color,
340 commands,
342 commands,
341 filemerge,
343 filemerge,
342 fileset,
344 fileset,
343 revset,
345 revset,
344 templatefilters,
346 templatefilters,
345 templatefuncs,
347 templatefuncs,
346 templatekw,
348 templatekw,
347 )
349 )
348
350
349 # list of (objname, loadermod, loadername) tuple:
351 # list of (objname, loadermod, loadername) tuple:
350 # - objname is the name of an object in extension module,
352 # - objname is the name of an object in extension module,
351 # from which extra information is loaded
353 # from which extra information is loaded
352 # - loadermod is the module where loader is placed
354 # - loadermod is the module where loader is placed
353 # - loadername is the name of the function,
355 # - loadername is the name of the function,
354 # which takes (ui, extensionname, extraobj) arguments
356 # which takes (ui, extensionname, extraobj) arguments
355 ui.log(b'extension', b'- loading extension registration objects\n')
357 ui.log(b'extension', b'- loading extension registration objects\n')
356 extraloaders = [
358 extraloaders = [
357 ('cmdtable', commands, 'loadcmdtable'),
359 ('cmdtable', commands, 'loadcmdtable'),
358 ('colortable', color, 'loadcolortable'),
360 ('colortable', color, 'loadcolortable'),
359 ('filesetpredicate', fileset, 'loadpredicate'),
361 ('filesetpredicate', fileset, 'loadpredicate'),
360 ('internalmerge', filemerge, 'loadinternalmerge'),
362 ('internalmerge', filemerge, 'loadinternalmerge'),
361 ('revsetpredicate', revset, 'loadpredicate'),
363 ('revsetpredicate', revset, 'loadpredicate'),
362 ('templatefilter', templatefilters, 'loadfilter'),
364 ('templatefilter', templatefilters, 'loadfilter'),
363 ('templatefunc', templatefuncs, 'loadfunction'),
365 ('templatefunc', templatefuncs, 'loadfunction'),
364 ('templatekeyword', templatekw, 'loadkeyword'),
366 ('templatekeyword', templatekw, 'loadkeyword'),
365 ]
367 ]
366 with util.timedcm('load registration objects') as stats:
368 with util.timedcm('load registration objects') as stats:
367 _loadextra(ui, newindex, extraloaders)
369 _loadextra(ui, newindex, extraloaders)
368 ui.log(b'extension', b'> extension registration object loading took %s\n',
370 ui.log(b'extension', b'> extension registration object loading took %s\n',
369 stats)
371 stats)
370
372
371 # Report per extension loading time (except reposetup)
373 # Report per extension loading time (except reposetup)
372 for name in sorted(loadingtime):
374 for name in sorted(loadingtime):
373 ui.log(b'extension', b'> extension %s take a total of %s to load\n',
375 ui.log(b'extension', b'> extension %s take a total of %s to load\n',
374 name, util.timecount(loadingtime[name]))
376 name, util.timecount(loadingtime[name]))
375
377
376 ui.log(b'extension', b'extension loading complete\n')
378 ui.log(b'extension', b'extension loading complete\n')
377
379
378 def _loadextra(ui, newindex, extraloaders):
380 def _loadextra(ui, newindex, extraloaders):
379 for name in _order[newindex:]:
381 for name in _order[newindex:]:
380 module = _extensions[name]
382 module = _extensions[name]
381 if not module:
383 if not module:
382 continue # loading this module failed
384 continue # loading this module failed
383
385
384 for objname, loadermod, loadername in extraloaders:
386 for objname, loadermod, loadername in extraloaders:
385 extraobj = getattr(module, objname, None)
387 extraobj = getattr(module, objname, None)
386 if extraobj is not None:
388 if extraobj is not None:
387 getattr(loadermod, loadername)(ui, name, extraobj)
389 getattr(loadermod, loadername)(ui, name, extraobj)
388
390
389 def afterloaded(extension, callback):
391 def afterloaded(extension, callback):
390 '''Run the specified function after a named extension is loaded.
392 '''Run the specified function after a named extension is loaded.
391
393
392 If the named extension is already loaded, the callback will be called
394 If the named extension is already loaded, the callback will be called
393 immediately.
395 immediately.
394
396
395 If the named extension never loads, the callback will be called after
397 If the named extension never loads, the callback will be called after
396 all extensions have been loaded.
398 all extensions have been loaded.
397
399
398 The callback receives the named argument ``loaded``, which is a boolean
400 The callback receives the named argument ``loaded``, which is a boolean
399 indicating whether the dependent extension actually loaded.
401 indicating whether the dependent extension actually loaded.
400 '''
402 '''
401
403
402 if extension in _extensions:
404 if extension in _extensions:
403 # Report loaded as False if the extension is disabled
405 # Report loaded as False if the extension is disabled
404 loaded = (_extensions[extension] is not None)
406 loaded = (_extensions[extension] is not None)
405 callback(loaded=loaded)
407 callback(loaded=loaded)
406 else:
408 else:
407 _aftercallbacks.setdefault(extension, []).append(callback)
409 _aftercallbacks.setdefault(extension, []).append(callback)
408
410
409 def populateui(ui):
411 def populateui(ui):
410 """Run extension hooks on the given ui to populate additional members,
412 """Run extension hooks on the given ui to populate additional members,
411 extend the class dynamically, etc.
413 extend the class dynamically, etc.
412
414
413 This will be called after the configuration is loaded, and/or extensions
415 This will be called after the configuration is loaded, and/or extensions
414 are loaded. In general, it's once per ui instance, but in command-server
416 are loaded. In general, it's once per ui instance, but in command-server
415 and hgweb, this may be called more than once with the same ui.
417 and hgweb, this may be called more than once with the same ui.
416 """
418 """
417 for name, mod in extensions(ui):
419 for name, mod in extensions(ui):
418 hook = getattr(mod, 'uipopulate', None)
420 hook = getattr(mod, 'uipopulate', None)
419 if not hook:
421 if not hook:
420 continue
422 continue
421 try:
423 try:
422 hook(ui)
424 hook(ui)
423 except Exception as inst:
425 except Exception as inst:
424 ui.traceback(force=True)
426 ui.traceback(force=True)
425 ui.warn(_('*** failed to populate ui by extension %s: %s\n')
427 ui.warn(_('*** failed to populate ui by extension %s: %s\n')
426 % (name, stringutil.forcebytestr(inst)))
428 % (name, stringutil.forcebytestr(inst)))
427
429
428 def bind(func, *args):
430 def bind(func, *args):
429 '''Partial function application
431 '''Partial function application
430
432
431 Returns a new function that is the partial application of args and kwargs
433 Returns a new function that is the partial application of args and kwargs
432 to func. For example,
434 to func. For example,
433
435
434 f(1, 2, bar=3) === bind(f, 1)(2, bar=3)'''
436 f(1, 2, bar=3) === bind(f, 1)(2, bar=3)'''
435 assert callable(func)
437 assert callable(func)
436 def closure(*a, **kw):
438 def closure(*a, **kw):
437 return func(*(args + a), **kw)
439 return func(*(args + a), **kw)
438 return closure
440 return closure
439
441
440 def _updatewrapper(wrap, origfn, unboundwrapper):
442 def _updatewrapper(wrap, origfn, unboundwrapper):
441 '''Copy and add some useful attributes to wrapper'''
443 '''Copy and add some useful attributes to wrapper'''
442 try:
444 try:
443 wrap.__name__ = origfn.__name__
445 wrap.__name__ = origfn.__name__
444 except AttributeError:
446 except AttributeError:
445 pass
447 pass
446 wrap.__module__ = getattr(origfn, '__module__')
448 wrap.__module__ = getattr(origfn, '__module__')
447 wrap.__doc__ = getattr(origfn, '__doc__')
449 wrap.__doc__ = getattr(origfn, '__doc__')
448 wrap.__dict__.update(getattr(origfn, '__dict__', {}))
450 wrap.__dict__.update(getattr(origfn, '__dict__', {}))
449 wrap._origfunc = origfn
451 wrap._origfunc = origfn
450 wrap._unboundwrapper = unboundwrapper
452 wrap._unboundwrapper = unboundwrapper
451
453
452 def wrapcommand(table, command, wrapper, synopsis=None, docstring=None):
454 def wrapcommand(table, command, wrapper, synopsis=None, docstring=None):
453 '''Wrap the command named `command' in table
455 '''Wrap the command named `command' in table
454
456
455 Replace command in the command table with wrapper. The wrapped command will
457 Replace command in the command table with wrapper. The wrapped command will
456 be inserted into the command table specified by the table argument.
458 be inserted into the command table specified by the table argument.
457
459
458 The wrapper will be called like
460 The wrapper will be called like
459
461
460 wrapper(orig, *args, **kwargs)
462 wrapper(orig, *args, **kwargs)
461
463
462 where orig is the original (wrapped) function, and *args, **kwargs
464 where orig is the original (wrapped) function, and *args, **kwargs
463 are the arguments passed to it.
465 are the arguments passed to it.
464
466
465 Optionally append to the command synopsis and docstring, used for help.
467 Optionally append to the command synopsis and docstring, used for help.
466 For example, if your extension wraps the ``bookmarks`` command to add the
468 For example, if your extension wraps the ``bookmarks`` command to add the
467 flags ``--remote`` and ``--all`` you might call this function like so:
469 flags ``--remote`` and ``--all`` you might call this function like so:
468
470
469 synopsis = ' [-a] [--remote]'
471 synopsis = ' [-a] [--remote]'
470 docstring = """
472 docstring = """
471
473
472 The ``remotenames`` extension adds the ``--remote`` and ``--all`` (``-a``)
474 The ``remotenames`` extension adds the ``--remote`` and ``--all`` (``-a``)
473 flags to the bookmarks command. Either flag will show the remote bookmarks
475 flags to the bookmarks command. Either flag will show the remote bookmarks
474 known to the repository; ``--remote`` will also suppress the output of the
476 known to the repository; ``--remote`` will also suppress the output of the
475 local bookmarks.
477 local bookmarks.
476 """
478 """
477
479
478 extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
480 extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
479 synopsis, docstring)
481 synopsis, docstring)
480 '''
482 '''
481 assert callable(wrapper)
483 assert callable(wrapper)
482 aliases, entry = cmdutil.findcmd(command, table)
484 aliases, entry = cmdutil.findcmd(command, table)
483 for alias, e in table.iteritems():
485 for alias, e in table.iteritems():
484 if e is entry:
486 if e is entry:
485 key = alias
487 key = alias
486 break
488 break
487
489
488 origfn = entry[0]
490 origfn = entry[0]
489 wrap = functools.partial(util.checksignature(wrapper),
491 wrap = functools.partial(util.checksignature(wrapper),
490 util.checksignature(origfn))
492 util.checksignature(origfn))
491 _updatewrapper(wrap, origfn, wrapper)
493 _updatewrapper(wrap, origfn, wrapper)
492 if docstring is not None:
494 if docstring is not None:
493 wrap.__doc__ += docstring
495 wrap.__doc__ += docstring
494
496
495 newentry = list(entry)
497 newentry = list(entry)
496 newentry[0] = wrap
498 newentry[0] = wrap
497 if synopsis is not None:
499 if synopsis is not None:
498 newentry[2] += synopsis
500 newentry[2] += synopsis
499 table[key] = tuple(newentry)
501 table[key] = tuple(newentry)
500 return entry
502 return entry
501
503
502 def wrapfilecache(cls, propname, wrapper):
504 def wrapfilecache(cls, propname, wrapper):
503 """Wraps a filecache property.
505 """Wraps a filecache property.
504
506
505 These can't be wrapped using the normal wrapfunction.
507 These can't be wrapped using the normal wrapfunction.
506 """
508 """
507 propname = pycompat.sysstr(propname)
509 propname = pycompat.sysstr(propname)
508 assert callable(wrapper)
510 assert callable(wrapper)
509 for currcls in cls.__mro__:
511 for currcls in cls.__mro__:
510 if propname in currcls.__dict__:
512 if propname in currcls.__dict__:
511 origfn = currcls.__dict__[propname].func
513 origfn = currcls.__dict__[propname].func
512 assert callable(origfn)
514 assert callable(origfn)
513 def wrap(*args, **kwargs):
515 def wrap(*args, **kwargs):
514 return wrapper(origfn, *args, **kwargs)
516 return wrapper(origfn, *args, **kwargs)
515 currcls.__dict__[propname].func = wrap
517 currcls.__dict__[propname].func = wrap
516 break
518 break
517
519
518 if currcls is object:
520 if currcls is object:
519 raise AttributeError(r"type '%s' has no property '%s'" % (
521 raise AttributeError(r"type '%s' has no property '%s'" % (
520 cls, propname))
522 cls, propname))
521
523
522 class wrappedfunction(object):
524 class wrappedfunction(object):
523 '''context manager for temporarily wrapping a function'''
525 '''context manager for temporarily wrapping a function'''
524
526
525 def __init__(self, container, funcname, wrapper):
527 def __init__(self, container, funcname, wrapper):
526 assert callable(wrapper)
528 assert callable(wrapper)
527 self._container = container
529 self._container = container
528 self._funcname = funcname
530 self._funcname = funcname
529 self._wrapper = wrapper
531 self._wrapper = wrapper
530
532
531 def __enter__(self):
533 def __enter__(self):
532 wrapfunction(self._container, self._funcname, self._wrapper)
534 wrapfunction(self._container, self._funcname, self._wrapper)
533
535
534 def __exit__(self, exctype, excvalue, traceback):
536 def __exit__(self, exctype, excvalue, traceback):
535 unwrapfunction(self._container, self._funcname, self._wrapper)
537 unwrapfunction(self._container, self._funcname, self._wrapper)
536
538
537 def wrapfunction(container, funcname, wrapper):
539 def wrapfunction(container, funcname, wrapper):
538 '''Wrap the function named funcname in container
540 '''Wrap the function named funcname in container
539
541
540 Replace the funcname member in the given container with the specified
542 Replace the funcname member in the given container with the specified
541 wrapper. The container is typically a module, class, or instance.
543 wrapper. The container is typically a module, class, or instance.
542
544
543 The wrapper will be called like
545 The wrapper will be called like
544
546
545 wrapper(orig, *args, **kwargs)
547 wrapper(orig, *args, **kwargs)
546
548
547 where orig is the original (wrapped) function, and *args, **kwargs
549 where orig is the original (wrapped) function, and *args, **kwargs
548 are the arguments passed to it.
550 are the arguments passed to it.
549
551
550 Wrapping methods of the repository object is not recommended since
552 Wrapping methods of the repository object is not recommended since
551 it conflicts with extensions that extend the repository by
553 it conflicts with extensions that extend the repository by
552 subclassing. All extensions that need to extend methods of
554 subclassing. All extensions that need to extend methods of
553 localrepository should use this subclassing trick: namely,
555 localrepository should use this subclassing trick: namely,
554 reposetup() should look like
556 reposetup() should look like
555
557
556 def reposetup(ui, repo):
558 def reposetup(ui, repo):
557 class myrepo(repo.__class__):
559 class myrepo(repo.__class__):
558 def whatever(self, *args, **kwargs):
560 def whatever(self, *args, **kwargs):
559 [...extension stuff...]
561 [...extension stuff...]
560 super(myrepo, self).whatever(*args, **kwargs)
562 super(myrepo, self).whatever(*args, **kwargs)
561 [...extension stuff...]
563 [...extension stuff...]
562
564
563 repo.__class__ = myrepo
565 repo.__class__ = myrepo
564
566
565 In general, combining wrapfunction() with subclassing does not
567 In general, combining wrapfunction() with subclassing does not
566 work. Since you cannot control what other extensions are loaded by
568 work. Since you cannot control what other extensions are loaded by
567 your end users, you should play nicely with others by using the
569 your end users, you should play nicely with others by using the
568 subclass trick.
570 subclass trick.
569 '''
571 '''
570 assert callable(wrapper)
572 assert callable(wrapper)
571
573
572 origfn = getattr(container, funcname)
574 origfn = getattr(container, funcname)
573 assert callable(origfn)
575 assert callable(origfn)
574 if inspect.ismodule(container):
576 if inspect.ismodule(container):
575 # origfn is not an instance or class method. "partial" can be used.
577 # origfn is not an instance or class method. "partial" can be used.
576 # "partial" won't insert a frame in traceback.
578 # "partial" won't insert a frame in traceback.
577 wrap = functools.partial(wrapper, origfn)
579 wrap = functools.partial(wrapper, origfn)
578 else:
580 else:
579 # "partial" cannot be safely used. Emulate its effect by using "bind".
581 # "partial" cannot be safely used. Emulate its effect by using "bind".
580 # The downside is one more frame in traceback.
582 # The downside is one more frame in traceback.
581 wrap = bind(wrapper, origfn)
583 wrap = bind(wrapper, origfn)
582 _updatewrapper(wrap, origfn, wrapper)
584 _updatewrapper(wrap, origfn, wrapper)
583 setattr(container, funcname, wrap)
585 setattr(container, funcname, wrap)
584 return origfn
586 return origfn
585
587
586 def unwrapfunction(container, funcname, wrapper=None):
588 def unwrapfunction(container, funcname, wrapper=None):
587 '''undo wrapfunction
589 '''undo wrapfunction
588
590
589 If wrappers is None, undo the last wrap. Otherwise removes the wrapper
591 If wrappers is None, undo the last wrap. Otherwise removes the wrapper
590 from the chain of wrappers.
592 from the chain of wrappers.
591
593
592 Return the removed wrapper.
594 Return the removed wrapper.
593 Raise IndexError if wrapper is None and nothing to unwrap; ValueError if
595 Raise IndexError if wrapper is None and nothing to unwrap; ValueError if
594 wrapper is not None but is not found in the wrapper chain.
596 wrapper is not None but is not found in the wrapper chain.
595 '''
597 '''
596 chain = getwrapperchain(container, funcname)
598 chain = getwrapperchain(container, funcname)
597 origfn = chain.pop()
599 origfn = chain.pop()
598 if wrapper is None:
600 if wrapper is None:
599 wrapper = chain[0]
601 wrapper = chain[0]
600 chain.remove(wrapper)
602 chain.remove(wrapper)
601 setattr(container, funcname, origfn)
603 setattr(container, funcname, origfn)
602 for w in reversed(chain):
604 for w in reversed(chain):
603 wrapfunction(container, funcname, w)
605 wrapfunction(container, funcname, w)
604 return wrapper
606 return wrapper
605
607
606 def getwrapperchain(container, funcname):
608 def getwrapperchain(container, funcname):
607 '''get a chain of wrappers of a function
609 '''get a chain of wrappers of a function
608
610
609 Return a list of functions: [newest wrapper, ..., oldest wrapper, origfunc]
611 Return a list of functions: [newest wrapper, ..., oldest wrapper, origfunc]
610
612
611 The wrapper functions are the ones passed to wrapfunction, whose first
613 The wrapper functions are the ones passed to wrapfunction, whose first
612 argument is origfunc.
614 argument is origfunc.
613 '''
615 '''
614 result = []
616 result = []
615 fn = getattr(container, funcname)
617 fn = getattr(container, funcname)
616 while fn:
618 while fn:
617 assert callable(fn)
619 assert callable(fn)
618 result.append(getattr(fn, '_unboundwrapper', fn))
620 result.append(getattr(fn, '_unboundwrapper', fn))
619 fn = getattr(fn, '_origfunc', None)
621 fn = getattr(fn, '_origfunc', None)
620 return result
622 return result
621
623
622 def _disabledpaths():
624 def _disabledpaths():
623 '''find paths of disabled extensions. returns a dict of {name: path}'''
625 '''find paths of disabled extensions. returns a dict of {name: path}'''
624 import hgext
626 import hgext
625 extpath = os.path.dirname(
627 extpath = os.path.dirname(
626 os.path.abspath(pycompat.fsencode(hgext.__file__)))
628 os.path.abspath(pycompat.fsencode(hgext.__file__)))
627 try: # might not be a filesystem path
629 try: # might not be a filesystem path
628 files = os.listdir(extpath)
630 files = os.listdir(extpath)
629 except OSError:
631 except OSError:
630 return {}
632 return {}
631
633
632 exts = {}
634 exts = {}
633 for e in files:
635 for e in files:
634 if e.endswith('.py'):
636 if e.endswith('.py'):
635 name = e.rsplit('.', 1)[0]
637 name = e.rsplit('.', 1)[0]
636 path = os.path.join(extpath, e)
638 path = os.path.join(extpath, e)
637 else:
639 else:
638 name = e
640 name = e
639 path = os.path.join(extpath, e, '__init__.py')
641 path = os.path.join(extpath, e, '__init__.py')
640 if not os.path.exists(path):
642 if not os.path.exists(path):
641 continue
643 continue
642 if name in exts or name in _order or name == '__init__':
644 if name in exts or name in _order or name == '__init__':
643 continue
645 continue
644 exts[name] = path
646 exts[name] = path
645 for name, path in _disabledextensions.iteritems():
647 for name, path in _disabledextensions.iteritems():
646 # If no path was provided for a disabled extension (e.g. "color=!"),
648 # If no path was provided for a disabled extension (e.g. "color=!"),
647 # don't replace the path we already found by the scan above.
649 # don't replace the path we already found by the scan above.
648 if path:
650 if path:
649 exts[name] = path
651 exts[name] = path
650 return exts
652 return exts
651
653
652 def _moduledoc(file):
654 def _moduledoc(file):
653 '''return the top-level python documentation for the given file
655 '''return the top-level python documentation for the given file
654
656
655 Loosely inspired by pydoc.source_synopsis(), but rewritten to
657 Loosely inspired by pydoc.source_synopsis(), but rewritten to
656 handle triple quotes and to return the whole text instead of just
658 handle triple quotes and to return the whole text instead of just
657 the synopsis'''
659 the synopsis'''
658 result = []
660 result = []
659
661
660 line = file.readline()
662 line = file.readline()
661 while line[:1] == '#' or not line.strip():
663 while line[:1] == '#' or not line.strip():
662 line = file.readline()
664 line = file.readline()
663 if not line:
665 if not line:
664 break
666 break
665
667
666 start = line[:3]
668 start = line[:3]
667 if start == '"""' or start == "'''":
669 if start == '"""' or start == "'''":
668 line = line[3:]
670 line = line[3:]
669 while line:
671 while line:
670 if line.rstrip().endswith(start):
672 if line.rstrip().endswith(start):
671 line = line.split(start)[0]
673 line = line.split(start)[0]
672 if line:
674 if line:
673 result.append(line)
675 result.append(line)
674 break
676 break
675 elif not line:
677 elif not line:
676 return None # unmatched delimiter
678 return None # unmatched delimiter
677 result.append(line)
679 result.append(line)
678 line = file.readline()
680 line = file.readline()
679 else:
681 else:
680 return None
682 return None
681
683
682 return ''.join(result)
684 return ''.join(result)
683
685
684 def _disabledhelp(path):
686 def _disabledhelp(path):
685 '''retrieve help synopsis of a disabled extension (without importing)'''
687 '''retrieve help synopsis of a disabled extension (without importing)'''
686 try:
688 try:
687 with open(path, 'rb') as src:
689 with open(path, 'rb') as src:
688 doc = _moduledoc(src)
690 doc = _moduledoc(src)
689 except IOError:
691 except IOError:
690 return
692 return
691
693
692 if doc: # extracting localized synopsis
694 if doc: # extracting localized synopsis
693 return gettext(doc)
695 return gettext(doc)
694 else:
696 else:
695 return _('(no help text available)')
697 return _('(no help text available)')
696
698
697 def disabled():
699 def disabled():
698 '''find disabled extensions from hgext. returns a dict of {name: desc}'''
700 '''find disabled extensions from hgext. returns a dict of {name: desc}'''
699 try:
701 try:
700 from hgext import __index__
702 from hgext import __index__
701 return dict((name, gettext(desc))
703 return dict((name, gettext(desc))
702 for name, desc in __index__.docs.iteritems()
704 for name, desc in __index__.docs.iteritems()
703 if name not in _order)
705 if name not in _order)
704 except (ImportError, AttributeError):
706 except (ImportError, AttributeError):
705 pass
707 pass
706
708
707 paths = _disabledpaths()
709 paths = _disabledpaths()
708 if not paths:
710 if not paths:
709 return {}
711 return {}
710
712
711 exts = {}
713 exts = {}
712 for name, path in paths.iteritems():
714 for name, path in paths.iteritems():
713 doc = _disabledhelp(path)
715 doc = _disabledhelp(path)
714 if doc:
716 if doc:
715 exts[name] = doc.splitlines()[0]
717 exts[name] = doc.splitlines()[0]
716
718
717 return exts
719 return exts
718
720
719 def disabledext(name):
721 def disabledext(name):
720 '''find a specific disabled extension from hgext. returns desc'''
722 '''find a specific disabled extension from hgext. returns desc'''
721 try:
723 try:
722 from hgext import __index__
724 from hgext import __index__
723 if name in _order: # enabled
725 if name in _order: # enabled
724 return
726 return
725 else:
727 else:
726 return gettext(__index__.docs.get(name))
728 return gettext(__index__.docs.get(name))
727 except (ImportError, AttributeError):
729 except (ImportError, AttributeError):
728 pass
730 pass
729
731
730 paths = _disabledpaths()
732 paths = _disabledpaths()
731 if name in paths:
733 if name in paths:
732 return _disabledhelp(paths[name])
734 return _disabledhelp(paths[name])
733
735
734 def _walkcommand(node):
736 def _walkcommand(node):
735 """Scan @command() decorators in the tree starting at node"""
737 """Scan @command() decorators in the tree starting at node"""
736 todo = collections.deque([node])
738 todo = collections.deque([node])
737 while todo:
739 while todo:
738 node = todo.popleft()
740 node = todo.popleft()
739 if not isinstance(node, ast.FunctionDef):
741 if not isinstance(node, ast.FunctionDef):
740 todo.extend(ast.iter_child_nodes(node))
742 todo.extend(ast.iter_child_nodes(node))
741 continue
743 continue
742 for d in node.decorator_list:
744 for d in node.decorator_list:
743 if not isinstance(d, ast.Call):
745 if not isinstance(d, ast.Call):
744 continue
746 continue
745 if not isinstance(d.func, ast.Name):
747 if not isinstance(d.func, ast.Name):
746 continue
748 continue
747 if d.func.id != r'command':
749 if d.func.id != r'command':
748 continue
750 continue
749 yield d
751 yield d
750
752
751 def _disabledcmdtable(path):
753 def _disabledcmdtable(path):
752 """Construct a dummy command table without loading the extension module
754 """Construct a dummy command table without loading the extension module
753
755
754 This may raise IOError or SyntaxError.
756 This may raise IOError or SyntaxError.
755 """
757 """
756 with open(path, 'rb') as src:
758 with open(path, 'rb') as src:
757 root = ast.parse(src.read(), path)
759 root = ast.parse(src.read(), path)
758 cmdtable = {}
760 cmdtable = {}
759 for node in _walkcommand(root):
761 for node in _walkcommand(root):
760 if not node.args:
762 if not node.args:
761 continue
763 continue
762 a = node.args[0]
764 a = node.args[0]
763 if isinstance(a, ast.Str):
765 if isinstance(a, ast.Str):
764 name = pycompat.sysbytes(a.s)
766 name = pycompat.sysbytes(a.s)
765 elif pycompat.ispy3 and isinstance(a, ast.Bytes):
767 elif pycompat.ispy3 and isinstance(a, ast.Bytes):
766 name = a.s
768 name = a.s
767 else:
769 else:
768 continue
770 continue
769 cmdtable[name] = (None, [], b'')
771 cmdtable[name] = (None, [], b'')
770 return cmdtable
772 return cmdtable
771
773
772 def _finddisabledcmd(ui, cmd, name, path, strict):
774 def _finddisabledcmd(ui, cmd, name, path, strict):
773 try:
775 try:
774 cmdtable = _disabledcmdtable(path)
776 cmdtable = _disabledcmdtable(path)
775 except (IOError, SyntaxError):
777 except (IOError, SyntaxError):
776 return
778 return
777 try:
779 try:
778 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
780 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
779 except (error.AmbiguousCommand, error.UnknownCommand):
781 except (error.AmbiguousCommand, error.UnknownCommand):
780 return
782 return
781 for c in aliases:
783 for c in aliases:
782 if c.startswith(cmd):
784 if c.startswith(cmd):
783 cmd = c
785 cmd = c
784 break
786 break
785 else:
787 else:
786 cmd = aliases[0]
788 cmd = aliases[0]
787 doc = _disabledhelp(path)
789 doc = _disabledhelp(path)
788 return (cmd, name, doc)
790 return (cmd, name, doc)
789
791
790 def disabledcmd(ui, cmd, strict=False):
792 def disabledcmd(ui, cmd, strict=False):
791 '''find cmd from disabled extensions without importing.
793 '''find cmd from disabled extensions without importing.
792 returns (cmdname, extname, doc)'''
794 returns (cmdname, extname, doc)'''
793
795
794 paths = _disabledpaths()
796 paths = _disabledpaths()
795 if not paths:
797 if not paths:
796 raise error.UnknownCommand(cmd)
798 raise error.UnknownCommand(cmd)
797
799
798 ext = None
800 ext = None
799 # first, search for an extension with the same name as the command
801 # first, search for an extension with the same name as the command
800 path = paths.pop(cmd, None)
802 path = paths.pop(cmd, None)
801 if path:
803 if path:
802 ext = _finddisabledcmd(ui, cmd, cmd, path, strict=strict)
804 ext = _finddisabledcmd(ui, cmd, cmd, path, strict=strict)
803 if not ext:
805 if not ext:
804 # otherwise, interrogate each extension until there's a match
806 # otherwise, interrogate each extension until there's a match
805 for name, path in paths.iteritems():
807 for name, path in paths.iteritems():
806 ext = _finddisabledcmd(ui, cmd, name, path, strict=strict)
808 ext = _finddisabledcmd(ui, cmd, name, path, strict=strict)
807 if ext:
809 if ext:
808 break
810 break
809 if ext:
811 if ext:
810 return ext
812 return ext
811
813
812 raise error.UnknownCommand(cmd)
814 raise error.UnknownCommand(cmd)
813
815
814 def enabled(shortname=True):
816 def enabled(shortname=True):
815 '''return a dict of {name: desc} of extensions'''
817 '''return a dict of {name: desc} of extensions'''
816 exts = {}
818 exts = {}
817 for ename, ext in extensions():
819 for ename, ext in extensions():
818 doc = (gettext(ext.__doc__) or _('(no help text available)'))
820 doc = (gettext(ext.__doc__) or _('(no help text available)'))
819 if shortname:
821 if shortname:
820 ename = ename.split('.')[-1]
822 ename = ename.split('.')[-1]
821 exts[ename] = doc.splitlines()[0].strip()
823 exts[ename] = doc.splitlines()[0].strip()
822
824
823 return exts
825 return exts
824
826
825 def notloaded():
827 def notloaded():
826 '''return short names of extensions that failed to load'''
828 '''return short names of extensions that failed to load'''
827 return [name for name, mod in _extensions.iteritems() if mod is None]
829 return [name for name, mod in _extensions.iteritems() if mod is None]
828
830
829 def moduleversion(module):
831 def moduleversion(module):
830 '''return version information from given module as a string'''
832 '''return version information from given module as a string'''
831 if (util.safehasattr(module, 'getversion')
833 if (util.safehasattr(module, 'getversion')
832 and callable(module.getversion)):
834 and callable(module.getversion)):
833 version = module.getversion()
835 version = module.getversion()
834 elif util.safehasattr(module, '__version__'):
836 elif util.safehasattr(module, '__version__'):
835 version = module.__version__
837 version = module.__version__
836 else:
838 else:
837 version = ''
839 version = ''
838 if isinstance(version, (list, tuple)):
840 if isinstance(version, (list, tuple)):
839 version = '.'.join(pycompat.bytestr(o) for o in version)
841 version = '.'.join(pycompat.bytestr(o) for o in version)
840 return version
842 return version
841
843
842 def ismoduleinternal(module):
844 def ismoduleinternal(module):
843 exttestedwith = getattr(module, 'testedwith', None)
845 exttestedwith = getattr(module, 'testedwith', None)
844 return exttestedwith == "ships-with-hg-core"
846 return exttestedwith == "ships-with-hg-core"
@@ -1,24 +1,24 b''
1 # Disable the $CAP wire protocol capability.
1 # Disable the $CAP wire protocol capability.
2
2
3 if test -z "$CAP"
3 if test -z "$CAP"
4 then
4 then
5 echo "CAP environment variable not set."
5 echo "CAP environment variable not set."
6 fi
6 fi
7
7
8 cat > notcapable-$CAP.py << EOF
8 cat > notcapable-$CAP.py << EOF
9 from mercurial import extensions, localrepo, repository
9 from mercurial import extensions, localrepo, repository
10 def extsetup():
10 def extsetup(ui):
11 extensions.wrapfunction(repository.peer, 'capable', wrapcapable)
11 extensions.wrapfunction(repository.peer, 'capable', wrapcapable)
12 extensions.wrapfunction(localrepo.localrepository, 'peer', wrappeer)
12 extensions.wrapfunction(localrepo.localrepository, 'peer', wrappeer)
13 def wrapcapable(orig, self, name, *args, **kwargs):
13 def wrapcapable(orig, self, name, *args, **kwargs):
14 if name in '$CAP'.split(' '):
14 if name in '$CAP'.split(' '):
15 return False
15 return False
16 return orig(self, name, *args, **kwargs)
16 return orig(self, name, *args, **kwargs)
17 def wrappeer(orig, self):
17 def wrappeer(orig, self):
18 # Since we're disabling some newer features, we need to make sure local
18 # Since we're disabling some newer features, we need to make sure local
19 # repos add in the legacy features again.
19 # repos add in the legacy features again.
20 return localrepo.locallegacypeer(self)
20 return localrepo.locallegacypeer(self)
21 EOF
21 EOF
22
22
23 echo '[extensions]' >> $HGRCPATH
23 echo '[extensions]' >> $HGRCPATH
24 echo "notcapable-$CAP = `pwd`/notcapable-$CAP.py" >> $HGRCPATH
24 echo "notcapable-$CAP = `pwd`/notcapable-$CAP.py" >> $HGRCPATH
@@ -1,236 +1,236 b''
1 $ hg init repo
1 $ hg init repo
2 $ cd repo
2 $ cd repo
3 $ echo a > a
3 $ echo a > a
4 $ hg add a
4 $ hg add a
5 $ hg commit -m test
5 $ hg commit -m test
6
6
7 Do we ever miss a sub-second change?:
7 Do we ever miss a sub-second change?:
8
8
9 $ for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
9 $ for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
10 > hg co -qC 0
10 > hg co -qC 0
11 > echo b > a
11 > echo b > a
12 > hg st
12 > hg st
13 > done
13 > done
14 M a
14 M a
15 M a
15 M a
16 M a
16 M a
17 M a
17 M a
18 M a
18 M a
19 M a
19 M a
20 M a
20 M a
21 M a
21 M a
22 M a
22 M a
23 M a
23 M a
24 M a
24 M a
25 M a
25 M a
26 M a
26 M a
27 M a
27 M a
28 M a
28 M a
29 M a
29 M a
30 M a
30 M a
31 M a
31 M a
32 M a
32 M a
33 M a
33 M a
34
34
35 $ echo test > b
35 $ echo test > b
36 $ mkdir dir1
36 $ mkdir dir1
37 $ echo test > dir1/c
37 $ echo test > dir1/c
38 $ echo test > d
38 $ echo test > d
39
39
40 $ echo test > e
40 $ echo test > e
41 #if execbit
41 #if execbit
42 A directory will typically have the execute bit -- make sure it doesn't get
42 A directory will typically have the execute bit -- make sure it doesn't get
43 confused with a file with the exec bit set
43 confused with a file with the exec bit set
44 $ chmod +x e
44 $ chmod +x e
45 #endif
45 #endif
46
46
47 $ hg add b dir1 d e
47 $ hg add b dir1 d e
48 adding dir1/c
48 adding dir1/c
49 $ hg commit -m test2
49 $ hg commit -m test2
50
50
51 $ cat >> $TESTTMP/dirstaterace.py << EOF
51 $ cat >> $TESTTMP/dirstaterace.py << EOF
52 > from mercurial import (
52 > from mercurial import (
53 > context,
53 > context,
54 > extensions,
54 > extensions,
55 > )
55 > )
56 > def extsetup():
56 > def extsetup(ui):
57 > extensions.wrapfunction(context.workingctx, '_checklookup', overridechecklookup)
57 > extensions.wrapfunction(context.workingctx, '_checklookup', overridechecklookup)
58 > def overridechecklookup(orig, self, files):
58 > def overridechecklookup(orig, self, files):
59 > # make an update that changes the dirstate from underneath
59 > # make an update that changes the dirstate from underneath
60 > self._repo.ui.system(br"sh '$TESTTMP/dirstaterace.sh'",
60 > self._repo.ui.system(br"sh '$TESTTMP/dirstaterace.sh'",
61 > cwd=self._repo.root)
61 > cwd=self._repo.root)
62 > return orig(self, files)
62 > return orig(self, files)
63 > EOF
63 > EOF
64
64
65 $ hg debugrebuilddirstate
65 $ hg debugrebuilddirstate
66 $ hg debugdirstate
66 $ hg debugdirstate
67 n 0 -1 unset a
67 n 0 -1 unset a
68 n 0 -1 unset b
68 n 0 -1 unset b
69 n 0 -1 unset d
69 n 0 -1 unset d
70 n 0 -1 unset dir1/c
70 n 0 -1 unset dir1/c
71 n 0 -1 unset e
71 n 0 -1 unset e
72
72
73 XXX Note that this returns M for files that got replaced by directories. This is
73 XXX Note that this returns M for files that got replaced by directories. This is
74 definitely a bug, but the fix for that is hard and the next status run is fine
74 definitely a bug, but the fix for that is hard and the next status run is fine
75 anyway.
75 anyway.
76
76
77 $ cat > $TESTTMP/dirstaterace.sh <<EOF
77 $ cat > $TESTTMP/dirstaterace.sh <<EOF
78 > rm b && rm -r dir1 && rm d && mkdir d && rm e && mkdir e
78 > rm b && rm -r dir1 && rm d && mkdir d && rm e && mkdir e
79 > EOF
79 > EOF
80
80
81 $ hg status --config extensions.dirstaterace=$TESTTMP/dirstaterace.py
81 $ hg status --config extensions.dirstaterace=$TESTTMP/dirstaterace.py
82 M d
82 M d
83 M e
83 M e
84 ! b
84 ! b
85 ! dir1/c
85 ! dir1/c
86 $ hg debugdirstate
86 $ hg debugdirstate
87 n 644 2 * a (glob)
87 n 644 2 * a (glob)
88 n 0 -1 unset b
88 n 0 -1 unset b
89 n 0 -1 unset d
89 n 0 -1 unset d
90 n 0 -1 unset dir1/c
90 n 0 -1 unset dir1/c
91 n 0 -1 unset e
91 n 0 -1 unset e
92
92
93 $ hg status
93 $ hg status
94 ! b
94 ! b
95 ! d
95 ! d
96 ! dir1/c
96 ! dir1/c
97 ! e
97 ! e
98
98
99 $ rmdir d e
99 $ rmdir d e
100 $ hg update -C -q .
100 $ hg update -C -q .
101
101
102 Test that dirstate changes aren't written out at the end of "hg
102 Test that dirstate changes aren't written out at the end of "hg
103 status", if .hg/dirstate is already changed simultaneously before
103 status", if .hg/dirstate is already changed simultaneously before
104 acquisition of wlock in workingctx._poststatusfixup().
104 acquisition of wlock in workingctx._poststatusfixup().
105
105
106 This avoidance is important to keep consistency of dirstate in race
106 This avoidance is important to keep consistency of dirstate in race
107 condition (see issue5584 for detail).
107 condition (see issue5584 for detail).
108
108
109 $ hg parents -q
109 $ hg parents -q
110 1:* (glob)
110 1:* (glob)
111
111
112 $ hg debugrebuilddirstate
112 $ hg debugrebuilddirstate
113 $ hg debugdirstate
113 $ hg debugdirstate
114 n 0 -1 unset a
114 n 0 -1 unset a
115 n 0 -1 unset b
115 n 0 -1 unset b
116 n 0 -1 unset d
116 n 0 -1 unset d
117 n 0 -1 unset dir1/c
117 n 0 -1 unset dir1/c
118 n 0 -1 unset e
118 n 0 -1 unset e
119
119
120 $ cat > $TESTTMP/dirstaterace.sh <<EOF
120 $ cat > $TESTTMP/dirstaterace.sh <<EOF
121 > # This script assumes timetable of typical issue5584 case below:
121 > # This script assumes timetable of typical issue5584 case below:
122 > #
122 > #
123 > # 1. "hg status" loads .hg/dirstate
123 > # 1. "hg status" loads .hg/dirstate
124 > # 2. "hg status" confirms clean-ness of FILE
124 > # 2. "hg status" confirms clean-ness of FILE
125 > # 3. "hg update -C 0" updates the working directory simultaneously
125 > # 3. "hg update -C 0" updates the working directory simultaneously
126 > # (FILE is removed, and FILE is dropped from .hg/dirstate)
126 > # (FILE is removed, and FILE is dropped from .hg/dirstate)
127 > # 4. "hg status" acquires wlock
127 > # 4. "hg status" acquires wlock
128 > # (.hg/dirstate is re-loaded = no FILE entry in dirstate)
128 > # (.hg/dirstate is re-loaded = no FILE entry in dirstate)
129 > # 5. "hg status" marks FILE in dirstate as clean
129 > # 5. "hg status" marks FILE in dirstate as clean
130 > # (FILE entry is added to in-memory dirstate)
130 > # (FILE entry is added to in-memory dirstate)
131 > # 6. "hg status" writes dirstate changes into .hg/dirstate
131 > # 6. "hg status" writes dirstate changes into .hg/dirstate
132 > # (FILE entry is written into .hg/dirstate)
132 > # (FILE entry is written into .hg/dirstate)
133 > #
133 > #
134 > # To reproduce similar situation easily and certainly, #2 and #3
134 > # To reproduce similar situation easily and certainly, #2 and #3
135 > # are swapped. "hg cat" below ensures #2 on "hg status" side.
135 > # are swapped. "hg cat" below ensures #2 on "hg status" side.
136 >
136 >
137 > hg update -q -C 0
137 > hg update -q -C 0
138 > hg cat -r 1 b > b
138 > hg cat -r 1 b > b
139 > EOF
139 > EOF
140
140
141 "hg status" below should excludes "e", of which exec flag is set, for
141 "hg status" below should excludes "e", of which exec flag is set, for
142 portability of test scenario, because unsure but missing "e" is
142 portability of test scenario, because unsure but missing "e" is
143 treated differently in _checklookup() according to runtime platform.
143 treated differently in _checklookup() according to runtime platform.
144
144
145 - "missing(!)" on POSIX, "pctx[f].cmp(self[f])" raises ENOENT
145 - "missing(!)" on POSIX, "pctx[f].cmp(self[f])" raises ENOENT
146 - "modified(M)" on Windows, "self.flags(f) != pctx.flags(f)" is True
146 - "modified(M)" on Windows, "self.flags(f) != pctx.flags(f)" is True
147
147
148 $ hg status --config extensions.dirstaterace=$TESTTMP/dirstaterace.py --debug -X path:e
148 $ hg status --config extensions.dirstaterace=$TESTTMP/dirstaterace.py --debug -X path:e
149 skip updating dirstate: identity mismatch
149 skip updating dirstate: identity mismatch
150 M a
150 M a
151 ! d
151 ! d
152 ! dir1/c
152 ! dir1/c
153
153
154 $ hg parents -q
154 $ hg parents -q
155 0:* (glob)
155 0:* (glob)
156 $ hg files
156 $ hg files
157 a
157 a
158 $ hg debugdirstate
158 $ hg debugdirstate
159 n * * * a (glob)
159 n * * * a (glob)
160
160
161 $ rm b
161 $ rm b
162
162
163 #if fsmonitor
163 #if fsmonitor
164
164
165 Create fsmonitor state.
165 Create fsmonitor state.
166
166
167 $ hg status
167 $ hg status
168 $ f --type .hg/fsmonitor.state
168 $ f --type .hg/fsmonitor.state
169 .hg/fsmonitor.state: file
169 .hg/fsmonitor.state: file
170
170
171 Test that invalidating fsmonitor state in the middle (which doesn't require the
171 Test that invalidating fsmonitor state in the middle (which doesn't require the
172 wlock) causes the fsmonitor update to be skipped.
172 wlock) causes the fsmonitor update to be skipped.
173 hg debugrebuilddirstate ensures that the dirstaterace hook will be called, but
173 hg debugrebuilddirstate ensures that the dirstaterace hook will be called, but
174 it also invalidates the fsmonitor state. So back it up and restore it.
174 it also invalidates the fsmonitor state. So back it up and restore it.
175
175
176 $ mv .hg/fsmonitor.state .hg/fsmonitor.state.tmp
176 $ mv .hg/fsmonitor.state .hg/fsmonitor.state.tmp
177 $ hg debugrebuilddirstate
177 $ hg debugrebuilddirstate
178 $ mv .hg/fsmonitor.state.tmp .hg/fsmonitor.state
178 $ mv .hg/fsmonitor.state.tmp .hg/fsmonitor.state
179
179
180 $ cat > $TESTTMP/dirstaterace.sh <<EOF
180 $ cat > $TESTTMP/dirstaterace.sh <<EOF
181 > rm .hg/fsmonitor.state
181 > rm .hg/fsmonitor.state
182 > EOF
182 > EOF
183
183
184 $ hg status --config extensions.dirstaterace=$TESTTMP/dirstaterace.py --debug
184 $ hg status --config extensions.dirstaterace=$TESTTMP/dirstaterace.py --debug
185 skip updating fsmonitor.state: identity mismatch
185 skip updating fsmonitor.state: identity mismatch
186 $ f .hg/fsmonitor.state
186 $ f .hg/fsmonitor.state
187 .hg/fsmonitor.state: file not found
187 .hg/fsmonitor.state: file not found
188
188
189 #endif
189 #endif
190
190
191 Set up a rebase situation for issue5581.
191 Set up a rebase situation for issue5581.
192
192
193 $ echo c2 > a
193 $ echo c2 > a
194 $ echo c2 > b
194 $ echo c2 > b
195 $ hg add b
195 $ hg add b
196 $ hg commit -m c2
196 $ hg commit -m c2
197 created new head
197 created new head
198 $ echo c3 >> a
198 $ echo c3 >> a
199 $ hg commit -m c3
199 $ hg commit -m c3
200 $ hg update 2
200 $ hg update 2
201 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
202 $ echo c4 >> a
202 $ echo c4 >> a
203 $ echo c4 >> b
203 $ echo c4 >> b
204 $ hg commit -m c4
204 $ hg commit -m c4
205 created new head
205 created new head
206
206
207 Configure a merge tool that runs status in the middle of the rebase. The goal of
207 Configure a merge tool that runs status in the middle of the rebase. The goal of
208 the status call is to trigger a potential bug if fsmonitor's state is written
208 the status call is to trigger a potential bug if fsmonitor's state is written
209 even though the wlock is held by another process. The output of 'hg status' in
209 even though the wlock is held by another process. The output of 'hg status' in
210 the merge tool goes to /dev/null because we're more interested in the results of
210 the merge tool goes to /dev/null because we're more interested in the results of
211 'hg status' run after the rebase.
211 'hg status' run after the rebase.
212
212
213 $ cat >> $TESTTMP/mergetool-race.sh << EOF
213 $ cat >> $TESTTMP/mergetool-race.sh << EOF
214 > echo "custom merge tool"
214 > echo "custom merge tool"
215 > printf "c2\nc3\nc4\n" > \$1
215 > printf "c2\nc3\nc4\n" > \$1
216 > hg --cwd "$TESTTMP/repo" status > /dev/null
216 > hg --cwd "$TESTTMP/repo" status > /dev/null
217 > echo "custom merge tool end"
217 > echo "custom merge tool end"
218 > EOF
218 > EOF
219 $ cat >> $HGRCPATH << EOF
219 $ cat >> $HGRCPATH << EOF
220 > [extensions]
220 > [extensions]
221 > rebase =
221 > rebase =
222 > [merge-tools]
222 > [merge-tools]
223 > test.executable=sh
223 > test.executable=sh
224 > test.args=$TESTTMP/mergetool-race.sh \$output
224 > test.args=$TESTTMP/mergetool-race.sh \$output
225 > EOF
225 > EOF
226
226
227 $ hg rebase -s . -d 3 --tool test
227 $ hg rebase -s . -d 3 --tool test
228 rebasing 4:b08445fd6b2a "c4" (tip)
228 rebasing 4:b08445fd6b2a "c4" (tip)
229 merging a
229 merging a
230 custom merge tool
230 custom merge tool
231 custom merge tool end
231 custom merge tool end
232 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/* (glob)
232 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/* (glob)
233
233
234 This hg status should be empty, whether or not fsmonitor is enabled (issue5581).
234 This hg status should be empty, whether or not fsmonitor is enabled (issue5581).
235
235
236 $ hg status
236 $ hg status
@@ -1,1856 +1,1856 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) (chg !)
87 uipopulate called (1 times) (chg !)
88 reposetup called for a (chg !)
88 reposetup called for a (chg !)
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) (chg !)
99 uipopulate called (1 times) (chg !)
100 reposetup called for a (chg !)
100 reposetup called for a (chg !)
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) (chg !)
110 uipopulate called (1 times) (chg !)
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 reposetup called for b
114 reposetup called for b
115 ui == repo.ui
115 ui == repo.ui
116 updating to branch default
116 updating to branch default
117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
118
118
119 $ hg bar
119 $ hg bar
120 uisetup called (no-chg !)
120 uisetup called (no-chg !)
121 uisetup called [status] (no-chg !)
121 uisetup called [status] (no-chg !)
122 uipopulate called (1 times)
122 uipopulate called (1 times)
123 uipopulate called (1 times) (chg !)
123 uipopulate called (1 times) (chg !)
124 Bar
124 Bar
125 $ echo 'foobar = !' >> $HGRCPATH
125 $ echo 'foobar = !' >> $HGRCPATH
126
126
127 module/__init__.py-style
127 module/__init__.py-style
128
128
129 $ echo "barfoo = $barfoopath" >> $HGRCPATH
129 $ echo "barfoo = $barfoopath" >> $HGRCPATH
130 $ cd a
130 $ cd a
131 $ hg foo
131 $ hg foo
132 uisetup called
132 uisetup called
133 uisetup called [status]
133 uisetup called [status]
134 uipopulate called (1 times)
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 reposetup called for a
137 reposetup called for a
138 ui == repo.ui
138 ui == repo.ui
139 uipopulate called (1 times) (chg !)
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 reposetup called for a (chg !)
144 reposetup called for a (chg !)
145 ui == repo.ui (chg !)
145 ui == repo.ui (chg !)
146 Foo
146 Foo
147 $ echo 'barfoo = !' >> $HGRCPATH
147 $ echo 'barfoo = !' >> $HGRCPATH
148
148
149 Check that extensions are loaded in phases:
149 Check that extensions are loaded in phases:
150
150
151 $ cat > foo.py <<EOF
151 $ cat > foo.py <<EOF
152 > from __future__ import print_function
152 > from __future__ import print_function
153 > import os
153 > import os
154 > from mercurial import exthelper
154 > from mercurial import exthelper
155 > name = os.path.basename(__file__).rsplit('.', 1)[0]
155 > name = os.path.basename(__file__).rsplit('.', 1)[0]
156 > print("1) %s imported" % name, flush=True)
156 > print("1) %s imported" % name, flush=True)
157 > eh = exthelper.exthelper()
157 > eh = exthelper.exthelper()
158 > @eh.uisetup
158 > @eh.uisetup
159 > def _uisetup(ui):
159 > def _uisetup(ui):
160 > print("2) %s uisetup" % name, flush=True)
160 > print("2) %s uisetup" % name, flush=True)
161 > @eh.extsetup
161 > @eh.extsetup
162 > def _extsetup(ui):
162 > def _extsetup(ui):
163 > print("3) %s extsetup" % name, flush=True)
163 > print("3) %s extsetup" % name, flush=True)
164 > @eh.uipopulate
164 > @eh.uipopulate
165 > def _uipopulate(ui):
165 > def _uipopulate(ui):
166 > print("4) %s uipopulate" % name, flush=True)
166 > print("4) %s uipopulate" % name, flush=True)
167 > @eh.reposetup
167 > @eh.reposetup
168 > def _reposetup(ui, repo):
168 > def _reposetup(ui, repo):
169 > print("5) %s reposetup" % name, flush=True)
169 > print("5) %s reposetup" % name, flush=True)
170 >
170 >
171 > extsetup = eh.finalextsetup
171 > extsetup = eh.finalextsetup
172 > reposetup = eh.finalreposetup
172 > reposetup = eh.finalreposetup
173 > uipopulate = eh.finaluipopulate
173 > uipopulate = eh.finaluipopulate
174 > uisetup = eh.finaluisetup
174 > uisetup = eh.finaluisetup
175 > revsetpredicate = eh.revsetpredicate
175 > revsetpredicate = eh.revsetpredicate
176 >
176 >
177 > bytesname = name.encode('utf-8')
177 > bytesname = name.encode('utf-8')
178 > # custom predicate to check registration of functions at loading
178 > # custom predicate to check registration of functions at loading
179 > from mercurial import (
179 > from mercurial import (
180 > smartset,
180 > smartset,
181 > )
181 > )
182 > @eh.revsetpredicate(bytesname, safe=True) # safe=True for query via hgweb
182 > @eh.revsetpredicate(bytesname, safe=True) # safe=True for query via hgweb
183 > def custompredicate(repo, subset, x):
183 > def custompredicate(repo, subset, x):
184 > return smartset.baseset([r for r in subset if r in {0}])
184 > return smartset.baseset([r for r in subset if r in {0}])
185 > EOF
185 > EOF
186 $ "$PYTHON" $TESTTMP/unflush.py foo.py
186 $ "$PYTHON" $TESTTMP/unflush.py foo.py
187
187
188 $ cp foo.py bar.py
188 $ cp foo.py bar.py
189 $ echo 'foo = foo.py' >> $HGRCPATH
189 $ echo 'foo = foo.py' >> $HGRCPATH
190 $ echo 'bar = bar.py' >> $HGRCPATH
190 $ echo 'bar = bar.py' >> $HGRCPATH
191
191
192 Check normal command's load order of extensions and registration of functions
192 Check normal command's load order of extensions and registration of functions
193
193
194 $ hg log -r "foo() and bar()" -q
194 $ hg log -r "foo() and bar()" -q
195 1) foo imported
195 1) foo imported
196 1) bar imported
196 1) bar imported
197 2) foo uisetup
197 2) foo uisetup
198 2) bar uisetup
198 2) bar uisetup
199 3) foo extsetup
199 3) foo extsetup
200 3) bar extsetup
200 3) bar extsetup
201 4) foo uipopulate
201 4) foo uipopulate
202 4) bar uipopulate
202 4) bar uipopulate
203 4) foo uipopulate
203 4) foo uipopulate
204 4) bar uipopulate
204 4) bar uipopulate
205 4) foo uipopulate
205 4) foo uipopulate
206 4) bar uipopulate
206 4) bar uipopulate
207 5) foo reposetup
207 5) foo reposetup
208 5) bar reposetup
208 5) bar reposetup
209 0:c24b9ac61126
209 0:c24b9ac61126
210
210
211 Check hgweb's load order of extensions and registration of functions
211 Check hgweb's load order of extensions and registration of functions
212
212
213 $ cat > hgweb.cgi <<EOF
213 $ cat > hgweb.cgi <<EOF
214 > #!$PYTHON
214 > #!$PYTHON
215 > from mercurial import demandimport; demandimport.enable()
215 > from mercurial import demandimport; demandimport.enable()
216 > from mercurial.hgweb import hgweb
216 > from mercurial.hgweb import hgweb
217 > from mercurial.hgweb import wsgicgi
217 > from mercurial.hgweb import wsgicgi
218 > application = hgweb(b'.', b'test repo')
218 > application = hgweb(b'.', b'test repo')
219 > wsgicgi.launch(application)
219 > wsgicgi.launch(application)
220 > EOF
220 > EOF
221 $ . "$TESTDIR/cgienv"
221 $ . "$TESTDIR/cgienv"
222
222
223 $ PATH_INFO='/' SCRIPT_NAME='' "$PYTHON" hgweb.cgi \
223 $ PATH_INFO='/' SCRIPT_NAME='' "$PYTHON" hgweb.cgi \
224 > | grep '^[0-9]) ' # ignores HTML output
224 > | grep '^[0-9]) ' # ignores HTML output
225 1) foo imported
225 1) foo imported
226 1) bar imported
226 1) bar imported
227 2) foo uisetup
227 2) foo uisetup
228 2) bar uisetup
228 2) bar uisetup
229 3) foo extsetup
229 3) foo extsetup
230 3) bar extsetup
230 3) bar extsetup
231 4) foo uipopulate
231 4) foo uipopulate
232 4) bar uipopulate
232 4) bar uipopulate
233 4) foo uipopulate
233 4) foo uipopulate
234 4) bar uipopulate
234 4) bar uipopulate
235 5) foo reposetup
235 5) foo reposetup
236 5) bar reposetup
236 5) bar reposetup
237
237
238 (check that revset predicate foo() and bar() are available)
238 (check that revset predicate foo() and bar() are available)
239
239
240 #if msys
240 #if msys
241 $ PATH_INFO='//shortlog'
241 $ PATH_INFO='//shortlog'
242 #else
242 #else
243 $ PATH_INFO='/shortlog'
243 $ PATH_INFO='/shortlog'
244 #endif
244 #endif
245 $ export PATH_INFO
245 $ export PATH_INFO
246 $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' "$PYTHON" hgweb.cgi \
246 $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' "$PYTHON" hgweb.cgi \
247 > | grep '<a href="/rev/[0-9a-z]*">'
247 > | grep '<a href="/rev/[0-9a-z]*">'
248 <a href="/rev/c24b9ac61126">add file</a>
248 <a href="/rev/c24b9ac61126">add file</a>
249
249
250 $ echo 'foo = !' >> $HGRCPATH
250 $ echo 'foo = !' >> $HGRCPATH
251 $ echo 'bar = !' >> $HGRCPATH
251 $ echo 'bar = !' >> $HGRCPATH
252
252
253 Check "from __future__ import absolute_import" support for external libraries
253 Check "from __future__ import absolute_import" support for external libraries
254
254
255 (import-checker.py reports issues for some of heredoc python code
255 (import-checker.py reports issues for some of heredoc python code
256 fragments below, because import-checker.py does not know test specific
256 fragments below, because import-checker.py does not know test specific
257 package hierarchy. NO_CHECK_* should be used as a limit mark of
257 package hierarchy. NO_CHECK_* should be used as a limit mark of
258 heredoc, in order to make import-checker.py ignore them. For
258 heredoc, in order to make import-checker.py ignore them. For
259 simplicity, all python code fragments below are generated with such
259 simplicity, all python code fragments below are generated with such
260 limit mark, regardless of importing module or not.)
260 limit mark, regardless of importing module or not.)
261
261
262 #if windows
262 #if windows
263 $ PATHSEP=";"
263 $ PATHSEP=";"
264 #else
264 #else
265 $ PATHSEP=":"
265 $ PATHSEP=":"
266 #endif
266 #endif
267 $ export PATHSEP
267 $ export PATHSEP
268
268
269 $ mkdir $TESTTMP/libroot
269 $ mkdir $TESTTMP/libroot
270 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
270 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
271 $ mkdir $TESTTMP/libroot/mod
271 $ mkdir $TESTTMP/libroot/mod
272 $ touch $TESTTMP/libroot/mod/__init__.py
272 $ touch $TESTTMP/libroot/mod/__init__.py
273 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
273 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
274
274
275 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<NO_CHECK_EOF
275 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<NO_CHECK_EOF
276 > from __future__ import absolute_import, print_function
276 > from __future__ import absolute_import, print_function
277 > import ambig # should load "libroot/ambig.py"
277 > import ambig # should load "libroot/ambig.py"
278 > s = ambig.s
278 > s = ambig.s
279 > NO_CHECK_EOF
279 > NO_CHECK_EOF
280 $ cat > loadabs.py <<NO_CHECK_EOF
280 $ cat > loadabs.py <<NO_CHECK_EOF
281 > import mod.ambigabs as ambigabs
281 > import mod.ambigabs as ambigabs
282 > def extsetup():
282 > def extsetup(ui):
283 > print('ambigabs.s=%s' % ambigabs.s, flush=True)
283 > print('ambigabs.s=%s' % ambigabs.s, flush=True)
284 > NO_CHECK_EOF
284 > NO_CHECK_EOF
285 $ "$PYTHON" $TESTTMP/unflush.py loadabs.py
285 $ "$PYTHON" $TESTTMP/unflush.py loadabs.py
286 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
286 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
287 ambigabs.s=libroot/ambig.py
287 ambigabs.s=libroot/ambig.py
288 $TESTTMP/a
288 $TESTTMP/a
289
289
290 #if no-py3
290 #if no-py3
291 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<NO_CHECK_EOF
291 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<NO_CHECK_EOF
292 > from __future__ import print_function
292 > from __future__ import print_function
293 > import ambig # should load "libroot/mod/ambig.py"
293 > import ambig # should load "libroot/mod/ambig.py"
294 > s = ambig.s
294 > s = ambig.s
295 > NO_CHECK_EOF
295 > NO_CHECK_EOF
296 $ cat > loadrel.py <<NO_CHECK_EOF
296 $ cat > loadrel.py <<NO_CHECK_EOF
297 > import mod.ambigrel as ambigrel
297 > import mod.ambigrel as ambigrel
298 > def extsetup():
298 > def extsetup(ui):
299 > print('ambigrel.s=%s' % ambigrel.s, flush=True)
299 > print('ambigrel.s=%s' % ambigrel.s, flush=True)
300 > NO_CHECK_EOF
300 > NO_CHECK_EOF
301 $ "$PYTHON" $TESTTMP/unflush.py loadrel.py
301 $ "$PYTHON" $TESTTMP/unflush.py loadrel.py
302 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
302 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
303 ambigrel.s=libroot/mod/ambig.py
303 ambigrel.s=libroot/mod/ambig.py
304 $TESTTMP/a
304 $TESTTMP/a
305 #endif
305 #endif
306
306
307 Check absolute/relative import of extension specific modules
307 Check absolute/relative import of extension specific modules
308
308
309 $ mkdir $TESTTMP/extroot
309 $ mkdir $TESTTMP/extroot
310 $ cat > $TESTTMP/extroot/bar.py <<NO_CHECK_EOF
310 $ cat > $TESTTMP/extroot/bar.py <<NO_CHECK_EOF
311 > s = b'this is extroot.bar'
311 > s = b'this is extroot.bar'
312 > NO_CHECK_EOF
312 > NO_CHECK_EOF
313 $ mkdir $TESTTMP/extroot/sub1
313 $ mkdir $TESTTMP/extroot/sub1
314 $ cat > $TESTTMP/extroot/sub1/__init__.py <<NO_CHECK_EOF
314 $ cat > $TESTTMP/extroot/sub1/__init__.py <<NO_CHECK_EOF
315 > s = b'this is extroot.sub1.__init__'
315 > s = b'this is extroot.sub1.__init__'
316 > NO_CHECK_EOF
316 > NO_CHECK_EOF
317 $ cat > $TESTTMP/extroot/sub1/baz.py <<NO_CHECK_EOF
317 $ cat > $TESTTMP/extroot/sub1/baz.py <<NO_CHECK_EOF
318 > s = b'this is extroot.sub1.baz'
318 > s = b'this is extroot.sub1.baz'
319 > NO_CHECK_EOF
319 > NO_CHECK_EOF
320 $ cat > $TESTTMP/extroot/__init__.py <<NO_CHECK_EOF
320 $ cat > $TESTTMP/extroot/__init__.py <<NO_CHECK_EOF
321 > from __future__ import absolute_import
321 > from __future__ import absolute_import
322 > s = b'this is extroot.__init__'
322 > s = b'this is extroot.__init__'
323 > from . import foo
323 > from . import foo
324 > def extsetup(ui):
324 > def extsetup(ui):
325 > ui.write(b'(extroot) ', foo.func(), b'\n')
325 > ui.write(b'(extroot) ', foo.func(), b'\n')
326 > ui.flush()
326 > ui.flush()
327 > NO_CHECK_EOF
327 > NO_CHECK_EOF
328
328
329 $ cat > $TESTTMP/extroot/foo.py <<NO_CHECK_EOF
329 $ cat > $TESTTMP/extroot/foo.py <<NO_CHECK_EOF
330 > # test absolute import
330 > # test absolute import
331 > buf = []
331 > buf = []
332 > def func():
332 > def func():
333 > # "not locals" case
333 > # "not locals" case
334 > import extroot.bar
334 > import extroot.bar
335 > buf.append(b'import extroot.bar in func(): %s' % extroot.bar.s)
335 > buf.append(b'import extroot.bar in func(): %s' % extroot.bar.s)
336 > return b'\n(extroot) '.join(buf)
336 > return b'\n(extroot) '.join(buf)
337 > # b"fromlist == ('*',)" case
337 > # b"fromlist == ('*',)" case
338 > from extroot.bar import *
338 > from extroot.bar import *
339 > buf.append(b'from extroot.bar import *: %s' % s)
339 > buf.append(b'from extroot.bar import *: %s' % s)
340 > # "not fromlist" and "if '.' in name" case
340 > # "not fromlist" and "if '.' in name" case
341 > import extroot.sub1.baz
341 > import extroot.sub1.baz
342 > buf.append(b'import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
342 > buf.append(b'import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
343 > # "not fromlist" and NOT "if '.' in name" case
343 > # "not fromlist" and NOT "if '.' in name" case
344 > import extroot
344 > import extroot
345 > buf.append(b'import extroot: %s' % extroot.s)
345 > buf.append(b'import extroot: %s' % extroot.s)
346 > # NOT "not fromlist" and NOT "level != -1" case
346 > # NOT "not fromlist" and NOT "level != -1" case
347 > from extroot.bar import s
347 > from extroot.bar import s
348 > buf.append(b'from extroot.bar import s: %s' % s)
348 > buf.append(b'from extroot.bar import s: %s' % s)
349 > NO_CHECK_EOF
349 > NO_CHECK_EOF
350 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
350 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
351 (extroot) from extroot.bar import *: this is extroot.bar
351 (extroot) from extroot.bar import *: this is extroot.bar
352 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
352 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
353 (extroot) import extroot: this is extroot.__init__
353 (extroot) import extroot: this is extroot.__init__
354 (extroot) from extroot.bar import s: this is extroot.bar
354 (extroot) from extroot.bar import s: this is extroot.bar
355 (extroot) import extroot.bar in func(): this is extroot.bar
355 (extroot) import extroot.bar in func(): this is extroot.bar
356 $TESTTMP/a
356 $TESTTMP/a
357
357
358 #if no-py3
358 #if no-py3
359 $ rm "$TESTTMP"/extroot/foo.*
359 $ rm "$TESTTMP"/extroot/foo.*
360 $ rm -Rf "$TESTTMP/extroot/__pycache__"
360 $ rm -Rf "$TESTTMP/extroot/__pycache__"
361 $ cat > $TESTTMP/extroot/foo.py <<NO_CHECK_EOF
361 $ cat > $TESTTMP/extroot/foo.py <<NO_CHECK_EOF
362 > # test relative import
362 > # test relative import
363 > buf = []
363 > buf = []
364 > def func():
364 > def func():
365 > # "not locals" case
365 > # "not locals" case
366 > import bar
366 > import bar
367 > buf.append('import bar in func(): %s' % bar.s)
367 > buf.append('import bar in func(): %s' % bar.s)
368 > return '\n(extroot) '.join(buf)
368 > return '\n(extroot) '.join(buf)
369 > # "fromlist == ('*',)" case
369 > # "fromlist == ('*',)" case
370 > from bar import *
370 > from bar import *
371 > buf.append('from bar import *: %s' % s)
371 > buf.append('from bar import *: %s' % s)
372 > # "not fromlist" and "if '.' in name" case
372 > # "not fromlist" and "if '.' in name" case
373 > import sub1.baz
373 > import sub1.baz
374 > buf.append('import sub1.baz: %s' % sub1.baz.s)
374 > buf.append('import sub1.baz: %s' % sub1.baz.s)
375 > # "not fromlist" and NOT "if '.' in name" case
375 > # "not fromlist" and NOT "if '.' in name" case
376 > import sub1
376 > import sub1
377 > buf.append('import sub1: %s' % sub1.s)
377 > buf.append('import sub1: %s' % sub1.s)
378 > # NOT "not fromlist" and NOT "level != -1" case
378 > # NOT "not fromlist" and NOT "level != -1" case
379 > from bar import s
379 > from bar import s
380 > buf.append('from bar import s: %s' % s)
380 > buf.append('from bar import s: %s' % s)
381 > NO_CHECK_EOF
381 > NO_CHECK_EOF
382 $ hg --config extensions.extroot=$TESTTMP/extroot root
382 $ hg --config extensions.extroot=$TESTTMP/extroot root
383 (extroot) from bar import *: this is extroot.bar
383 (extroot) from bar import *: this is extroot.bar
384 (extroot) import sub1.baz: this is extroot.sub1.baz
384 (extroot) import sub1.baz: this is extroot.sub1.baz
385 (extroot) import sub1: this is extroot.sub1.__init__
385 (extroot) import sub1: this is extroot.sub1.__init__
386 (extroot) from bar import s: this is extroot.bar
386 (extroot) from bar import s: this is extroot.bar
387 (extroot) import bar in func(): this is extroot.bar
387 (extroot) import bar in func(): this is extroot.bar
388 $TESTTMP/a
388 $TESTTMP/a
389 #endif
389 #endif
390
390
391 #if demandimport
391 #if demandimport
392
392
393 Examine whether module loading is delayed until actual referring, even
393 Examine whether module loading is delayed until actual referring, even
394 though module is imported with "absolute_import" feature.
394 though module is imported with "absolute_import" feature.
395
395
396 Files below in each packages are used for described purpose:
396 Files below in each packages are used for described purpose:
397
397
398 - "called": examine whether "from MODULE import ATTR" works correctly
398 - "called": examine whether "from MODULE import ATTR" works correctly
399 - "unused": examine whether loading is delayed correctly
399 - "unused": examine whether loading is delayed correctly
400 - "used": examine whether "from PACKAGE import MODULE" works correctly
400 - "used": examine whether "from PACKAGE import MODULE" works correctly
401
401
402 Package hierarchy is needed to examine whether demand importing works
402 Package hierarchy is needed to examine whether demand importing works
403 as expected for "from SUB.PACK.AGE import MODULE".
403 as expected for "from SUB.PACK.AGE import MODULE".
404
404
405 Setup "external library" to be imported with "absolute_import"
405 Setup "external library" to be imported with "absolute_import"
406 feature.
406 feature.
407
407
408 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
408 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
409 $ touch $TESTTMP/extlibroot/__init__.py
409 $ touch $TESTTMP/extlibroot/__init__.py
410 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
410 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
411 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
411 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
412
412
413 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<NO_CHECK_EOF
413 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<NO_CHECK_EOF
414 > def func():
414 > def func():
415 > return b"this is extlibroot.lsub1.lsub2.called.func()"
415 > return b"this is extlibroot.lsub1.lsub2.called.func()"
416 > NO_CHECK_EOF
416 > NO_CHECK_EOF
417 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<NO_CHECK_EOF
417 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<NO_CHECK_EOF
418 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
418 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
419 > NO_CHECK_EOF
419 > NO_CHECK_EOF
420 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<NO_CHECK_EOF
420 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<NO_CHECK_EOF
421 > detail = b"this is extlibroot.lsub1.lsub2.used"
421 > detail = b"this is extlibroot.lsub1.lsub2.used"
422 > NO_CHECK_EOF
422 > NO_CHECK_EOF
423
423
424 Setup sub-package of "external library", which causes instantiation of
424 Setup sub-package of "external library", which causes instantiation of
425 demandmod in "recurse down the module chain" code path. Relative
425 demandmod in "recurse down the module chain" code path. Relative
426 importing with "absolute_import" feature isn't tested, because "level
426 importing with "absolute_import" feature isn't tested, because "level
427 >=1 " doesn't cause instantiation of demandmod.
427 >=1 " doesn't cause instantiation of demandmod.
428
428
429 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
429 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
430 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<NO_CHECK_EOF
430 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<NO_CHECK_EOF
431 > detail = b"this is extlibroot.recursedown.abs.used"
431 > detail = b"this is extlibroot.recursedown.abs.used"
432 > NO_CHECK_EOF
432 > NO_CHECK_EOF
433 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<NO_CHECK_EOF
433 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<NO_CHECK_EOF
434 > from __future__ import absolute_import
434 > from __future__ import absolute_import
435 > from extlibroot.recursedown.abs.used import detail
435 > from extlibroot.recursedown.abs.used import detail
436 > NO_CHECK_EOF
436 > NO_CHECK_EOF
437
437
438 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
438 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
439 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<NO_CHECK_EOF
439 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<NO_CHECK_EOF
440 > detail = b"this is extlibroot.recursedown.legacy.used"
440 > detail = b"this is extlibroot.recursedown.legacy.used"
441 > NO_CHECK_EOF
441 > NO_CHECK_EOF
442 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<NO_CHECK_EOF
442 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<NO_CHECK_EOF
443 > # legacy style (level == -1) import
443 > # legacy style (level == -1) import
444 > from extlibroot.recursedown.legacy.used import detail
444 > from extlibroot.recursedown.legacy.used import detail
445 > NO_CHECK_EOF
445 > NO_CHECK_EOF
446
446
447 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<NO_CHECK_EOF
447 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<NO_CHECK_EOF
448 > from __future__ import absolute_import
448 > from __future__ import absolute_import
449 > from extlibroot.recursedown.abs import detail as absdetail
449 > from extlibroot.recursedown.abs import detail as absdetail
450 > from .legacy import detail as legacydetail
450 > from .legacy import detail as legacydetail
451 > NO_CHECK_EOF
451 > NO_CHECK_EOF
452
452
453 Setup package that re-exports an attribute of its submodule as the same
453 Setup package that re-exports an attribute of its submodule as the same
454 name. This leaves 'shadowing.used' pointing to 'used.detail', but still
454 name. This leaves 'shadowing.used' pointing to 'used.detail', but still
455 the submodule 'used' should be somehow accessible. (issue5617)
455 the submodule 'used' should be somehow accessible. (issue5617)
456
456
457 $ mkdir -p $TESTTMP/extlibroot/shadowing
457 $ mkdir -p $TESTTMP/extlibroot/shadowing
458 $ cat > $TESTTMP/extlibroot/shadowing/used.py <<NO_CHECK_EOF
458 $ cat > $TESTTMP/extlibroot/shadowing/used.py <<NO_CHECK_EOF
459 > detail = b"this is extlibroot.shadowing.used"
459 > detail = b"this is extlibroot.shadowing.used"
460 > NO_CHECK_EOF
460 > NO_CHECK_EOF
461 $ cat > $TESTTMP/extlibroot/shadowing/proxied.py <<NO_CHECK_EOF
461 $ cat > $TESTTMP/extlibroot/shadowing/proxied.py <<NO_CHECK_EOF
462 > from __future__ import absolute_import
462 > from __future__ import absolute_import
463 > from extlibroot.shadowing.used import detail
463 > from extlibroot.shadowing.used import detail
464 > NO_CHECK_EOF
464 > NO_CHECK_EOF
465 $ cat > $TESTTMP/extlibroot/shadowing/__init__.py <<NO_CHECK_EOF
465 $ cat > $TESTTMP/extlibroot/shadowing/__init__.py <<NO_CHECK_EOF
466 > from __future__ import absolute_import
466 > from __future__ import absolute_import
467 > from .used import detail as used
467 > from .used import detail as used
468 > NO_CHECK_EOF
468 > NO_CHECK_EOF
469
469
470 Setup extension local modules to be imported with "absolute_import"
470 Setup extension local modules to be imported with "absolute_import"
471 feature.
471 feature.
472
472
473 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
473 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
474 $ touch $TESTTMP/absextroot/xsub1/__init__.py
474 $ touch $TESTTMP/absextroot/xsub1/__init__.py
475 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
475 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
476
476
477 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<NO_CHECK_EOF
477 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<NO_CHECK_EOF
478 > def func():
478 > def func():
479 > return b"this is absextroot.xsub1.xsub2.called.func()"
479 > return b"this is absextroot.xsub1.xsub2.called.func()"
480 > NO_CHECK_EOF
480 > NO_CHECK_EOF
481 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<NO_CHECK_EOF
481 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<NO_CHECK_EOF
482 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
482 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
483 > NO_CHECK_EOF
483 > NO_CHECK_EOF
484 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<NO_CHECK_EOF
484 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<NO_CHECK_EOF
485 > detail = b"this is absextroot.xsub1.xsub2.used"
485 > detail = b"this is absextroot.xsub1.xsub2.used"
486 > NO_CHECK_EOF
486 > NO_CHECK_EOF
487
487
488 Setup extension local modules to examine whether demand importing
488 Setup extension local modules to examine whether demand importing
489 works as expected in "level > 1" case.
489 works as expected in "level > 1" case.
490
490
491 $ cat > $TESTTMP/absextroot/relimportee.py <<NO_CHECK_EOF
491 $ cat > $TESTTMP/absextroot/relimportee.py <<NO_CHECK_EOF
492 > detail = b"this is absextroot.relimportee"
492 > detail = b"this is absextroot.relimportee"
493 > NO_CHECK_EOF
493 > NO_CHECK_EOF
494 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<NO_CHECK_EOF
494 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<NO_CHECK_EOF
495 > from __future__ import absolute_import
495 > from __future__ import absolute_import
496 > from mercurial import pycompat
496 > from mercurial import pycompat
497 > from ... import relimportee
497 > from ... import relimportee
498 > detail = b"this relimporter imports %r" % (
498 > detail = b"this relimporter imports %r" % (
499 > pycompat.bytestr(relimportee.detail))
499 > pycompat.bytestr(relimportee.detail))
500 > NO_CHECK_EOF
500 > NO_CHECK_EOF
501
501
502 Setup modules, which actually import extension local modules at
502 Setup modules, which actually import extension local modules at
503 runtime.
503 runtime.
504
504
505 $ cat > $TESTTMP/absextroot/absolute.py << NO_CHECK_EOF
505 $ cat > $TESTTMP/absextroot/absolute.py << NO_CHECK_EOF
506 > from __future__ import absolute_import
506 > from __future__ import absolute_import
507 >
507 >
508 > # import extension local modules absolutely (level = 0)
508 > # import extension local modules absolutely (level = 0)
509 > from absextroot.xsub1.xsub2 import used, unused
509 > from absextroot.xsub1.xsub2 import used, unused
510 > from absextroot.xsub1.xsub2.called import func
510 > from absextroot.xsub1.xsub2.called import func
511 >
511 >
512 > def getresult():
512 > def getresult():
513 > result = []
513 > result = []
514 > result.append(used.detail)
514 > result.append(used.detail)
515 > result.append(func())
515 > result.append(func())
516 > return result
516 > return result
517 > NO_CHECK_EOF
517 > NO_CHECK_EOF
518
518
519 $ cat > $TESTTMP/absextroot/relative.py << NO_CHECK_EOF
519 $ cat > $TESTTMP/absextroot/relative.py << NO_CHECK_EOF
520 > from __future__ import absolute_import
520 > from __future__ import absolute_import
521 >
521 >
522 > # import extension local modules relatively (level == 1)
522 > # import extension local modules relatively (level == 1)
523 > from .xsub1.xsub2 import used, unused
523 > from .xsub1.xsub2 import used, unused
524 > from .xsub1.xsub2.called import func
524 > from .xsub1.xsub2.called import func
525 >
525 >
526 > # import a module, which implies "importing with level > 1"
526 > # import a module, which implies "importing with level > 1"
527 > from .xsub1.xsub2 import relimporter
527 > from .xsub1.xsub2 import relimporter
528 >
528 >
529 > def getresult():
529 > def getresult():
530 > result = []
530 > result = []
531 > result.append(used.detail)
531 > result.append(used.detail)
532 > result.append(func())
532 > result.append(func())
533 > result.append(relimporter.detail)
533 > result.append(relimporter.detail)
534 > return result
534 > return result
535 > NO_CHECK_EOF
535 > NO_CHECK_EOF
536
536
537 Setup main procedure of extension.
537 Setup main procedure of extension.
538
538
539 $ cat > $TESTTMP/absextroot/__init__.py <<NO_CHECK_EOF
539 $ cat > $TESTTMP/absextroot/__init__.py <<NO_CHECK_EOF
540 > from __future__ import absolute_import
540 > from __future__ import absolute_import
541 > from mercurial import registrar
541 > from mercurial import registrar
542 > cmdtable = {}
542 > cmdtable = {}
543 > command = registrar.command(cmdtable)
543 > command = registrar.command(cmdtable)
544 >
544 >
545 > # "absolute" and "relative" shouldn't be imported before actual
545 > # "absolute" and "relative" shouldn't be imported before actual
546 > # command execution, because (1) they import same modules, and (2)
546 > # command execution, because (1) they import same modules, and (2)
547 > # preceding import (= instantiate "demandmod" object instead of
547 > # preceding import (= instantiate "demandmod" object instead of
548 > # real "module" object) might hide problem of succeeding import.
548 > # real "module" object) might hide problem of succeeding import.
549 >
549 >
550 > @command(b'showabsolute', [], norepo=True)
550 > @command(b'showabsolute', [], norepo=True)
551 > def showabsolute(ui, *args, **opts):
551 > def showabsolute(ui, *args, **opts):
552 > from absextroot import absolute
552 > from absextroot import absolute
553 > ui.write(b'ABS: %s\n' % b'\nABS: '.join(absolute.getresult()))
553 > ui.write(b'ABS: %s\n' % b'\nABS: '.join(absolute.getresult()))
554 >
554 >
555 > @command(b'showrelative', [], norepo=True)
555 > @command(b'showrelative', [], norepo=True)
556 > def showrelative(ui, *args, **opts):
556 > def showrelative(ui, *args, **opts):
557 > from . import relative
557 > from . import relative
558 > ui.write(b'REL: %s\n' % b'\nREL: '.join(relative.getresult()))
558 > ui.write(b'REL: %s\n' % b'\nREL: '.join(relative.getresult()))
559 >
559 >
560 > # import modules from external library
560 > # import modules from external library
561 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
561 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
562 > from extlibroot.lsub1.lsub2.called import func as lfunc
562 > from extlibroot.lsub1.lsub2.called import func as lfunc
563 > from extlibroot.recursedown import absdetail, legacydetail
563 > from extlibroot.recursedown import absdetail, legacydetail
564 > from extlibroot.shadowing import proxied
564 > from extlibroot.shadowing import proxied
565 >
565 >
566 > def uisetup(ui):
566 > def uisetup(ui):
567 > result = []
567 > result = []
568 > result.append(lused.detail)
568 > result.append(lused.detail)
569 > result.append(lfunc())
569 > result.append(lfunc())
570 > result.append(absdetail)
570 > result.append(absdetail)
571 > result.append(legacydetail)
571 > result.append(legacydetail)
572 > result.append(proxied.detail)
572 > result.append(proxied.detail)
573 > ui.write(b'LIB: %s\n' % b'\nLIB: '.join(result))
573 > ui.write(b'LIB: %s\n' % b'\nLIB: '.join(result))
574 > NO_CHECK_EOF
574 > NO_CHECK_EOF
575
575
576 Examine module importing.
576 Examine module importing.
577
577
578 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
578 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
579 LIB: this is extlibroot.lsub1.lsub2.used
579 LIB: this is extlibroot.lsub1.lsub2.used
580 LIB: this is extlibroot.lsub1.lsub2.called.func()
580 LIB: this is extlibroot.lsub1.lsub2.called.func()
581 LIB: this is extlibroot.recursedown.abs.used
581 LIB: this is extlibroot.recursedown.abs.used
582 LIB: this is extlibroot.recursedown.legacy.used
582 LIB: this is extlibroot.recursedown.legacy.used
583 LIB: this is extlibroot.shadowing.used
583 LIB: this is extlibroot.shadowing.used
584 ABS: this is absextroot.xsub1.xsub2.used
584 ABS: this is absextroot.xsub1.xsub2.used
585 ABS: this is absextroot.xsub1.xsub2.called.func()
585 ABS: this is absextroot.xsub1.xsub2.called.func()
586
586
587 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
587 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
588 LIB: this is extlibroot.lsub1.lsub2.used
588 LIB: this is extlibroot.lsub1.lsub2.used
589 LIB: this is extlibroot.lsub1.lsub2.called.func()
589 LIB: this is extlibroot.lsub1.lsub2.called.func()
590 LIB: this is extlibroot.recursedown.abs.used
590 LIB: this is extlibroot.recursedown.abs.used
591 LIB: this is extlibroot.recursedown.legacy.used
591 LIB: this is extlibroot.recursedown.legacy.used
592 LIB: this is extlibroot.shadowing.used
592 LIB: this is extlibroot.shadowing.used
593 REL: this is absextroot.xsub1.xsub2.used
593 REL: this is absextroot.xsub1.xsub2.used
594 REL: this is absextroot.xsub1.xsub2.called.func()
594 REL: this is absextroot.xsub1.xsub2.called.func()
595 REL: this relimporter imports 'this is absextroot.relimportee'
595 REL: this relimporter imports 'this is absextroot.relimportee'
596
596
597 Examine whether sub-module is imported relatively as expected.
597 Examine whether sub-module is imported relatively as expected.
598
598
599 See also issue5208 for detail about example case on Python 3.x.
599 See also issue5208 for detail about example case on Python 3.x.
600
600
601 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
601 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
602 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
602 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
603
603
604 $ cat > $TESTTMP/notexist.py <<NO_CHECK_EOF
604 $ cat > $TESTTMP/notexist.py <<NO_CHECK_EOF
605 > text = 'notexist.py at root is loaded unintentionally\n'
605 > text = 'notexist.py at root is loaded unintentionally\n'
606 > NO_CHECK_EOF
606 > NO_CHECK_EOF
607
607
608 $ cat > $TESTTMP/checkrelativity.py <<NO_CHECK_EOF
608 $ cat > $TESTTMP/checkrelativity.py <<NO_CHECK_EOF
609 > from mercurial import registrar
609 > from mercurial import registrar
610 > cmdtable = {}
610 > cmdtable = {}
611 > command = registrar.command(cmdtable)
611 > command = registrar.command(cmdtable)
612 >
612 >
613 > # demand import avoids failure of importing notexist here
613 > # demand import avoids failure of importing notexist here
614 > import extlibroot.lsub1.lsub2.notexist
614 > import extlibroot.lsub1.lsub2.notexist
615 >
615 >
616 > @command(b'checkrelativity', [], norepo=True)
616 > @command(b'checkrelativity', [], norepo=True)
617 > def checkrelativity(ui, *args, **opts):
617 > def checkrelativity(ui, *args, **opts):
618 > try:
618 > try:
619 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
619 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
620 > return 1 # unintentional success
620 > return 1 # unintentional success
621 > except ImportError:
621 > except ImportError:
622 > pass # intentional failure
622 > pass # intentional failure
623 > NO_CHECK_EOF
623 > NO_CHECK_EOF
624
624
625 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity)
625 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity)
626
626
627 #endif
627 #endif
628
628
629 (Here, module importing tests are finished. Therefore, use other than
629 (Here, module importing tests are finished. Therefore, use other than
630 NO_CHECK_* limit mark for heredoc python files, in order to apply
630 NO_CHECK_* limit mark for heredoc python files, in order to apply
631 import-checker.py or so on their contents)
631 import-checker.py or so on their contents)
632
632
633 Make sure a broken uisetup doesn't globally break hg:
633 Make sure a broken uisetup doesn't globally break hg:
634 $ cat > $TESTTMP/baduisetup.py <<EOF
634 $ cat > $TESTTMP/baduisetup.py <<EOF
635 > def uisetup(ui):
635 > def uisetup(ui):
636 > 1/0
636 > 1/0
637 > EOF
637 > EOF
638
638
639 Even though the extension fails during uisetup, hg is still basically usable:
639 Even though the extension fails during uisetup, hg is still basically usable:
640 $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version
640 $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version
641 Traceback (most recent call last):
641 Traceback (most recent call last):
642 File "*/mercurial/extensions.py", line *, in _runuisetup (glob)
642 File "*/mercurial/extensions.py", line *, in _runuisetup (glob)
643 uisetup(ui)
643 uisetup(ui)
644 File "$TESTTMP/baduisetup.py", line 2, in uisetup
644 File "$TESTTMP/baduisetup.py", line 2, in uisetup
645 1/0
645 1/0
646 ZeroDivisionError: * by zero (glob)
646 ZeroDivisionError: * by zero (glob)
647 *** failed to set up extension baduisetup: * by zero (glob)
647 *** failed to set up extension baduisetup: * by zero (glob)
648 Mercurial Distributed SCM (version *) (glob)
648 Mercurial Distributed SCM (version *) (glob)
649 (see https://mercurial-scm.org for more information)
649 (see https://mercurial-scm.org for more information)
650
650
651 Copyright (C) 2005-* Matt Mackall and others (glob)
651 Copyright (C) 2005-* Matt Mackall and others (glob)
652 This is free software; see the source for copying conditions. There is NO
652 This is free software; see the source for copying conditions. There is NO
653 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
653 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
654
654
655 $ cd ..
655 $ cd ..
656
656
657 hide outer repo
657 hide outer repo
658 $ hg init
658 $ hg init
659
659
660 $ cat > empty.py <<EOF
660 $ cat > empty.py <<EOF
661 > '''empty cmdtable
661 > '''empty cmdtable
662 > '''
662 > '''
663 > cmdtable = {}
663 > cmdtable = {}
664 > EOF
664 > EOF
665 $ emptypath=`pwd`/empty.py
665 $ emptypath=`pwd`/empty.py
666 $ echo "empty = $emptypath" >> $HGRCPATH
666 $ echo "empty = $emptypath" >> $HGRCPATH
667 $ hg help empty
667 $ hg help empty
668 empty extension - empty cmdtable
668 empty extension - empty cmdtable
669
669
670 no commands defined
670 no commands defined
671
671
672
672
673 $ echo 'empty = !' >> $HGRCPATH
673 $ echo 'empty = !' >> $HGRCPATH
674
674
675 $ cat > debugextension.py <<EOF
675 $ cat > debugextension.py <<EOF
676 > '''only debugcommands
676 > '''only debugcommands
677 > '''
677 > '''
678 > from mercurial import registrar
678 > from mercurial import registrar
679 > cmdtable = {}
679 > cmdtable = {}
680 > command = registrar.command(cmdtable)
680 > command = registrar.command(cmdtable)
681 > @command(b'debugfoobar', [], b'hg debugfoobar')
681 > @command(b'debugfoobar', [], b'hg debugfoobar')
682 > def debugfoobar(ui, repo, *args, **opts):
682 > def debugfoobar(ui, repo, *args, **opts):
683 > "yet another debug command"
683 > "yet another debug command"
684 > pass
684 > pass
685 > @command(b'foo', [], b'hg foo')
685 > @command(b'foo', [], b'hg foo')
686 > def foo(ui, repo, *args, **opts):
686 > def foo(ui, repo, *args, **opts):
687 > """yet another foo command
687 > """yet another foo command
688 > This command has been DEPRECATED since forever.
688 > This command has been DEPRECATED since forever.
689 > """
689 > """
690 > pass
690 > pass
691 > EOF
691 > EOF
692 $ debugpath=`pwd`/debugextension.py
692 $ debugpath=`pwd`/debugextension.py
693 $ echo "debugextension = $debugpath" >> $HGRCPATH
693 $ echo "debugextension = $debugpath" >> $HGRCPATH
694
694
695 $ hg help debugextension
695 $ hg help debugextension
696 hg debugextensions
696 hg debugextensions
697
697
698 show information about active extensions
698 show information about active extensions
699
699
700 options:
700 options:
701
701
702 -T --template TEMPLATE display with template
702 -T --template TEMPLATE display with template
703
703
704 (some details hidden, use --verbose to show complete help)
704 (some details hidden, use --verbose to show complete help)
705
705
706
706
707 $ hg --verbose help debugextension
707 $ hg --verbose help debugextension
708 hg debugextensions
708 hg debugextensions
709
709
710 show information about active extensions
710 show information about active extensions
711
711
712 options:
712 options:
713
713
714 -T --template TEMPLATE display with template
714 -T --template TEMPLATE display with template
715
715
716 global options ([+] can be repeated):
716 global options ([+] can be repeated):
717
717
718 -R --repository REPO repository root directory or name of overlay bundle
718 -R --repository REPO repository root directory or name of overlay bundle
719 file
719 file
720 --cwd DIR change working directory
720 --cwd DIR change working directory
721 -y --noninteractive do not prompt, automatically pick the first choice for
721 -y --noninteractive do not prompt, automatically pick the first choice for
722 all prompts
722 all prompts
723 -q --quiet suppress output
723 -q --quiet suppress output
724 -v --verbose enable additional output
724 -v --verbose enable additional output
725 --color TYPE when to colorize (boolean, always, auto, never, or
725 --color TYPE when to colorize (boolean, always, auto, never, or
726 debug)
726 debug)
727 --config CONFIG [+] set/override config option (use 'section.name=value')
727 --config CONFIG [+] set/override config option (use 'section.name=value')
728 --debug enable debugging output
728 --debug enable debugging output
729 --debugger start debugger
729 --debugger start debugger
730 --encoding ENCODE set the charset encoding (default: ascii)
730 --encoding ENCODE set the charset encoding (default: ascii)
731 --encodingmode MODE set the charset encoding mode (default: strict)
731 --encodingmode MODE set the charset encoding mode (default: strict)
732 --traceback always print a traceback on exception
732 --traceback always print a traceback on exception
733 --time time how long the command takes
733 --time time how long the command takes
734 --profile print command execution profile
734 --profile print command execution profile
735 --version output version information and exit
735 --version output version information and exit
736 -h --help display help and exit
736 -h --help display help and exit
737 --hidden consider hidden changesets
737 --hidden consider hidden changesets
738 --pager TYPE when to paginate (boolean, always, auto, or never)
738 --pager TYPE when to paginate (boolean, always, auto, or never)
739 (default: auto)
739 (default: auto)
740
740
741
741
742
742
743
743
744
744
745
745
746 $ hg --debug help debugextension
746 $ hg --debug help debugextension
747 hg debugextensions
747 hg debugextensions
748
748
749 show information about active extensions
749 show information about active extensions
750
750
751 options:
751 options:
752
752
753 -T --template TEMPLATE display with template
753 -T --template TEMPLATE display with template
754
754
755 global options ([+] can be repeated):
755 global options ([+] can be repeated):
756
756
757 -R --repository REPO repository root directory or name of overlay bundle
757 -R --repository REPO repository root directory or name of overlay bundle
758 file
758 file
759 --cwd DIR change working directory
759 --cwd DIR change working directory
760 -y --noninteractive do not prompt, automatically pick the first choice for
760 -y --noninteractive do not prompt, automatically pick the first choice for
761 all prompts
761 all prompts
762 -q --quiet suppress output
762 -q --quiet suppress output
763 -v --verbose enable additional output
763 -v --verbose enable additional output
764 --color TYPE when to colorize (boolean, always, auto, never, or
764 --color TYPE when to colorize (boolean, always, auto, never, or
765 debug)
765 debug)
766 --config CONFIG [+] set/override config option (use 'section.name=value')
766 --config CONFIG [+] set/override config option (use 'section.name=value')
767 --debug enable debugging output
767 --debug enable debugging output
768 --debugger start debugger
768 --debugger start debugger
769 --encoding ENCODE set the charset encoding (default: ascii)
769 --encoding ENCODE set the charset encoding (default: ascii)
770 --encodingmode MODE set the charset encoding mode (default: strict)
770 --encodingmode MODE set the charset encoding mode (default: strict)
771 --traceback always print a traceback on exception
771 --traceback always print a traceback on exception
772 --time time how long the command takes
772 --time time how long the command takes
773 --profile print command execution profile
773 --profile print command execution profile
774 --version output version information and exit
774 --version output version information and exit
775 -h --help display help and exit
775 -h --help display help and exit
776 --hidden consider hidden changesets
776 --hidden consider hidden changesets
777 --pager TYPE when to paginate (boolean, always, auto, or never)
777 --pager TYPE when to paginate (boolean, always, auto, or never)
778 (default: auto)
778 (default: auto)
779
779
780
780
781
781
782
782
783
783
784 $ echo 'debugextension = !' >> $HGRCPATH
784 $ echo 'debugextension = !' >> $HGRCPATH
785
785
786 Asking for help about a deprecated extension should do something useful:
786 Asking for help about a deprecated extension should do something useful:
787
787
788 $ hg help glog
788 $ hg help glog
789 'glog' is provided by the following extension:
789 'glog' is provided by the following extension:
790
790
791 graphlog command to view revision graphs from a shell (DEPRECATED)
791 graphlog command to view revision graphs from a shell (DEPRECATED)
792
792
793 (use 'hg help extensions' for information on enabling extensions)
793 (use 'hg help extensions' for information on enabling extensions)
794
794
795 Extension module help vs command help:
795 Extension module help vs command help:
796
796
797 $ echo 'extdiff =' >> $HGRCPATH
797 $ echo 'extdiff =' >> $HGRCPATH
798 $ hg help extdiff
798 $ hg help extdiff
799 hg extdiff [OPT]... [FILE]...
799 hg extdiff [OPT]... [FILE]...
800
800
801 use external program to diff repository (or selected files)
801 use external program to diff repository (or selected files)
802
802
803 Show differences between revisions for the specified files, using an
803 Show differences between revisions for the specified files, using an
804 external program. The default program used is diff, with default options
804 external program. The default program used is diff, with default options
805 "-Npru".
805 "-Npru".
806
806
807 To select a different program, use the -p/--program option. The program
807 To select a different program, use the -p/--program option. The program
808 will be passed the names of two directories to compare. To pass additional
808 will be passed the names of two directories to compare. To pass additional
809 options to the program, use -o/--option. These will be passed before the
809 options to the program, use -o/--option. These will be passed before the
810 names of the directories to compare.
810 names of the directories to compare.
811
811
812 When two revision arguments are given, then changes are shown between
812 When two revision arguments are given, then changes are shown between
813 those revisions. If only one revision is specified then that revision is
813 those revisions. If only one revision is specified then that revision is
814 compared to the working directory, and, when no revisions are specified,
814 compared to the working directory, and, when no revisions are specified,
815 the working directory files are compared to its parent.
815 the working directory files are compared to its parent.
816
816
817 (use 'hg help -e extdiff' to show help for the extdiff extension)
817 (use 'hg help -e extdiff' to show help for the extdiff extension)
818
818
819 options ([+] can be repeated):
819 options ([+] can be repeated):
820
820
821 -p --program CMD comparison program to run
821 -p --program CMD comparison program to run
822 -o --option OPT [+] pass option to comparison program
822 -o --option OPT [+] pass option to comparison program
823 -r --rev REV [+] revision
823 -r --rev REV [+] revision
824 -c --change REV change made by revision
824 -c --change REV change made by revision
825 --patch compare patches for two revisions
825 --patch compare patches for two revisions
826 -I --include PATTERN [+] include names matching the given patterns
826 -I --include PATTERN [+] include names matching the given patterns
827 -X --exclude PATTERN [+] exclude names matching the given patterns
827 -X --exclude PATTERN [+] exclude names matching the given patterns
828 -S --subrepos recurse into subrepositories
828 -S --subrepos recurse into subrepositories
829
829
830 (some details hidden, use --verbose to show complete help)
830 (some details hidden, use --verbose to show complete help)
831
831
832
832
833
833
834
834
835
835
836
836
837
837
838
838
839
839
840
840
841 $ hg help --extension extdiff
841 $ hg help --extension extdiff
842 extdiff extension - command to allow external programs to compare revisions
842 extdiff extension - command to allow external programs to compare revisions
843
843
844 The extdiff Mercurial extension allows you to use external programs to compare
844 The extdiff Mercurial extension allows you to use external programs to compare
845 revisions, or revision with working directory. The external diff programs are
845 revisions, or revision with working directory. The external diff programs are
846 called with a configurable set of options and two non-option arguments: paths
846 called with a configurable set of options and two non-option arguments: paths
847 to directories containing snapshots of files to compare.
847 to directories containing snapshots of files to compare.
848
848
849 If there is more than one file being compared and the "child" revision is the
849 If there is more than one file being compared and the "child" revision is the
850 working directory, any modifications made in the external diff program will be
850 working directory, any modifications made in the external diff program will be
851 copied back to the working directory from the temporary directory.
851 copied back to the working directory from the temporary directory.
852
852
853 The extdiff extension also allows you to configure new diff commands, so you
853 The extdiff extension also allows you to configure new diff commands, so you
854 do not need to type 'hg extdiff -p kdiff3' always.
854 do not need to type 'hg extdiff -p kdiff3' always.
855
855
856 [extdiff]
856 [extdiff]
857 # add new command that runs GNU diff(1) in 'context diff' mode
857 # add new command that runs GNU diff(1) in 'context diff' mode
858 cdiff = gdiff -Nprc5
858 cdiff = gdiff -Nprc5
859 ## or the old way:
859 ## or the old way:
860 #cmd.cdiff = gdiff
860 #cmd.cdiff = gdiff
861 #opts.cdiff = -Nprc5
861 #opts.cdiff = -Nprc5
862
862
863 # add new command called meld, runs meld (no need to name twice). If
863 # add new command called meld, runs meld (no need to name twice). If
864 # the meld executable is not available, the meld tool in [merge-tools]
864 # the meld executable is not available, the meld tool in [merge-tools]
865 # will be used, if available
865 # will be used, if available
866 meld =
866 meld =
867
867
868 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
868 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
869 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
869 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
870 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
870 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
871 # your .vimrc
871 # your .vimrc
872 vimdiff = gvim -f "+next" \
872 vimdiff = gvim -f "+next" \
873 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
873 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
874
874
875 Tool arguments can include variables that are expanded at runtime:
875 Tool arguments can include variables that are expanded at runtime:
876
876
877 $parent1, $plabel1 - filename, descriptive label of first parent
877 $parent1, $plabel1 - filename, descriptive label of first parent
878 $child, $clabel - filename, descriptive label of child revision
878 $child, $clabel - filename, descriptive label of child revision
879 $parent2, $plabel2 - filename, descriptive label of second parent
879 $parent2, $plabel2 - filename, descriptive label of second parent
880 $root - repository root
880 $root - repository root
881 $parent is an alias for $parent1.
881 $parent is an alias for $parent1.
882
882
883 The extdiff extension will look in your [diff-tools] and [merge-tools]
883 The extdiff extension will look in your [diff-tools] and [merge-tools]
884 sections for diff tool arguments, when none are specified in [extdiff].
884 sections for diff tool arguments, when none are specified in [extdiff].
885
885
886 [extdiff]
886 [extdiff]
887 kdiff3 =
887 kdiff3 =
888
888
889 [diff-tools]
889 [diff-tools]
890 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
890 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
891
891
892 You can use -I/-X and list of file or directory names like normal 'hg diff'
892 You can use -I/-X and list of file or directory names like normal 'hg diff'
893 command. The extdiff extension makes snapshots of only needed files, so
893 command. The extdiff extension makes snapshots of only needed files, so
894 running the external diff program will actually be pretty fast (at least
894 running the external diff program will actually be pretty fast (at least
895 faster than having to compare the entire tree).
895 faster than having to compare the entire tree).
896
896
897 list of commands:
897 list of commands:
898
898
899 extdiff use external program to diff repository (or selected files)
899 extdiff use external program to diff repository (or selected files)
900
900
901 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
901 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
902
902
903
903
904
904
905
905
906
906
907
907
908
908
909
909
910
910
911
911
912
912
913
913
914
914
915
915
916
916
917
917
918 $ echo 'extdiff = !' >> $HGRCPATH
918 $ echo 'extdiff = !' >> $HGRCPATH
919
919
920 Test help topic with same name as extension
920 Test help topic with same name as extension
921
921
922 $ cat > multirevs.py <<EOF
922 $ cat > multirevs.py <<EOF
923 > from mercurial import commands, registrar
923 > from mercurial import commands, registrar
924 > cmdtable = {}
924 > cmdtable = {}
925 > command = registrar.command(cmdtable)
925 > command = registrar.command(cmdtable)
926 > """multirevs extension
926 > """multirevs extension
927 > Big multi-line module docstring."""
927 > Big multi-line module docstring."""
928 > @command(b'multirevs', [], b'ARG', norepo=True)
928 > @command(b'multirevs', [], b'ARG', norepo=True)
929 > def multirevs(ui, repo, arg, *args, **opts):
929 > def multirevs(ui, repo, arg, *args, **opts):
930 > """multirevs command"""
930 > """multirevs command"""
931 > pass
931 > pass
932 > EOF
932 > EOF
933 $ echo "multirevs = multirevs.py" >> $HGRCPATH
933 $ echo "multirevs = multirevs.py" >> $HGRCPATH
934
934
935 $ hg help multirevs | tail
935 $ hg help multirevs | tail
936 used):
936 used):
937
937
938 hg update :@
938 hg update :@
939
939
940 - Show diff between tags 1.3 and 1.5 (this works because the first and the
940 - Show diff between tags 1.3 and 1.5 (this works because the first and the
941 last revisions of the revset are used):
941 last revisions of the revset are used):
942
942
943 hg diff -r 1.3::1.5
943 hg diff -r 1.3::1.5
944
944
945 use 'hg help -c multirevs' to see help for the multirevs command
945 use 'hg help -c multirevs' to see help for the multirevs command
946
946
947
947
948
948
949
949
950
950
951
951
952 $ hg help -c multirevs
952 $ hg help -c multirevs
953 hg multirevs ARG
953 hg multirevs ARG
954
954
955 multirevs command
955 multirevs command
956
956
957 (some details hidden, use --verbose to show complete help)
957 (some details hidden, use --verbose to show complete help)
958
958
959
959
960
960
961 $ hg multirevs
961 $ hg multirevs
962 hg multirevs: invalid arguments
962 hg multirevs: invalid arguments
963 hg multirevs ARG
963 hg multirevs ARG
964
964
965 multirevs command
965 multirevs command
966
966
967 (use 'hg multirevs -h' to show more help)
967 (use 'hg multirevs -h' to show more help)
968 [255]
968 [255]
969
969
970
970
971
971
972 $ echo "multirevs = !" >> $HGRCPATH
972 $ echo "multirevs = !" >> $HGRCPATH
973
973
974 Issue811: Problem loading extensions twice (by site and by user)
974 Issue811: Problem loading extensions twice (by site and by user)
975
975
976 $ cat <<EOF >> $HGRCPATH
976 $ cat <<EOF >> $HGRCPATH
977 > mq =
977 > mq =
978 > strip =
978 > strip =
979 > hgext.mq =
979 > hgext.mq =
980 > hgext/mq =
980 > hgext/mq =
981 > EOF
981 > EOF
982
982
983 Show extensions:
983 Show extensions:
984 (note that mq force load strip, also checking it's not loaded twice)
984 (note that mq force load strip, also checking it's not loaded twice)
985
985
986 #if no-extraextensions
986 #if no-extraextensions
987 $ hg debugextensions
987 $ hg debugextensions
988 mq
988 mq
989 strip
989 strip
990 #endif
990 #endif
991
991
992 For extensions, which name matches one of its commands, help
992 For extensions, which name matches one of its commands, help
993 message should ask '-v -e' to get list of built-in aliases
993 message should ask '-v -e' to get list of built-in aliases
994 along with extension help itself
994 along with extension help itself
995
995
996 $ mkdir $TESTTMP/d
996 $ mkdir $TESTTMP/d
997 $ cat > $TESTTMP/d/dodo.py <<EOF
997 $ cat > $TESTTMP/d/dodo.py <<EOF
998 > """
998 > """
999 > This is an awesome 'dodo' extension. It does nothing and
999 > This is an awesome 'dodo' extension. It does nothing and
1000 > writes 'Foo foo'
1000 > writes 'Foo foo'
1001 > """
1001 > """
1002 > from mercurial import commands, registrar
1002 > from mercurial import commands, registrar
1003 > cmdtable = {}
1003 > cmdtable = {}
1004 > command = registrar.command(cmdtable)
1004 > command = registrar.command(cmdtable)
1005 > @command(b'dodo', [], b'hg dodo')
1005 > @command(b'dodo', [], b'hg dodo')
1006 > def dodo(ui, *args, **kwargs):
1006 > def dodo(ui, *args, **kwargs):
1007 > """Does nothing"""
1007 > """Does nothing"""
1008 > ui.write(b"I do nothing. Yay\\n")
1008 > ui.write(b"I do nothing. Yay\\n")
1009 > @command(b'foofoo', [], b'hg foofoo')
1009 > @command(b'foofoo', [], b'hg foofoo')
1010 > def foofoo(ui, *args, **kwargs):
1010 > def foofoo(ui, *args, **kwargs):
1011 > """Writes 'Foo foo'"""
1011 > """Writes 'Foo foo'"""
1012 > ui.write(b"Foo foo\\n")
1012 > ui.write(b"Foo foo\\n")
1013 > EOF
1013 > EOF
1014 $ dodopath=$TESTTMP/d/dodo.py
1014 $ dodopath=$TESTTMP/d/dodo.py
1015
1015
1016 $ echo "dodo = $dodopath" >> $HGRCPATH
1016 $ echo "dodo = $dodopath" >> $HGRCPATH
1017
1017
1018 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
1018 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
1019 $ hg help -e dodo
1019 $ hg help -e dodo
1020 dodo extension -
1020 dodo extension -
1021
1021
1022 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
1022 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
1023
1023
1024 list of commands:
1024 list of commands:
1025
1025
1026 dodo Does nothing
1026 dodo Does nothing
1027 foofoo Writes 'Foo foo'
1027 foofoo Writes 'Foo foo'
1028
1028
1029 (use 'hg help -v -e dodo' to show built-in aliases and global options)
1029 (use 'hg help -v -e dodo' to show built-in aliases and global options)
1030
1030
1031 Make sure that '-v -e' prints list of built-in aliases along with
1031 Make sure that '-v -e' prints list of built-in aliases along with
1032 extension help itself
1032 extension help itself
1033 $ hg help -v -e dodo
1033 $ hg help -v -e dodo
1034 dodo extension -
1034 dodo extension -
1035
1035
1036 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
1036 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
1037
1037
1038 list of commands:
1038 list of commands:
1039
1039
1040 dodo Does nothing
1040 dodo Does nothing
1041 foofoo Writes 'Foo foo'
1041 foofoo Writes 'Foo foo'
1042
1042
1043 global options ([+] can be repeated):
1043 global options ([+] can be repeated):
1044
1044
1045 -R --repository REPO repository root directory or name of overlay bundle
1045 -R --repository REPO repository root directory or name of overlay bundle
1046 file
1046 file
1047 --cwd DIR change working directory
1047 --cwd DIR change working directory
1048 -y --noninteractive do not prompt, automatically pick the first choice for
1048 -y --noninteractive do not prompt, automatically pick the first choice for
1049 all prompts
1049 all prompts
1050 -q --quiet suppress output
1050 -q --quiet suppress output
1051 -v --verbose enable additional output
1051 -v --verbose enable additional output
1052 --color TYPE when to colorize (boolean, always, auto, never, or
1052 --color TYPE when to colorize (boolean, always, auto, never, or
1053 debug)
1053 debug)
1054 --config CONFIG [+] set/override config option (use 'section.name=value')
1054 --config CONFIG [+] set/override config option (use 'section.name=value')
1055 --debug enable debugging output
1055 --debug enable debugging output
1056 --debugger start debugger
1056 --debugger start debugger
1057 --encoding ENCODE set the charset encoding (default: ascii)
1057 --encoding ENCODE set the charset encoding (default: ascii)
1058 --encodingmode MODE set the charset encoding mode (default: strict)
1058 --encodingmode MODE set the charset encoding mode (default: strict)
1059 --traceback always print a traceback on exception
1059 --traceback always print a traceback on exception
1060 --time time how long the command takes
1060 --time time how long the command takes
1061 --profile print command execution profile
1061 --profile print command execution profile
1062 --version output version information and exit
1062 --version output version information and exit
1063 -h --help display help and exit
1063 -h --help display help and exit
1064 --hidden consider hidden changesets
1064 --hidden consider hidden changesets
1065 --pager TYPE when to paginate (boolean, always, auto, or never)
1065 --pager TYPE when to paginate (boolean, always, auto, or never)
1066 (default: auto)
1066 (default: auto)
1067
1067
1068 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
1068 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
1069 $ hg help -v dodo
1069 $ hg help -v dodo
1070 hg dodo
1070 hg dodo
1071
1071
1072 Does nothing
1072 Does nothing
1073
1073
1074 (use 'hg help -e dodo' to show help for the dodo extension)
1074 (use 'hg help -e dodo' to show help for the dodo extension)
1075
1075
1076 options:
1076 options:
1077
1077
1078 --mq operate on patch repository
1078 --mq operate on patch repository
1079
1079
1080 global options ([+] can be repeated):
1080 global options ([+] can be repeated):
1081
1081
1082 -R --repository REPO repository root directory or name of overlay bundle
1082 -R --repository REPO repository root directory or name of overlay bundle
1083 file
1083 file
1084 --cwd DIR change working directory
1084 --cwd DIR change working directory
1085 -y --noninteractive do not prompt, automatically pick the first choice for
1085 -y --noninteractive do not prompt, automatically pick the first choice for
1086 all prompts
1086 all prompts
1087 -q --quiet suppress output
1087 -q --quiet suppress output
1088 -v --verbose enable additional output
1088 -v --verbose enable additional output
1089 --color TYPE when to colorize (boolean, always, auto, never, or
1089 --color TYPE when to colorize (boolean, always, auto, never, or
1090 debug)
1090 debug)
1091 --config CONFIG [+] set/override config option (use 'section.name=value')
1091 --config CONFIG [+] set/override config option (use 'section.name=value')
1092 --debug enable debugging output
1092 --debug enable debugging output
1093 --debugger start debugger
1093 --debugger start debugger
1094 --encoding ENCODE set the charset encoding (default: ascii)
1094 --encoding ENCODE set the charset encoding (default: ascii)
1095 --encodingmode MODE set the charset encoding mode (default: strict)
1095 --encodingmode MODE set the charset encoding mode (default: strict)
1096 --traceback always print a traceback on exception
1096 --traceback always print a traceback on exception
1097 --time time how long the command takes
1097 --time time how long the command takes
1098 --profile print command execution profile
1098 --profile print command execution profile
1099 --version output version information and exit
1099 --version output version information and exit
1100 -h --help display help and exit
1100 -h --help display help and exit
1101 --hidden consider hidden changesets
1101 --hidden consider hidden changesets
1102 --pager TYPE when to paginate (boolean, always, auto, or never)
1102 --pager TYPE when to paginate (boolean, always, auto, or never)
1103 (default: auto)
1103 (default: auto)
1104
1104
1105 In case when extension name doesn't match any of its commands,
1105 In case when extension name doesn't match any of its commands,
1106 help message should ask for '-v' to get list of built-in aliases
1106 help message should ask for '-v' to get list of built-in aliases
1107 along with extension help
1107 along with extension help
1108 $ cat > $TESTTMP/d/dudu.py <<EOF
1108 $ cat > $TESTTMP/d/dudu.py <<EOF
1109 > """
1109 > """
1110 > This is an awesome 'dudu' extension. It does something and
1110 > This is an awesome 'dudu' extension. It does something and
1111 > also writes 'Beep beep'
1111 > also writes 'Beep beep'
1112 > """
1112 > """
1113 > from mercurial import commands, registrar
1113 > from mercurial import commands, registrar
1114 > cmdtable = {}
1114 > cmdtable = {}
1115 > command = registrar.command(cmdtable)
1115 > command = registrar.command(cmdtable)
1116 > @command(b'something', [], b'hg something')
1116 > @command(b'something', [], b'hg something')
1117 > def something(ui, *args, **kwargs):
1117 > def something(ui, *args, **kwargs):
1118 > """Does something"""
1118 > """Does something"""
1119 > ui.write(b"I do something. Yaaay\\n")
1119 > ui.write(b"I do something. Yaaay\\n")
1120 > @command(b'beep', [], b'hg beep')
1120 > @command(b'beep', [], b'hg beep')
1121 > def beep(ui, *args, **kwargs):
1121 > def beep(ui, *args, **kwargs):
1122 > """Writes 'Beep beep'"""
1122 > """Writes 'Beep beep'"""
1123 > ui.write(b"Beep beep\\n")
1123 > ui.write(b"Beep beep\\n")
1124 > EOF
1124 > EOF
1125 $ dudupath=$TESTTMP/d/dudu.py
1125 $ dudupath=$TESTTMP/d/dudu.py
1126
1126
1127 $ echo "dudu = $dudupath" >> $HGRCPATH
1127 $ echo "dudu = $dudupath" >> $HGRCPATH
1128
1128
1129 $ hg help -e dudu
1129 $ hg help -e dudu
1130 dudu extension -
1130 dudu extension -
1131
1131
1132 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1132 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1133 beep'
1133 beep'
1134
1134
1135 list of commands:
1135 list of commands:
1136
1136
1137 beep Writes 'Beep beep'
1137 beep Writes 'Beep beep'
1138 something Does something
1138 something Does something
1139
1139
1140 (use 'hg help -v dudu' to show built-in aliases and global options)
1140 (use 'hg help -v dudu' to show built-in aliases and global options)
1141
1141
1142 In case when extension name doesn't match any of its commands,
1142 In case when extension name doesn't match any of its commands,
1143 help options '-v' and '-v -e' should be equivalent
1143 help options '-v' and '-v -e' should be equivalent
1144 $ hg help -v dudu
1144 $ hg help -v dudu
1145 dudu extension -
1145 dudu extension -
1146
1146
1147 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1147 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1148 beep'
1148 beep'
1149
1149
1150 list of commands:
1150 list of commands:
1151
1151
1152 beep Writes 'Beep beep'
1152 beep Writes 'Beep beep'
1153 something Does something
1153 something Does something
1154
1154
1155 global options ([+] can be repeated):
1155 global options ([+] can be repeated):
1156
1156
1157 -R --repository REPO repository root directory or name of overlay bundle
1157 -R --repository REPO repository root directory or name of overlay bundle
1158 file
1158 file
1159 --cwd DIR change working directory
1159 --cwd DIR change working directory
1160 -y --noninteractive do not prompt, automatically pick the first choice for
1160 -y --noninteractive do not prompt, automatically pick the first choice for
1161 all prompts
1161 all prompts
1162 -q --quiet suppress output
1162 -q --quiet suppress output
1163 -v --verbose enable additional output
1163 -v --verbose enable additional output
1164 --color TYPE when to colorize (boolean, always, auto, never, or
1164 --color TYPE when to colorize (boolean, always, auto, never, or
1165 debug)
1165 debug)
1166 --config CONFIG [+] set/override config option (use 'section.name=value')
1166 --config CONFIG [+] set/override config option (use 'section.name=value')
1167 --debug enable debugging output
1167 --debug enable debugging output
1168 --debugger start debugger
1168 --debugger start debugger
1169 --encoding ENCODE set the charset encoding (default: ascii)
1169 --encoding ENCODE set the charset encoding (default: ascii)
1170 --encodingmode MODE set the charset encoding mode (default: strict)
1170 --encodingmode MODE set the charset encoding mode (default: strict)
1171 --traceback always print a traceback on exception
1171 --traceback always print a traceback on exception
1172 --time time how long the command takes
1172 --time time how long the command takes
1173 --profile print command execution profile
1173 --profile print command execution profile
1174 --version output version information and exit
1174 --version output version information and exit
1175 -h --help display help and exit
1175 -h --help display help and exit
1176 --hidden consider hidden changesets
1176 --hidden consider hidden changesets
1177 --pager TYPE when to paginate (boolean, always, auto, or never)
1177 --pager TYPE when to paginate (boolean, always, auto, or never)
1178 (default: auto)
1178 (default: auto)
1179
1179
1180 $ hg help -v -e dudu
1180 $ hg help -v -e dudu
1181 dudu extension -
1181 dudu extension -
1182
1182
1183 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1183 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1184 beep'
1184 beep'
1185
1185
1186 list of commands:
1186 list of commands:
1187
1187
1188 beep Writes 'Beep beep'
1188 beep Writes 'Beep beep'
1189 something Does something
1189 something Does something
1190
1190
1191 global options ([+] can be repeated):
1191 global options ([+] can be repeated):
1192
1192
1193 -R --repository REPO repository root directory or name of overlay bundle
1193 -R --repository REPO repository root directory or name of overlay bundle
1194 file
1194 file
1195 --cwd DIR change working directory
1195 --cwd DIR change working directory
1196 -y --noninteractive do not prompt, automatically pick the first choice for
1196 -y --noninteractive do not prompt, automatically pick the first choice for
1197 all prompts
1197 all prompts
1198 -q --quiet suppress output
1198 -q --quiet suppress output
1199 -v --verbose enable additional output
1199 -v --verbose enable additional output
1200 --color TYPE when to colorize (boolean, always, auto, never, or
1200 --color TYPE when to colorize (boolean, always, auto, never, or
1201 debug)
1201 debug)
1202 --config CONFIG [+] set/override config option (use 'section.name=value')
1202 --config CONFIG [+] set/override config option (use 'section.name=value')
1203 --debug enable debugging output
1203 --debug enable debugging output
1204 --debugger start debugger
1204 --debugger start debugger
1205 --encoding ENCODE set the charset encoding (default: ascii)
1205 --encoding ENCODE set the charset encoding (default: ascii)
1206 --encodingmode MODE set the charset encoding mode (default: strict)
1206 --encodingmode MODE set the charset encoding mode (default: strict)
1207 --traceback always print a traceback on exception
1207 --traceback always print a traceback on exception
1208 --time time how long the command takes
1208 --time time how long the command takes
1209 --profile print command execution profile
1209 --profile print command execution profile
1210 --version output version information and exit
1210 --version output version information and exit
1211 -h --help display help and exit
1211 -h --help display help and exit
1212 --hidden consider hidden changesets
1212 --hidden consider hidden changesets
1213 --pager TYPE when to paginate (boolean, always, auto, or never)
1213 --pager TYPE when to paginate (boolean, always, auto, or never)
1214 (default: auto)
1214 (default: auto)
1215
1215
1216 Disabled extension commands:
1216 Disabled extension commands:
1217
1217
1218 $ ORGHGRCPATH=$HGRCPATH
1218 $ ORGHGRCPATH=$HGRCPATH
1219 $ HGRCPATH=
1219 $ HGRCPATH=
1220 $ export HGRCPATH
1220 $ export HGRCPATH
1221 $ hg help email
1221 $ hg help email
1222 'email' is provided by the following extension:
1222 'email' is provided by the following extension:
1223
1223
1224 patchbomb command to send changesets as (a series of) patch emails
1224 patchbomb command to send changesets as (a series of) patch emails
1225
1225
1226 (use 'hg help extensions' for information on enabling extensions)
1226 (use 'hg help extensions' for information on enabling extensions)
1227
1227
1228
1228
1229 $ hg qdel
1229 $ hg qdel
1230 hg: unknown command 'qdel'
1230 hg: unknown command 'qdel'
1231 'qdelete' is provided by the following extension:
1231 'qdelete' is provided by the following extension:
1232
1232
1233 mq manage a stack of patches
1233 mq manage a stack of patches
1234
1234
1235 (use 'hg help extensions' for information on enabling extensions)
1235 (use 'hg help extensions' for information on enabling extensions)
1236 [255]
1236 [255]
1237
1237
1238
1238
1239 $ hg churn
1239 $ hg churn
1240 hg: unknown command 'churn'
1240 hg: unknown command 'churn'
1241 'churn' is provided by the following extension:
1241 'churn' is provided by the following extension:
1242
1242
1243 churn command to display statistics about repository history
1243 churn command to display statistics about repository history
1244
1244
1245 (use 'hg help extensions' for information on enabling extensions)
1245 (use 'hg help extensions' for information on enabling extensions)
1246 [255]
1246 [255]
1247
1247
1248
1248
1249
1249
1250 Disabled extensions:
1250 Disabled extensions:
1251
1251
1252 $ hg help churn
1252 $ hg help churn
1253 churn extension - command to display statistics about repository history
1253 churn extension - command to display statistics about repository history
1254
1254
1255 (use 'hg help extensions' for information on enabling extensions)
1255 (use 'hg help extensions' for information on enabling extensions)
1256
1256
1257 $ hg help patchbomb
1257 $ hg help patchbomb
1258 patchbomb extension - command to send changesets as (a series of) patch emails
1258 patchbomb extension - command to send changesets as (a series of) patch emails
1259
1259
1260 The series is started off with a "[PATCH 0 of N]" introduction, which
1260 The series is started off with a "[PATCH 0 of N]" introduction, which
1261 describes the series as a whole.
1261 describes the series as a whole.
1262
1262
1263 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1263 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1264 line of the changeset description as the subject text. The message contains
1264 line of the changeset description as the subject text. The message contains
1265 two or three body parts:
1265 two or three body parts:
1266
1266
1267 - The changeset description.
1267 - The changeset description.
1268 - [Optional] The result of running diffstat on the patch.
1268 - [Optional] The result of running diffstat on the patch.
1269 - The patch itself, as generated by 'hg export'.
1269 - The patch itself, as generated by 'hg export'.
1270
1270
1271 Each message refers to the first in the series using the In-Reply-To and
1271 Each message refers to the first in the series using the In-Reply-To and
1272 References headers, so they will show up as a sequence in threaded mail and
1272 References headers, so they will show up as a sequence in threaded mail and
1273 news readers, and in mail archives.
1273 news readers, and in mail archives.
1274
1274
1275 To configure other defaults, add a section like this to your configuration
1275 To configure other defaults, add a section like this to your configuration
1276 file:
1276 file:
1277
1277
1278 [email]
1278 [email]
1279 from = My Name <my@email>
1279 from = My Name <my@email>
1280 to = recipient1, recipient2, ...
1280 to = recipient1, recipient2, ...
1281 cc = cc1, cc2, ...
1281 cc = cc1, cc2, ...
1282 bcc = bcc1, bcc2, ...
1282 bcc = bcc1, bcc2, ...
1283 reply-to = address1, address2, ...
1283 reply-to = address1, address2, ...
1284
1284
1285 Use "[patchbomb]" as configuration section name if you need to override global
1285 Use "[patchbomb]" as configuration section name if you need to override global
1286 "[email]" address settings.
1286 "[email]" address settings.
1287
1287
1288 Then you can use the 'hg email' command to mail a series of changesets as a
1288 Then you can use the 'hg email' command to mail a series of changesets as a
1289 patchbomb.
1289 patchbomb.
1290
1290
1291 You can also either configure the method option in the email section to be a
1291 You can also either configure the method option in the email section to be a
1292 sendmail compatible mailer or fill out the [smtp] section so that the
1292 sendmail compatible mailer or fill out the [smtp] section so that the
1293 patchbomb extension can automatically send patchbombs directly from the
1293 patchbomb extension can automatically send patchbombs directly from the
1294 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1294 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1295
1295
1296 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1296 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1297 supply one via configuration or the command line. You can override this to
1297 supply one via configuration or the command line. You can override this to
1298 never prompt by configuring an empty value:
1298 never prompt by configuring an empty value:
1299
1299
1300 [email]
1300 [email]
1301 cc =
1301 cc =
1302
1302
1303 You can control the default inclusion of an introduction message with the
1303 You can control the default inclusion of an introduction message with the
1304 "patchbomb.intro" configuration option. The configuration is always
1304 "patchbomb.intro" configuration option. The configuration is always
1305 overwritten by command line flags like --intro and --desc:
1305 overwritten by command line flags like --intro and --desc:
1306
1306
1307 [patchbomb]
1307 [patchbomb]
1308 intro=auto # include introduction message if more than 1 patch (default)
1308 intro=auto # include introduction message if more than 1 patch (default)
1309 intro=never # never include an introduction message
1309 intro=never # never include an introduction message
1310 intro=always # always include an introduction message
1310 intro=always # always include an introduction message
1311
1311
1312 You can specify a template for flags to be added in subject prefixes. Flags
1312 You can specify a template for flags to be added in subject prefixes. Flags
1313 specified by --flag option are exported as "{flags}" keyword:
1313 specified by --flag option are exported as "{flags}" keyword:
1314
1314
1315 [patchbomb]
1315 [patchbomb]
1316 flagtemplate = "{separate(' ',
1316 flagtemplate = "{separate(' ',
1317 ifeq(branch, 'default', '', branch|upper),
1317 ifeq(branch, 'default', '', branch|upper),
1318 flags)}"
1318 flags)}"
1319
1319
1320 You can set patchbomb to always ask for confirmation by setting
1320 You can set patchbomb to always ask for confirmation by setting
1321 "patchbomb.confirm" to true.
1321 "patchbomb.confirm" to true.
1322
1322
1323 (use 'hg help extensions' for information on enabling extensions)
1323 (use 'hg help extensions' for information on enabling extensions)
1324
1324
1325
1325
1326 Broken disabled extension and command:
1326 Broken disabled extension and command:
1327
1327
1328 $ mkdir hgext
1328 $ mkdir hgext
1329 $ echo > hgext/__init__.py
1329 $ echo > hgext/__init__.py
1330 $ cat > hgext/broken.py <<NO_CHECK_EOF
1330 $ cat > hgext/broken.py <<NO_CHECK_EOF
1331 > "broken extension'
1331 > "broken extension'
1332 > NO_CHECK_EOF
1332 > NO_CHECK_EOF
1333 $ cat > path.py <<EOF
1333 $ cat > path.py <<EOF
1334 > import os
1334 > import os
1335 > import sys
1335 > import sys
1336 > sys.path.insert(0, os.environ['HGEXTPATH'])
1336 > sys.path.insert(0, os.environ['HGEXTPATH'])
1337 > EOF
1337 > EOF
1338 $ HGEXTPATH=`pwd`
1338 $ HGEXTPATH=`pwd`
1339 $ export HGEXTPATH
1339 $ export HGEXTPATH
1340
1340
1341 $ hg --config extensions.path=./path.py help broken
1341 $ hg --config extensions.path=./path.py help broken
1342 broken extension - (no help text available)
1342 broken extension - (no help text available)
1343
1343
1344 (use 'hg help extensions' for information on enabling extensions)
1344 (use 'hg help extensions' for information on enabling extensions)
1345
1345
1346
1346
1347 $ cat > hgext/forest.py <<EOF
1347 $ cat > hgext/forest.py <<EOF
1348 > cmdtable = None
1348 > cmdtable = None
1349 > @command()
1349 > @command()
1350 > def f():
1350 > def f():
1351 > pass
1351 > pass
1352 > @command(123)
1352 > @command(123)
1353 > def g():
1353 > def g():
1354 > pass
1354 > pass
1355 > EOF
1355 > EOF
1356 $ hg --config extensions.path=./path.py help foo
1356 $ hg --config extensions.path=./path.py help foo
1357 abort: no such help topic: foo
1357 abort: no such help topic: foo
1358 (try 'hg help --keyword foo')
1358 (try 'hg help --keyword foo')
1359 [255]
1359 [255]
1360
1360
1361 $ cat > throw.py <<EOF
1361 $ cat > throw.py <<EOF
1362 > from mercurial import commands, registrar, util
1362 > from mercurial import commands, registrar, util
1363 > cmdtable = {}
1363 > cmdtable = {}
1364 > command = registrar.command(cmdtable)
1364 > command = registrar.command(cmdtable)
1365 > class Bogon(Exception): pass
1365 > class Bogon(Exception): pass
1366 > @command(b'throw', [], b'hg throw', norepo=True)
1366 > @command(b'throw', [], b'hg throw', norepo=True)
1367 > def throw(ui, **opts):
1367 > def throw(ui, **opts):
1368 > """throws an exception"""
1368 > """throws an exception"""
1369 > raise Bogon()
1369 > raise Bogon()
1370 > EOF
1370 > EOF
1371
1371
1372 No declared supported version, extension complains:
1372 No declared supported version, extension complains:
1373 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1373 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1374 ** Unknown exception encountered with possibly-broken third-party extension throw
1374 ** Unknown exception encountered with possibly-broken third-party extension throw
1375 ** which supports versions unknown of Mercurial.
1375 ** which supports versions unknown of Mercurial.
1376 ** Please disable throw and try your action again.
1376 ** Please disable throw and try your action again.
1377 ** If that fixes the bug please report it to the extension author.
1377 ** If that fixes the bug please report it to the extension author.
1378 ** Python * (glob)
1378 ** Python * (glob)
1379 ** Mercurial Distributed SCM * (glob)
1379 ** Mercurial Distributed SCM * (glob)
1380 ** Extensions loaded: throw
1380 ** Extensions loaded: throw
1381
1381
1382 empty declaration of supported version, extension complains:
1382 empty declaration of supported version, extension complains:
1383 $ echo "testedwith = ''" >> throw.py
1383 $ echo "testedwith = ''" >> throw.py
1384 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1384 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1385 ** Unknown exception encountered with possibly-broken third-party extension throw
1385 ** Unknown exception encountered with possibly-broken third-party extension throw
1386 ** which supports versions unknown of Mercurial.
1386 ** which supports versions unknown of Mercurial.
1387 ** Please disable throw and try your action again.
1387 ** Please disable throw and try your action again.
1388 ** If that fixes the bug please report it to the extension author.
1388 ** If that fixes the bug please report it to the extension author.
1389 ** Python * (glob)
1389 ** Python * (glob)
1390 ** Mercurial Distributed SCM (*) (glob)
1390 ** Mercurial Distributed SCM (*) (glob)
1391 ** Extensions loaded: throw
1391 ** Extensions loaded: throw
1392
1392
1393 If the extension specifies a buglink, show that:
1393 If the extension specifies a buglink, show that:
1394 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1394 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1395 $ rm -f throw.pyc throw.pyo
1395 $ rm -f throw.pyc throw.pyo
1396 $ rm -Rf __pycache__
1396 $ rm -Rf __pycache__
1397 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1397 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1398 ** Unknown exception encountered with possibly-broken third-party extension throw
1398 ** Unknown exception encountered with possibly-broken third-party extension throw
1399 ** which supports versions unknown of Mercurial.
1399 ** which supports versions unknown of Mercurial.
1400 ** Please disable throw and try your action again.
1400 ** Please disable throw and try your action again.
1401 ** If that fixes the bug please report it to http://example.com/bts
1401 ** If that fixes the bug please report it to http://example.com/bts
1402 ** Python * (glob)
1402 ** Python * (glob)
1403 ** Mercurial Distributed SCM (*) (glob)
1403 ** Mercurial Distributed SCM (*) (glob)
1404 ** Extensions loaded: throw
1404 ** Extensions loaded: throw
1405
1405
1406 If the extensions declare outdated versions, accuse the older extension first:
1406 If the extensions declare outdated versions, accuse the older extension first:
1407 $ echo "from mercurial import util" >> older.py
1407 $ echo "from mercurial import util" >> older.py
1408 $ echo "util.version = lambda:b'2.2'" >> older.py
1408 $ echo "util.version = lambda:b'2.2'" >> older.py
1409 $ echo "testedwith = b'1.9.3'" >> older.py
1409 $ echo "testedwith = b'1.9.3'" >> older.py
1410 $ echo "testedwith = b'2.1.1'" >> throw.py
1410 $ echo "testedwith = b'2.1.1'" >> throw.py
1411 $ rm -f throw.pyc throw.pyo
1411 $ rm -f throw.pyc throw.pyo
1412 $ rm -Rf __pycache__
1412 $ rm -Rf __pycache__
1413 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1413 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1414 > throw 2>&1 | egrep '^\*\*'
1414 > throw 2>&1 | egrep '^\*\*'
1415 ** Unknown exception encountered with possibly-broken third-party extension older
1415 ** Unknown exception encountered with possibly-broken third-party extension older
1416 ** which supports versions 1.9 of Mercurial.
1416 ** which supports versions 1.9 of Mercurial.
1417 ** Please disable older and try your action again.
1417 ** Please disable older and try your action again.
1418 ** If that fixes the bug please report it to the extension author.
1418 ** If that fixes the bug please report it to the extension author.
1419 ** Python * (glob)
1419 ** Python * (glob)
1420 ** Mercurial Distributed SCM (version 2.2)
1420 ** Mercurial Distributed SCM (version 2.2)
1421 ** Extensions loaded: throw, older
1421 ** Extensions loaded: throw, older
1422
1422
1423 One extension only tested with older, one only with newer versions:
1423 One extension only tested with older, one only with newer versions:
1424 $ echo "util.version = lambda:b'2.1'" >> older.py
1424 $ echo "util.version = lambda:b'2.1'" >> older.py
1425 $ rm -f older.pyc older.pyo
1425 $ rm -f older.pyc older.pyo
1426 $ rm -Rf __pycache__
1426 $ rm -Rf __pycache__
1427 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1427 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1428 > throw 2>&1 | egrep '^\*\*'
1428 > throw 2>&1 | egrep '^\*\*'
1429 ** Unknown exception encountered with possibly-broken third-party extension older
1429 ** Unknown exception encountered with possibly-broken third-party extension older
1430 ** which supports versions 1.9 of Mercurial.
1430 ** which supports versions 1.9 of Mercurial.
1431 ** Please disable older and try your action again.
1431 ** Please disable older and try your action again.
1432 ** If that fixes the bug please report it to the extension author.
1432 ** If that fixes the bug please report it to the extension author.
1433 ** Python * (glob)
1433 ** Python * (glob)
1434 ** Mercurial Distributed SCM (version 2.1)
1434 ** Mercurial Distributed SCM (version 2.1)
1435 ** Extensions loaded: throw, older
1435 ** Extensions loaded: throw, older
1436
1436
1437 Older extension is tested with current version, the other only with newer:
1437 Older extension is tested with current version, the other only with newer:
1438 $ echo "util.version = lambda:b'1.9.3'" >> older.py
1438 $ echo "util.version = lambda:b'1.9.3'" >> older.py
1439 $ rm -f older.pyc older.pyo
1439 $ rm -f older.pyc older.pyo
1440 $ rm -Rf __pycache__
1440 $ rm -Rf __pycache__
1441 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1441 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1442 > throw 2>&1 | egrep '^\*\*'
1442 > throw 2>&1 | egrep '^\*\*'
1443 ** Unknown exception encountered with possibly-broken third-party extension throw
1443 ** Unknown exception encountered with possibly-broken third-party extension throw
1444 ** which supports versions 2.1 of Mercurial.
1444 ** which supports versions 2.1 of Mercurial.
1445 ** Please disable throw and try your action again.
1445 ** Please disable throw and try your action again.
1446 ** If that fixes the bug please report it to http://example.com/bts
1446 ** If that fixes the bug please report it to http://example.com/bts
1447 ** Python * (glob)
1447 ** Python * (glob)
1448 ** Mercurial Distributed SCM (version 1.9.3)
1448 ** Mercurial Distributed SCM (version 1.9.3)
1449 ** Extensions loaded: throw, older
1449 ** Extensions loaded: throw, older
1450
1450
1451 Ability to point to a different point
1451 Ability to point to a different point
1452 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1452 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1453 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1453 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1454 ** unknown exception encountered, please report by visiting
1454 ** unknown exception encountered, please report by visiting
1455 ** Your Local Goat Lenders
1455 ** Your Local Goat Lenders
1456 ** Python * (glob)
1456 ** Python * (glob)
1457 ** Mercurial Distributed SCM (*) (glob)
1457 ** Mercurial Distributed SCM (*) (glob)
1458 ** Extensions loaded: throw, older
1458 ** Extensions loaded: throw, older
1459
1459
1460 Declare the version as supporting this hg version, show regular bts link:
1460 Declare the version as supporting this hg version, show regular bts link:
1461 $ hgver=`hg debuginstall -T '{hgver}'`
1461 $ hgver=`hg debuginstall -T '{hgver}'`
1462 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1462 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1463 $ if [ -z "$hgver" ]; then
1463 $ if [ -z "$hgver" ]; then
1464 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1464 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1465 > fi
1465 > fi
1466 $ rm -f throw.pyc throw.pyo
1466 $ rm -f throw.pyc throw.pyo
1467 $ rm -Rf __pycache__
1467 $ rm -Rf __pycache__
1468 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1468 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1469 ** unknown exception encountered, please report by visiting
1469 ** unknown exception encountered, please report by visiting
1470 ** https://mercurial-scm.org/wiki/BugTracker
1470 ** https://mercurial-scm.org/wiki/BugTracker
1471 ** Python * (glob)
1471 ** Python * (glob)
1472 ** Mercurial Distributed SCM (*) (glob)
1472 ** Mercurial Distributed SCM (*) (glob)
1473 ** Extensions loaded: throw
1473 ** Extensions loaded: throw
1474
1474
1475 Patch version is ignored during compatibility check
1475 Patch version is ignored during compatibility check
1476 $ echo "testedwith = b'3.2'" >> throw.py
1476 $ echo "testedwith = b'3.2'" >> throw.py
1477 $ echo "util.version = lambda:b'3.2.2'" >> throw.py
1477 $ echo "util.version = lambda:b'3.2.2'" >> throw.py
1478 $ rm -f throw.pyc throw.pyo
1478 $ rm -f throw.pyc throw.pyo
1479 $ rm -Rf __pycache__
1479 $ rm -Rf __pycache__
1480 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1480 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1481 ** unknown exception encountered, please report by visiting
1481 ** unknown exception encountered, please report by visiting
1482 ** https://mercurial-scm.org/wiki/BugTracker
1482 ** https://mercurial-scm.org/wiki/BugTracker
1483 ** Python * (glob)
1483 ** Python * (glob)
1484 ** Mercurial Distributed SCM (*) (glob)
1484 ** Mercurial Distributed SCM (*) (glob)
1485 ** Extensions loaded: throw
1485 ** Extensions loaded: throw
1486
1486
1487 Test version number support in 'hg version':
1487 Test version number support in 'hg version':
1488 $ echo '__version__ = (1, 2, 3)' >> throw.py
1488 $ echo '__version__ = (1, 2, 3)' >> throw.py
1489 $ rm -f throw.pyc throw.pyo
1489 $ rm -f throw.pyc throw.pyo
1490 $ rm -Rf __pycache__
1490 $ rm -Rf __pycache__
1491 $ hg version -v
1491 $ hg version -v
1492 Mercurial Distributed SCM (version *) (glob)
1492 Mercurial Distributed SCM (version *) (glob)
1493 (see https://mercurial-scm.org for more information)
1493 (see https://mercurial-scm.org for more information)
1494
1494
1495 Copyright (C) 2005-* Matt Mackall and others (glob)
1495 Copyright (C) 2005-* Matt Mackall and others (glob)
1496 This is free software; see the source for copying conditions. There is NO
1496 This is free software; see the source for copying conditions. There is NO
1497 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1497 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1498
1498
1499 Enabled extensions:
1499 Enabled extensions:
1500
1500
1501
1501
1502 $ hg version -v --config extensions.throw=throw.py
1502 $ hg version -v --config extensions.throw=throw.py
1503 Mercurial Distributed SCM (version *) (glob)
1503 Mercurial Distributed SCM (version *) (glob)
1504 (see https://mercurial-scm.org for more information)
1504 (see https://mercurial-scm.org for more information)
1505
1505
1506 Copyright (C) 2005-* Matt Mackall and others (glob)
1506 Copyright (C) 2005-* Matt Mackall and others (glob)
1507 This is free software; see the source for copying conditions. There is NO
1507 This is free software; see the source for copying conditions. There is NO
1508 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1508 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1509
1509
1510 Enabled extensions:
1510 Enabled extensions:
1511
1511
1512 throw external 1.2.3
1512 throw external 1.2.3
1513 $ echo 'getversion = lambda: b"1.twentythree"' >> throw.py
1513 $ echo 'getversion = lambda: b"1.twentythree"' >> throw.py
1514 $ rm -f throw.pyc throw.pyo
1514 $ rm -f throw.pyc throw.pyo
1515 $ rm -Rf __pycache__
1515 $ rm -Rf __pycache__
1516 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1516 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1517 Mercurial Distributed SCM (version *) (glob)
1517 Mercurial Distributed SCM (version *) (glob)
1518 (see https://mercurial-scm.org for more information)
1518 (see https://mercurial-scm.org for more information)
1519
1519
1520 Copyright (C) 2005-* Matt Mackall and others (glob)
1520 Copyright (C) 2005-* Matt Mackall and others (glob)
1521 This is free software; see the source for copying conditions. There is NO
1521 This is free software; see the source for copying conditions. There is NO
1522 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1522 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1523
1523
1524 Enabled extensions:
1524 Enabled extensions:
1525
1525
1526 throw external 1.twentythree
1526 throw external 1.twentythree
1527 strip internal
1527 strip internal
1528
1528
1529 $ hg version -q --config extensions.throw=throw.py
1529 $ hg version -q --config extensions.throw=throw.py
1530 Mercurial Distributed SCM (version *) (glob)
1530 Mercurial Distributed SCM (version *) (glob)
1531
1531
1532 Test template output:
1532 Test template output:
1533
1533
1534 $ hg version --config extensions.strip= -T'{extensions}'
1534 $ hg version --config extensions.strip= -T'{extensions}'
1535 strip
1535 strip
1536
1536
1537 Test JSON output of version:
1537 Test JSON output of version:
1538
1538
1539 $ hg version -Tjson
1539 $ hg version -Tjson
1540 [
1540 [
1541 {
1541 {
1542 "extensions": [],
1542 "extensions": [],
1543 "ver": "*" (glob)
1543 "ver": "*" (glob)
1544 }
1544 }
1545 ]
1545 ]
1546
1546
1547 $ hg version --config extensions.throw=throw.py -Tjson
1547 $ hg version --config extensions.throw=throw.py -Tjson
1548 [
1548 [
1549 {
1549 {
1550 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1550 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1551 "ver": "3.2.2"
1551 "ver": "3.2.2"
1552 }
1552 }
1553 ]
1553 ]
1554
1554
1555 $ hg version --config extensions.strip= -Tjson
1555 $ hg version --config extensions.strip= -Tjson
1556 [
1556 [
1557 {
1557 {
1558 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1558 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1559 "ver": "*" (glob)
1559 "ver": "*" (glob)
1560 }
1560 }
1561 ]
1561 ]
1562
1562
1563 Test template output of version:
1563 Test template output of version:
1564
1564
1565 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1565 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1566 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1566 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1567 throw 1.twentythree (external)
1567 throw 1.twentythree (external)
1568 strip (internal)
1568 strip (internal)
1569
1569
1570 Refuse to load extensions with minimum version requirements
1570 Refuse to load extensions with minimum version requirements
1571
1571
1572 $ cat > minversion1.py << EOF
1572 $ cat > minversion1.py << EOF
1573 > from mercurial import util
1573 > from mercurial import util
1574 > util.version = lambda: b'3.5.2'
1574 > util.version = lambda: b'3.5.2'
1575 > minimumhgversion = b'3.6'
1575 > minimumhgversion = b'3.6'
1576 > EOF
1576 > EOF
1577 $ hg --config extensions.minversion=minversion1.py version
1577 $ hg --config extensions.minversion=minversion1.py version
1578 (third party extension minversion requires version 3.6 or newer of Mercurial (current: 3.5.2); disabling)
1578 (third party extension minversion requires version 3.6 or newer of Mercurial (current: 3.5.2); disabling)
1579 Mercurial Distributed SCM (version 3.5.2)
1579 Mercurial Distributed SCM (version 3.5.2)
1580 (see https://mercurial-scm.org for more information)
1580 (see https://mercurial-scm.org for more information)
1581
1581
1582 Copyright (C) 2005-* Matt Mackall and others (glob)
1582 Copyright (C) 2005-* Matt Mackall and others (glob)
1583 This is free software; see the source for copying conditions. There is NO
1583 This is free software; see the source for copying conditions. There is NO
1584 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1584 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1585
1585
1586 $ cat > minversion2.py << EOF
1586 $ cat > minversion2.py << EOF
1587 > from mercurial import util
1587 > from mercurial import util
1588 > util.version = lambda: b'3.6'
1588 > util.version = lambda: b'3.6'
1589 > minimumhgversion = b'3.7'
1589 > minimumhgversion = b'3.7'
1590 > EOF
1590 > EOF
1591 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1591 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1592 (third party extension minversion requires version 3.7 or newer of Mercurial (current: 3.6); disabling)
1592 (third party extension minversion requires version 3.7 or newer of Mercurial (current: 3.6); disabling)
1593
1593
1594 Can load version that is only off by point release
1594 Can load version that is only off by point release
1595
1595
1596 $ cat > minversion2.py << EOF
1596 $ cat > minversion2.py << EOF
1597 > from mercurial import util
1597 > from mercurial import util
1598 > util.version = lambda: b'3.6.1'
1598 > util.version = lambda: b'3.6.1'
1599 > minimumhgversion = b'3.6'
1599 > minimumhgversion = b'3.6'
1600 > EOF
1600 > EOF
1601 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1601 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1602 [1]
1602 [1]
1603
1603
1604 Can load minimum version identical to current
1604 Can load minimum version identical to current
1605
1605
1606 $ cat > minversion3.py << EOF
1606 $ cat > minversion3.py << EOF
1607 > from mercurial import util
1607 > from mercurial import util
1608 > util.version = lambda: b'3.5'
1608 > util.version = lambda: b'3.5'
1609 > minimumhgversion = b'3.5'
1609 > minimumhgversion = b'3.5'
1610 > EOF
1610 > EOF
1611 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1611 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1612 [1]
1612 [1]
1613
1613
1614 Restore HGRCPATH
1614 Restore HGRCPATH
1615
1615
1616 $ HGRCPATH=$ORGHGRCPATH
1616 $ HGRCPATH=$ORGHGRCPATH
1617 $ export HGRCPATH
1617 $ export HGRCPATH
1618
1618
1619 Commands handling multiple repositories at a time should invoke only
1619 Commands handling multiple repositories at a time should invoke only
1620 "reposetup()" of extensions enabling in the target repository.
1620 "reposetup()" of extensions enabling in the target repository.
1621
1621
1622 $ mkdir reposetup-test
1622 $ mkdir reposetup-test
1623 $ cd reposetup-test
1623 $ cd reposetup-test
1624
1624
1625 $ cat > $TESTTMP/reposetuptest.py <<EOF
1625 $ cat > $TESTTMP/reposetuptest.py <<EOF
1626 > from mercurial import extensions
1626 > from mercurial import extensions
1627 > def reposetup(ui, repo):
1627 > def reposetup(ui, repo):
1628 > ui.write(b'reposetup() for %s\n' % (repo.root))
1628 > ui.write(b'reposetup() for %s\n' % (repo.root))
1629 > ui.flush()
1629 > ui.flush()
1630 > EOF
1630 > EOF
1631 $ hg init src
1631 $ hg init src
1632 $ echo a > src/a
1632 $ echo a > src/a
1633 $ hg -R src commit -Am '#0 at src/a'
1633 $ hg -R src commit -Am '#0 at src/a'
1634 adding a
1634 adding a
1635 $ echo '[extensions]' >> src/.hg/hgrc
1635 $ echo '[extensions]' >> src/.hg/hgrc
1636 $ echo '# enable extension locally' >> src/.hg/hgrc
1636 $ echo '# enable extension locally' >> src/.hg/hgrc
1637 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1637 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1638 $ hg -R src status
1638 $ hg -R src status
1639 reposetup() for $TESTTMP/reposetup-test/src
1639 reposetup() for $TESTTMP/reposetup-test/src
1640 reposetup() for $TESTTMP/reposetup-test/src (chg !)
1640 reposetup() for $TESTTMP/reposetup-test/src (chg !)
1641
1641
1642 #if no-extraextensions
1642 #if no-extraextensions
1643 $ hg --cwd src debugextensions
1643 $ hg --cwd src debugextensions
1644 reposetup() for $TESTTMP/reposetup-test/src
1644 reposetup() for $TESTTMP/reposetup-test/src
1645 dodo (untested!)
1645 dodo (untested!)
1646 dudu (untested!)
1646 dudu (untested!)
1647 mq
1647 mq
1648 reposetuptest (untested!)
1648 reposetuptest (untested!)
1649 strip
1649 strip
1650 #endif
1650 #endif
1651
1651
1652 $ hg clone -U src clone-dst1
1652 $ hg clone -U src clone-dst1
1653 reposetup() for $TESTTMP/reposetup-test/src
1653 reposetup() for $TESTTMP/reposetup-test/src
1654 $ hg init push-dst1
1654 $ hg init push-dst1
1655 $ hg -q -R src push push-dst1
1655 $ hg -q -R src push push-dst1
1656 reposetup() for $TESTTMP/reposetup-test/src
1656 reposetup() for $TESTTMP/reposetup-test/src
1657 $ hg init pull-src1
1657 $ hg init pull-src1
1658 $ hg -q -R pull-src1 pull src
1658 $ hg -q -R pull-src1 pull src
1659 reposetup() for $TESTTMP/reposetup-test/src
1659 reposetup() for $TESTTMP/reposetup-test/src
1660
1660
1661 $ cat <<EOF >> $HGRCPATH
1661 $ cat <<EOF >> $HGRCPATH
1662 > [extensions]
1662 > [extensions]
1663 > # disable extension globally and explicitly
1663 > # disable extension globally and explicitly
1664 > reposetuptest = !
1664 > reposetuptest = !
1665 > EOF
1665 > EOF
1666 $ hg clone -U src clone-dst2
1666 $ hg clone -U src clone-dst2
1667 reposetup() for $TESTTMP/reposetup-test/src
1667 reposetup() for $TESTTMP/reposetup-test/src
1668 $ hg init push-dst2
1668 $ hg init push-dst2
1669 $ hg -q -R src push push-dst2
1669 $ hg -q -R src push push-dst2
1670 reposetup() for $TESTTMP/reposetup-test/src
1670 reposetup() for $TESTTMP/reposetup-test/src
1671 $ hg init pull-src2
1671 $ hg init pull-src2
1672 $ hg -q -R pull-src2 pull src
1672 $ hg -q -R pull-src2 pull src
1673 reposetup() for $TESTTMP/reposetup-test/src
1673 reposetup() for $TESTTMP/reposetup-test/src
1674
1674
1675 $ cat <<EOF >> $HGRCPATH
1675 $ cat <<EOF >> $HGRCPATH
1676 > [extensions]
1676 > [extensions]
1677 > # enable extension globally
1677 > # enable extension globally
1678 > reposetuptest = $TESTTMP/reposetuptest.py
1678 > reposetuptest = $TESTTMP/reposetuptest.py
1679 > EOF
1679 > EOF
1680 $ hg clone -U src clone-dst3
1680 $ hg clone -U src clone-dst3
1681 reposetup() for $TESTTMP/reposetup-test/src
1681 reposetup() for $TESTTMP/reposetup-test/src
1682 reposetup() for $TESTTMP/reposetup-test/clone-dst3
1682 reposetup() for $TESTTMP/reposetup-test/clone-dst3
1683 $ hg init push-dst3
1683 $ hg init push-dst3
1684 reposetup() for $TESTTMP/reposetup-test/push-dst3
1684 reposetup() for $TESTTMP/reposetup-test/push-dst3
1685 $ hg -q -R src push push-dst3
1685 $ hg -q -R src push push-dst3
1686 reposetup() for $TESTTMP/reposetup-test/src
1686 reposetup() for $TESTTMP/reposetup-test/src
1687 reposetup() for $TESTTMP/reposetup-test/push-dst3
1687 reposetup() for $TESTTMP/reposetup-test/push-dst3
1688 $ hg init pull-src3
1688 $ hg init pull-src3
1689 reposetup() for $TESTTMP/reposetup-test/pull-src3
1689 reposetup() for $TESTTMP/reposetup-test/pull-src3
1690 $ hg -q -R pull-src3 pull src
1690 $ hg -q -R pull-src3 pull src
1691 reposetup() for $TESTTMP/reposetup-test/pull-src3
1691 reposetup() for $TESTTMP/reposetup-test/pull-src3
1692 reposetup() for $TESTTMP/reposetup-test/src
1692 reposetup() for $TESTTMP/reposetup-test/src
1693
1693
1694 $ echo '[extensions]' >> src/.hg/hgrc
1694 $ echo '[extensions]' >> src/.hg/hgrc
1695 $ echo '# disable extension locally' >> src/.hg/hgrc
1695 $ echo '# disable extension locally' >> src/.hg/hgrc
1696 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1696 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1697 $ hg clone -U src clone-dst4
1697 $ hg clone -U src clone-dst4
1698 reposetup() for $TESTTMP/reposetup-test/clone-dst4
1698 reposetup() for $TESTTMP/reposetup-test/clone-dst4
1699 $ hg init push-dst4
1699 $ hg init push-dst4
1700 reposetup() for $TESTTMP/reposetup-test/push-dst4
1700 reposetup() for $TESTTMP/reposetup-test/push-dst4
1701 $ hg -q -R src push push-dst4
1701 $ hg -q -R src push push-dst4
1702 reposetup() for $TESTTMP/reposetup-test/push-dst4
1702 reposetup() for $TESTTMP/reposetup-test/push-dst4
1703 $ hg init pull-src4
1703 $ hg init pull-src4
1704 reposetup() for $TESTTMP/reposetup-test/pull-src4
1704 reposetup() for $TESTTMP/reposetup-test/pull-src4
1705 $ hg -q -R pull-src4 pull src
1705 $ hg -q -R pull-src4 pull src
1706 reposetup() for $TESTTMP/reposetup-test/pull-src4
1706 reposetup() for $TESTTMP/reposetup-test/pull-src4
1707
1707
1708 disabling in command line overlays with all configuration
1708 disabling in command line overlays with all configuration
1709 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1709 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1710 $ hg --config extensions.reposetuptest=! init push-dst5
1710 $ hg --config extensions.reposetuptest=! init push-dst5
1711 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1711 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1712 $ hg --config extensions.reposetuptest=! init pull-src5
1712 $ hg --config extensions.reposetuptest=! init pull-src5
1713 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1713 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1714
1714
1715 $ cat <<EOF >> $HGRCPATH
1715 $ cat <<EOF >> $HGRCPATH
1716 > [extensions]
1716 > [extensions]
1717 > # disable extension globally and explicitly
1717 > # disable extension globally and explicitly
1718 > reposetuptest = !
1718 > reposetuptest = !
1719 > EOF
1719 > EOF
1720 $ hg init parent
1720 $ hg init parent
1721 $ hg init parent/sub1
1721 $ hg init parent/sub1
1722 $ echo 1 > parent/sub1/1
1722 $ echo 1 > parent/sub1/1
1723 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1723 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1724 adding 1
1724 adding 1
1725 $ hg init parent/sub2
1725 $ hg init parent/sub2
1726 $ hg init parent/sub2/sub21
1726 $ hg init parent/sub2/sub21
1727 $ echo 21 > parent/sub2/sub21/21
1727 $ echo 21 > parent/sub2/sub21/21
1728 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1728 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1729 adding 21
1729 adding 21
1730 $ cat > parent/sub2/.hgsub <<EOF
1730 $ cat > parent/sub2/.hgsub <<EOF
1731 > sub21 = sub21
1731 > sub21 = sub21
1732 > EOF
1732 > EOF
1733 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1733 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1734 adding .hgsub
1734 adding .hgsub
1735 $ hg init parent/sub3
1735 $ hg init parent/sub3
1736 $ echo 3 > parent/sub3/3
1736 $ echo 3 > parent/sub3/3
1737 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1737 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1738 adding 3
1738 adding 3
1739 $ cat > parent/.hgsub <<EOF
1739 $ cat > parent/.hgsub <<EOF
1740 > sub1 = sub1
1740 > sub1 = sub1
1741 > sub2 = sub2
1741 > sub2 = sub2
1742 > sub3 = sub3
1742 > sub3 = sub3
1743 > EOF
1743 > EOF
1744 $ hg -R parent commit -Am '#0 at parent'
1744 $ hg -R parent commit -Am '#0 at parent'
1745 adding .hgsub
1745 adding .hgsub
1746 $ echo '[extensions]' >> parent/.hg/hgrc
1746 $ echo '[extensions]' >> parent/.hg/hgrc
1747 $ echo '# enable extension locally' >> parent/.hg/hgrc
1747 $ echo '# enable extension locally' >> parent/.hg/hgrc
1748 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1748 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1749 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1749 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1750 $ hg -R parent status -S -A
1750 $ hg -R parent status -S -A
1751 reposetup() for $TESTTMP/reposetup-test/parent
1751 reposetup() for $TESTTMP/reposetup-test/parent
1752 reposetup() for $TESTTMP/reposetup-test/parent/sub2
1752 reposetup() for $TESTTMP/reposetup-test/parent/sub2
1753 C .hgsub
1753 C .hgsub
1754 C .hgsubstate
1754 C .hgsubstate
1755 C sub1/1
1755 C sub1/1
1756 C sub2/.hgsub
1756 C sub2/.hgsub
1757 C sub2/.hgsubstate
1757 C sub2/.hgsubstate
1758 C sub2/sub21/21
1758 C sub2/sub21/21
1759 C sub3/3
1759 C sub3/3
1760
1760
1761 $ cd ..
1761 $ cd ..
1762
1762
1763 Prohibit registration of commands that don't use @command (issue5137)
1763 Prohibit registration of commands that don't use @command (issue5137)
1764
1764
1765 $ hg init deprecated
1765 $ hg init deprecated
1766 $ cd deprecated
1766 $ cd deprecated
1767
1767
1768 $ cat <<EOF > deprecatedcmd.py
1768 $ cat <<EOF > deprecatedcmd.py
1769 > def deprecatedcmd(repo, ui):
1769 > def deprecatedcmd(repo, ui):
1770 > pass
1770 > pass
1771 > cmdtable = {
1771 > cmdtable = {
1772 > b'deprecatedcmd': (deprecatedcmd, [], b''),
1772 > b'deprecatedcmd': (deprecatedcmd, [], b''),
1773 > }
1773 > }
1774 > EOF
1774 > EOF
1775 $ cat <<EOF > .hg/hgrc
1775 $ cat <<EOF > .hg/hgrc
1776 > [extensions]
1776 > [extensions]
1777 > deprecatedcmd = `pwd`/deprecatedcmd.py
1777 > deprecatedcmd = `pwd`/deprecatedcmd.py
1778 > mq = !
1778 > mq = !
1779 > hgext.mq = !
1779 > hgext.mq = !
1780 > hgext/mq = !
1780 > hgext/mq = !
1781 > EOF
1781 > EOF
1782
1782
1783 $ hg deprecatedcmd > /dev/null
1783 $ hg deprecatedcmd > /dev/null
1784 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1784 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1785 *** (use @command decorator to register 'deprecatedcmd')
1785 *** (use @command decorator to register 'deprecatedcmd')
1786 hg: unknown command 'deprecatedcmd'
1786 hg: unknown command 'deprecatedcmd'
1787 (use 'hg help' for a list of commands)
1787 (use 'hg help' for a list of commands)
1788 [255]
1788 [255]
1789
1789
1790 the extension shouldn't be loaded at all so the mq works:
1790 the extension shouldn't be loaded at all so the mq works:
1791
1791
1792 $ hg qseries --config extensions.mq= > /dev/null
1792 $ hg qseries --config extensions.mq= > /dev/null
1793 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1793 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1794 *** (use @command decorator to register 'deprecatedcmd')
1794 *** (use @command decorator to register 'deprecatedcmd')
1795
1795
1796 $ cd ..
1796 $ cd ..
1797
1797
1798 Test synopsis and docstring extending
1798 Test synopsis and docstring extending
1799
1799
1800 $ hg init exthelp
1800 $ hg init exthelp
1801 $ cat > exthelp.py <<EOF
1801 $ cat > exthelp.py <<EOF
1802 > from mercurial import commands, extensions
1802 > from mercurial import commands, extensions
1803 > def exbookmarks(orig, *args, **opts):
1803 > def exbookmarks(orig, *args, **opts):
1804 > return orig(*args, **opts)
1804 > return orig(*args, **opts)
1805 > def uisetup(ui):
1805 > def uisetup(ui):
1806 > synopsis = b' GREPME [--foo] [-x]'
1806 > synopsis = b' GREPME [--foo] [-x]'
1807 > docstring = '''
1807 > docstring = '''
1808 > GREPME make sure that this is in the help!
1808 > GREPME make sure that this is in the help!
1809 > '''
1809 > '''
1810 > extensions.wrapcommand(commands.table, b'bookmarks', exbookmarks,
1810 > extensions.wrapcommand(commands.table, b'bookmarks', exbookmarks,
1811 > synopsis, docstring)
1811 > synopsis, docstring)
1812 > EOF
1812 > EOF
1813 $ abspath=`pwd`/exthelp.py
1813 $ abspath=`pwd`/exthelp.py
1814 $ echo '[extensions]' >> $HGRCPATH
1814 $ echo '[extensions]' >> $HGRCPATH
1815 $ echo "exthelp = $abspath" >> $HGRCPATH
1815 $ echo "exthelp = $abspath" >> $HGRCPATH
1816 $ cd exthelp
1816 $ cd exthelp
1817 $ hg help bookmarks | grep GREPME
1817 $ hg help bookmarks | grep GREPME
1818 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1818 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1819 GREPME make sure that this is in the help!
1819 GREPME make sure that this is in the help!
1820 $ cd ..
1820 $ cd ..
1821
1821
1822 Show deprecation warning for the use of cmdutil.command
1822 Show deprecation warning for the use of cmdutil.command
1823
1823
1824 $ cat > nonregistrar.py <<EOF
1824 $ cat > nonregistrar.py <<EOF
1825 > from mercurial import cmdutil
1825 > from mercurial import cmdutil
1826 > cmdtable = {}
1826 > cmdtable = {}
1827 > command = cmdutil.command(cmdtable)
1827 > command = cmdutil.command(cmdtable)
1828 > @command(b'foo', [], norepo=True)
1828 > @command(b'foo', [], norepo=True)
1829 > def foo(ui):
1829 > def foo(ui):
1830 > pass
1830 > pass
1831 > EOF
1831 > EOF
1832
1832
1833 Prohibit the use of unicode strings as the default value of options
1833 Prohibit the use of unicode strings as the default value of options
1834
1834
1835 $ hg init $TESTTMP/opt-unicode-default
1835 $ hg init $TESTTMP/opt-unicode-default
1836
1836
1837 $ cat > $TESTTMP/test_unicode_default_value.py << EOF
1837 $ cat > $TESTTMP/test_unicode_default_value.py << EOF
1838 > from __future__ import print_function
1838 > from __future__ import print_function
1839 > from mercurial import registrar
1839 > from mercurial import registrar
1840 > cmdtable = {}
1840 > cmdtable = {}
1841 > command = registrar.command(cmdtable)
1841 > command = registrar.command(cmdtable)
1842 > @command(b'dummy', [(b'', b'opt', u'value', u'help')], 'ext [OPTIONS]')
1842 > @command(b'dummy', [(b'', b'opt', u'value', u'help')], 'ext [OPTIONS]')
1843 > def ext(*args, **opts):
1843 > def ext(*args, **opts):
1844 > print(opts[b'opt'], flush=True)
1844 > print(opts[b'opt'], flush=True)
1845 > EOF
1845 > EOF
1846 $ "$PYTHON" $TESTTMP/unflush.py $TESTTMP/test_unicode_default_value.py
1846 $ "$PYTHON" $TESTTMP/unflush.py $TESTTMP/test_unicode_default_value.py
1847 $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF
1847 $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF
1848 > [extensions]
1848 > [extensions]
1849 > test_unicode_default_value = $TESTTMP/test_unicode_default_value.py
1849 > test_unicode_default_value = $TESTTMP/test_unicode_default_value.py
1850 > EOF
1850 > EOF
1851 $ hg -R $TESTTMP/opt-unicode-default dummy
1851 $ hg -R $TESTTMP/opt-unicode-default dummy
1852 *** failed to import extension test_unicode_default_value from $TESTTMP/test_unicode_default_value.py: unicode *'value' found in cmdtable.dummy (glob)
1852 *** failed to import extension test_unicode_default_value from $TESTTMP/test_unicode_default_value.py: unicode *'value' found in cmdtable.dummy (glob)
1853 *** (use b'' to make it byte string)
1853 *** (use b'' to make it byte string)
1854 hg: unknown command 'dummy'
1854 hg: unknown command 'dummy'
1855 (did you mean summary?)
1855 (did you mean summary?)
1856 [255]
1856 [255]
@@ -1,414 +1,414 b''
1 #require serve
1 #require serve
2
2
3 This test is a duplicate of 'test-http.t', feel free to factor out
3 This test is a duplicate of 'test-http.t', feel free to factor out
4 parts that are not bundle1/bundle2 specific.
4 parts that are not bundle1/bundle2 specific.
5
5
6 $ cat << EOF >> $HGRCPATH
6 $ cat << EOF >> $HGRCPATH
7 > [devel]
7 > [devel]
8 > # This test is dedicated to interaction through old bundle
8 > # This test is dedicated to interaction through old bundle
9 > legacy.exchange = bundle1
9 > legacy.exchange = bundle1
10 > EOF
10 > EOF
11
11
12 $ hg init test
12 $ hg init test
13 $ cd test
13 $ cd test
14 $ echo foo>foo
14 $ echo foo>foo
15 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
15 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
16 $ echo foo>foo.d/foo
16 $ echo foo>foo.d/foo
17 $ echo bar>foo.d/bAr.hg.d/BaR
17 $ echo bar>foo.d/bAr.hg.d/BaR
18 $ echo bar>foo.d/baR.d.hg/bAR
18 $ echo bar>foo.d/baR.d.hg/bAR
19 $ hg commit -A -m 1
19 $ hg commit -A -m 1
20 adding foo
20 adding foo
21 adding foo.d/bAr.hg.d/BaR
21 adding foo.d/bAr.hg.d/BaR
22 adding foo.d/baR.d.hg/bAR
22 adding foo.d/baR.d.hg/bAR
23 adding foo.d/foo
23 adding foo.d/foo
24 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
24 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
25 $ hg serve --config server.uncompressed=False -p $HGPORT1 -d --pid-file=../hg2.pid
25 $ hg serve --config server.uncompressed=False -p $HGPORT1 -d --pid-file=../hg2.pid
26
26
27 Test server address cannot be reused
27 Test server address cannot be reused
28
28
29 $ hg serve -p $HGPORT1 2>&1
29 $ hg serve -p $HGPORT1 2>&1
30 abort: cannot start server at 'localhost:$HGPORT1': $EADDRINUSE$
30 abort: cannot start server at 'localhost:$HGPORT1': $EADDRINUSE$
31 [255]
31 [255]
32
32
33 $ cd ..
33 $ cd ..
34 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
34 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
35
35
36 clone via stream
36 clone via stream
37
37
38 #if no-reposimplestore
38 #if no-reposimplestore
39 $ hg clone --stream http://localhost:$HGPORT/ copy 2>&1
39 $ hg clone --stream http://localhost:$HGPORT/ copy 2>&1
40 streaming all changes
40 streaming all changes
41 6 files to transfer, 606 bytes of data
41 6 files to transfer, 606 bytes of data
42 transferred * bytes in * seconds (*/sec) (glob)
42 transferred * bytes in * seconds (*/sec) (glob)
43 searching for changes
43 searching for changes
44 no changes found
44 no changes found
45 updating to branch default
45 updating to branch default
46 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 $ hg verify -R copy
47 $ hg verify -R copy
48 checking changesets
48 checking changesets
49 checking manifests
49 checking manifests
50 crosschecking files in changesets and manifests
50 crosschecking files in changesets and manifests
51 checking files
51 checking files
52 checked 1 changesets with 4 changes to 4 files
52 checked 1 changesets with 4 changes to 4 files
53 #endif
53 #endif
54
54
55 try to clone via stream, should use pull instead
55 try to clone via stream, should use pull instead
56
56
57 $ hg clone --stream http://localhost:$HGPORT1/ copy2
57 $ hg clone --stream http://localhost:$HGPORT1/ copy2
58 warning: stream clone requested but server has them disabled
58 warning: stream clone requested but server has them disabled
59 requesting all changes
59 requesting all changes
60 adding changesets
60 adding changesets
61 adding manifests
61 adding manifests
62 adding file changes
62 adding file changes
63 added 1 changesets with 4 changes to 4 files
63 added 1 changesets with 4 changes to 4 files
64 new changesets 8b6053c928fe
64 new changesets 8b6053c928fe
65 updating to branch default
65 updating to branch default
66 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
66 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
67
67
68 try to clone via stream but missing requirements, so should use pull instead
68 try to clone via stream but missing requirements, so should use pull instead
69
69
70 $ cat > $TESTTMP/removesupportedformat.py << EOF
70 $ cat > $TESTTMP/removesupportedformat.py << EOF
71 > from mercurial import localrepo
71 > from mercurial import localrepo
72 > def extsetup(ui):
72 > def extsetup(ui):
73 > localrepo.localrepository.supportedformats.remove(b'generaldelta')
73 > localrepo.localrepository.supportedformats.remove(b'generaldelta')
74 > EOF
74 > EOF
75
75
76 $ hg clone --config extensions.rsf=$TESTTMP/removesupportedformat.py --stream http://localhost:$HGPORT/ copy3
76 $ hg clone --config extensions.rsf=$TESTTMP/removesupportedformat.py --stream http://localhost:$HGPORT/ copy3
77 warning: stream clone requested but client is missing requirements: generaldelta
77 warning: stream clone requested but client is missing requirements: generaldelta
78 (see https://www.mercurial-scm.org/wiki/MissingRequirement for more information)
78 (see https://www.mercurial-scm.org/wiki/MissingRequirement for more information)
79 requesting all changes
79 requesting all changes
80 adding changesets
80 adding changesets
81 adding manifests
81 adding manifests
82 adding file changes
82 adding file changes
83 added 1 changesets with 4 changes to 4 files
83 added 1 changesets with 4 changes to 4 files
84 new changesets 8b6053c928fe
84 new changesets 8b6053c928fe
85 updating to branch default
85 updating to branch default
86 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
86 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
87
87
88 clone via pull
88 clone via pull
89
89
90 $ hg clone http://localhost:$HGPORT1/ copy-pull
90 $ hg clone http://localhost:$HGPORT1/ copy-pull
91 requesting all changes
91 requesting all changes
92 adding changesets
92 adding changesets
93 adding manifests
93 adding manifests
94 adding file changes
94 adding file changes
95 added 1 changesets with 4 changes to 4 files
95 added 1 changesets with 4 changes to 4 files
96 new changesets 8b6053c928fe
96 new changesets 8b6053c928fe
97 updating to branch default
97 updating to branch default
98 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
99 $ hg verify -R copy-pull
99 $ hg verify -R copy-pull
100 checking changesets
100 checking changesets
101 checking manifests
101 checking manifests
102 crosschecking files in changesets and manifests
102 crosschecking files in changesets and manifests
103 checking files
103 checking files
104 checked 1 changesets with 4 changes to 4 files
104 checked 1 changesets with 4 changes to 4 files
105 $ cd test
105 $ cd test
106 $ echo bar > bar
106 $ echo bar > bar
107 $ hg commit -A -d '1 0' -m 2
107 $ hg commit -A -d '1 0' -m 2
108 adding bar
108 adding bar
109 $ cd ..
109 $ cd ..
110
110
111 clone over http with --update
111 clone over http with --update
112
112
113 $ hg clone http://localhost:$HGPORT1/ updated --update 0
113 $ hg clone http://localhost:$HGPORT1/ updated --update 0
114 requesting all changes
114 requesting all changes
115 adding changesets
115 adding changesets
116 adding manifests
116 adding manifests
117 adding file changes
117 adding file changes
118 added 2 changesets with 5 changes to 5 files
118 added 2 changesets with 5 changes to 5 files
119 new changesets 8b6053c928fe:5fed3813f7f5
119 new changesets 8b6053c928fe:5fed3813f7f5
120 updating to branch default
120 updating to branch default
121 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
121 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 $ hg log -r . -R updated
122 $ hg log -r . -R updated
123 changeset: 0:8b6053c928fe
123 changeset: 0:8b6053c928fe
124 user: test
124 user: test
125 date: Thu Jan 01 00:00:00 1970 +0000
125 date: Thu Jan 01 00:00:00 1970 +0000
126 summary: 1
126 summary: 1
127
127
128 $ rm -rf updated
128 $ rm -rf updated
129
129
130 incoming via HTTP
130 incoming via HTTP
131
131
132 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
132 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
133 adding changesets
133 adding changesets
134 adding manifests
134 adding manifests
135 adding file changes
135 adding file changes
136 added 1 changesets with 4 changes to 4 files
136 added 1 changesets with 4 changes to 4 files
137 new changesets 8b6053c928fe
137 new changesets 8b6053c928fe
138 updating to branch default
138 updating to branch default
139 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
139 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
140 $ cd partial
140 $ cd partial
141 $ touch LOCAL
141 $ touch LOCAL
142 $ hg ci -qAm LOCAL
142 $ hg ci -qAm LOCAL
143 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
143 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
144 comparing with http://localhost:$HGPORT1/
144 comparing with http://localhost:$HGPORT1/
145 searching for changes
145 searching for changes
146 2
146 2
147 $ cd ..
147 $ cd ..
148
148
149 pull
149 pull
150
150
151 $ cd copy-pull
151 $ cd copy-pull
152 $ cat >> .hg/hgrc <<EOF
152 $ cat >> .hg/hgrc <<EOF
153 > [hooks]
153 > [hooks]
154 > changegroup = sh -c "printenv.py changegroup"
154 > changegroup = sh -c "printenv.py changegroup"
155 > EOF
155 > EOF
156 $ hg pull
156 $ hg pull
157 pulling from http://localhost:$HGPORT1/
157 pulling from http://localhost:$HGPORT1/
158 searching for changes
158 searching for changes
159 adding changesets
159 adding changesets
160 adding manifests
160 adding manifests
161 adding file changes
161 adding file changes
162 added 1 changesets with 1 changes to 1 files
162 added 1 changesets with 1 changes to 1 files
163 new changesets 5fed3813f7f5
163 new changesets 5fed3813f7f5
164 changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=http://localhost:$HGPORT1/
164 changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=http://localhost:$HGPORT1/
165 (run 'hg update' to get a working copy)
165 (run 'hg update' to get a working copy)
166 $ cd ..
166 $ cd ..
167
167
168 clone from invalid URL
168 clone from invalid URL
169
169
170 $ hg clone http://localhost:$HGPORT/bad
170 $ hg clone http://localhost:$HGPORT/bad
171 abort: HTTP Error 404: Not Found
171 abort: HTTP Error 404: Not Found
172 [255]
172 [255]
173
173
174 test http authentication
174 test http authentication
175 + use the same server to test server side streaming preference
175 + use the same server to test server side streaming preference
176
176
177 $ cd test
177 $ cd test
178 $ cat << EOT > userpass.py
178 $ cat << EOT > userpass.py
179 > import base64
179 > import base64
180 > from mercurial.hgweb import common
180 > from mercurial.hgweb import common
181 > def perform_authentication(hgweb, req, op):
181 > def perform_authentication(hgweb, req, op):
182 > auth = req.headers.get(b'Authorization')
182 > auth = req.headers.get(b'Authorization')
183 > if not auth:
183 > if not auth:
184 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, b'who',
184 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, b'who',
185 > [(b'WWW-Authenticate', b'Basic Realm="mercurial"')])
185 > [(b'WWW-Authenticate', b'Basic Realm="mercurial"')])
186 > if base64.b64decode(auth.split()[1]).split(b':', 1) != [b'user',
186 > if base64.b64decode(auth.split()[1]).split(b':', 1) != [b'user',
187 > b'pass']:
187 > b'pass']:
188 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, b'no')
188 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, b'no')
189 > def extsetup():
189 > def extsetup(ui):
190 > common.permhooks.insert(0, perform_authentication)
190 > common.permhooks.insert(0, perform_authentication)
191 > EOT
191 > EOT
192 $ hg serve --config extensions.x=userpass.py -p $HGPORT2 -d --pid-file=pid \
192 $ hg serve --config extensions.x=userpass.py -p $HGPORT2 -d --pid-file=pid \
193 > --config server.preferuncompressed=True \
193 > --config server.preferuncompressed=True \
194 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
194 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
195 $ cat pid >> $DAEMON_PIDS
195 $ cat pid >> $DAEMON_PIDS
196
196
197 $ cat << EOF > get_pass.py
197 $ cat << EOF > get_pass.py
198 > import getpass
198 > import getpass
199 > def newgetpass(arg):
199 > def newgetpass(arg):
200 > return "pass"
200 > return "pass"
201 > getpass.getpass = newgetpass
201 > getpass.getpass = newgetpass
202 > EOF
202 > EOF
203
203
204 $ hg id http://localhost:$HGPORT2/
204 $ hg id http://localhost:$HGPORT2/
205 abort: http authorization required for http://localhost:$HGPORT2/
205 abort: http authorization required for http://localhost:$HGPORT2/
206 [255]
206 [255]
207 $ hg id http://localhost:$HGPORT2/
207 $ hg id http://localhost:$HGPORT2/
208 abort: http authorization required for http://localhost:$HGPORT2/
208 abort: http authorization required for http://localhost:$HGPORT2/
209 [255]
209 [255]
210 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
210 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
211 http authorization required for http://localhost:$HGPORT2/
211 http authorization required for http://localhost:$HGPORT2/
212 realm: mercurial
212 realm: mercurial
213 user: user
213 user: user
214 password: 5fed3813f7f5
214 password: 5fed3813f7f5
215 $ hg id http://user:pass@localhost:$HGPORT2/
215 $ hg id http://user:pass@localhost:$HGPORT2/
216 5fed3813f7f5
216 5fed3813f7f5
217 $ echo '[auth]' >> .hg/hgrc
217 $ echo '[auth]' >> .hg/hgrc
218 $ echo 'l.schemes=http' >> .hg/hgrc
218 $ echo 'l.schemes=http' >> .hg/hgrc
219 $ echo 'l.prefix=lo' >> .hg/hgrc
219 $ echo 'l.prefix=lo' >> .hg/hgrc
220 $ echo 'l.username=user' >> .hg/hgrc
220 $ echo 'l.username=user' >> .hg/hgrc
221 $ echo 'l.password=pass' >> .hg/hgrc
221 $ echo 'l.password=pass' >> .hg/hgrc
222 $ hg id http://localhost:$HGPORT2/
222 $ hg id http://localhost:$HGPORT2/
223 5fed3813f7f5
223 5fed3813f7f5
224 $ hg id http://localhost:$HGPORT2/
224 $ hg id http://localhost:$HGPORT2/
225 5fed3813f7f5
225 5fed3813f7f5
226 $ hg id http://user@localhost:$HGPORT2/
226 $ hg id http://user@localhost:$HGPORT2/
227 5fed3813f7f5
227 5fed3813f7f5
228
228
229 #if no-reposimplestore
229 #if no-reposimplestore
230 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
230 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
231 streaming all changes
231 streaming all changes
232 7 files to transfer, 916 bytes of data
232 7 files to transfer, 916 bytes of data
233 transferred * bytes in * seconds (*/sec) (glob)
233 transferred * bytes in * seconds (*/sec) (glob)
234 searching for changes
234 searching for changes
235 no changes found
235 no changes found
236 updating to branch default
236 updating to branch default
237 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
237 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
238 #endif
238 #endif
239
239
240 --pull should override server's preferuncompressed
240 --pull should override server's preferuncompressed
241
241
242 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
242 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
243 requesting all changes
243 requesting all changes
244 adding changesets
244 adding changesets
245 adding manifests
245 adding manifests
246 adding file changes
246 adding file changes
247 added 2 changesets with 5 changes to 5 files
247 added 2 changesets with 5 changes to 5 files
248 new changesets 8b6053c928fe:5fed3813f7f5
248 new changesets 8b6053c928fe:5fed3813f7f5
249 updating to branch default
249 updating to branch default
250 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
250 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
251
251
252 $ hg id http://user2@localhost:$HGPORT2/
252 $ hg id http://user2@localhost:$HGPORT2/
253 abort: http authorization required for http://localhost:$HGPORT2/
253 abort: http authorization required for http://localhost:$HGPORT2/
254 [255]
254 [255]
255 $ hg id http://user:pass2@localhost:$HGPORT2/
255 $ hg id http://user:pass2@localhost:$HGPORT2/
256 abort: HTTP Error 403: no
256 abort: HTTP Error 403: no
257 [255]
257 [255]
258
258
259 $ hg -R dest-pull tag -r tip top
259 $ hg -R dest-pull tag -r tip top
260 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/
260 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/
261 pushing to http://user:***@localhost:$HGPORT2/
261 pushing to http://user:***@localhost:$HGPORT2/
262 searching for changes
262 searching for changes
263 remote: adding changesets
263 remote: adding changesets
264 remote: adding manifests
264 remote: adding manifests
265 remote: adding file changes
265 remote: adding file changes
266 remote: added 1 changesets with 1 changes to 1 files
266 remote: added 1 changesets with 1 changes to 1 files
267 $ hg rollback -q
267 $ hg rollback -q
268
268
269 $ sed 's/.*] "/"/' < ../access.log
269 $ sed 's/.*] "/"/' < ../access.log
270 "GET /?cmd=capabilities HTTP/1.1" 401 -
270 "GET /?cmd=capabilities HTTP/1.1" 401 -
271 "GET /?cmd=capabilities HTTP/1.1" 401 -
271 "GET /?cmd=capabilities HTTP/1.1" 401 -
272 "GET /?cmd=capabilities HTTP/1.1" 401 -
272 "GET /?cmd=capabilities HTTP/1.1" 401 -
273 "GET /?cmd=capabilities HTTP/1.1" 200 -
273 "GET /?cmd=capabilities HTTP/1.1" 200 -
274 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
274 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
275 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
275 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
276 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
276 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
277 "GET /?cmd=capabilities HTTP/1.1" 401 -
277 "GET /?cmd=capabilities HTTP/1.1" 401 -
278 "GET /?cmd=capabilities HTTP/1.1" 200 -
278 "GET /?cmd=capabilities HTTP/1.1" 200 -
279 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
279 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
280 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
280 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
281 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
281 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
282 "GET /?cmd=capabilities HTTP/1.1" 401 -
282 "GET /?cmd=capabilities HTTP/1.1" 401 -
283 "GET /?cmd=capabilities HTTP/1.1" 200 -
283 "GET /?cmd=capabilities HTTP/1.1" 200 -
284 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
284 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
285 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
285 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
286 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
286 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
287 "GET /?cmd=capabilities HTTP/1.1" 401 -
287 "GET /?cmd=capabilities HTTP/1.1" 401 -
288 "GET /?cmd=capabilities HTTP/1.1" 200 -
288 "GET /?cmd=capabilities HTTP/1.1" 200 -
289 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
289 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
290 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
290 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
291 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
291 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
292 "GET /?cmd=capabilities HTTP/1.1" 401 -
292 "GET /?cmd=capabilities HTTP/1.1" 401 -
293 "GET /?cmd=capabilities HTTP/1.1" 200 -
293 "GET /?cmd=capabilities HTTP/1.1" 200 -
294 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
294 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
295 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
295 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
296 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
296 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
297 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
297 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
298 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
298 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
299 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
299 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
300 "GET /?cmd=stream_out HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
300 "GET /?cmd=stream_out HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
301 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
301 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
302 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D5fed3813f7f5e1824344fdc9cf8f63bb662c292d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
302 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D5fed3813f7f5e1824344fdc9cf8f63bb662c292d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
303 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
303 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
304 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
304 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
305 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
305 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
306 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
306 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
307 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
307 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
308 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
308 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
309 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
309 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
310 "GET /?cmd=capabilities HTTP/1.1" 401 -
310 "GET /?cmd=capabilities HTTP/1.1" 401 -
311 "GET /?cmd=capabilities HTTP/1.1" 401 -
311 "GET /?cmd=capabilities HTTP/1.1" 401 -
312 "GET /?cmd=capabilities HTTP/1.1" 403 -
312 "GET /?cmd=capabilities HTTP/1.1" 403 -
313 "GET /?cmd=capabilities HTTP/1.1" 401 -
313 "GET /?cmd=capabilities HTTP/1.1" 401 -
314 "GET /?cmd=capabilities HTTP/1.1" 200 -
314 "GET /?cmd=capabilities HTTP/1.1" 200 -
315 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
315 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
316 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
316 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
317 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
317 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
318 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
318 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
319 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
319 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
320 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=686173686564+5eb5abfefeea63c80dd7553bcc3783f37e0c5524* (glob)
320 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=686173686564+5eb5abfefeea63c80dd7553bcc3783f37e0c5524* (glob)
321 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
321 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
322
322
323 $ cd ..
323 $ cd ..
324
324
325 clone of serve with repo in root and unserved subrepo (issue2970)
325 clone of serve with repo in root and unserved subrepo (issue2970)
326
326
327 $ hg --cwd test init sub
327 $ hg --cwd test init sub
328 $ echo empty > test/sub/empty
328 $ echo empty > test/sub/empty
329 $ hg --cwd test/sub add empty
329 $ hg --cwd test/sub add empty
330 $ hg --cwd test/sub commit -qm 'add empty'
330 $ hg --cwd test/sub commit -qm 'add empty'
331 $ hg --cwd test/sub tag -r 0 something
331 $ hg --cwd test/sub tag -r 0 something
332 $ echo sub = sub > test/.hgsub
332 $ echo sub = sub > test/.hgsub
333 $ hg --cwd test add .hgsub
333 $ hg --cwd test add .hgsub
334 $ hg --cwd test commit -qm 'add subrepo'
334 $ hg --cwd test commit -qm 'add subrepo'
335 $ hg clone http://localhost:$HGPORT noslash-clone
335 $ hg clone http://localhost:$HGPORT noslash-clone
336 requesting all changes
336 requesting all changes
337 adding changesets
337 adding changesets
338 adding manifests
338 adding manifests
339 adding file changes
339 adding file changes
340 added 3 changesets with 7 changes to 7 files
340 added 3 changesets with 7 changes to 7 files
341 new changesets 8b6053c928fe:56f9bc90cce6
341 new changesets 8b6053c928fe:56f9bc90cce6
342 updating to branch default
342 updating to branch default
343 cloning subrepo sub from http://localhost:$HGPORT/sub
343 cloning subrepo sub from http://localhost:$HGPORT/sub
344 abort: HTTP Error 404: Not Found
344 abort: HTTP Error 404: Not Found
345 [255]
345 [255]
346 $ hg clone http://localhost:$HGPORT/ slash-clone
346 $ hg clone http://localhost:$HGPORT/ slash-clone
347 requesting all changes
347 requesting all changes
348 adding changesets
348 adding changesets
349 adding manifests
349 adding manifests
350 adding file changes
350 adding file changes
351 added 3 changesets with 7 changes to 7 files
351 added 3 changesets with 7 changes to 7 files
352 new changesets 8b6053c928fe:56f9bc90cce6
352 new changesets 8b6053c928fe:56f9bc90cce6
353 updating to branch default
353 updating to branch default
354 cloning subrepo sub from http://localhost:$HGPORT/sub
354 cloning subrepo sub from http://localhost:$HGPORT/sub
355 abort: HTTP Error 404: Not Found
355 abort: HTTP Error 404: Not Found
356 [255]
356 [255]
357
357
358 check error log
358 check error log
359
359
360 $ cat error.log
360 $ cat error.log
361
361
362 Check error reporting while pulling/cloning
362 Check error reporting while pulling/cloning
363
363
364 $ $RUNTESTDIR/killdaemons.py
364 $ $RUNTESTDIR/killdaemons.py
365 $ hg serve -R test -p $HGPORT -d --pid-file=hg3.pid -E error.log --config extensions.crash=${TESTDIR}/crashgetbundler.py
365 $ hg serve -R test -p $HGPORT -d --pid-file=hg3.pid -E error.log --config extensions.crash=${TESTDIR}/crashgetbundler.py
366 $ cat hg3.pid >> $DAEMON_PIDS
366 $ cat hg3.pid >> $DAEMON_PIDS
367 $ hg clone http://localhost:$HGPORT/ abort-clone
367 $ hg clone http://localhost:$HGPORT/ abort-clone
368 requesting all changes
368 requesting all changes
369 abort: remote error:
369 abort: remote error:
370 this is an exercise
370 this is an exercise
371 [255]
371 [255]
372 $ cat error.log
372 $ cat error.log
373
373
374 disable pull-based clones
374 disable pull-based clones
375
375
376 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg4.pid -E error.log --config server.disablefullbundle=True
376 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg4.pid -E error.log --config server.disablefullbundle=True
377 $ cat hg4.pid >> $DAEMON_PIDS
377 $ cat hg4.pid >> $DAEMON_PIDS
378 $ hg clone http://localhost:$HGPORT1/ disable-pull-clone
378 $ hg clone http://localhost:$HGPORT1/ disable-pull-clone
379 requesting all changes
379 requesting all changes
380 abort: remote error:
380 abort: remote error:
381 server has pull-based clones disabled
381 server has pull-based clones disabled
382 [255]
382 [255]
383
383
384 #if no-reposimplestore
384 #if no-reposimplestore
385 ... but keep stream clones working
385 ... but keep stream clones working
386
386
387 $ hg clone --stream --noupdate http://localhost:$HGPORT1/ test-stream-clone
387 $ hg clone --stream --noupdate http://localhost:$HGPORT1/ test-stream-clone
388 streaming all changes
388 streaming all changes
389 * files to transfer, * of data (glob)
389 * files to transfer, * of data (glob)
390 transferred * in * seconds (* KB/sec) (glob)
390 transferred * in * seconds (* KB/sec) (glob)
391 searching for changes
391 searching for changes
392 no changes found
392 no changes found
393 #endif
393 #endif
394
394
395 ... and also keep partial clones and pulls working
395 ... and also keep partial clones and pulls working
396 $ hg clone http://localhost:$HGPORT1 --rev 0 test-partial-clone
396 $ hg clone http://localhost:$HGPORT1 --rev 0 test-partial-clone
397 adding changesets
397 adding changesets
398 adding manifests
398 adding manifests
399 adding file changes
399 adding file changes
400 added 1 changesets with 4 changes to 4 files
400 added 1 changesets with 4 changes to 4 files
401 new changesets 8b6053c928fe
401 new changesets 8b6053c928fe
402 updating to branch default
402 updating to branch default
403 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
403 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
404 $ hg pull -R test-partial-clone
404 $ hg pull -R test-partial-clone
405 pulling from http://localhost:$HGPORT1/
405 pulling from http://localhost:$HGPORT1/
406 searching for changes
406 searching for changes
407 adding changesets
407 adding changesets
408 adding manifests
408 adding manifests
409 adding file changes
409 adding file changes
410 added 2 changesets with 3 changes to 3 files
410 added 2 changesets with 3 changes to 3 files
411 new changesets 5fed3813f7f5:56f9bc90cce6
411 new changesets 5fed3813f7f5:56f9bc90cce6
412 (run 'hg update' to get a working copy)
412 (run 'hg update' to get a working copy)
413
413
414 $ cat error.log
414 $ cat error.log
@@ -1,564 +1,564 b''
1 #require serve
1 #require serve
2
2
3 $ hg init test
3 $ hg init test
4 $ cd test
4 $ cd test
5 $ echo foo>foo
5 $ echo foo>foo
6 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
6 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
7 $ echo foo>foo.d/foo
7 $ echo foo>foo.d/foo
8 $ echo bar>foo.d/bAr.hg.d/BaR
8 $ echo bar>foo.d/bAr.hg.d/BaR
9 $ echo bar>foo.d/baR.d.hg/bAR
9 $ echo bar>foo.d/baR.d.hg/bAR
10 $ hg commit -A -m 1
10 $ hg commit -A -m 1
11 adding foo
11 adding foo
12 adding foo.d/bAr.hg.d/BaR
12 adding foo.d/bAr.hg.d/BaR
13 adding foo.d/baR.d.hg/bAR
13 adding foo.d/baR.d.hg/bAR
14 adding foo.d/foo
14 adding foo.d/foo
15 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
15 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
16 $ hg serve --config server.uncompressed=False -p $HGPORT1 -d --pid-file=../hg2.pid
16 $ hg serve --config server.uncompressed=False -p $HGPORT1 -d --pid-file=../hg2.pid
17
17
18 Test server address cannot be reused
18 Test server address cannot be reused
19
19
20 $ hg serve -p $HGPORT1 2>&1
20 $ hg serve -p $HGPORT1 2>&1
21 abort: cannot start server at 'localhost:$HGPORT1': $EADDRINUSE$
21 abort: cannot start server at 'localhost:$HGPORT1': $EADDRINUSE$
22 [255]
22 [255]
23
23
24 $ cd ..
24 $ cd ..
25 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
25 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
26
26
27 clone via stream
27 clone via stream
28
28
29 #if no-reposimplestore
29 #if no-reposimplestore
30 $ hg clone --stream http://localhost:$HGPORT/ copy 2>&1
30 $ hg clone --stream http://localhost:$HGPORT/ copy 2>&1
31 streaming all changes
31 streaming all changes
32 9 files to transfer, 715 bytes of data
32 9 files to transfer, 715 bytes of data
33 transferred * bytes in * seconds (*/sec) (glob)
33 transferred * bytes in * seconds (*/sec) (glob)
34 updating to branch default
34 updating to branch default
35 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
35 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 $ hg verify -R copy
36 $ hg verify -R copy
37 checking changesets
37 checking changesets
38 checking manifests
38 checking manifests
39 crosschecking files in changesets and manifests
39 crosschecking files in changesets and manifests
40 checking files
40 checking files
41 checked 1 changesets with 4 changes to 4 files
41 checked 1 changesets with 4 changes to 4 files
42 #endif
42 #endif
43
43
44 try to clone via stream, should use pull instead
44 try to clone via stream, should use pull instead
45
45
46 $ hg clone --stream http://localhost:$HGPORT1/ copy2
46 $ hg clone --stream http://localhost:$HGPORT1/ copy2
47 warning: stream clone requested but server has them disabled
47 warning: stream clone requested but server has them disabled
48 requesting all changes
48 requesting all changes
49 adding changesets
49 adding changesets
50 adding manifests
50 adding manifests
51 adding file changes
51 adding file changes
52 added 1 changesets with 4 changes to 4 files
52 added 1 changesets with 4 changes to 4 files
53 new changesets 8b6053c928fe
53 new changesets 8b6053c928fe
54 updating to branch default
54 updating to branch default
55 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
55 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
56
56
57 try to clone via stream but missing requirements, so should use pull instead
57 try to clone via stream but missing requirements, so should use pull instead
58
58
59 $ cat > $TESTTMP/removesupportedformat.py << EOF
59 $ cat > $TESTTMP/removesupportedformat.py << EOF
60 > from mercurial import localrepo
60 > from mercurial import localrepo
61 > def extsetup(ui):
61 > def extsetup(ui):
62 > localrepo.localrepository.supportedformats.remove(b'generaldelta')
62 > localrepo.localrepository.supportedformats.remove(b'generaldelta')
63 > EOF
63 > EOF
64
64
65 $ hg clone --config extensions.rsf=$TESTTMP/removesupportedformat.py --stream http://localhost:$HGPORT/ copy3
65 $ hg clone --config extensions.rsf=$TESTTMP/removesupportedformat.py --stream http://localhost:$HGPORT/ copy3
66 warning: stream clone requested but client is missing requirements: generaldelta
66 warning: stream clone requested but client is missing requirements: generaldelta
67 (see https://www.mercurial-scm.org/wiki/MissingRequirement for more information)
67 (see https://www.mercurial-scm.org/wiki/MissingRequirement for more information)
68 requesting all changes
68 requesting all changes
69 adding changesets
69 adding changesets
70 adding manifests
70 adding manifests
71 adding file changes
71 adding file changes
72 added 1 changesets with 4 changes to 4 files
72 added 1 changesets with 4 changes to 4 files
73 new changesets 8b6053c928fe
73 new changesets 8b6053c928fe
74 updating to branch default
74 updating to branch default
75 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
75 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
76
76
77 clone via pull
77 clone via pull
78
78
79 $ hg clone http://localhost:$HGPORT1/ copy-pull
79 $ hg clone http://localhost:$HGPORT1/ copy-pull
80 requesting all changes
80 requesting all changes
81 adding changesets
81 adding changesets
82 adding manifests
82 adding manifests
83 adding file changes
83 adding file changes
84 added 1 changesets with 4 changes to 4 files
84 added 1 changesets with 4 changes to 4 files
85 new changesets 8b6053c928fe
85 new changesets 8b6053c928fe
86 updating to branch default
86 updating to branch default
87 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 $ hg verify -R copy-pull
88 $ hg verify -R copy-pull
89 checking changesets
89 checking changesets
90 checking manifests
90 checking manifests
91 crosschecking files in changesets and manifests
91 crosschecking files in changesets and manifests
92 checking files
92 checking files
93 checked 1 changesets with 4 changes to 4 files
93 checked 1 changesets with 4 changes to 4 files
94 $ cd test
94 $ cd test
95 $ echo bar > bar
95 $ echo bar > bar
96 $ hg commit -A -d '1 0' -m 2
96 $ hg commit -A -d '1 0' -m 2
97 adding bar
97 adding bar
98 $ cd ..
98 $ cd ..
99
99
100 clone over http with --update
100 clone over http with --update
101
101
102 $ hg clone http://localhost:$HGPORT1/ updated --update 0
102 $ hg clone http://localhost:$HGPORT1/ updated --update 0
103 requesting all changes
103 requesting all changes
104 adding changesets
104 adding changesets
105 adding manifests
105 adding manifests
106 adding file changes
106 adding file changes
107 added 2 changesets with 5 changes to 5 files
107 added 2 changesets with 5 changes to 5 files
108 new changesets 8b6053c928fe:5fed3813f7f5
108 new changesets 8b6053c928fe:5fed3813f7f5
109 updating to branch default
109 updating to branch default
110 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
110 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 $ hg log -r . -R updated
111 $ hg log -r . -R updated
112 changeset: 0:8b6053c928fe
112 changeset: 0:8b6053c928fe
113 user: test
113 user: test
114 date: Thu Jan 01 00:00:00 1970 +0000
114 date: Thu Jan 01 00:00:00 1970 +0000
115 summary: 1
115 summary: 1
116
116
117 $ rm -rf updated
117 $ rm -rf updated
118
118
119 incoming via HTTP
119 incoming via HTTP
120
120
121 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
121 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
122 adding changesets
122 adding changesets
123 adding manifests
123 adding manifests
124 adding file changes
124 adding file changes
125 added 1 changesets with 4 changes to 4 files
125 added 1 changesets with 4 changes to 4 files
126 new changesets 8b6053c928fe
126 new changesets 8b6053c928fe
127 updating to branch default
127 updating to branch default
128 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
128 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
129 $ cd partial
129 $ cd partial
130 $ touch LOCAL
130 $ touch LOCAL
131 $ hg ci -qAm LOCAL
131 $ hg ci -qAm LOCAL
132 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
132 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
133 comparing with http://localhost:$HGPORT1/
133 comparing with http://localhost:$HGPORT1/
134 searching for changes
134 searching for changes
135 2
135 2
136 $ cd ..
136 $ cd ..
137
137
138 pull
138 pull
139
139
140 $ cd copy-pull
140 $ cd copy-pull
141 $ cat >> .hg/hgrc <<EOF
141 $ cat >> .hg/hgrc <<EOF
142 > [hooks]
142 > [hooks]
143 > changegroup = sh -c "printenv.py --line changegroup"
143 > changegroup = sh -c "printenv.py --line changegroup"
144 > EOF
144 > EOF
145 $ hg pull
145 $ hg pull
146 pulling from http://localhost:$HGPORT1/
146 pulling from http://localhost:$HGPORT1/
147 searching for changes
147 searching for changes
148 adding changesets
148 adding changesets
149 adding manifests
149 adding manifests
150 adding file changes
150 adding file changes
151 added 1 changesets with 1 changes to 1 files
151 added 1 changesets with 1 changes to 1 files
152 new changesets 5fed3813f7f5
152 new changesets 5fed3813f7f5
153 changegroup hook: HG_HOOKNAME=changegroup
153 changegroup hook: HG_HOOKNAME=changegroup
154 HG_HOOKTYPE=changegroup
154 HG_HOOKTYPE=changegroup
155 HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d
155 HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d
156 HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d
156 HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d
157 HG_SOURCE=pull
157 HG_SOURCE=pull
158 HG_TXNID=TXN:$ID$
158 HG_TXNID=TXN:$ID$
159 HG_URL=http://localhost:$HGPORT1/
159 HG_URL=http://localhost:$HGPORT1/
160
160
161 (run 'hg update' to get a working copy)
161 (run 'hg update' to get a working copy)
162 $ cd ..
162 $ cd ..
163
163
164 clone from invalid URL
164 clone from invalid URL
165
165
166 $ hg clone http://localhost:$HGPORT/bad
166 $ hg clone http://localhost:$HGPORT/bad
167 abort: HTTP Error 404: Not Found
167 abort: HTTP Error 404: Not Found
168 [255]
168 [255]
169
169
170 test http authentication
170 test http authentication
171 + use the same server to test server side streaming preference
171 + use the same server to test server side streaming preference
172
172
173 $ cd test
173 $ cd test
174 $ cat << EOT > userpass.py
174 $ cat << EOT > userpass.py
175 > import base64
175 > import base64
176 > from mercurial.hgweb import common
176 > from mercurial.hgweb import common
177 > def perform_authentication(hgweb, req, op):
177 > def perform_authentication(hgweb, req, op):
178 > auth = req.headers.get(b'Authorization')
178 > auth = req.headers.get(b'Authorization')
179 > if not auth:
179 > if not auth:
180 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, b'who',
180 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, b'who',
181 > [(b'WWW-Authenticate', b'Basic Realm="mercurial"')])
181 > [(b'WWW-Authenticate', b'Basic Realm="mercurial"')])
182 > if base64.b64decode(auth.split()[1]).split(b':', 1) != [b'user', b'pass']:
182 > if base64.b64decode(auth.split()[1]).split(b':', 1) != [b'user', b'pass']:
183 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, b'no')
183 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, b'no')
184 > def extsetup():
184 > def extsetup(ui):
185 > common.permhooks.insert(0, perform_authentication)
185 > common.permhooks.insert(0, perform_authentication)
186 > EOT
186 > EOT
187 $ hg serve --config extensions.x=userpass.py -p $HGPORT2 -d --pid-file=pid \
187 $ hg serve --config extensions.x=userpass.py -p $HGPORT2 -d --pid-file=pid \
188 > --config server.preferuncompressed=True \
188 > --config server.preferuncompressed=True \
189 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
189 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
190 $ cat pid >> $DAEMON_PIDS
190 $ cat pid >> $DAEMON_PIDS
191
191
192 $ cat << EOF > get_pass.py
192 $ cat << EOF > get_pass.py
193 > import getpass
193 > import getpass
194 > def newgetpass(arg):
194 > def newgetpass(arg):
195 > return "pass"
195 > return "pass"
196 > getpass.getpass = newgetpass
196 > getpass.getpass = newgetpass
197 > EOF
197 > EOF
198
198
199 $ hg id http://localhost:$HGPORT2/
199 $ hg id http://localhost:$HGPORT2/
200 abort: http authorization required for http://localhost:$HGPORT2/
200 abort: http authorization required for http://localhost:$HGPORT2/
201 [255]
201 [255]
202 $ hg id http://localhost:$HGPORT2/
202 $ hg id http://localhost:$HGPORT2/
203 abort: http authorization required for http://localhost:$HGPORT2/
203 abort: http authorization required for http://localhost:$HGPORT2/
204 [255]
204 [255]
205 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
205 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
206 http authorization required for http://localhost:$HGPORT2/
206 http authorization required for http://localhost:$HGPORT2/
207 realm: mercurial
207 realm: mercurial
208 user: user
208 user: user
209 password: 5fed3813f7f5
209 password: 5fed3813f7f5
210 $ hg id http://user:pass@localhost:$HGPORT2/
210 $ hg id http://user:pass@localhost:$HGPORT2/
211 5fed3813f7f5
211 5fed3813f7f5
212 $ echo '[auth]' >> .hg/hgrc
212 $ echo '[auth]' >> .hg/hgrc
213 $ echo 'l.schemes=http' >> .hg/hgrc
213 $ echo 'l.schemes=http' >> .hg/hgrc
214 $ echo 'l.prefix=lo' >> .hg/hgrc
214 $ echo 'l.prefix=lo' >> .hg/hgrc
215 $ echo 'l.username=user' >> .hg/hgrc
215 $ echo 'l.username=user' >> .hg/hgrc
216 $ echo 'l.password=pass' >> .hg/hgrc
216 $ echo 'l.password=pass' >> .hg/hgrc
217 $ hg id http://localhost:$HGPORT2/
217 $ hg id http://localhost:$HGPORT2/
218 5fed3813f7f5
218 5fed3813f7f5
219 $ hg id http://localhost:$HGPORT2/
219 $ hg id http://localhost:$HGPORT2/
220 5fed3813f7f5
220 5fed3813f7f5
221 $ hg id http://user@localhost:$HGPORT2/
221 $ hg id http://user@localhost:$HGPORT2/
222 5fed3813f7f5
222 5fed3813f7f5
223
223
224 #if no-reposimplestore
224 #if no-reposimplestore
225 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
225 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
226 streaming all changes
226 streaming all changes
227 10 files to transfer, 1.01 KB of data
227 10 files to transfer, 1.01 KB of data
228 transferred * KB in * seconds (*/sec) (glob)
228 transferred * KB in * seconds (*/sec) (glob)
229 updating to branch default
229 updating to branch default
230 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
230 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
231 #endif
231 #endif
232
232
233 --pull should override server's preferuncompressed
233 --pull should override server's preferuncompressed
234 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
234 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
235 requesting all changes
235 requesting all changes
236 adding changesets
236 adding changesets
237 adding manifests
237 adding manifests
238 adding file changes
238 adding file changes
239 added 2 changesets with 5 changes to 5 files
239 added 2 changesets with 5 changes to 5 files
240 new changesets 8b6053c928fe:5fed3813f7f5
240 new changesets 8b6053c928fe:5fed3813f7f5
241 updating to branch default
241 updating to branch default
242 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
242 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
243
243
244 $ hg id http://user2@localhost:$HGPORT2/
244 $ hg id http://user2@localhost:$HGPORT2/
245 abort: http authorization required for http://localhost:$HGPORT2/
245 abort: http authorization required for http://localhost:$HGPORT2/
246 [255]
246 [255]
247 $ hg id http://user:pass2@localhost:$HGPORT2/
247 $ hg id http://user:pass2@localhost:$HGPORT2/
248 abort: HTTP Error 403: no
248 abort: HTTP Error 403: no
249 [255]
249 [255]
250
250
251 $ hg -R dest-pull tag -r tip top
251 $ hg -R dest-pull tag -r tip top
252 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/
252 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/
253 pushing to http://user:***@localhost:$HGPORT2/
253 pushing to http://user:***@localhost:$HGPORT2/
254 searching for changes
254 searching for changes
255 remote: adding changesets
255 remote: adding changesets
256 remote: adding manifests
256 remote: adding manifests
257 remote: adding file changes
257 remote: adding file changes
258 remote: added 1 changesets with 1 changes to 1 files
258 remote: added 1 changesets with 1 changes to 1 files
259 $ hg rollback -q
259 $ hg rollback -q
260 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/ --debug --config devel.debug.peer-request=yes
260 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/ --debug --config devel.debug.peer-request=yes
261 pushing to http://user:***@localhost:$HGPORT2/
261 pushing to http://user:***@localhost:$HGPORT2/
262 using http://localhost:$HGPORT2/
262 using http://localhost:$HGPORT2/
263 http auth: user user, password ****
263 http auth: user user, password ****
264 sending capabilities command
264 sending capabilities command
265 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=capabilities
265 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=capabilities
266 http auth: user user, password ****
266 http auth: user user, password ****
267 devel-peer-request: finished in *.???? seconds (200) (glob)
267 devel-peer-request: finished in *.???? seconds (200) (glob)
268 query 1; heads
268 query 1; heads
269 devel-peer-request: batched-content
269 devel-peer-request: batched-content
270 devel-peer-request: - heads (0 arguments)
270 devel-peer-request: - heads (0 arguments)
271 devel-peer-request: - known (1 arguments)
271 devel-peer-request: - known (1 arguments)
272 sending batch command
272 sending batch command
273 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=batch
273 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=batch
274 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
274 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
275 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
275 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
276 devel-peer-request: 68 bytes of commands arguments in headers
276 devel-peer-request: 68 bytes of commands arguments in headers
277 devel-peer-request: finished in *.???? seconds (200) (glob)
277 devel-peer-request: finished in *.???? seconds (200) (glob)
278 searching for changes
278 searching for changes
279 all remote heads known locally
279 all remote heads known locally
280 preparing listkeys for "phases"
280 preparing listkeys for "phases"
281 sending listkeys command
281 sending listkeys command
282 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
282 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
283 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
283 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
284 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
284 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
285 devel-peer-request: 16 bytes of commands arguments in headers
285 devel-peer-request: 16 bytes of commands arguments in headers
286 devel-peer-request: finished in *.???? seconds (200) (glob)
286 devel-peer-request: finished in *.???? seconds (200) (glob)
287 received listkey for "phases": 58 bytes
287 received listkey for "phases": 58 bytes
288 checking for updated bookmarks
288 checking for updated bookmarks
289 preparing listkeys for "bookmarks"
289 preparing listkeys for "bookmarks"
290 sending listkeys command
290 sending listkeys command
291 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
291 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
292 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
292 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
293 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
293 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
294 devel-peer-request: 19 bytes of commands arguments in headers
294 devel-peer-request: 19 bytes of commands arguments in headers
295 devel-peer-request: finished in *.???? seconds (200) (glob)
295 devel-peer-request: finished in *.???? seconds (200) (glob)
296 received listkey for "bookmarks": 0 bytes
296 received listkey for "bookmarks": 0 bytes
297 sending branchmap command
297 sending branchmap command
298 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=branchmap
298 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=branchmap
299 devel-peer-request: Vary X-HgProto-1
299 devel-peer-request: Vary X-HgProto-1
300 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
300 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
301 devel-peer-request: finished in *.???? seconds (200) (glob)
301 devel-peer-request: finished in *.???? seconds (200) (glob)
302 preparing listkeys for "bookmarks"
302 preparing listkeys for "bookmarks"
303 sending listkeys command
303 sending listkeys command
304 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
304 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
305 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
305 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
306 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
306 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
307 devel-peer-request: 19 bytes of commands arguments in headers
307 devel-peer-request: 19 bytes of commands arguments in headers
308 devel-peer-request: finished in *.???? seconds (200) (glob)
308 devel-peer-request: finished in *.???? seconds (200) (glob)
309 received listkey for "bookmarks": 0 bytes
309 received listkey for "bookmarks": 0 bytes
310 1 changesets found
310 1 changesets found
311 list of changesets:
311 list of changesets:
312 7f4e523d01f2cc3765ac8934da3d14db775ff872
312 7f4e523d01f2cc3765ac8934da3d14db775ff872
313 bundle2-output-bundle: "HG20", 5 parts total
313 bundle2-output-bundle: "HG20", 5 parts total
314 bundle2-output-part: "replycaps" 205 bytes payload
314 bundle2-output-part: "replycaps" 205 bytes payload
315 bundle2-output-part: "check:phases" 24 bytes payload
315 bundle2-output-part: "check:phases" 24 bytes payload
316 bundle2-output-part: "check:heads" streamed payload
316 bundle2-output-part: "check:heads" streamed payload
317 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
317 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
318 bundle2-output-part: "phase-heads" 24 bytes payload
318 bundle2-output-part: "phase-heads" 24 bytes payload
319 sending unbundle command
319 sending unbundle command
320 sending 1013 bytes
320 sending 1013 bytes
321 devel-peer-request: POST http://localhost:$HGPORT2/?cmd=unbundle
321 devel-peer-request: POST http://localhost:$HGPORT2/?cmd=unbundle
322 devel-peer-request: Content-length 1013
322 devel-peer-request: Content-length 1013
323 devel-peer-request: Content-type application/mercurial-0.1
323 devel-peer-request: Content-type application/mercurial-0.1
324 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
324 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
325 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
325 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
326 devel-peer-request: 16 bytes of commands arguments in headers
326 devel-peer-request: 16 bytes of commands arguments in headers
327 devel-peer-request: 1013 bytes of data
327 devel-peer-request: 1013 bytes of data
328 devel-peer-request: finished in *.???? seconds (200) (glob)
328 devel-peer-request: finished in *.???? seconds (200) (glob)
329 bundle2-input-bundle: no-transaction
329 bundle2-input-bundle: no-transaction
330 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
330 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
331 bundle2-input-part: "output" (advisory) (params: 0 advisory) supported
331 bundle2-input-part: "output" (advisory) (params: 0 advisory) supported
332 bundle2-input-part: total payload size 100
332 bundle2-input-part: total payload size 100
333 remote: adding changesets
333 remote: adding changesets
334 remote: adding manifests
334 remote: adding manifests
335 remote: adding file changes
335 remote: adding file changes
336 remote: added 1 changesets with 1 changes to 1 files
336 remote: added 1 changesets with 1 changes to 1 files
337 bundle2-input-part: "output" (advisory) supported
337 bundle2-input-part: "output" (advisory) supported
338 bundle2-input-bundle: 2 parts total
338 bundle2-input-bundle: 2 parts total
339 preparing listkeys for "phases"
339 preparing listkeys for "phases"
340 sending listkeys command
340 sending listkeys command
341 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
341 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
342 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
342 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
343 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
343 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
344 devel-peer-request: 16 bytes of commands arguments in headers
344 devel-peer-request: 16 bytes of commands arguments in headers
345 devel-peer-request: finished in *.???? seconds (200) (glob)
345 devel-peer-request: finished in *.???? seconds (200) (glob)
346 received listkey for "phases": 15 bytes
346 received listkey for "phases": 15 bytes
347 $ hg rollback -q
347 $ hg rollback -q
348
348
349 $ sed 's/.*] "/"/' < ../access.log
349 $ sed 's/.*] "/"/' < ../access.log
350 "GET /?cmd=capabilities HTTP/1.1" 401 -
350 "GET /?cmd=capabilities HTTP/1.1" 401 -
351 "GET /?cmd=capabilities HTTP/1.1" 401 -
351 "GET /?cmd=capabilities HTTP/1.1" 401 -
352 "GET /?cmd=capabilities HTTP/1.1" 401 -
352 "GET /?cmd=capabilities HTTP/1.1" 401 -
353 "GET /?cmd=capabilities HTTP/1.1" 200 -
353 "GET /?cmd=capabilities HTTP/1.1" 200 -
354 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
354 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
355 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
355 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
356 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
356 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
357 "GET /?cmd=capabilities HTTP/1.1" 401 -
357 "GET /?cmd=capabilities HTTP/1.1" 401 -
358 "GET /?cmd=capabilities HTTP/1.1" 200 -
358 "GET /?cmd=capabilities HTTP/1.1" 200 -
359 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
359 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
360 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
360 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
361 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
361 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
362 "GET /?cmd=capabilities HTTP/1.1" 401 -
362 "GET /?cmd=capabilities HTTP/1.1" 401 -
363 "GET /?cmd=capabilities HTTP/1.1" 200 -
363 "GET /?cmd=capabilities HTTP/1.1" 200 -
364 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
364 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
365 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
365 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
366 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
366 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
367 "GET /?cmd=capabilities HTTP/1.1" 401 -
367 "GET /?cmd=capabilities HTTP/1.1" 401 -
368 "GET /?cmd=capabilities HTTP/1.1" 200 -
368 "GET /?cmd=capabilities HTTP/1.1" 200 -
369 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
369 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
370 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
370 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
371 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
371 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
372 "GET /?cmd=capabilities HTTP/1.1" 401 -
372 "GET /?cmd=capabilities HTTP/1.1" 401 -
373 "GET /?cmd=capabilities HTTP/1.1" 200 -
373 "GET /?cmd=capabilities HTTP/1.1" 200 -
374 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
374 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
375 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
375 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
376 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
376 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
377 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
377 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
378 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
378 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
379 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
379 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
380 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=0&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&stream=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
380 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=0&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&stream=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
381 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
381 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
382 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
382 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
383 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
383 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
384 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=1&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
384 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=1&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
385 "GET /?cmd=capabilities HTTP/1.1" 401 -
385 "GET /?cmd=capabilities HTTP/1.1" 401 -
386 "GET /?cmd=capabilities HTTP/1.1" 401 -
386 "GET /?cmd=capabilities HTTP/1.1" 401 -
387 "GET /?cmd=capabilities HTTP/1.1" 403 -
387 "GET /?cmd=capabilities HTTP/1.1" 403 -
388 "GET /?cmd=capabilities HTTP/1.1" 401 -
388 "GET /?cmd=capabilities HTTP/1.1" 401 -
389 "GET /?cmd=capabilities HTTP/1.1" 200 -
389 "GET /?cmd=capabilities HTTP/1.1" 200 -
390 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
390 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
391 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
391 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
392 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
392 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
393 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
393 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
394 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
394 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
395 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365* (glob)
395 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365* (glob)
396 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
396 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
397 "GET /?cmd=capabilities HTTP/1.1" 401 -
397 "GET /?cmd=capabilities HTTP/1.1" 401 -
398 "GET /?cmd=capabilities HTTP/1.1" 200 -
398 "GET /?cmd=capabilities HTTP/1.1" 200 -
399 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
399 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
400 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
400 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
401 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
401 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
402 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
402 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
403 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
403 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
404 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
404 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
405 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
405 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
406
406
407 $ cd ..
407 $ cd ..
408
408
409 clone of serve with repo in root and unserved subrepo (issue2970)
409 clone of serve with repo in root and unserved subrepo (issue2970)
410
410
411 $ hg --cwd test init sub
411 $ hg --cwd test init sub
412 $ echo empty > test/sub/empty
412 $ echo empty > test/sub/empty
413 $ hg --cwd test/sub add empty
413 $ hg --cwd test/sub add empty
414 $ hg --cwd test/sub commit -qm 'add empty'
414 $ hg --cwd test/sub commit -qm 'add empty'
415 $ hg --cwd test/sub tag -r 0 something
415 $ hg --cwd test/sub tag -r 0 something
416 $ echo sub = sub > test/.hgsub
416 $ echo sub = sub > test/.hgsub
417 $ hg --cwd test add .hgsub
417 $ hg --cwd test add .hgsub
418 $ hg --cwd test commit -qm 'add subrepo'
418 $ hg --cwd test commit -qm 'add subrepo'
419 $ hg clone http://localhost:$HGPORT noslash-clone
419 $ hg clone http://localhost:$HGPORT noslash-clone
420 requesting all changes
420 requesting all changes
421 adding changesets
421 adding changesets
422 adding manifests
422 adding manifests
423 adding file changes
423 adding file changes
424 added 3 changesets with 7 changes to 7 files
424 added 3 changesets with 7 changes to 7 files
425 new changesets 8b6053c928fe:56f9bc90cce6
425 new changesets 8b6053c928fe:56f9bc90cce6
426 updating to branch default
426 updating to branch default
427 cloning subrepo sub from http://localhost:$HGPORT/sub
427 cloning subrepo sub from http://localhost:$HGPORT/sub
428 abort: HTTP Error 404: Not Found
428 abort: HTTP Error 404: Not Found
429 [255]
429 [255]
430 $ hg clone http://localhost:$HGPORT/ slash-clone
430 $ hg clone http://localhost:$HGPORT/ slash-clone
431 requesting all changes
431 requesting all changes
432 adding changesets
432 adding changesets
433 adding manifests
433 adding manifests
434 adding file changes
434 adding file changes
435 added 3 changesets with 7 changes to 7 files
435 added 3 changesets with 7 changes to 7 files
436 new changesets 8b6053c928fe:56f9bc90cce6
436 new changesets 8b6053c928fe:56f9bc90cce6
437 updating to branch default
437 updating to branch default
438 cloning subrepo sub from http://localhost:$HGPORT/sub
438 cloning subrepo sub from http://localhost:$HGPORT/sub
439 abort: HTTP Error 404: Not Found
439 abort: HTTP Error 404: Not Found
440 [255]
440 [255]
441
441
442 check error log
442 check error log
443
443
444 $ cat error.log
444 $ cat error.log
445
445
446 check abort error reporting while pulling/cloning
446 check abort error reporting while pulling/cloning
447
447
448 $ $RUNTESTDIR/killdaemons.py
448 $ $RUNTESTDIR/killdaemons.py
449 $ hg serve -R test -p $HGPORT -d --pid-file=hg3.pid -E error.log --config extensions.crash=${TESTDIR}/crashgetbundler.py
449 $ hg serve -R test -p $HGPORT -d --pid-file=hg3.pid -E error.log --config extensions.crash=${TESTDIR}/crashgetbundler.py
450 $ cat hg3.pid >> $DAEMON_PIDS
450 $ cat hg3.pid >> $DAEMON_PIDS
451 $ hg clone http://localhost:$HGPORT/ abort-clone
451 $ hg clone http://localhost:$HGPORT/ abort-clone
452 requesting all changes
452 requesting all changes
453 remote: abort: this is an exercise
453 remote: abort: this is an exercise
454 abort: pull failed on remote
454 abort: pull failed on remote
455 [255]
455 [255]
456 $ cat error.log
456 $ cat error.log
457
457
458 disable pull-based clones
458 disable pull-based clones
459
459
460 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg4.pid -E error.log --config server.disablefullbundle=True
460 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg4.pid -E error.log --config server.disablefullbundle=True
461 $ cat hg4.pid >> $DAEMON_PIDS
461 $ cat hg4.pid >> $DAEMON_PIDS
462 $ hg clone http://localhost:$HGPORT1/ disable-pull-clone
462 $ hg clone http://localhost:$HGPORT1/ disable-pull-clone
463 requesting all changes
463 requesting all changes
464 remote: abort: server has pull-based clones disabled
464 remote: abort: server has pull-based clones disabled
465 abort: pull failed on remote
465 abort: pull failed on remote
466 (remove --pull if specified or upgrade Mercurial)
466 (remove --pull if specified or upgrade Mercurial)
467 [255]
467 [255]
468
468
469 #if no-reposimplestore
469 #if no-reposimplestore
470 ... but keep stream clones working
470 ... but keep stream clones working
471
471
472 $ hg clone --stream --noupdate http://localhost:$HGPORT1/ test-stream-clone
472 $ hg clone --stream --noupdate http://localhost:$HGPORT1/ test-stream-clone
473 streaming all changes
473 streaming all changes
474 * files to transfer, * of data (glob)
474 * files to transfer, * of data (glob)
475 transferred * in * seconds (*/sec) (glob)
475 transferred * in * seconds (*/sec) (glob)
476 $ cat error.log
476 $ cat error.log
477 #endif
477 #endif
478
478
479 ... and also keep partial clones and pulls working
479 ... and also keep partial clones and pulls working
480 $ hg clone http://localhost:$HGPORT1 --rev 0 test/partial/clone
480 $ hg clone http://localhost:$HGPORT1 --rev 0 test/partial/clone
481 adding changesets
481 adding changesets
482 adding manifests
482 adding manifests
483 adding file changes
483 adding file changes
484 added 1 changesets with 4 changes to 4 files
484 added 1 changesets with 4 changes to 4 files
485 new changesets 8b6053c928fe
485 new changesets 8b6053c928fe
486 updating to branch default
486 updating to branch default
487 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
487 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
488 $ hg pull -R test/partial/clone
488 $ hg pull -R test/partial/clone
489 pulling from http://localhost:$HGPORT1/
489 pulling from http://localhost:$HGPORT1/
490 searching for changes
490 searching for changes
491 adding changesets
491 adding changesets
492 adding manifests
492 adding manifests
493 adding file changes
493 adding file changes
494 added 2 changesets with 3 changes to 3 files
494 added 2 changesets with 3 changes to 3 files
495 new changesets 5fed3813f7f5:56f9bc90cce6
495 new changesets 5fed3813f7f5:56f9bc90cce6
496 (run 'hg update' to get a working copy)
496 (run 'hg update' to get a working copy)
497
497
498 $ hg clone -U -r 0 test/partial/clone test/another/clone
498 $ hg clone -U -r 0 test/partial/clone test/another/clone
499 adding changesets
499 adding changesets
500 adding manifests
500 adding manifests
501 adding file changes
501 adding file changes
502 added 1 changesets with 4 changes to 4 files
502 added 1 changesets with 4 changes to 4 files
503 new changesets 8b6053c928fe
503 new changesets 8b6053c928fe
504
504
505 corrupt cookies file should yield a warning
505 corrupt cookies file should yield a warning
506
506
507 $ cat > $TESTTMP/cookies.txt << EOF
507 $ cat > $TESTTMP/cookies.txt << EOF
508 > bad format
508 > bad format
509 > EOF
509 > EOF
510
510
511 $ hg --config auth.cookiefile=$TESTTMP/cookies.txt id http://localhost:$HGPORT/
511 $ hg --config auth.cookiefile=$TESTTMP/cookies.txt id http://localhost:$HGPORT/
512 (error loading cookie file $TESTTMP/cookies.txt: '*/cookies.txt' does not look like a Netscape format cookies file; continuing without cookies) (glob)
512 (error loading cookie file $TESTTMP/cookies.txt: '*/cookies.txt' does not look like a Netscape format cookies file; continuing without cookies) (glob)
513 56f9bc90cce6
513 56f9bc90cce6
514
514
515 $ killdaemons.py
515 $ killdaemons.py
516
516
517 Create dummy authentication handler that looks for cookies. It doesn't do anything
517 Create dummy authentication handler that looks for cookies. It doesn't do anything
518 useful. It just raises an HTTP 500 with details about the Cookie request header.
518 useful. It just raises an HTTP 500 with details about the Cookie request header.
519 We raise HTTP 500 because its message is printed in the abort message.
519 We raise HTTP 500 because its message is printed in the abort message.
520
520
521 $ cat > cookieauth.py << EOF
521 $ cat > cookieauth.py << EOF
522 > from mercurial import util
522 > from mercurial import util
523 > from mercurial.hgweb import common
523 > from mercurial.hgweb import common
524 > def perform_authentication(hgweb, req, op):
524 > def perform_authentication(hgweb, req, op):
525 > cookie = req.headers.get(b'Cookie')
525 > cookie = req.headers.get(b'Cookie')
526 > if not cookie:
526 > if not cookie:
527 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, b'no-cookie')
527 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, b'no-cookie')
528 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, b'Cookie: %s' % cookie)
528 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, b'Cookie: %s' % cookie)
529 > def extsetup():
529 > def extsetup(ui):
530 > common.permhooks.insert(0, perform_authentication)
530 > common.permhooks.insert(0, perform_authentication)
531 > EOF
531 > EOF
532
532
533 $ hg serve --config extensions.cookieauth=cookieauth.py -R test -p $HGPORT -d --pid-file=pid
533 $ hg serve --config extensions.cookieauth=cookieauth.py -R test -p $HGPORT -d --pid-file=pid
534 $ cat pid > $DAEMON_PIDS
534 $ cat pid > $DAEMON_PIDS
535
535
536 Request without cookie sent should fail due to lack of cookie
536 Request without cookie sent should fail due to lack of cookie
537
537
538 $ hg id http://localhost:$HGPORT
538 $ hg id http://localhost:$HGPORT
539 abort: HTTP Error 500: no-cookie
539 abort: HTTP Error 500: no-cookie
540 [255]
540 [255]
541
541
542 Populate a cookies file
542 Populate a cookies file
543
543
544 $ cat > cookies.txt << EOF
544 $ cat > cookies.txt << EOF
545 > # HTTP Cookie File
545 > # HTTP Cookie File
546 > # Expiration is 2030-01-01 at midnight
546 > # Expiration is 2030-01-01 at midnight
547 > .example.com TRUE / FALSE 1893456000 hgkey examplevalue
547 > .example.com TRUE / FALSE 1893456000 hgkey examplevalue
548 > EOF
548 > EOF
549
549
550 Should not send a cookie for another domain
550 Should not send a cookie for another domain
551
551
552 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
552 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
553 abort: HTTP Error 500: no-cookie
553 abort: HTTP Error 500: no-cookie
554 [255]
554 [255]
555
555
556 Add a cookie entry for our test server and verify it is sent
556 Add a cookie entry for our test server and verify it is sent
557
557
558 $ cat >> cookies.txt << EOF
558 $ cat >> cookies.txt << EOF
559 > localhost.local FALSE / FALSE 1893456000 hgkey localhostvalue
559 > localhost.local FALSE / FALSE 1893456000 hgkey localhostvalue
560 > EOF
560 > EOF
561
561
562 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
562 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
563 abort: HTTP Error 500: Cookie: hgkey=localhostvalue
563 abort: HTTP Error 500: Cookie: hgkey=localhostvalue
564 [255]
564 [255]
@@ -1,464 +1,464 b''
1 #testcases sshv1 sshv2
1 #testcases sshv1 sshv2
2
2
3 #if sshv2
3 #if sshv2
4 $ cat >> $HGRCPATH << EOF
4 $ cat >> $HGRCPATH << EOF
5 > [experimental]
5 > [experimental]
6 > sshpeer.advertise-v2 = true
6 > sshpeer.advertise-v2 = true
7 > sshserver.support-v2 = true
7 > sshserver.support-v2 = true
8 > EOF
8 > EOF
9 #endif
9 #endif
10
10
11 This file contains testcases that tend to be related to the wire protocol part
11 This file contains testcases that tend to be related to the wire protocol part
12 of largefiles.
12 of largefiles.
13
13
14 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
14 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
15 $ mkdir "${USERCACHE}"
15 $ mkdir "${USERCACHE}"
16 $ cat >> $HGRCPATH <<EOF
16 $ cat >> $HGRCPATH <<EOF
17 > [extensions]
17 > [extensions]
18 > largefiles=
18 > largefiles=
19 > purge=
19 > purge=
20 > rebase=
20 > rebase=
21 > transplant=
21 > transplant=
22 > [phases]
22 > [phases]
23 > publish=False
23 > publish=False
24 > [largefiles]
24 > [largefiles]
25 > minsize=2
25 > minsize=2
26 > patterns=glob:**.dat
26 > patterns=glob:**.dat
27 > usercache=${USERCACHE}
27 > usercache=${USERCACHE}
28 > [web]
28 > [web]
29 > allow-archive = zip
29 > allow-archive = zip
30 > [hooks]
30 > [hooks]
31 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
31 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
32 > EOF
32 > EOF
33
33
34
34
35 #if serve
35 #if serve
36 vanilla clients not locked out from largefiles servers on vanilla repos
36 vanilla clients not locked out from largefiles servers on vanilla repos
37 $ mkdir r1
37 $ mkdir r1
38 $ cd r1
38 $ cd r1
39 $ hg init
39 $ hg init
40 $ echo c1 > f1
40 $ echo c1 > f1
41 $ hg add f1
41 $ hg add f1
42 $ hg commit -m "m1"
42 $ hg commit -m "m1"
43 Invoking status precommit hook
43 Invoking status precommit hook
44 A f1
44 A f1
45 $ cd ..
45 $ cd ..
46 $ hg serve -R r1 -d -p $HGPORT --pid-file hg.pid
46 $ hg serve -R r1 -d -p $HGPORT --pid-file hg.pid
47 $ cat hg.pid >> $DAEMON_PIDS
47 $ cat hg.pid >> $DAEMON_PIDS
48 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT r2
48 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT r2
49 requesting all changes
49 requesting all changes
50 adding changesets
50 adding changesets
51 adding manifests
51 adding manifests
52 adding file changes
52 adding file changes
53 added 1 changesets with 1 changes to 1 files
53 added 1 changesets with 1 changes to 1 files
54 new changesets b6eb3a2e2efe (1 drafts)
54 new changesets b6eb3a2e2efe (1 drafts)
55 updating to branch default
55 updating to branch default
56 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
56 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
57
57
58 largefiles clients still work with vanilla servers
58 largefiles clients still work with vanilla servers
59 $ hg serve --config extensions.largefiles=! -R r1 -d -p $HGPORT1 --pid-file hg.pid
59 $ hg serve --config extensions.largefiles=! -R r1 -d -p $HGPORT1 --pid-file hg.pid
60 $ cat hg.pid >> $DAEMON_PIDS
60 $ cat hg.pid >> $DAEMON_PIDS
61 $ hg clone http://localhost:$HGPORT1 r3
61 $ hg clone http://localhost:$HGPORT1 r3
62 requesting all changes
62 requesting all changes
63 adding changesets
63 adding changesets
64 adding manifests
64 adding manifests
65 adding file changes
65 adding file changes
66 added 1 changesets with 1 changes to 1 files
66 added 1 changesets with 1 changes to 1 files
67 new changesets b6eb3a2e2efe (1 drafts)
67 new changesets b6eb3a2e2efe (1 drafts)
68 updating to branch default
68 updating to branch default
69 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
69 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
70 #endif
70 #endif
71
71
72 vanilla clients locked out from largefiles http repos
72 vanilla clients locked out from largefiles http repos
73 $ mkdir r4
73 $ mkdir r4
74 $ cd r4
74 $ cd r4
75 $ hg init
75 $ hg init
76 $ echo c1 > f1
76 $ echo c1 > f1
77 $ hg add --large f1
77 $ hg add --large f1
78 $ hg commit -m "m1"
78 $ hg commit -m "m1"
79 Invoking status precommit hook
79 Invoking status precommit hook
80 A f1
80 A f1
81 $ cd ..
81 $ cd ..
82
82
83 largefiles can be pushed locally (issue3583)
83 largefiles can be pushed locally (issue3583)
84 $ hg init dest
84 $ hg init dest
85 $ cd r4
85 $ cd r4
86 $ hg outgoing ../dest
86 $ hg outgoing ../dest
87 comparing with ../dest
87 comparing with ../dest
88 searching for changes
88 searching for changes
89 changeset: 0:639881c12b4c
89 changeset: 0:639881c12b4c
90 tag: tip
90 tag: tip
91 user: test
91 user: test
92 date: Thu Jan 01 00:00:00 1970 +0000
92 date: Thu Jan 01 00:00:00 1970 +0000
93 summary: m1
93 summary: m1
94
94
95 $ hg push ../dest
95 $ hg push ../dest
96 pushing to ../dest
96 pushing to ../dest
97 searching for changes
97 searching for changes
98 adding changesets
98 adding changesets
99 adding manifests
99 adding manifests
100 adding file changes
100 adding file changes
101 added 1 changesets with 1 changes to 1 files
101 added 1 changesets with 1 changes to 1 files
102
102
103 exit code with nothing outgoing (issue3611)
103 exit code with nothing outgoing (issue3611)
104 $ hg outgoing ../dest
104 $ hg outgoing ../dest
105 comparing with ../dest
105 comparing with ../dest
106 searching for changes
106 searching for changes
107 no changes found
107 no changes found
108 [1]
108 [1]
109 $ cd ..
109 $ cd ..
110
110
111 #if serve
111 #if serve
112 $ hg serve -R r4 -d -p $HGPORT2 --pid-file hg.pid
112 $ hg serve -R r4 -d -p $HGPORT2 --pid-file hg.pid
113 $ cat hg.pid >> $DAEMON_PIDS
113 $ cat hg.pid >> $DAEMON_PIDS
114 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT2 r5
114 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT2 r5
115 abort: remote error:
115 abort: remote error:
116
116
117 This repository uses the largefiles extension.
117 This repository uses the largefiles extension.
118
118
119 Please enable it in your Mercurial config file.
119 Please enable it in your Mercurial config file.
120 [255]
120 [255]
121
121
122 used all HGPORTs, kill all daemons
122 used all HGPORTs, kill all daemons
123 $ killdaemons.py
123 $ killdaemons.py
124 #endif
124 #endif
125
125
126 vanilla clients locked out from largefiles ssh repos
126 vanilla clients locked out from largefiles ssh repos
127 $ hg --config extensions.largefiles=! clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/r4 r5
127 $ hg --config extensions.largefiles=! clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/r4 r5
128 remote:
128 remote:
129 remote: This repository uses the largefiles extension.
129 remote: This repository uses the largefiles extension.
130 remote:
130 remote:
131 remote: Please enable it in your Mercurial config file.
131 remote: Please enable it in your Mercurial config file.
132 remote:
132 remote:
133 remote: -
133 remote: -
134 abort: remote error
134 abort: remote error
135 (check previous remote output)
135 (check previous remote output)
136 [255]
136 [255]
137
137
138 #if serve
138 #if serve
139
139
140 largefiles clients refuse to push largefiles repos to vanilla servers
140 largefiles clients refuse to push largefiles repos to vanilla servers
141 $ mkdir r6
141 $ mkdir r6
142 $ cd r6
142 $ cd r6
143 $ hg init
143 $ hg init
144 $ echo c1 > f1
144 $ echo c1 > f1
145 $ hg add f1
145 $ hg add f1
146 $ hg commit -m "m1"
146 $ hg commit -m "m1"
147 Invoking status precommit hook
147 Invoking status precommit hook
148 A f1
148 A f1
149 $ cat >> .hg/hgrc <<!
149 $ cat >> .hg/hgrc <<!
150 > [web]
150 > [web]
151 > push_ssl = false
151 > push_ssl = false
152 > allow_push = *
152 > allow_push = *
153 > !
153 > !
154 $ cd ..
154 $ cd ..
155 $ hg clone r6 r7
155 $ hg clone r6 r7
156 updating to branch default
156 updating to branch default
157 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
157 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
158 $ cd r7
158 $ cd r7
159 $ echo c2 > f2
159 $ echo c2 > f2
160 $ hg add --large f2
160 $ hg add --large f2
161 $ hg commit -m "m2"
161 $ hg commit -m "m2"
162 Invoking status precommit hook
162 Invoking status precommit hook
163 A f2
163 A f2
164 $ hg verify --large
164 $ hg verify --large
165 checking changesets
165 checking changesets
166 checking manifests
166 checking manifests
167 crosschecking files in changesets and manifests
167 crosschecking files in changesets and manifests
168 checking files
168 checking files
169 checked 2 changesets with 2 changes to 2 files
169 checked 2 changesets with 2 changes to 2 files
170 searching 1 changesets for largefiles
170 searching 1 changesets for largefiles
171 verified existence of 1 revisions of 1 largefiles
171 verified existence of 1 revisions of 1 largefiles
172 $ hg serve --config extensions.largefiles=! -R ../r6 -d -p $HGPORT --pid-file ../hg.pid
172 $ hg serve --config extensions.largefiles=! -R ../r6 -d -p $HGPORT --pid-file ../hg.pid
173 $ cat ../hg.pid >> $DAEMON_PIDS
173 $ cat ../hg.pid >> $DAEMON_PIDS
174 $ hg push http://localhost:$HGPORT
174 $ hg push http://localhost:$HGPORT
175 pushing to http://localhost:$HGPORT/
175 pushing to http://localhost:$HGPORT/
176 searching for changes
176 searching for changes
177 abort: http://localhost:$HGPORT/ does not appear to be a largefile store
177 abort: http://localhost:$HGPORT/ does not appear to be a largefile store
178 [255]
178 [255]
179 $ cd ..
179 $ cd ..
180
180
181 putlfile errors are shown (issue3123)
181 putlfile errors are shown (issue3123)
182 Corrupt the cached largefile in r7 and move it out of the servers usercache
182 Corrupt the cached largefile in r7 and move it out of the servers usercache
183 $ mv r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 .
183 $ mv r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 .
184 $ echo 'client side corruption' > r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
184 $ echo 'client side corruption' > r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
185 $ rm "$USERCACHE/4cdac4d8b084d0b599525cf732437fb337d422a8"
185 $ rm "$USERCACHE/4cdac4d8b084d0b599525cf732437fb337d422a8"
186 $ hg init empty
186 $ hg init empty
187 $ hg serve -R empty -d -p $HGPORT1 --pid-file hg.pid \
187 $ hg serve -R empty -d -p $HGPORT1 --pid-file hg.pid \
188 > --config 'web.allow_push=*' --config web.push_ssl=False
188 > --config 'web.allow_push=*' --config web.push_ssl=False
189 $ cat hg.pid >> $DAEMON_PIDS
189 $ cat hg.pid >> $DAEMON_PIDS
190 $ hg push -R r7 http://localhost:$HGPORT1
190 $ hg push -R r7 http://localhost:$HGPORT1
191 pushing to http://localhost:$HGPORT1/
191 pushing to http://localhost:$HGPORT1/
192 searching for changes
192 searching for changes
193 remote: largefiles: failed to put 4cdac4d8b084d0b599525cf732437fb337d422a8 into store: largefile contents do not match hash
193 remote: largefiles: failed to put 4cdac4d8b084d0b599525cf732437fb337d422a8 into store: largefile contents do not match hash
194 abort: remotestore: could not put $TESTTMP/r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 to remote store http://localhost:$HGPORT1/
194 abort: remotestore: could not put $TESTTMP/r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 to remote store http://localhost:$HGPORT1/
195 [255]
195 [255]
196 $ mv 4cdac4d8b084d0b599525cf732437fb337d422a8 r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
196 $ mv 4cdac4d8b084d0b599525cf732437fb337d422a8 r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
197 Push of file that exists on server but is corrupted - magic healing would be nice ... but too magic
197 Push of file that exists on server but is corrupted - magic healing would be nice ... but too magic
198 $ echo "server side corruption" > empty/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
198 $ echo "server side corruption" > empty/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
199 $ hg push -R r7 http://localhost:$HGPORT1
199 $ hg push -R r7 http://localhost:$HGPORT1
200 pushing to http://localhost:$HGPORT1/
200 pushing to http://localhost:$HGPORT1/
201 searching for changes
201 searching for changes
202 remote: adding changesets
202 remote: adding changesets
203 remote: adding manifests
203 remote: adding manifests
204 remote: adding file changes
204 remote: adding file changes
205 remote: added 2 changesets with 2 changes to 2 files
205 remote: added 2 changesets with 2 changes to 2 files
206 $ cat empty/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
206 $ cat empty/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
207 server side corruption
207 server side corruption
208 $ rm -rf empty
208 $ rm -rf empty
209
209
210 Push a largefiles repository to a served empty repository
210 Push a largefiles repository to a served empty repository
211 $ hg init r8
211 $ hg init r8
212 $ echo c3 > r8/f1
212 $ echo c3 > r8/f1
213 $ hg add --large r8/f1 -R r8
213 $ hg add --large r8/f1 -R r8
214 $ hg commit -m "m1" -R r8
214 $ hg commit -m "m1" -R r8
215 Invoking status precommit hook
215 Invoking status precommit hook
216 A f1
216 A f1
217 $ hg init empty
217 $ hg init empty
218 $ hg serve -R empty -d -p $HGPORT2 --pid-file hg.pid \
218 $ hg serve -R empty -d -p $HGPORT2 --pid-file hg.pid \
219 > --config 'web.allow_push=*' --config web.push_ssl=False
219 > --config 'web.allow_push=*' --config web.push_ssl=False
220 $ cat hg.pid >> $DAEMON_PIDS
220 $ cat hg.pid >> $DAEMON_PIDS
221 $ rm "${USERCACHE}"/*
221 $ rm "${USERCACHE}"/*
222 $ hg push -R r8 http://localhost:$HGPORT2/#default
222 $ hg push -R r8 http://localhost:$HGPORT2/#default
223 pushing to http://localhost:$HGPORT2/
223 pushing to http://localhost:$HGPORT2/
224 searching for changes
224 searching for changes
225 remote: adding changesets
225 remote: adding changesets
226 remote: adding manifests
226 remote: adding manifests
227 remote: adding file changes
227 remote: adding file changes
228 remote: added 1 changesets with 1 changes to 1 files
228 remote: added 1 changesets with 1 changes to 1 files
229 $ [ -f "${USERCACHE}"/02a439e5c31c526465ab1a0ca1f431f76b827b90 ]
229 $ [ -f "${USERCACHE}"/02a439e5c31c526465ab1a0ca1f431f76b827b90 ]
230 $ [ -f empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 ]
230 $ [ -f empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 ]
231
231
232 Clone over http, no largefiles pulled on clone.
232 Clone over http, no largefiles pulled on clone.
233
233
234 $ hg clone http://localhost:$HGPORT2/#default http-clone -U
234 $ hg clone http://localhost:$HGPORT2/#default http-clone -U
235 adding changesets
235 adding changesets
236 adding manifests
236 adding manifests
237 adding file changes
237 adding file changes
238 added 1 changesets with 1 changes to 1 files
238 added 1 changesets with 1 changes to 1 files
239 new changesets cf03e5bb9936 (1 drafts)
239 new changesets cf03e5bb9936 (1 drafts)
240
240
241 Archive contains largefiles
241 Archive contains largefiles
242 >>> import os
242 >>> import os
243 >>> from mercurial import urllibcompat
243 >>> from mercurial import urllibcompat
244 >>> u = 'http://localhost:%s/archive/default.zip' % os.environ['HGPORT2']
244 >>> u = 'http://localhost:%s/archive/default.zip' % os.environ['HGPORT2']
245 >>> with open('archive.zip', 'wb') as f:
245 >>> with open('archive.zip', 'wb') as f:
246 ... f.write(urllibcompat.urlreq.urlopen(u).read()) and None
246 ... f.write(urllibcompat.urlreq.urlopen(u).read()) and None
247 $ unzip -t archive.zip
247 $ unzip -t archive.zip
248 Archive: archive.zip
248 Archive: archive.zip
249 testing: empty-default/.hg_archival.txt*OK (glob)
249 testing: empty-default/.hg_archival.txt*OK (glob)
250 testing: empty-default/f1*OK (glob)
250 testing: empty-default/f1*OK (glob)
251 No errors detected in compressed data of archive.zip.
251 No errors detected in compressed data of archive.zip.
252
252
253 test 'verify' with remotestore:
253 test 'verify' with remotestore:
254
254
255 $ rm "${USERCACHE}"/02a439e5c31c526465ab1a0ca1f431f76b827b90
255 $ rm "${USERCACHE}"/02a439e5c31c526465ab1a0ca1f431f76b827b90
256 $ mv empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 .
256 $ mv empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 .
257 $ hg -R http-clone verify --large --lfa
257 $ hg -R http-clone verify --large --lfa
258 checking changesets
258 checking changesets
259 checking manifests
259 checking manifests
260 crosschecking files in changesets and manifests
260 crosschecking files in changesets and manifests
261 checking files
261 checking files
262 checked 1 changesets with 1 changes to 1 files
262 checked 1 changesets with 1 changes to 1 files
263 searching 1 changesets for largefiles
263 searching 1 changesets for largefiles
264 changeset 0:cf03e5bb9936: f1 missing
264 changeset 0:cf03e5bb9936: f1 missing
265 verified existence of 1 revisions of 1 largefiles
265 verified existence of 1 revisions of 1 largefiles
266 [1]
266 [1]
267 $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/
267 $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/
268 $ hg -R http-clone -q verify --large --lfa
268 $ hg -R http-clone -q verify --large --lfa
269
269
270 largefiles pulled on update - a largefile missing on the server:
270 largefiles pulled on update - a largefile missing on the server:
271 $ mv empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 .
271 $ mv empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 .
272 $ hg -R http-clone up --config largefiles.usercache=http-clone-usercache
272 $ hg -R http-clone up --config largefiles.usercache=http-clone-usercache
273 getting changed largefiles
273 getting changed largefiles
274 f1: largefile 02a439e5c31c526465ab1a0ca1f431f76b827b90 not available from http://localhost:$HGPORT2/
274 f1: largefile 02a439e5c31c526465ab1a0ca1f431f76b827b90 not available from http://localhost:$HGPORT2/
275 0 largefiles updated, 0 removed
275 0 largefiles updated, 0 removed
276 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
276 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
277 $ hg -R http-clone st
277 $ hg -R http-clone st
278 ! f1
278 ! f1
279 $ hg -R http-clone up -Cqr null
279 $ hg -R http-clone up -Cqr null
280
280
281 largefiles pulled on update - a largefile corrupted on the server:
281 largefiles pulled on update - a largefile corrupted on the server:
282 $ echo corruption > empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90
282 $ echo corruption > empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90
283 $ hg -R http-clone up --config largefiles.usercache=http-clone-usercache
283 $ hg -R http-clone up --config largefiles.usercache=http-clone-usercache
284 getting changed largefiles
284 getting changed largefiles
285 f1: data corruption (expected 02a439e5c31c526465ab1a0ca1f431f76b827b90, got 6a7bb2556144babe3899b25e5428123735bb1e27)
285 f1: data corruption (expected 02a439e5c31c526465ab1a0ca1f431f76b827b90, got 6a7bb2556144babe3899b25e5428123735bb1e27)
286 0 largefiles updated, 0 removed
286 0 largefiles updated, 0 removed
287 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
287 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
288 $ hg -R http-clone st
288 $ hg -R http-clone st
289 ! f1
289 ! f1
290 $ [ ! -f http-clone/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 ]
290 $ [ ! -f http-clone/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 ]
291 $ [ ! -f http-clone/f1 ]
291 $ [ ! -f http-clone/f1 ]
292 $ [ ! -f http-clone-usercache ]
292 $ [ ! -f http-clone-usercache ]
293 $ hg -R http-clone verify --large --lfc
293 $ hg -R http-clone verify --large --lfc
294 checking changesets
294 checking changesets
295 checking manifests
295 checking manifests
296 crosschecking files in changesets and manifests
296 crosschecking files in changesets and manifests
297 checking files
297 checking files
298 checked 1 changesets with 1 changes to 1 files
298 checked 1 changesets with 1 changes to 1 files
299 searching 1 changesets for largefiles
299 searching 1 changesets for largefiles
300 verified contents of 1 revisions of 1 largefiles
300 verified contents of 1 revisions of 1 largefiles
301 $ hg -R http-clone up -Cqr null
301 $ hg -R http-clone up -Cqr null
302
302
303 largefiles pulled on update - no server side problems:
303 largefiles pulled on update - no server side problems:
304 $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/
304 $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/
305 $ hg -R http-clone --debug up --config largefiles.usercache=http-clone-usercache --config progress.debug=true
305 $ hg -R http-clone --debug up --config largefiles.usercache=http-clone-usercache --config progress.debug=true
306 resolving manifests
306 resolving manifests
307 branchmerge: False, force: False, partial: False
307 branchmerge: False, force: False, partial: False
308 ancestor: 000000000000, local: 000000000000+, remote: cf03e5bb9936
308 ancestor: 000000000000, local: 000000000000+, remote: cf03e5bb9936
309 .hglf/f1: remote created -> g
309 .hglf/f1: remote created -> g
310 getting .hglf/f1
310 getting .hglf/f1
311 updating: .hglf/f1 1/1 files (100.00%)
311 updating: .hglf/f1 1/1 files (100.00%)
312 getting changed largefiles
312 getting changed largefiles
313 using http://localhost:$HGPORT2/
313 using http://localhost:$HGPORT2/
314 sending capabilities command
314 sending capabilities command
315 sending statlfile command
315 sending statlfile command
316 getting largefiles: 0/1 files (0.00%)
316 getting largefiles: 0/1 files (0.00%)
317 getting f1:02a439e5c31c526465ab1a0ca1f431f76b827b90
317 getting f1:02a439e5c31c526465ab1a0ca1f431f76b827b90
318 sending getlfile command
318 sending getlfile command
319 found 02a439e5c31c526465ab1a0ca1f431f76b827b90 in store
319 found 02a439e5c31c526465ab1a0ca1f431f76b827b90 in store
320 1 largefiles updated, 0 removed
320 1 largefiles updated, 0 removed
321 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
321 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
322
322
323 $ ls http-clone-usercache/*
323 $ ls http-clone-usercache/*
324 http-clone-usercache/02a439e5c31c526465ab1a0ca1f431f76b827b90
324 http-clone-usercache/02a439e5c31c526465ab1a0ca1f431f76b827b90
325
325
326 $ rm -rf empty http-clone*
326 $ rm -rf empty http-clone*
327
327
328 used all HGPORTs, kill all daemons
328 used all HGPORTs, kill all daemons
329 $ killdaemons.py
329 $ killdaemons.py
330
330
331 largefiles should batch verify remote calls
331 largefiles should batch verify remote calls
332
332
333 $ hg init batchverifymain
333 $ hg init batchverifymain
334 $ cd batchverifymain
334 $ cd batchverifymain
335 $ echo "aaa" >> a
335 $ echo "aaa" >> a
336 $ hg add --large a
336 $ hg add --large a
337 $ hg commit -m "a"
337 $ hg commit -m "a"
338 Invoking status precommit hook
338 Invoking status precommit hook
339 A a
339 A a
340 $ echo "bbb" >> b
340 $ echo "bbb" >> b
341 $ hg add --large b
341 $ hg add --large b
342 $ hg commit -m "b"
342 $ hg commit -m "b"
343 Invoking status precommit hook
343 Invoking status precommit hook
344 A b
344 A b
345 $ cd ..
345 $ cd ..
346 $ hg serve -R batchverifymain -d -p $HGPORT --pid-file hg.pid \
346 $ hg serve -R batchverifymain -d -p $HGPORT --pid-file hg.pid \
347 > -A access.log
347 > -A access.log
348 $ cat hg.pid >> $DAEMON_PIDS
348 $ cat hg.pid >> $DAEMON_PIDS
349 $ hg clone --noupdate http://localhost:$HGPORT batchverifyclone
349 $ hg clone --noupdate http://localhost:$HGPORT batchverifyclone
350 requesting all changes
350 requesting all changes
351 adding changesets
351 adding changesets
352 adding manifests
352 adding manifests
353 adding file changes
353 adding file changes
354 added 2 changesets with 2 changes to 2 files
354 added 2 changesets with 2 changes to 2 files
355 new changesets 567253b0f523:04d19c27a332 (2 drafts)
355 new changesets 567253b0f523:04d19c27a332 (2 drafts)
356 $ hg -R batchverifyclone verify --large --lfa
356 $ hg -R batchverifyclone verify --large --lfa
357 checking changesets
357 checking changesets
358 checking manifests
358 checking manifests
359 crosschecking files in changesets and manifests
359 crosschecking files in changesets and manifests
360 checking files
360 checking files
361 checked 2 changesets with 2 changes to 2 files
361 checked 2 changesets with 2 changes to 2 files
362 searching 2 changesets for largefiles
362 searching 2 changesets for largefiles
363 verified existence of 2 revisions of 2 largefiles
363 verified existence of 2 revisions of 2 largefiles
364 $ tail -1 access.log
364 $ tail -1 access.log
365 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=statlfile+sha%3D972a1a11f19934401291cc99117ec614933374ce%3Bstatlfile+sha%3Dc801c9cfe94400963fcb683246217d5db77f9a9a x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
365 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=statlfile+sha%3D972a1a11f19934401291cc99117ec614933374ce%3Bstatlfile+sha%3Dc801c9cfe94400963fcb683246217d5db77f9a9a x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
366 $ hg -R batchverifyclone update
366 $ hg -R batchverifyclone update
367 getting changed largefiles
367 getting changed largefiles
368 2 largefiles updated, 0 removed
368 2 largefiles updated, 0 removed
369 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
369 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
370
370
371 Clear log file before next test
371 Clear log file before next test
372
372
373 $ printf "" > access.log
373 $ printf "" > access.log
374
374
375 Verify should check file on remote server only when file is not
375 Verify should check file on remote server only when file is not
376 available locally.
376 available locally.
377
377
378 $ echo "ccc" >> batchverifymain/c
378 $ echo "ccc" >> batchverifymain/c
379 $ hg -R batchverifymain status
379 $ hg -R batchverifymain status
380 ? c
380 ? c
381 $ hg -R batchverifymain add --large batchverifymain/c
381 $ hg -R batchverifymain add --large batchverifymain/c
382 $ hg -R batchverifymain commit -m "c"
382 $ hg -R batchverifymain commit -m "c"
383 Invoking status precommit hook
383 Invoking status precommit hook
384 A c
384 A c
385 $ hg -R batchverifyclone pull
385 $ hg -R batchverifyclone pull
386 pulling from http://localhost:$HGPORT/
386 pulling from http://localhost:$HGPORT/
387 searching for changes
387 searching for changes
388 adding changesets
388 adding changesets
389 adding manifests
389 adding manifests
390 adding file changes
390 adding file changes
391 added 1 changesets with 1 changes to 1 files
391 added 1 changesets with 1 changes to 1 files
392 new changesets 6bba8cb6935d (1 drafts)
392 new changesets 6bba8cb6935d (1 drafts)
393 (run 'hg update' to get a working copy)
393 (run 'hg update' to get a working copy)
394 $ hg -R batchverifyclone verify --lfa
394 $ hg -R batchverifyclone verify --lfa
395 checking changesets
395 checking changesets
396 checking manifests
396 checking manifests
397 crosschecking files in changesets and manifests
397 crosschecking files in changesets and manifests
398 checking files
398 checking files
399 checked 3 changesets with 3 changes to 3 files
399 checked 3 changesets with 3 changes to 3 files
400 searching 3 changesets for largefiles
400 searching 3 changesets for largefiles
401 verified existence of 3 revisions of 3 largefiles
401 verified existence of 3 revisions of 3 largefiles
402 $ tail -1 access.log
402 $ tail -1 access.log
403 $LOCALIP - - [$LOGDATE$] "GET /?cmd=statlfile HTTP/1.1" 200 - x-hgarg-1:sha=c8559c3c9cfb42131794b7d8009230403b9b454c x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
403 $LOCALIP - - [$LOGDATE$] "GET /?cmd=statlfile HTTP/1.1" 200 - x-hgarg-1:sha=c8559c3c9cfb42131794b7d8009230403b9b454c x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
404
404
405 $ killdaemons.py
405 $ killdaemons.py
406
406
407 largefiles should not ask for password again after successful authorization
407 largefiles should not ask for password again after successful authorization
408
408
409 $ hg init credentialmain
409 $ hg init credentialmain
410 $ cd credentialmain
410 $ cd credentialmain
411 $ echo "aaa" >> a
411 $ echo "aaa" >> a
412 $ hg add --large a
412 $ hg add --large a
413 $ hg commit -m "a"
413 $ hg commit -m "a"
414 Invoking status precommit hook
414 Invoking status precommit hook
415 A a
415 A a
416
416
417 Before running server clear the user cache to force clone to download
417 Before running server clear the user cache to force clone to download
418 a large file from the server rather than to get it from the cache
418 a large file from the server rather than to get it from the cache
419
419
420 $ rm "${USERCACHE}"/*
420 $ rm "${USERCACHE}"/*
421
421
422 $ cd ..
422 $ cd ..
423 $ cat << EOT > userpass.py
423 $ cat << EOT > userpass.py
424 > import base64
424 > import base64
425 > from mercurial.hgweb import common
425 > from mercurial.hgweb import common
426 > def perform_authentication(hgweb, req, op):
426 > def perform_authentication(hgweb, req, op):
427 > auth = req.headers.get(b'Authorization')
427 > auth = req.headers.get(b'Authorization')
428 > if not auth:
428 > if not auth:
429 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, b'who',
429 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, b'who',
430 > [(b'WWW-Authenticate', b'Basic Realm="mercurial"')])
430 > [(b'WWW-Authenticate', b'Basic Realm="mercurial"')])
431 > if base64.b64decode(auth.split()[1]).split(b':', 1) != [b'user', b'pass']:
431 > if base64.b64decode(auth.split()[1]).split(b':', 1) != [b'user', b'pass']:
432 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, b'no')
432 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, b'no')
433 > def extsetup():
433 > def extsetup(ui):
434 > common.permhooks.insert(0, perform_authentication)
434 > common.permhooks.insert(0, perform_authentication)
435 > EOT
435 > EOT
436 $ hg serve --config extensions.x=userpass.py -R credentialmain \
436 $ hg serve --config extensions.x=userpass.py -R credentialmain \
437 > -d -p $HGPORT --pid-file hg.pid -A access.log
437 > -d -p $HGPORT --pid-file hg.pid -A access.log
438 $ cat hg.pid >> $DAEMON_PIDS
438 $ cat hg.pid >> $DAEMON_PIDS
439 $ cat << EOF > get_pass.py
439 $ cat << EOF > get_pass.py
440 > import getpass
440 > import getpass
441 > def newgetpass(arg):
441 > def newgetpass(arg):
442 > return "pass"
442 > return "pass"
443 > getpass.getpass = newgetpass
443 > getpass.getpass = newgetpass
444 > EOF
444 > EOF
445 $ hg clone --config ui.interactive=true --config extensions.getpass=get_pass.py \
445 $ hg clone --config ui.interactive=true --config extensions.getpass=get_pass.py \
446 > http://user@localhost:$HGPORT credentialclone
446 > http://user@localhost:$HGPORT credentialclone
447 http authorization required for http://localhost:$HGPORT/
447 http authorization required for http://localhost:$HGPORT/
448 realm: mercurial
448 realm: mercurial
449 user: user
449 user: user
450 password: requesting all changes
450 password: requesting all changes
451 adding changesets
451 adding changesets
452 adding manifests
452 adding manifests
453 adding file changes
453 adding file changes
454 added 1 changesets with 1 changes to 1 files
454 added 1 changesets with 1 changes to 1 files
455 new changesets 567253b0f523 (1 drafts)
455 new changesets 567253b0f523 (1 drafts)
456 updating to branch default
456 updating to branch default
457 getting changed largefiles
457 getting changed largefiles
458 1 largefiles updated, 0 removed
458 1 largefiles updated, 0 removed
459 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
459 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
460
460
461 $ killdaemons.py
461 $ killdaemons.py
462 $ rm hg.pid access.log
462 $ rm hg.pid access.log
463
463
464 #endif
464 #endif
@@ -1,481 +1,481 b''
1 #require serve no-reposimplestore no-chg
1 #require serve no-reposimplestore no-chg
2
2
3 $ cat >> $HGRCPATH <<EOF
3 $ cat >> $HGRCPATH <<EOF
4 > [extensions]
4 > [extensions]
5 > lfs=
5 > lfs=
6 > [lfs]
6 > [lfs]
7 > track=all()
7 > track=all()
8 > [web]
8 > [web]
9 > push_ssl = False
9 > push_ssl = False
10 > allow-push = *
10 > allow-push = *
11 > EOF
11 > EOF
12
12
13 Serving LFS files can experimentally be turned off. The long term solution is
13 Serving LFS files can experimentally be turned off. The long term solution is
14 to support the 'verify' action in both client and server, so that the server can
14 to support the 'verify' action in both client and server, so that the server can
15 tell the client to store files elsewhere.
15 tell the client to store files elsewhere.
16
16
17 $ hg init server
17 $ hg init server
18 $ hg --config "lfs.usercache=$TESTTMP/servercache" \
18 $ hg --config "lfs.usercache=$TESTTMP/servercache" \
19 > --config experimental.lfs.serve=False -R server serve -d \
19 > --config experimental.lfs.serve=False -R server serve -d \
20 > -p $HGPORT --pid-file=hg.pid -A $TESTTMP/access.log -E $TESTTMP/errors.log
20 > -p $HGPORT --pid-file=hg.pid -A $TESTTMP/access.log -E $TESTTMP/errors.log
21 $ cat hg.pid >> $DAEMON_PIDS
21 $ cat hg.pid >> $DAEMON_PIDS
22
22
23 Uploads fail...
23 Uploads fail...
24
24
25 $ hg init client
25 $ hg init client
26 $ echo 'this-is-an-lfs-file' > client/lfs.bin
26 $ echo 'this-is-an-lfs-file' > client/lfs.bin
27 $ hg -R client ci -Am 'initial commit'
27 $ hg -R client ci -Am 'initial commit'
28 adding lfs.bin
28 adding lfs.bin
29 $ hg -R client push http://localhost:$HGPORT
29 $ hg -R client push http://localhost:$HGPORT
30 pushing to http://localhost:$HGPORT/
30 pushing to http://localhost:$HGPORT/
31 searching for changes
31 searching for changes
32 abort: LFS HTTP error: HTTP Error 400: no such method: .git!
32 abort: LFS HTTP error: HTTP Error 400: no such method: .git!
33 (check that lfs serving is enabled on http://localhost:$HGPORT/.git/info/lfs and "upload" is supported)
33 (check that lfs serving is enabled on http://localhost:$HGPORT/.git/info/lfs and "upload" is supported)
34 [255]
34 [255]
35
35
36 ... so do a local push to make the data available. Remove the blob from the
36 ... so do a local push to make the data available. Remove the blob from the
37 default cache, so it attempts to download.
37 default cache, so it attempts to download.
38 $ hg --config "lfs.usercache=$TESTTMP/servercache" \
38 $ hg --config "lfs.usercache=$TESTTMP/servercache" \
39 > --config "lfs.url=null://" \
39 > --config "lfs.url=null://" \
40 > -R client push -q server
40 > -R client push -q server
41 $ mv `hg config lfs.usercache` $TESTTMP/servercache
41 $ mv `hg config lfs.usercache` $TESTTMP/servercache
42
42
43 Downloads fail...
43 Downloads fail...
44
44
45 $ hg clone http://localhost:$HGPORT httpclone
45 $ hg clone http://localhost:$HGPORT httpclone
46 (remote is using large file support (lfs); lfs will be enabled for this repository)
46 (remote is using large file support (lfs); lfs will be enabled for this repository)
47 requesting all changes
47 requesting all changes
48 adding changesets
48 adding changesets
49 adding manifests
49 adding manifests
50 adding file changes
50 adding file changes
51 added 1 changesets with 1 changes to 1 files
51 added 1 changesets with 1 changes to 1 files
52 new changesets 525251863cad
52 new changesets 525251863cad
53 updating to branch default
53 updating to branch default
54 abort: LFS HTTP error: HTTP Error 400: no such method: .git!
54 abort: LFS HTTP error: HTTP Error 400: no such method: .git!
55 (check that lfs serving is enabled on http://localhost:$HGPORT/.git/info/lfs and "download" is supported)
55 (check that lfs serving is enabled on http://localhost:$HGPORT/.git/info/lfs and "download" is supported)
56 [255]
56 [255]
57
57
58 $ "$PYTHON" $RUNTESTDIR/killdaemons.py $DAEMON_PIDS
58 $ "$PYTHON" $RUNTESTDIR/killdaemons.py $DAEMON_PIDS
59
59
60 $ cat $TESTTMP/access.log $TESTTMP/errors.log
60 $ cat $TESTTMP/access.log $TESTTMP/errors.log
61 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
61 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
62 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D525251863cad618e55d483555f3d00a2ca99597e x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
62 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D525251863cad618e55d483555f3d00a2ca99597e x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
63 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
63 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
64 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
64 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
65 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 400 - (glob)
65 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 400 - (glob)
66 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
66 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
67 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
67 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
68 $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Arev-branch-cache%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
68 $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Arev-branch-cache%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
69 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 400 - (glob)
69 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 400 - (glob)
70
70
71 $ rm -f $TESTTMP/access.log $TESTTMP/errors.log
71 $ rm -f $TESTTMP/access.log $TESTTMP/errors.log
72 $ hg --config "lfs.usercache=$TESTTMP/servercache" -R server serve -d \
72 $ hg --config "lfs.usercache=$TESTTMP/servercache" -R server serve -d \
73 > -p $HGPORT --pid-file=hg.pid --prefix=subdir/mount/point \
73 > -p $HGPORT --pid-file=hg.pid --prefix=subdir/mount/point \
74 > -A $TESTTMP/access.log -E $TESTTMP/errors.log
74 > -A $TESTTMP/access.log -E $TESTTMP/errors.log
75 $ cat hg.pid >> $DAEMON_PIDS
75 $ cat hg.pid >> $DAEMON_PIDS
76
76
77 Reasonable hint for a misconfigured blob server
77 Reasonable hint for a misconfigured blob server
78
78
79 $ hg -R httpclone update default --config lfs.url=http://localhost:$HGPORT/missing
79 $ hg -R httpclone update default --config lfs.url=http://localhost:$HGPORT/missing
80 abort: LFS HTTP error: HTTP Error 404: Not Found!
80 abort: LFS HTTP error: HTTP Error 404: Not Found!
81 (the "lfs.url" config may be used to override http://localhost:$HGPORT/missing)
81 (the "lfs.url" config may be used to override http://localhost:$HGPORT/missing)
82 [255]
82 [255]
83
83
84 $ hg -R httpclone update default --config lfs.url=http://localhost:$HGPORT2/missing
84 $ hg -R httpclone update default --config lfs.url=http://localhost:$HGPORT2/missing
85 abort: LFS error: *onnection *refused*! (glob) (?)
85 abort: LFS error: *onnection *refused*! (glob) (?)
86 abort: LFS error: $EADDRNOTAVAIL$! (glob) (?)
86 abort: LFS error: $EADDRNOTAVAIL$! (glob) (?)
87 (the "lfs.url" config may be used to override http://localhost:$HGPORT2/missing)
87 (the "lfs.url" config may be used to override http://localhost:$HGPORT2/missing)
88 [255]
88 [255]
89
89
90 Blob URIs are correct when --prefix is used
90 Blob URIs are correct when --prefix is used
91
91
92 $ hg clone --debug http://localhost:$HGPORT/subdir/mount/point cloned2
92 $ hg clone --debug http://localhost:$HGPORT/subdir/mount/point cloned2
93 using http://localhost:$HGPORT/subdir/mount/point
93 using http://localhost:$HGPORT/subdir/mount/point
94 sending capabilities command
94 sending capabilities command
95 (remote is using large file support (lfs); lfs will be enabled for this repository)
95 (remote is using large file support (lfs); lfs will be enabled for this repository)
96 query 1; heads
96 query 1; heads
97 sending batch command
97 sending batch command
98 requesting all changes
98 requesting all changes
99 sending getbundle command
99 sending getbundle command
100 bundle2-input-bundle: with-transaction
100 bundle2-input-bundle: with-transaction
101 bundle2-input-part: "changegroup" (params: 1 mandatory 1 advisory) supported
101 bundle2-input-part: "changegroup" (params: 1 mandatory 1 advisory) supported
102 adding changesets
102 adding changesets
103 add changeset 525251863cad
103 add changeset 525251863cad
104 adding manifests
104 adding manifests
105 adding file changes
105 adding file changes
106 adding lfs.bin revisions
106 adding lfs.bin revisions
107 added 1 changesets with 1 changes to 1 files
107 added 1 changesets with 1 changes to 1 files
108 bundle2-input-part: total payload size 648
108 bundle2-input-part: total payload size 648
109 bundle2-input-part: "listkeys" (params: 1 mandatory) supported
109 bundle2-input-part: "listkeys" (params: 1 mandatory) supported
110 bundle2-input-part: "phase-heads" supported
110 bundle2-input-part: "phase-heads" supported
111 bundle2-input-part: total payload size 24
111 bundle2-input-part: total payload size 24
112 bundle2-input-part: "cache:rev-branch-cache" (advisory) supported
112 bundle2-input-part: "cache:rev-branch-cache" (advisory) supported
113 bundle2-input-part: total payload size 39
113 bundle2-input-part: total payload size 39
114 bundle2-input-bundle: 3 parts total
114 bundle2-input-bundle: 3 parts total
115 checking for updated bookmarks
115 checking for updated bookmarks
116 updating the branch cache
116 updating the branch cache
117 new changesets 525251863cad
117 new changesets 525251863cad
118 updating to branch default
118 updating to branch default
119 resolving manifests
119 resolving manifests
120 branchmerge: False, force: False, partial: False
120 branchmerge: False, force: False, partial: False
121 ancestor: 000000000000, local: 000000000000+, remote: 525251863cad
121 ancestor: 000000000000, local: 000000000000+, remote: 525251863cad
122 lfs: assuming remote store: http://localhost:$HGPORT/subdir/mount/point/.git/info/lfs
122 lfs: assuming remote store: http://localhost:$HGPORT/subdir/mount/point/.git/info/lfs
123 Status: 200
123 Status: 200
124 Content-Length: 371
124 Content-Length: 371
125 Content-Type: application/vnd.git-lfs+json
125 Content-Type: application/vnd.git-lfs+json
126 Date: $HTTP_DATE$
126 Date: $HTTP_DATE$
127 Server: testing stub value
127 Server: testing stub value
128 {
128 {
129 "objects": [
129 "objects": [
130 {
130 {
131 "actions": {
131 "actions": {
132 "download": {
132 "download": {
133 "expires_at": "$ISO_8601_DATE_TIME$"
133 "expires_at": "$ISO_8601_DATE_TIME$"
134 "header": {
134 "header": {
135 "Accept": "application/vnd.git-lfs"
135 "Accept": "application/vnd.git-lfs"
136 }
136 }
137 "href": "http://localhost:$HGPORT/subdir/mount/point/.hg/lfs/objects/f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e"
137 "href": "http://localhost:$HGPORT/subdir/mount/point/.hg/lfs/objects/f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e"
138 }
138 }
139 }
139 }
140 "oid": "f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e"
140 "oid": "f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e"
141 "size": 20
141 "size": 20
142 }
142 }
143 ]
143 ]
144 "transfer": "basic"
144 "transfer": "basic"
145 }
145 }
146 lfs: downloading f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e (20 bytes)
146 lfs: downloading f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e (20 bytes)
147 Status: 200
147 Status: 200
148 Content-Length: 20
148 Content-Length: 20
149 Content-Type: application/octet-stream
149 Content-Type: application/octet-stream
150 Date: $HTTP_DATE$
150 Date: $HTTP_DATE$
151 Server: testing stub value
151 Server: testing stub value
152 lfs: adding f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e to the usercache
152 lfs: adding f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e to the usercache
153 lfs: processed: f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e
153 lfs: processed: f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e
154 lfs: downloaded 1 files (20 bytes)
154 lfs: downloaded 1 files (20 bytes)
155 lfs.bin: remote created -> g
155 lfs.bin: remote created -> g
156 getting lfs.bin
156 getting lfs.bin
157 lfs: found f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e in the local lfs store
157 lfs: found f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e in the local lfs store
158 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
158 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
159 (sent 3 HTTP requests and * bytes; received * bytes in responses) (glob)
159 (sent 3 HTTP requests and * bytes; received * bytes in responses) (glob)
160
160
161 $ "$PYTHON" $RUNTESTDIR/killdaemons.py $DAEMON_PIDS
161 $ "$PYTHON" $RUNTESTDIR/killdaemons.py $DAEMON_PIDS
162
162
163 $ cat $TESTTMP/access.log $TESTTMP/errors.log
163 $ cat $TESTTMP/access.log $TESTTMP/errors.log
164 $LOCALIP - - [$LOGDATE$] "POST /missing/objects/batch HTTP/1.1" 404 - (glob)
164 $LOCALIP - - [$LOGDATE$] "POST /missing/objects/batch HTTP/1.1" 404 - (glob)
165 $LOCALIP - - [$LOGDATE$] "GET /subdir/mount/point?cmd=capabilities HTTP/1.1" 200 - (glob)
165 $LOCALIP - - [$LOGDATE$] "GET /subdir/mount/point?cmd=capabilities HTTP/1.1" 200 - (glob)
166 $LOCALIP - - [$LOGDATE$] "GET /subdir/mount/point?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
166 $LOCALIP - - [$LOGDATE$] "GET /subdir/mount/point?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
167 $LOCALIP - - [$LOGDATE$] "GET /subdir/mount/point?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Arev-branch-cache%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
167 $LOCALIP - - [$LOGDATE$] "GET /subdir/mount/point?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Arev-branch-cache%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
168 $LOCALIP - - [$LOGDATE$] "POST /subdir/mount/point/.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
168 $LOCALIP - - [$LOGDATE$] "POST /subdir/mount/point/.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
169 $LOCALIP - - [$LOGDATE$] "GET /subdir/mount/point/.hg/lfs/objects/f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e HTTP/1.1" 200 - (glob)
169 $LOCALIP - - [$LOGDATE$] "GET /subdir/mount/point/.hg/lfs/objects/f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e HTTP/1.1" 200 - (glob)
170
170
171 Blobs that already exist in the usercache are linked into the repo store, even
171 Blobs that already exist in the usercache are linked into the repo store, even
172 though the client doesn't send the blob.
172 though the client doesn't send the blob.
173
173
174 $ hg init server2
174 $ hg init server2
175 $ hg --config "lfs.usercache=$TESTTMP/servercache" -R server2 serve -d \
175 $ hg --config "lfs.usercache=$TESTTMP/servercache" -R server2 serve -d \
176 > -p $HGPORT --pid-file=hg.pid \
176 > -p $HGPORT --pid-file=hg.pid \
177 > -A $TESTTMP/access.log -E $TESTTMP/errors.log
177 > -A $TESTTMP/access.log -E $TESTTMP/errors.log
178 $ cat hg.pid >> $DAEMON_PIDS
178 $ cat hg.pid >> $DAEMON_PIDS
179
179
180 $ hg --config "lfs.usercache=$TESTTMP/servercache" -R cloned2 --debug \
180 $ hg --config "lfs.usercache=$TESTTMP/servercache" -R cloned2 --debug \
181 > push http://localhost:$HGPORT | grep '^[{} ]'
181 > push http://localhost:$HGPORT | grep '^[{} ]'
182 {
182 {
183 "objects": [
183 "objects": [
184 {
184 {
185 "oid": "f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e"
185 "oid": "f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e"
186 "size": 20
186 "size": 20
187 }
187 }
188 ]
188 ]
189 "transfer": "basic"
189 "transfer": "basic"
190 }
190 }
191 $ find server2/.hg/store/lfs/objects | sort
191 $ find server2/.hg/store/lfs/objects | sort
192 server2/.hg/store/lfs/objects
192 server2/.hg/store/lfs/objects
193 server2/.hg/store/lfs/objects/f0
193 server2/.hg/store/lfs/objects/f0
194 server2/.hg/store/lfs/objects/f0/3217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e
194 server2/.hg/store/lfs/objects/f0/3217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e
195 $ "$PYTHON" $RUNTESTDIR/killdaemons.py $DAEMON_PIDS
195 $ "$PYTHON" $RUNTESTDIR/killdaemons.py $DAEMON_PIDS
196 $ cat $TESTTMP/errors.log
196 $ cat $TESTTMP/errors.log
197
197
198 $ cat >> $TESTTMP/lfsstoreerror.py <<EOF
198 $ cat >> $TESTTMP/lfsstoreerror.py <<EOF
199 > import errno
199 > import errno
200 > from hgext.lfs import blobstore
200 > from hgext.lfs import blobstore
201 >
201 >
202 > _numverifies = 0
202 > _numverifies = 0
203 > _readerr = True
203 > _readerr = True
204 >
204 >
205 > def reposetup(ui, repo):
205 > def reposetup(ui, repo):
206 > # Nothing to do with a remote repo
206 > # Nothing to do with a remote repo
207 > if not repo.local():
207 > if not repo.local():
208 > return
208 > return
209 >
209 >
210 > store = repo.svfs.lfslocalblobstore
210 > store = repo.svfs.lfslocalblobstore
211 > class badstore(store.__class__):
211 > class badstore(store.__class__):
212 > def download(self, oid, src):
212 > def download(self, oid, src):
213 > '''Called in the server to handle reading from the client in a
213 > '''Called in the server to handle reading from the client in a
214 > PUT request.'''
214 > PUT request.'''
215 > origread = src.read
215 > origread = src.read
216 > def _badread(nbytes):
216 > def _badread(nbytes):
217 > # Simulate bad data/checksum failure from the client
217 > # Simulate bad data/checksum failure from the client
218 > return b'0' * len(origread(nbytes))
218 > return b'0' * len(origread(nbytes))
219 > src.read = _badread
219 > src.read = _badread
220 > super(badstore, self).download(oid, src)
220 > super(badstore, self).download(oid, src)
221 >
221 >
222 > def _read(self, vfs, oid, verify):
222 > def _read(self, vfs, oid, verify):
223 > '''Called in the server to read data for a GET request, and then
223 > '''Called in the server to read data for a GET request, and then
224 > calls self._verify() on it before returning.'''
224 > calls self._verify() on it before returning.'''
225 > global _readerr
225 > global _readerr
226 > # One time simulation of a read error
226 > # One time simulation of a read error
227 > if _readerr:
227 > if _readerr:
228 > _readerr = False
228 > _readerr = False
229 > raise IOError(errno.EIO, '%s: I/O error' % oid)
229 > raise IOError(errno.EIO, '%s: I/O error' % oid)
230 > # Simulate corrupt content on client download
230 > # Simulate corrupt content on client download
231 > blobstore._verify(oid, 'dummy content')
231 > blobstore._verify(oid, 'dummy content')
232 >
232 >
233 > def verify(self, oid):
233 > def verify(self, oid):
234 > '''Called in the server to populate the Batch API response,
234 > '''Called in the server to populate the Batch API response,
235 > letting the client re-upload if the file is corrupt.'''
235 > letting the client re-upload if the file is corrupt.'''
236 > # Fail verify in Batch API for one clone command and one push
236 > # Fail verify in Batch API for one clone command and one push
237 > # command with an IOError. Then let it through to access other
237 > # command with an IOError. Then let it through to access other
238 > # functions. Checksum failure is tested elsewhere.
238 > # functions. Checksum failure is tested elsewhere.
239 > global _numverifies
239 > global _numverifies
240 > _numverifies += 1
240 > _numverifies += 1
241 > if _numverifies <= 2:
241 > if _numverifies <= 2:
242 > raise IOError(errno.EIO, '%s: I/O error' % oid)
242 > raise IOError(errno.EIO, '%s: I/O error' % oid)
243 > return super(badstore, self).verify(oid)
243 > return super(badstore, self).verify(oid)
244 >
244 >
245 > store.__class__ = badstore
245 > store.__class__ = badstore
246 > EOF
246 > EOF
247
247
248 $ rm -rf `hg config lfs.usercache`
248 $ rm -rf `hg config lfs.usercache`
249 $ rm -f $TESTTMP/access.log $TESTTMP/errors.log
249 $ rm -f $TESTTMP/access.log $TESTTMP/errors.log
250 $ hg --config "lfs.usercache=$TESTTMP/servercache" \
250 $ hg --config "lfs.usercache=$TESTTMP/servercache" \
251 > --config extensions.lfsstoreerror=$TESTTMP/lfsstoreerror.py \
251 > --config extensions.lfsstoreerror=$TESTTMP/lfsstoreerror.py \
252 > -R server serve -d \
252 > -R server serve -d \
253 > -p $HGPORT1 --pid-file=hg.pid -A $TESTTMP/access.log -E $TESTTMP/errors.log
253 > -p $HGPORT1 --pid-file=hg.pid -A $TESTTMP/access.log -E $TESTTMP/errors.log
254 $ cat hg.pid >> $DAEMON_PIDS
254 $ cat hg.pid >> $DAEMON_PIDS
255
255
256 Test an I/O error in localstore.verify() (Batch API) with GET
256 Test an I/O error in localstore.verify() (Batch API) with GET
257
257
258 $ hg clone http://localhost:$HGPORT1 httpclone2
258 $ hg clone http://localhost:$HGPORT1 httpclone2
259 (remote is using large file support (lfs); lfs will be enabled for this repository)
259 (remote is using large file support (lfs); lfs will be enabled for this repository)
260 requesting all changes
260 requesting all changes
261 adding changesets
261 adding changesets
262 adding manifests
262 adding manifests
263 adding file changes
263 adding file changes
264 added 1 changesets with 1 changes to 1 files
264 added 1 changesets with 1 changes to 1 files
265 new changesets 525251863cad
265 new changesets 525251863cad
266 updating to branch default
266 updating to branch default
267 abort: LFS server error for "lfs.bin": Internal server error!
267 abort: LFS server error for "lfs.bin": Internal server error!
268 [255]
268 [255]
269
269
270 Test an I/O error in localstore.verify() (Batch API) with PUT
270 Test an I/O error in localstore.verify() (Batch API) with PUT
271
271
272 $ echo foo > client/lfs.bin
272 $ echo foo > client/lfs.bin
273 $ hg -R client ci -m 'mod lfs'
273 $ hg -R client ci -m 'mod lfs'
274 $ hg -R client push http://localhost:$HGPORT1
274 $ hg -R client push http://localhost:$HGPORT1
275 pushing to http://localhost:$HGPORT1/
275 pushing to http://localhost:$HGPORT1/
276 searching for changes
276 searching for changes
277 abort: LFS server error for "unknown": Internal server error!
277 abort: LFS server error for "unknown": Internal server error!
278 [255]
278 [255]
279 TODO: figure out how to associate the file name in the error above
279 TODO: figure out how to associate the file name in the error above
280
280
281 Test a bad checksum sent by the client in the transfer API
281 Test a bad checksum sent by the client in the transfer API
282
282
283 $ hg -R client push http://localhost:$HGPORT1
283 $ hg -R client push http://localhost:$HGPORT1
284 pushing to http://localhost:$HGPORT1/
284 pushing to http://localhost:$HGPORT1/
285 searching for changes
285 searching for changes
286 abort: LFS HTTP error: HTTP Error 422: corrupt blob (oid=b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c, action=upload)!
286 abort: LFS HTTP error: HTTP Error 422: corrupt blob (oid=b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c, action=upload)!
287 [255]
287 [255]
288
288
289 $ echo 'test lfs file' > server/lfs3.bin
289 $ echo 'test lfs file' > server/lfs3.bin
290 $ hg --config experimental.lfs.disableusercache=True \
290 $ hg --config experimental.lfs.disableusercache=True \
291 > -R server ci -Aqm 'another lfs file'
291 > -R server ci -Aqm 'another lfs file'
292 $ hg -R client pull -q http://localhost:$HGPORT1
292 $ hg -R client pull -q http://localhost:$HGPORT1
293
293
294 Test an I/O error during the processing of the GET request
294 Test an I/O error during the processing of the GET request
295
295
296 $ hg --config lfs.url=http://localhost:$HGPORT1/.git/info/lfs \
296 $ hg --config lfs.url=http://localhost:$HGPORT1/.git/info/lfs \
297 > -R client update -r tip
297 > -R client update -r tip
298 abort: LFS HTTP error: HTTP Error 500: Internal Server Error (oid=276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d, action=download)!
298 abort: LFS HTTP error: HTTP Error 500: Internal Server Error (oid=276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d, action=download)!
299 [255]
299 [255]
300
300
301 Test a checksum failure during the processing of the GET request
301 Test a checksum failure during the processing of the GET request
302
302
303 $ hg --config lfs.url=http://localhost:$HGPORT1/.git/info/lfs \
303 $ hg --config lfs.url=http://localhost:$HGPORT1/.git/info/lfs \
304 > -R client update -r tip
304 > -R client update -r tip
305 abort: LFS HTTP error: HTTP Error 422: corrupt blob (oid=276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d, action=download)!
305 abort: LFS HTTP error: HTTP Error 422: corrupt blob (oid=276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d, action=download)!
306 [255]
306 [255]
307
307
308 $ "$PYTHON" $RUNTESTDIR/killdaemons.py $DAEMON_PIDS
308 $ "$PYTHON" $RUNTESTDIR/killdaemons.py $DAEMON_PIDS
309
309
310 $ cat $TESTTMP/access.log
310 $ cat $TESTTMP/access.log
311 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
311 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
312 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
312 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
313 $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Arev-branch-cache%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
313 $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Arev-branch-cache%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
314 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
314 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
315 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
315 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
316 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D392c05922088bacf8e68a6939b480017afbf245d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
316 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D392c05922088bacf8e68a6939b480017afbf245d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
317 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
317 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
318 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
318 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
319 $LOCALIP - - [$LOGDATE$] "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
319 $LOCALIP - - [$LOGDATE$] "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
320 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
320 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
321 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
321 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
322 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
322 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
323 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D392c05922088bacf8e68a6939b480017afbf245d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
323 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D392c05922088bacf8e68a6939b480017afbf245d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
324 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
324 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
325 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
325 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
326 $LOCALIP - - [$LOGDATE$] "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
326 $LOCALIP - - [$LOGDATE$] "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
327 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
327 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
328 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
328 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
329 $LOCALIP - - [$LOGDATE$] "PUT /.hg/lfs/objects/b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c HTTP/1.1" 422 - (glob)
329 $LOCALIP - - [$LOGDATE$] "PUT /.hg/lfs/objects/b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c HTTP/1.1" 422 - (glob)
330 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
330 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
331 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D392c05922088bacf8e68a6939b480017afbf245d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
331 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D392c05922088bacf8e68a6939b480017afbf245d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
332 $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Arev-branch-cache%250Astream%253Dv2&cg=1&common=525251863cad618e55d483555f3d00a2ca99597e&heads=506bf3d83f78c54b89e81c6411adee19fdf02156+525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
332 $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Arev-branch-cache%250Astream%253Dv2&cg=1&common=525251863cad618e55d483555f3d00a2ca99597e&heads=506bf3d83f78c54b89e81c6411adee19fdf02156+525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
333 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
333 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
334 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d HTTP/1.1" 500 - (glob)
334 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d HTTP/1.1" 500 - (glob)
335 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
335 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
336 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d HTTP/1.1" 422 - (glob)
336 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d HTTP/1.1" 422 - (glob)
337
337
338 $ grep -v ' File "' $TESTTMP/errors.log
338 $ grep -v ' File "' $TESTTMP/errors.log
339 $LOCALIP - - [$ERRDATE$] HG error: Exception happened while processing request '/.git/info/lfs/objects/batch': (glob)
339 $LOCALIP - - [$ERRDATE$] HG error: Exception happened while processing request '/.git/info/lfs/objects/batch': (glob)
340 $LOCALIP - - [$ERRDATE$] HG error: Traceback (most recent call last): (glob)
340 $LOCALIP - - [$ERRDATE$] HG error: Traceback (most recent call last): (glob)
341 $LOCALIP - - [$ERRDATE$] HG error: verifies = store.verify(oid) (glob)
341 $LOCALIP - - [$ERRDATE$] HG error: verifies = store.verify(oid) (glob)
342 $LOCALIP - - [$ERRDATE$] HG error: raise IOError(errno.EIO, '%s: I/O error' % oid) (glob)
342 $LOCALIP - - [$ERRDATE$] HG error: raise IOError(errno.EIO, '%s: I/O error' % oid) (glob)
343 $LOCALIP - - [$ERRDATE$] HG error: IOError: [Errno 5] f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e: I/O error (glob)
343 $LOCALIP - - [$ERRDATE$] HG error: IOError: [Errno 5] f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e: I/O error (glob)
344 $LOCALIP - - [$ERRDATE$] HG error: (glob)
344 $LOCALIP - - [$ERRDATE$] HG error: (glob)
345 $LOCALIP - - [$ERRDATE$] HG error: Exception happened while processing request '/.git/info/lfs/objects/batch': (glob)
345 $LOCALIP - - [$ERRDATE$] HG error: Exception happened while processing request '/.git/info/lfs/objects/batch': (glob)
346 $LOCALIP - - [$ERRDATE$] HG error: Traceback (most recent call last): (glob)
346 $LOCALIP - - [$ERRDATE$] HG error: Traceback (most recent call last): (glob)
347 $LOCALIP - - [$ERRDATE$] HG error: verifies = store.verify(oid) (glob)
347 $LOCALIP - - [$ERRDATE$] HG error: verifies = store.verify(oid) (glob)
348 $LOCALIP - - [$ERRDATE$] HG error: raise IOError(errno.EIO, '%s: I/O error' % oid) (glob)
348 $LOCALIP - - [$ERRDATE$] HG error: raise IOError(errno.EIO, '%s: I/O error' % oid) (glob)
349 $LOCALIP - - [$ERRDATE$] HG error: IOError: [Errno 5] b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c: I/O error (glob)
349 $LOCALIP - - [$ERRDATE$] HG error: IOError: [Errno 5] b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c: I/O error (glob)
350 $LOCALIP - - [$ERRDATE$] HG error: (glob)
350 $LOCALIP - - [$ERRDATE$] HG error: (glob)
351 $LOCALIP - - [$ERRDATE$] HG error: Exception happened while processing request '/.hg/lfs/objects/b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c': (glob)
351 $LOCALIP - - [$ERRDATE$] HG error: Exception happened while processing request '/.hg/lfs/objects/b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c': (glob)
352 $LOCALIP - - [$ERRDATE$] HG error: Traceback (most recent call last): (glob)
352 $LOCALIP - - [$ERRDATE$] HG error: Traceback (most recent call last): (glob)
353 $LOCALIP - - [$ERRDATE$] HG error: localstore.download(oid, req.bodyfh) (glob)
353 $LOCALIP - - [$ERRDATE$] HG error: localstore.download(oid, req.bodyfh) (glob)
354 $LOCALIP - - [$ERRDATE$] HG error: super(badstore, self).download(oid, src) (glob)
354 $LOCALIP - - [$ERRDATE$] HG error: super(badstore, self).download(oid, src) (glob)
355 $LOCALIP - - [$ERRDATE$] HG error: % oid) (glob)
355 $LOCALIP - - [$ERRDATE$] HG error: % oid) (glob)
356 $LOCALIP - - [$ERRDATE$] HG error: LfsCorruptionError: corrupt remote lfs object: b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c (glob)
356 $LOCALIP - - [$ERRDATE$] HG error: LfsCorruptionError: corrupt remote lfs object: b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c (glob)
357 $LOCALIP - - [$ERRDATE$] HG error: (glob)
357 $LOCALIP - - [$ERRDATE$] HG error: (glob)
358 $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d': (glob)
358 $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d': (glob)
359 Traceback (most recent call last):
359 Traceback (most recent call last):
360 self.do_write()
360 self.do_write()
361 self.do_hgweb()
361 self.do_hgweb()
362 for chunk in self.server.application(env, self._start_response):
362 for chunk in self.server.application(env, self._start_response):
363 for r in self._runwsgi(req, res, repo):
363 for r in self._runwsgi(req, res, repo):
364 rctx, req, res, self.check_perm)
364 rctx, req, res, self.check_perm)
365 return func(*(args + a), **kw)
365 return func(*(args + a), **kw)
366 lambda perm:
366 lambda perm:
367 res.setbodybytes(localstore.read(oid))
367 res.setbodybytes(localstore.read(oid))
368 blob = self._read(self.vfs, oid, verify)
368 blob = self._read(self.vfs, oid, verify)
369 raise IOError(errno.EIO, '%s: I/O error' % oid)
369 raise IOError(errno.EIO, '%s: I/O error' % oid)
370 IOError: [Errno 5] 276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d: I/O error
370 IOError: [Errno 5] 276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d: I/O error
371
371
372 $LOCALIP - - [$ERRDATE$] HG error: Exception happened while processing request '/.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d': (glob)
372 $LOCALIP - - [$ERRDATE$] HG error: Exception happened while processing request '/.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d': (glob)
373 $LOCALIP - - [$ERRDATE$] HG error: Traceback (most recent call last): (glob)
373 $LOCALIP - - [$ERRDATE$] HG error: Traceback (most recent call last): (glob)
374 $LOCALIP - - [$ERRDATE$] HG error: res.setbodybytes(localstore.read(oid)) (glob)
374 $LOCALIP - - [$ERRDATE$] HG error: res.setbodybytes(localstore.read(oid)) (glob)
375 $LOCALIP - - [$ERRDATE$] HG error: blob = self._read(self.vfs, oid, verify) (glob)
375 $LOCALIP - - [$ERRDATE$] HG error: blob = self._read(self.vfs, oid, verify) (glob)
376 $LOCALIP - - [$ERRDATE$] HG error: blobstore._verify(oid, 'dummy content') (glob)
376 $LOCALIP - - [$ERRDATE$] HG error: blobstore._verify(oid, 'dummy content') (glob)
377 $LOCALIP - - [$ERRDATE$] HG error: hint=_('run hg verify')) (glob)
377 $LOCALIP - - [$ERRDATE$] HG error: hint=_('run hg verify')) (glob)
378 $LOCALIP - - [$ERRDATE$] HG error: LfsCorruptionError: detected corrupt lfs object: 276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d (glob)
378 $LOCALIP - - [$ERRDATE$] HG error: LfsCorruptionError: detected corrupt lfs object: 276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d (glob)
379 $LOCALIP - - [$ERRDATE$] HG error: (glob)
379 $LOCALIP - - [$ERRDATE$] HG error: (glob)
380
380
381 Basic Authorization headers are returned by the Batch API, and sent back with
381 Basic Authorization headers are returned by the Batch API, and sent back with
382 the GET/PUT request.
382 the GET/PUT request.
383
383
384 $ rm -f $TESTTMP/access.log $TESTTMP/errors.log
384 $ rm -f $TESTTMP/access.log $TESTTMP/errors.log
385
385
386 $ cat >> $HGRCPATH << EOF
386 $ cat >> $HGRCPATH << EOF
387 > [experimental]
387 > [experimental]
388 > lfs.disableusercache = True
388 > lfs.disableusercache = True
389 > [auth]
389 > [auth]
390 > l.schemes=http
390 > l.schemes=http
391 > l.prefix=lo
391 > l.prefix=lo
392 > l.username=user
392 > l.username=user
393 > l.password=pass
393 > l.password=pass
394 > EOF
394 > EOF
395
395
396 $ cat << EOF > userpass.py
396 $ cat << EOF > userpass.py
397 > import base64
397 > import base64
398 > from mercurial.hgweb import common
398 > from mercurial.hgweb import common
399 > def perform_authentication(hgweb, req, op):
399 > def perform_authentication(hgweb, req, op):
400 > auth = req.headers.get(b'Authorization')
400 > auth = req.headers.get(b'Authorization')
401 > if not auth:
401 > if not auth:
402 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, b'who',
402 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, b'who',
403 > [(b'WWW-Authenticate', b'Basic Realm="mercurial"')])
403 > [(b'WWW-Authenticate', b'Basic Realm="mercurial"')])
404 > if base64.b64decode(auth.split()[1]).split(b':', 1) != [b'user',
404 > if base64.b64decode(auth.split()[1]).split(b':', 1) != [b'user',
405 > b'pass']:
405 > b'pass']:
406 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, b'no')
406 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, b'no')
407 > def extsetup():
407 > def extsetup(ui):
408 > common.permhooks.insert(0, perform_authentication)
408 > common.permhooks.insert(0, perform_authentication)
409 > EOF
409 > EOF
410
410
411 $ hg --config extensions.x=$TESTTMP/userpass.py \
411 $ hg --config extensions.x=$TESTTMP/userpass.py \
412 > -R server serve -d -p $HGPORT1 --pid-file=hg.pid \
412 > -R server serve -d -p $HGPORT1 --pid-file=hg.pid \
413 > -A $TESTTMP/access.log -E $TESTTMP/errors.log
413 > -A $TESTTMP/access.log -E $TESTTMP/errors.log
414 $ mv hg.pid $DAEMON_PIDS
414 $ mv hg.pid $DAEMON_PIDS
415
415
416 $ hg clone --debug http://localhost:$HGPORT1 auth_clone | egrep '^[{}]| '
416 $ hg clone --debug http://localhost:$HGPORT1 auth_clone | egrep '^[{}]| '
417 {
417 {
418 "objects": [
418 "objects": [
419 {
419 {
420 "actions": {
420 "actions": {
421 "download": {
421 "download": {
422 "expires_at": "$ISO_8601_DATE_TIME$"
422 "expires_at": "$ISO_8601_DATE_TIME$"
423 "header": {
423 "header": {
424 "Accept": "application/vnd.git-lfs"
424 "Accept": "application/vnd.git-lfs"
425 "Authorization": "Basic dXNlcjpwYXNz"
425 "Authorization": "Basic dXNlcjpwYXNz"
426 }
426 }
427 "href": "http://localhost:$HGPORT1/.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d"
427 "href": "http://localhost:$HGPORT1/.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d"
428 }
428 }
429 }
429 }
430 "oid": "276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d"
430 "oid": "276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d"
431 "size": 14
431 "size": 14
432 }
432 }
433 ]
433 ]
434 "transfer": "basic"
434 "transfer": "basic"
435 }
435 }
436
436
437 $ echo 'another blob' > auth_clone/lfs.blob
437 $ echo 'another blob' > auth_clone/lfs.blob
438 $ hg -R auth_clone ci -Aqm 'add blob'
438 $ hg -R auth_clone ci -Aqm 'add blob'
439 $ hg -R auth_clone --debug push | egrep '^[{}]| '
439 $ hg -R auth_clone --debug push | egrep '^[{}]| '
440 {
440 {
441 "objects": [
441 "objects": [
442 {
442 {
443 "actions": {
443 "actions": {
444 "upload": {
444 "upload": {
445 "expires_at": "$ISO_8601_DATE_TIME$"
445 "expires_at": "$ISO_8601_DATE_TIME$"
446 "header": {
446 "header": {
447 "Accept": "application/vnd.git-lfs"
447 "Accept": "application/vnd.git-lfs"
448 "Authorization": "Basic dXNlcjpwYXNz"
448 "Authorization": "Basic dXNlcjpwYXNz"
449 }
449 }
450 "href": "http://localhost:$HGPORT1/.hg/lfs/objects/df14287d8d75f076a6459e7a3703ca583ca9fb3f4918caed10c77ac8622d49b3"
450 "href": "http://localhost:$HGPORT1/.hg/lfs/objects/df14287d8d75f076a6459e7a3703ca583ca9fb3f4918caed10c77ac8622d49b3"
451 }
451 }
452 }
452 }
453 "oid": "df14287d8d75f076a6459e7a3703ca583ca9fb3f4918caed10c77ac8622d49b3"
453 "oid": "df14287d8d75f076a6459e7a3703ca583ca9fb3f4918caed10c77ac8622d49b3"
454 "size": 13
454 "size": 13
455 }
455 }
456 ]
456 ]
457 "transfer": "basic"
457 "transfer": "basic"
458 }
458 }
459
459
460 $ "$PYTHON" $RUNTESTDIR/killdaemons.py $DAEMON_PIDS
460 $ "$PYTHON" $RUNTESTDIR/killdaemons.py $DAEMON_PIDS
461
461
462 $ cat $TESTTMP/access.log $TESTTMP/errors.log
462 $ cat $TESTTMP/access.log $TESTTMP/errors.log
463 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 401 - (glob)
463 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 401 - (glob)
464 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
464 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
465 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
465 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
466 $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Arev-branch-cache%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=506bf3d83f78c54b89e81c6411adee19fdf02156+525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
466 $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Arev-branch-cache%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=506bf3d83f78c54b89e81c6411adee19fdf02156+525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
467 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 401 - (glob)
467 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 401 - (glob)
468 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
468 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
469 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d HTTP/1.1" 200 - (glob)
469 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d HTTP/1.1" 200 - (glob)
470 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 401 - (glob)
470 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 401 - (glob)
471 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
471 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
472 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D525251863cad618e55d483555f3d00a2ca99597e+4d9397055dc0c205f3132f331f36353ab1a525a3 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
472 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D525251863cad618e55d483555f3d00a2ca99597e+4d9397055dc0c205f3132f331f36353ab1a525a3 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
473 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
473 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
474 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
474 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
475 $LOCALIP - - [$LOGDATE$] "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
475 $LOCALIP - - [$LOGDATE$] "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
476 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
476 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
477 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 401 - (glob)
477 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 401 - (glob)
478 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
478 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
479 $LOCALIP - - [$LOGDATE$] "PUT /.hg/lfs/objects/df14287d8d75f076a6459e7a3703ca583ca9fb3f4918caed10c77ac8622d49b3 HTTP/1.1" 201 - (glob)
479 $LOCALIP - - [$LOGDATE$] "PUT /.hg/lfs/objects/df14287d8d75f076a6459e7a3703ca583ca9fb3f4918caed10c77ac8622d49b3 HTTP/1.1" 201 - (glob)
480 $LOCALIP - - [$LOGDATE$] "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
480 $LOCALIP - - [$LOGDATE$] "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
481 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
481 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
General Comments 0
You need to be logged in to leave comments. Login now